【北京迅为】i.MX6ULL终结者Linux I2C驱动实验Linux I2C驱动框架简介

在Linux内核中I2C的体系结构分为3个部分:
① I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册、注销方法等。
② I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。
③ I2C设备驱动:I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。

1 I2C总线驱动

I2C总线和platform总线差不多,只不过platform总线是虚拟出来的一条总线,而I2C总线是实际存在的。对于使用I2C通信的设备,在驱动中直接使用I2C总结即可。I2C总线驱动的重点是I2C适配器驱动,主要涉及到两个结构体:i2c_adapter 和 i2c_algorithm。在Linux内核中用i2c_adapter结构体来表示I2C适配器。i2c_adapter 结构体定义在include/linux/i2c.h 文件中,结构体内容如下:

498 struct i2c_adapter {
    
     
499        struct module *owner; 
500        unsigned int class; /* classes to allow probing for */ 
501        const struct i2c_algorithm *algo; /* 总线访问算法 */ 
502        void *algo_data; 
503 
504        /* data fields that are valid for all devices */ 
505        struct rt_mutex bus_lock; 
506 
507        int timeout; /* in jiffies */ 
508        int retries; 
509        struct device dev; /* the adapter device */ 
510 
511        int nr; 
512        char name[48]; 
513        struct completion dev_released; 
514 
515        struct mutex userspace_clients_lock; 
516        struct list_head userspace_clients; 
517 
518        struct i2c_bus_recovery_info *bus_recovery_info; 
519        const struct i2c_adapter_quirks *quirks; 
520 };

第 501 行,i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法。
i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中,部分内容如下:

391 struct i2c_algorithm {
    
     
...... 
398        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, 
399                        int num); 
400        int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, 
401        unsigned short flags, char read_write, 
402        u8 command, int size, union i2c_smbus_data *data); 
403 
404        /* To determine what the adapter supports */ 
405        u32 (*functionality) (struct i2c_adapter *); 
...... 
411 }; 

第 398 行,master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。
第 400 行,smbus_xfer 就是 SMBUS 总线的传输函数。
综上所述,I2C 总线驱动,或者说 I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter,这两个函数的原型如下:

int i2c_add_adapter(struct i2c_adapter *adapter) 
int i2c_add_numbered_adapter(struct i2c_adapter *adap)

这两个函数的区别在于 i2c_add_adapter 使用动态的总线号,而 i2c_add_numbered_adapter使用静态总线号。函数参数和返回值含义如下:
adapter 或 adap:要添加到 Linux 内核中的 i2c_adapter,也就是 I2C 适配器。
返回值:0,成功;负值,失败。
如果要删除 I2C 适配器的话使用 i2c_del_adapter 函数即可,函数原型如下:
void i2c_del_adapter(struct i2c_adapter * adap)
函数参数和返回值含义如下:
adap:要删除的 I2C 适配器。
返回值:无。
关于 I2C 的总线(控制器或适配器)驱动就讲解到这里,一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,比如 I.MX6U 的 I2C 适配器驱动 NXP 已经编写好了,这个不需要用户去编写。因此 我们不用关心I2C 总线驱动具体是如何实现的,我们只要专注于 I2C 设备驱动即可。

2 I2C设备驱动

在I2C设备驱动中主要有两个重要的结构体:i2c_client 和 i2c_driver。i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容,类似于 platform_driver。
i2c_client 结构体定义在 include/linux/i2c.h 文件中,内容如下:

217 struct i2c_client {
    
     
218        unsigned short flags; /* 标志 */ 
219        unsigned short addr; /* 芯片地址,7 位,存在低 7 位*/ 
...... 
222        char name[I2C_NAME_SIZE]; /* 名字 */ 
223        struct i2c_adapter *adapter; /* 对应的 I2C 适配器 */ 
224        struct device dev; /* 设备结构体 */ 
225        int irq; /* 中断 */ 
226        struct list_head detected; 
...... 
230 };

一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个 i2c_client。
i2c_driver 类似 platform_driver,是I2C驱动编写的重点内容。i2c_driver 结构体定义在 include/linux/i2c.h 文件中,内容如下:

161 struct i2c_driver {
    
     
162        unsigned int class; 
163 
164        /* Notifies the driver that a new bus has appeared. You should 
165        * avoid using this, it will be removed in a near future. 
166        */ 
167        int (*attach_adapter)(struct i2c_adapter *) __deprecated; 
168 
169        /* Standard driver model interfaces */ 
170        int (*probe)(struct i2c_client *, const struct i2c_device_id *); 
171      int (*remove)(struct i2c_client *); 
172 
173        /* driver model interfaces that don't relate to enumeration */ 
174        void (*shutdown)(struct i2c_client *); 
175 
176        /* Alert callback, for example for the SMBus alert protocol. 
177        * The format and meaning of the data value depends on the 
178        * protocol.For the SMBus alert protocol, there is a single bit 
179        * of data passed as the alert response's low bit ("event 
180     flag"). */ 
181        void (*alert)(struct i2c_client *, unsigned int data);
182 
183        /* a ioctl like command that can be used to perform specific 
184        * functions with the device. 
185        */ 
186        int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); 
187 
188        struct device_driver driver; 
189        const struct i2c_device_id *id_table; 
190 
191        /* Device detection callback for automatic device creation */ 
192        int (*detect)(struct i2c_client *, struct i2c_board_info *); 
193        const unsigned short *address_list; 
194        struct list_head clients; 
195 };

