- 了解i2c device 创建过程 摘录位置
I2C设备的4种构建方法:
- i2c_register_board_info
- i2c_new_device 或i2c_new_probed_device
- 用户空间直接调用
- 查询所有adapter
1.静态注册设备( i2c_register_board_info)
相关结构体:
8 struct i2c_devinfo {
9 struct list_head list;
10 int busnum;
11 struct i2c_board_info board_info;
12 };
403 struct i2c_board_info {
404 char type[I2C_NAME_SIZE];
405 unsigned short flags;
406 unsigned short addr;
407 const char *dev_name;
408 void *platform_data;
409 struct device_node *of_node;
410 struct fwnode_handle *fwnode;
411 const struct property_entry *properties;
412 const struct resource *resources;
413 unsigned int num_resources;
414 int irq;
415 };
1).定义一个 i2c_board_info 结构体,有名字,和设备地址
static struct i2c_board_info my_i2c_dev_info = {
I2C_BOARD_INFO("my_i2c_dev", 0x2d),
};
2).注册设备
i2c_register_board_info(int busnum, struct i2c_board_info const * info, unsigned n)
* busnum:哪一条总线,也就是选择哪一个i2c控制器
* info:i2c设备信息
* n:info中有几个设备
函数i2c_register_board_info:静态声明i2c device
i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{
devinfo->busnum = busnum; /* 选择i2c总线 */
devinfo->board_info = *info; /* 绑定设备信息 */
list_add_tail(&devinfo->list, &__i2c_board_list); /* 将设备信息添加进链表中 */
}
将设备信息添加到链表__i2c_board_list之后,在哪里使用了 __i2c_board_list 这个链表?
i2c_scan_static_board_info(struct i2c_adapter *adapter)
list_for_each_entry(devinfo, &__i2c_board_list, list) /* 遍历i2c设备链表 */
i2c_new_device(adapter, &devinfo->board_info); /* 创建一个i2c_client设备 */
struct i2c_client *client;
哪里调用 i2c_scan_static_board_info?
注册i2c adapter的时候,调用i2c_add_adapter或者i2c_add_numbered_adapter,该函数调用i2c_register_adapter,最终调用i2c_new_device创建i2c device。
int i2c_register_adapter(struct i2c_adapter *adap)
{
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
}
具体调用关系如下:
int i2c_register_adapter(struct i2c_adapter *adap)
i2c_scan_static_board_info(adap)
list_for_each_entry(devinfo, &__i2c_board_list, list)
i2c_new_device //实例化i2c device
所以使用这个方法添加i2c设备,每次都要重新编译内核,无法动态注册i2c设备,不适合我们动态加载insmod。
2.i2c_new_device 或 i2c_new_probed_device
i2c_new_device: 认为设备肯定存在
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
* adap:指定i2c设备器,以后访问设备的时候,使用过哪一个设备器(i2c主机控制器)去访问
* info:指定i2c设备信息
i2c_new_probed_device:对于"已经识别出来的设备"(probed_device),才会创建(“new”)
struct i2c_client *i2c_new_probed_device(struct i2c_adapter *adap,
struct i2c_board_info *info,
unsigned short const *addr_list,
int (*probe)(struct i2c_adapter *, unsigned short addr))
* adap:设配器,设备在哪一条总线上
* info:设备的名字信息
* addr_list:设备的地址列表
* probe:会调用这个函数去判断设备是否真正存在,如果不提供此函数的话,有默认的函数
2.1.i2c_new_device 注册i2c设备代码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
static struct i2c_board_info my_i2c_dev_info = {
I2C_BOARD_INFO("my_i2c_dev", 0x50), /* 这个名字和地址很重要 */
};
static struct i2c_client *my_i2c_client;
static int i2c_dev_init(void)
{
struct i2c_adapter *i2c_adap;
i2c_adap = i2c_get_adapter(0); /* 得到i2c设备器,i2c设备在哪条总线上 */
my_i2c_client = i2c_new_device(i2c_adap, &my_i2c_dev_info);
i2c_put_adapter(i2c_adap);
return 0;
}
static void i2c_dev_exit(void)
{
i2c_unregister_device(my_i2c_client);
}
module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
MODULE_LICENSE("GPL");
2.2.i2c_new_probe_device注册i2c设备代码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
static struct i2c_client *my_i2c_client;
static const unsigned short addr_list[] = { 0x55, 0x50, I2C_CLIENT_END };
static int my_i2c_dev_init(void)
{
struct i2c_adapter *i2c_adap;
struct i2c_board_info my_i2c_dev_info;
memset(&my_i2c_dev_info, 0, sizeof(struct i2c_board_info));
strlcpy(my_i2c_dev_info.type, "my_i2c_dev", I2C_NAME_SIZE);
i2c_adap = i2c_get_adapter(0);
my_i2c_client = i2c_new_probed_device(i2c_adap, &my_i2c_dev_info, addr_list, NULL);
i2c_put_adapter(i2c_adap);
if (my_i2c_client )
return 0;
else
return -ENODEV;
}
static void my_i2c_dev_exit(void)
{
i2c_unregister_device(my_i2c_client );
}
module_init(my_i2c_dev_init);
module_exit(my_i2c_dev_exit);
MODULE_LICENSE("GPL");
3.从用户空间创建设备
创建设备:(调用i2c_new_device)
echo my_i2c_dev 0x55 > sys/devices/platform/s3c2440-i2c.0/i2c-0/new_device
删除设备:(调用i2c_unregister_device)
echo 0x55 > sys/devices/platform/s3c2440-i2c.0/i2c-0/delete_device
4.查询所有adapter
前面的3种方法都要事先确定适配器(I2C总线,I2C控制器);如果事先并不知道这个I2C设备在哪个适配器上,怎么办?去class表示的所有的适配器上查找有一些I2C设备的地址是一样,怎么继续分配它是哪一款?通过使用detect函数。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>
static int __devinit my_i2c_drv_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int __devexit my_i2c_drv_remove(struct i2c_client *client)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static const struct i2c_device_id my_dev_id_table[] = {
{ "my_i2c_dev", 0 },
{}
};
static int my_i2c_drv_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
/* 能运行到这里, 表示该addr的设备是存在的
* 但是有些设备单凭地址无法分辨(A芯片的地址是0x50, B芯片的地址也是0x50)
* 还需要进一步读写I2C设备来分辨是哪款芯片
* detect就是用来进一步分辨这个芯片是哪一款,并且设置info->type
*/
printk("my_i2c_drv_detect: addr = 0x%x\n", client->addr);
/* 进一步判断是哪一款 */
strlcpy(info->type, "my_i2c_dev", I2C_NAME_SIZE);
return 0;
}
static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };
/* 1. 分配/设置i2c_driver */
static struct i2c_driver my_i2c_driver = {
.class = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
.driver = {
.name = "my_i2c_dev",
.owner = THIS_MODULE,
},
.probe = my_i2c_drv_probe,
.remove = __devexit_p(my_i2c_drv_remove),
.id_table = my_dev_id_table,
.detect = my_i2c_drv_detect, /* 用这个函数来检测设备确实存在 */
.address_list = addr_list, /* 这些设备的地址 */
};
module_i2c_driver(my_i2c_drv_init);
MODULE_LICENSE("GPL");
上面的代码回去.class指定的这一类i2c设配器中,查找.address_list这些地址,如果设备存在,就会调用.detect函数,确认是哪一个i2c设备,然后调用i2c_new_device。
函数调用流程如下:
i2c_add_driver
i2c_register_driver
driver_register(&driver->driver); /* 会去dev链表中找到匹配的设备,调用probe函数 */
/* 对于每个设配器调用__process_new_driver */
i2c_for_each_dev(driver, __process_new_driver);
__process_new_driver
i2c_do_add_adapter
i2c_detect
address_list = driver->address_list; /* 得到要检测的所有地址 */
i2c_detect_address
i2c_check_addr_validity /* 检测地址是否存在 */
driver->detect(temp_client, &info); /* 如果地址存在,调用detect */
i2c_new_device /* 创建设备 */
device_add
i2c_device_probe
driver->probe
of_irq_get //获取irq,填充 client->irq = irq;
关于i2c slave device irq:
323 static int i2c_device_probe(struct device *dev)
324 {
325 struct i2c_client *client = i2c_verify_client(dev);
326 struct i2c_driver *driver;
327
334 if (!client->irq && !driver->disable_i2c_core_irq_mapping) {
335 int irq = -ENOENT;
336
337 if (client->flags & I2C_CLIENT_HOST_NOTIFY) {
338 dev_dbg(dev, "Using Host Notify IRQ\n");
339 irq = i2c_smbus_host_notify_to_irq(client);
340 } else if (dev->of_node) {
341 irq = of_irq_get_byname(dev->of_node, "irq");
342 if (irq == -EINVAL || irq == -ENODATA) {
343 irq = of_irq_get(dev->of_node, 0);
345 }
346 } else if (ACPI_COMPANION(dev)) {
347 irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
348 }
如上所示,dtsi定义s5m8767-pmic中断号,调用i2c_device_probe中的of_irq_get获取该中断号,添加到client->irq = irq;
arch/arm/boot/dts/exynos4412-itop-elite.dts:
227 &i2c_1 {
228 ...
236 s5m8767: s5m8767-pmic@66 {
237 compatible = "samsung,s5m8767-pmic";
238 reg = <0x66>;
239
240 interrupt-parent = <&gpx1>;
241 interrupts = <7 IRQ_TYPE_NONE>;
242
获取该中断号:
drivers/mfd/sec-irq.c:
sec_pmic_probe
->sec_pmic->irq = i2c->irq;
参考:
- Documentation/i2c/instantiating-devices