本文的分析对象为NXP的IMX8MM的代码,内核版本为4.14.98
I2C实际上有两部分驱动并且是分层的。
方式概述:实际上有两个步骤:
设备树描述的是设备信息,不是驱动的信息。设备树文件会自动被内核展开,生成对应的设备信息,并且插入到对应的链表中。注意:在i2c设备(client)树描述中,会有和adapter设备树描述进行绑定(关联)的信息(number),(int nr;和int busnum;)。通过这个信息client和adapter进行绑定,最后会形成client->adapter这样的结构。
PS:i2c从设备(client)成功注册后会主动查找合适的adapter。adapter成功注册后,会主动遍历i2c从设备链表,找到可以用这个adatper的设备。
i2c_driver 可以驱动多个client,每当有一个client的匹配信息和这个driver匹配信息一致时,driver的的probe函数都会被调用一次。并将client的相关信息传递到probe()函数参数中。
adapter对应的的信息,实际上就是i2c的控制器。它是可以通过内存映射从而cpu能访问的设备。
531 i2c3: i2c@30a40000 {
532 #address-cells = <1>;
533 #size-cells = <0>;
534 compatible = "fsl,imx8mm-i2c", "fsl,imx21-i2c";
535 reg = <0x0 0x30a40000 0x0 0x10000>;
536 interrupts = ;
537 clocks = <&clk IMX8MM_CLK_I2C3_ROOT>;
538 status = "disabled";
539 };
i2c从设备(client)对应的信息,它不属于可内存映射的设备,cpu也不能访问它。
745 &i2c3 {
746 clock-frequency = <400000>;
747 pinctrl-names = "default";
748 pinctrl-0 = <&pinctrl_i2c3>;
749 status = "okay";
750 /*
751 pca6416: gpio@20 {
752 compatible = "ti,tca6416"; //主要是这个匹配信息
753 reg = <0x20>; //主要是这个i2c地址信息
754 gpio-controller;
755 #gpio-cells = <2>;
756 };
757 */
758
759 };
PS:代码是米尔科技imx8mm源码,algos目录里面有imx对应的i2c发送算法。
/drivers/i2c
tree -L 1
├── algos
├── busses
├── i2c-boardinfo.c
├── i2c-core-acpi.c
├── i2c-core-base.c //i2c核心基本函数
├── i2c-core.h
├── i2c-core-of.c //i2c设备数据相关的操作函数
├── i2c-core-slave.c
├── i2c-core-smbus.c
├── i2c-dev.c //创建了适配器的节点,为用户空间访问i2c适配器的方法。可用于调试
├── i2c-mux.c
├── i2c-slave-eeprom.c
├── i2c-smbus.c
├── i2c-stub.c
├── Kconfig
├── Makefile
└── muxes
431 struct i2c_algorithm {
...
438 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
439 int num);
440 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
441 unsigned short flags, char read_write,
442 u8 command, int size, union i2c_smbus_data *data);
443
444 /* To determine what the adapter supports */
445 u32 (*functionality) (struct i2c_adapter *);
446
447 #if IS_ENABLED(CONFIG_I2C_SLAVE)
448 int (*reg_slave)(struct i2c_client *client);
449 int (*unreg_slave)(struct i2c_client *client);
450 #endif
451 };
570 struct i2c_adapter {
571 struct module *owner;
572 unsigned int class; /* classes to allow probing for */
573 const struct i2c_algorithm *algo; /* the algorithm to access the bus */
574 void *algo_data;
575
576 /* data fields that are valid for all devices */
577 const struct i2c_lock_operations *lock_ops;
578 struct rt_mutex bus_lock;
579 struct rt_mutex mux_lock;
580
581 int timeout; /* in jiffies */
582 int retries;
583 struct device dev; /* the adapter device */
584
585 int nr;
586 char name[48];
587 struct completion dev_released;
588
589 struct mutex userspace_clients_lock;
590 struct list_head userspace_clients;
591
592 struct i2c_bus_recovery_info *bus_recovery_info;
593 const struct i2c_adapter_quirks *quirks;
594
595 struct irq_domain *host_notify_domain;
596 };
597 #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
175 struct i2c_driver {
176 unsigned int class;
177
181 int (*attach_adapter)(struct i2c_adapter *) __deprecated;
182
183 /* Standard driver model interfaces */
184 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
185 int (*remove)(struct i2c_client *);
190 int (*probe_new)(struct i2c_client *);
191
192 /* driver model interfaces that don't relate to enumeration */
193 void (*shutdown)(struct i2c_client *);
202 void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
203 unsigned int data);
208 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
209
210 struct device_driver driver;
211 const struct i2c_device_id *id_table;
212
213 /* Device detection callback for automatic device creation */
214 int (*detect)(struct i2c_client *, struct i2c_board_info *);
215 const unsigned short *address_list;
216 struct list_head clients;
217
218 bool disable_i2c_core_irq_mapping;
219 };
220 #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
241 struct i2c_client {
242 unsigned short flags; /* div., see below */
243 unsigned short addr; /* chip address - NOTE: 7bit */
244 /* addresses are stored in the */
245 /* _LOWER_ 7 bits */
246 char name[I2C_NAME_SIZE];
247 struct i2c_adapter *adapter; /* the adapter we sit on */
248 struct device dev; /* the device structure */
249 int irq; /* irq issued by device */
250 struct list_head detected;
251 #if IS_ENABLED(CONFIG_I2C_SLAVE)
252 i2c_slave_cb_t slave_cb; /* callback for slave mode */
253 #endif
254 };
327 struct i2c_board_info {
328 char type[I2C_NAME_SIZE];
329 unsigned short flags;
330 unsigned short addr;
331 void *platform_data;
332 struct dev_archdata *archdata;
333 struct device_node *of_node;
334 struct fwnode_handle *fwnode;
335 const struct property_entry *properties;
336 const struct resource *resources;
337 unsigned int num_resources;
338 int irq;
339 };
17 struct i2c_devinfo {
18 struct list_head list;
19 int busnum;//这个很重要,根据这个获取对应的adapter
20 struct i2c_board_info board_info;
21 };
说明:busnum
1099 static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
1100 {
1101 struct i2c_devinfo *devinfo;
1102
1103 down_read(&__i2c_board_lock);
1104 list_for_each_entry(devinfo, &__i2c_board_list, list) {
1105 if (devinfo->busnum == adapter->nr //设备和adapter匹配
1106 && !i2c_new_device(adapter,
1107 &devinfo->board_info))
1108 dev_err(&adapter->dev,
1109 "Can't create device at 0x%02x\n",
1110 devinfo->board_info.addr);
1111 }
1112 up_read(&__i2c_board_lock);
1113 }
理解i2c驱动,理解上面的结构体以及它们的关系非常重要。然而它们又是错综复杂的。i2c驱动复杂的原因是什么呢?主要还是因为i2c要驱动各种繁杂的外围设备。采用上面的方式可以灵活的配置来满足不同的需求。
术语解释:
adapter 适配器对应的是SOC外围的外设,比如说i2c0/i2c1等部分,因为它们的功能有可能不同,所以要适配叫适配器。
algorithm 算法,adapter会驱动各种外围的i2c设备,它们各自的要求不一样,所以要用不同的算法来适配。
client 客户端或者从端设备,是指SOC外接的i2c设备。比如说AT24XX的EEPROM、使用i2c配置的摄像头等。
driver 驱动则是包含上面的所有的集成者。
我们的疑问是i2c_algorhtm是怎么来配置的?
主要是通过里面的i2c时序来实现的,也和相应的soc相关。
i2c_client会作为i2c_driver的参数来使用,而i2c_client中包含有i2c_adapter,最后i2c_adapter会调用i2c_algorithm来实现i2c的数据传输。
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(c->adapter,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
上面这条语句很重要,直接判断client中的adapter是否支持需要的功能。
另外需要注意的是不同的soc会在驱动的Makefile文件中选用对应的板级驱动,比如imx8mm会在buses中编译i2c_imx.c文件。
疑问:在probe函数中的c->adapter是怎么来的?
参考前面驱动构架
--drivers/i2c/busses/i2c-imx.c
言归正传:i2c的adatper总线驱动(platform驱动)在匹配后会调用i2c_imx_probe()函数,会依次调用i2c_add_numbered_adapter
-> __i2c_add_numbered_adapter
-> i2c_register_adapter
-> of_i2c_register_devices
-> of_i2c_register_device
-> i2c_new_device()这个函数中实际分配内存创建了client,并将client和dapter进行绑定。
{
client = kzalloc(sizeof *client, GFP_KERNEL);
client->adapter = adap;
}
其中在void of_i2c_register_devices(struct i2c_adapter *adap)中
100 for_each_available_child_of_node(bus, node) {
101 if (of_node_test_and_set_flag(node, OF_POPULATED))
102 continue;
103
104 client = of_i2c_register_device(adap, node);
105 if (IS_ERR(client)) {
106 dev_warn(&adap->dev,
107 "Failed to create I2C device for %pOF\n",
108 node);
109 of_node_clear_flag(node, OF_POPULATED);
110 }
111 }
会遍历所有的i2c总线信息的子节点,找到所有的i2c子节点,最终调用i2c_new_device()函数来创建i2c的设备,创建的所有i2c_client的adapter都指向当前的adapter(client->adapter = adap;)。
所以在i2c_driver的probe()(struct i2c_client *c,const struct i2c_device_id *id)函数的参数i2c_client->dapater已经有实实在在的适配器了,并且adapter也有指定的algorithem。
疑问:i2c的从设备(client)和adapter怎么配对的?
在设备树文件中,client的信息是依附于adapter的,本身是一体的,设备树会解析相关的信息。
疑问:i2c的adapter设备信息是怎么创建的?哪里创建的?用的什么函数?
应该是platform平台设备驱动展开的。
疑问+验证:通过设备树来创建设备的过程是怎样的?
724 struct i2c_client *
725 i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
726 {
727 struct i2c_client *client;
728 int status;
729
730 client = kzalloc(sizeof *client, GFP_KERNEL);
731 if (!client)
732 return NULL;
733
734 client->adapter = adap;
1099 static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
1100 {
1101 struct i2c_devinfo *devinfo;
1102
1103 down_read(&__i2c_board_lock);
1104 list_for_each_entry(devinfo, &__i2c_board_list, list) {
1105 if (devinfo->busnum == adapter->nr
1106 && !i2c_new_device(adapter,
1107 &devinfo->board_info))
1108 dev_err(&adapter->dev,
1109 "Can't create device at 0x%02x\n",
1110 devinfo->board_info.addr);
1111 }
1112 up_read(&__i2c_board_lock);
1113 }
i2c驱动主要分为2个部分:i2c总线驱动、i2c设备驱动。前者一般由soc厂商实现了的,开发者一般要根据需要实现后者。
i2c总线驱动:实际上是依附于plantform总线的总线。比如imx的i2c驱动/drivers/i2c/busses/i2c-imx.c,其中就实现了algorithm。
总线匹配:i2c总线驱动和i2c设备驱动,在其中任意一个成功注册后都会去匹配另外一个,匹配完成后,i2c_client->adapter会指向匹配的adapter。
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
...
}
这个函数用于i2c设备的匹配。
i2c设备驱动的cilent参数是怎么传递的?
实际上是在下面的函数中实现的:
static int i2c_device_probe(struct device *dev)
{
...
if (!driver->id_table &&
!i2c_acpi_match_device(dev->driver->acpi_match_table, client) &&
!i2c_of_match_device(dev->driver->of_match_table, client))
return -ENODEV;
...
}
我们在某个i2c总线上添加了一个外部设备,在外部设备的驱动中probe()中的cilent参数,总线是怎么知道使用的哪一个soc上的具体的i2c呢?
这个实际上是具体看对应的设备树文件就知道了,比如:
i2c2: i2c@30a30000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx8mm-i2c", "fsl,imx21-i2c";
reg = <0x0 0x30a30000 0x0 0x10000>;
interrupts = ;
clocks = <&clk IMX8MM_CLK_I2C2_ROOT>;
status = "disabled";
};
而挂在这个总线上的外部设备描述如下:
&i2c2 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
wm8904: wm8904@1a {
compatible = "wlf,wm8904";
reg = <0x1a>;
clocks = <&clk IMX8MM_CLK_SAI3_ROOT>;
clock-names = "mclk";
};
rtc: rtc@32 {
compatible = "epson,rx8025";
reg = <0x32>;
};
ov5640_mipi: ov5640_mipi@3c {
compatible = "ovti,ov5640_mipi";
//reg = <0x3c>; //ov5640 addr: 0x78>>1 = 0x3c
reg = <0x44>; //0x88>>1 = 0x44
...
};
tp9950_mipi: tp9950_mipi@3c {
compatible = "techpoint,tp9950_mipi";
reg = <0x45>; //0x8a>>1 = 0x45
...
}
可以看到所有的外挂的i2c外部设备,从设备树信息结构上看都是依附于i2c2的。当我们匹配了某个i2c外部设备wm8904、rtc、ov5640_mipi、tp9950_mipi等设备时,系统中的i2c总线通过设备树是有依据来知道使用的是soc的哪个i2c的,上面列举的几个外部i2c设备驱动的probe函数的参数必然是i2c2的cilent。