第 170 行,当 I2C 设备和驱动匹配成功以后 probe 函数就会执行,和 platform 驱动一样。
第 188 行,device_driver 驱动结构体,如果使用设备树的话,需要设置 device_driver 的
of_match_table 成员变量,也就是驱动的兼容(compatible)属性。
第 189 行,id_table 是传统的、未使用设备树的设备匹配 ID 表。

在编写I2C设备的驱动时,主要就是创建i2c_driver结构体,并实现里面的内容。
i2c_driver结构体创建完成后,使用int i2c_register_driver函数向Linux内核中注册i2c设备。函数原型如下:

int i2c_register_driver(struct module *owner,  
struct i2c_driver *driver) 

函数参数和返回值含义如下:
owner:一般为 THIS_MODULE。
driver:要注册的 i2c_driver。
返回值:0,成功;负值,失败。
另外 i2c_add_driver 也常常用于注册 i2c_driver,i2c_add_driver 是一个宏,定义如下:
587 #define i2c_add_driver(driver) \
588 i2c_register_driver(THIS_MODULE, driver)
可以看出i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver。
在主线I2C设备驱动的同时需要将前面注册的i2c_driver也从Linux内核中注销掉。用i2c_del_driver 函数,此函数原型如下:
void i2c_del_driver(struct i2c_driver *driver)
函数参数和返回值含义如下:
driver:要注销的 i2c_driver。
返回值:无。
i2c_driver 的注册示例代码如下:

1 /* i2c 驱动的 probe 函数 */ 
2 static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id) 
3 {
    
     
4      /* 函数具体程序 */ 
5        return 0; 
6 } 
7 
8 /* i2c 驱动的 remove 函数 */ 
9 static int ap3216c_remove(struct i2c_client *client) 
10 {
    
     
11      /* 函数具体程序 */ 
12         return 0; 
13 } 
14 
15 /* 传统匹配方式 ID 列表 */ 
16 static const struct i2c_device_id xxx_id[] = {
    
     
17         {
    
    "xxx", 0}, 
18         {
    
    } 
19 }; 
20 
21 /* 设备树匹配列表 */ 
22 static const struct of_device_id xxx_of_match[] = {
    
     
23         {
    
     .compatible = "xxx" }, 
24         {
    
     /* Sentinel */ } 
25 }; 
26 
27 /* i2c 驱动结构体 */ 
28 static struct i2c_driver xxx_driver = {
    
     
29         .probe = xxx_probe, 
30         .remove = xxx_remove, 
31      .driver = {
    
     
32             .owner = THIS_MODULE, 
33             .name = "xxx", 
34             .of_match_table = xxx_of_match, 
35         }, 
36         .id_table = xxx_id, 
37 }; 
38 
39 /* 驱动入口函数 */ 
40 static int __init xxx_init(void) 
41 {
    
     
42     int ret = 0; 
43 
44     ret = i2c_add_driver(&xxx_driver);
45     return ret; 
46 } 
47 
48 /* 驱动出口函数 */ 
49 static void __exit xxx_exit(void) 
50 {
    
     
51     i2c_del_driver(&xxx_driver); 
52 } 
53 
54 module_init(xxx_init); 
55 module_exit(xxx_exit); 

第 16~19 行,i2c_device_id,无设备树的时候匹配 ID 表。
第 22~25 行,of_device_id,设备树所使用的匹配表。
第 28~37 行,i2c_driver,当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行,这些和 platform 驱动一样,probe 函数里面基本就是标准的字符设备驱动那一套了。

3 I2C核心

在I2C核心层完成的就是I2C设备和I2C驱动的匹配过程。I2C核心部分的文件是drivers/i2c/i2c-core.c,I2C核心层提供了一些与硬件无关的API函数。
1、i2c_adapter 注册/注销函数
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)
2、i2c_driver 注册/注销函数
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
设备和驱动的匹配过程也是在 I2C 总线完成的,I2C 总线的数据结构为 i2c_bus_type,定义在 drivers/i2c/i2c-core.c 文件,i2c_bus_type 内容如下:

736 struct bus_type i2c_bus_type = {
    
     
737 		.name = "i2c", 
738 		.match = i2c_device_match, 
739 		.probe = i2c_device_probe, 
740 		.remove = i2c_device_remove, 
741 		.shutdown = i2c_device_shutdown, 
742 };

match成员变量就是I2C 总线上设备和驱动匹配函数,也就是 i2c_device_match 这个函数,此函数内容如下:

457 static int i2c_device_match(struct device *dev, struct device_driver *drv)
458 {
    
     
459        struct i2c_client *client = i2c_verify_client(dev); 
460        struct i2c_driver *driver; 
461 
462      if (!client) 
463            return 0; 
464 
465        /* Attempt an OF style match */ 
466        if (of_driver_match_device(dev, drv)) 
467        return 1; 
468 
469        /* Then ACPI style match */ 
470        if (acpi_driver_match_device(dev, drv)) 
471            return 1; 
472 
473        driver = to_i2c_driver(drv); 
474        /* match on an id table if there is one */ 
475        if (driver->id_table) 
476            return i2c_match_id(driver->id_table, client) != NULL; 
477 
478        return 0; 
479 }

第 466 行,of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较 I2C 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 I2C设备和驱动匹配。
第 470 行,acpi_driver_match_device 函数用于 ACPI 形式的匹配。
第 476 行,i2c_match_id 函数用于传统的、无设备树的 I2C 设备和驱动匹配过程。比较 I2C设备名字和 i2c_device_id 的 name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/BeiJingXunWei/article/details/112481632