linux之I2C驱动分析

前言

代码

本文的分析对象为NXP的IMX8MM的代码,内核版本为4.14.98

说明

I2C实际上有两部分驱动并且是分层的。

  • I2C主机驱动。
  • 2C设备驱动。
    对于I2C主机驱动,一旦编写完成就不需要再做修改,其他的I2C设备直接调用主机驱动提供的API函数完成读写操作即可。这个正好符合Linux的驱动分离与分层的思想,因此Linux内核也将I2C驱动分为两部分:
  1. I2C总线驱动,I2C总线驱动就是SOC的I2C控制器驱动,也叫做I2C适配器驱动。
  2. I2C设备驱动,I2C设备驱动就是针对具体的I2C设备而编写的驱动。

整体驱动构架理解:

方式概述:实际上有两个步骤:

  1. client(bord_info)会先和adpater匹配绑定成一体(可以理解为主机驱动和设备驱动关联)。
  2. 然后如果有驱动的匹配信息和client一致时,会调用驱动的probe函数。

设备树描述的是设备信息,不是驱动的信息。设备树文件会自动被内核展开,生成对应的设备信息,并且插入到对应的链表中。注意:在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。

你可能感兴趣的:(Linux驱动,linux,i2c,i2c驱动,imx8mm)