锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

【正点原子MP157连载】第四十章 Linux I2C驱动实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

时间:2023-01-29 03:00:00 电阻597502100alf连接器电阻698318安全传感器温度传感器99st400700n接近传感器scl1204

1)实验平台:正点原子STM32MP157开发板
2)购买链接:https://item.taobao.com/item.htm?&id=629270721801
3)全套实验源码 手册 视频下载地址:http://www.openedv.com/thread-318813-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子STM32MP157技术交流群:691905614
在这里插入图片描述

第四十章 Linux I2C驱动实验

对于 I2C 我相信大家都很熟悉,基本上做过单片机开发的朋友都接触过,在电子产品硬件设计当中,I2C 它是连接各种同步、串行、低速、近距离通信接口的常见方式 IC、它们都提供传感器和其他设备 I2C 接口与 SoC 陀螺仪、加速度计、触摸屏等主控连接,它最大的优点是可以在总线上扩展多个外围设备的支持。
Linux 为了方便驱动开发工程师在内核中添加自己的内核开发人员 I2C 设备驱动程序更容易 linux 下驱动自己 I2C 然后引入接口硬件 I2C 总线框架。与 Linux 下的 platform 虚拟总线不同的是,I2C 是实际的物理总线,所以 I2C 总线框架也是如此Linux 下总线、设备、驱动模型的产品。
让我们学习如何学习这一章。 Linux 下的 I2C 以及如何使用总线框架 I2C 编写一个总线框架 I2C 接口外设驱动程序;本章的重点是学习 Linux 下的 I2C 总线框架。

40.1 I2C & AP3216C简介
40.1.1 I2C简介
I2C是一种常见的总线协议,I2C是NXP公司设计,I2C在主控制器和从机之间使用两条线进行数据通信。一条是SCL(串行时钟线),另一个是SDA(串行数据线),这两条数据线需要连接拉电阻,总线空闲时SCL和SDA高电平。I2C总线标准模式下的速度可达1000Kb/S,快速模式下可以达到400Kb/S。I2C总线工作是按照一定的协议运行的。让我们看看I2C协议。
I2C它支持多从机,即一个I2C多个可以挂在控制器下I2C不同的设备I2C设备有不同的设备地址I2C可通过主控制器I2C访问指定设备的设备地址I2C设备,一个I2C多个总线连接I2C设备如图40.1.1.1所示:

图40.1.1.1 I2C多设备连接结构图
图40.1.1.1中SDA和SCL这两条线必须连接一个上拉电阻,通常是4.7K。其余的I2C从设备挂接到SDA和SCL这两条线可以通过SDA和SCL多次访问这两条线I2C设备。
接下来看看I2C协议相关术语:
1、起始位
顾名思义,也就是说I2C通信的起始标志可以通过这个起始位置告诉I2C从机器开始,我将开始I2C通信了。在SCL高电平时,SDA如图40所示,位,如图40所示.1.1.2所示:

图40.1.1.2 I2C通信起始位
2、停止位
停止就是停止I2C与起始位功能相反的通信标志位。SCL当位高电平时,SDA如图40所示,上升如图40所示.1.1.3所示:

图40.1.1.3 I2C通信停止位
3、数据传输
I2C数据传输时,总线应确保SCL高电平期间,SDA因此,数据稳定SDA数据变化只能在上面SCL如图40所示,发生在低电平期间.1.1.4所示:

图40.1.1.4 I2C数据传输
4、应答信号
当I2C主机发送8位数据后,会SDA设置为输入状态,等待I2C从机回应,即等待I2C从机器告诉主机它收到了数据。响应信号是从机器发出的,主机需要提供响应信号所需的时钟。主机发送8位数据后的时钟信号用于响应信号。从机通过将SDA降低表示响应信号,表示通信成功,否则表示通信失败。
5、I2C写时序
主机通过I2C通信总线和从机之间只有两个操作:写和读,I2C总线单字节写顺序如图40.1.1.5所示:

图40.1.1.5 I2C写时序
图40.1.1.5就是I2C写时序,我们来看看写时序的具体步骤:
一、开始信号。
2)、发送I2C设备地址,每个I2C所有设备都有设备地址,通过发送具体的设备地址来决定访问哪个I2C设备。这是8位数据,其中高7位是设备地址,最后一位是读写位。如果是1,说明是读操作,如果是0,说明是写操作。
3)、 I2C在设备地址后面跟着一个读写位,为0表示写作操作,为1表示读写操作。
4),从机发送ACK应答信号。
5)重新发送开始信号。
6)发送要写入数据的寄存器地址。
7),从机发送ACK应答信号。
8)发送要写入寄存器的数据。
9),从机发送ACK应答信号。
十、停止信号。
6、I2C读时序
I2C总线单字节读顺序如图40.1.1.6所示:

图40.1.1.6 I2C单字节读时序
I2C单字节读时序比写时序复杂。读时序分为四个步骤。第一步是发送设备地址,第二步是发送要读取的寄存器地址,第三步是重新发送设备地址。最后一步是I2C从设备输出中读取的寄存器值,我们具体来看这一步。
1)主机发送起始信号。
2)主机发送要读取的东西I2C设备地址。
3)读写控制位,因为是向I2C从设备发送数据,所以写信号。
4),从机发送ACK应答信号。
5)重新发送START信号。
6)主机发送要读取的寄存器地址。
7)、从机发送的ACK应答信号。
8)重新发送START信号。
9)重新发送要读取的东西I2C设备地址。
10),读写控制位,这里是读信号,说明下一步是从I2C从设备中读取数据。
从机发送ACK应答信号。
12)、从I2C读取在设备中的数据。
13)主机发出NO ACK信号表示读取完成,无需从机发送ACK信号了。
14)主机发出STOP信号,停止I2C通信。
7、I2C多字节读写时序
有时我们需要读写多个字节。多字节读写顺序与单字节基本相同,但我们可以在读写数据时连续发送多个自己的数据。其他控制顺序与单字节相同。
40.1.2 STM32MP1 I2C 简介
STM32MP157D 有 6 个 I2C 接口,其中 I2C4 和 I2C6 可以在 A7 安全模式或 A7 在非安全模式下使用,M4 无法使用,STM32MP157 的 I2C 部分特点如下:
①、兼容I2C第03版总线规范。
②、从模式和主模式支持多主模式功能。
③、支持标准模式 (Sm)、快速模式 (Fm) 和超快速模式 (Fm ),其中,标准模式100kHz,快速模式400 kHz,超快速模式可达1 MHz。
④、7 位置和10位寻址模式。
⑤、多个7位从地址,所有7位地址响应模式。
⑥、软件复位。
⑦、带DMA功能1字节缓冲。
⑧、广播呼叫。
关于STM32M157 IIC更多详细的介绍,请参考《STM32MP157参考手册相关章节。
40.1.3 AP3216C简介
STM32MP通过开发板I2C5连接三合一环境传感器:AP3216C,AP3216C敦南科技推出的传感器支持环境光强度(ALS)、接近距离(PS)红外强度(IR)检测这三个环境参数。芯片可以通过IIC接口与主控相连,并支持中断,AP3216C特点如下:
①、I2C在快速模式下,波特率可达400Kbit/S
②、选择多种工作模式:ALS、PS IR、ALS PS IR、PD等等。
③、内建温度补偿电路。
④、宽工作温度范围(-30)°C ~ 80°C)。
⑤、超小封装,4.1mm x 2.4mm x 1.35mm
⑥、环境光传感器具有16位分辨率。
⑦、10位分辨率接近传感器和红外传感器。
AP3216C通常用于手机、平板电脑、导航设备等,其内置接近传感器可用于检测是否有物体接近,如手机用于检测耳朵是否接触听筒,如果检测到手机,手机将关闭手机屏幕以节省电力。您还可以使用环境光传感器来检测光强可以实现自动背光亮度调整。
AP3216C结构如图40.1.3.1所示:

图40.1.3.1 AP3216C结构图
AP3216设备地址为0X1E,几乎所有I2C从器件,AP3216C还有一些寄存器,我们可以通过这些寄存器配置AP3216C并读取相应的数据。AP3216C我们使用的寄存器如表40.1.3.1所示:

表40.1.3.1 本章使用的AP3216C寄存器表
在表40.1.3.1中,0X该寄存器用于设置模式控制寄存器AP3216C一般来说,工作模式开始设置为0X04,也就是先件复位一次AP3216C。接下来根据实际使用情况选择合适的工作模式,比如设置为0X03,也就是开启ALS+PS+IR。0X0A~0X0F这6个寄存器就是数据寄存器,保存着ALS、PS和IR这三个传感器获取到的数据值。如果同时打开ALS、PS和IR的读取间隔最少要112.5ms,因为AP3216C完成一次转换需要112.5ms。关于AP3216C的介绍就到这里,如果要想详细的研究此芯片的话,请大家自行查阅其数据手册。
40.2 Linux I2C总线框架简介
使用裸机的方式编写一个 I2C 器件的驱动程序,我们一般需要实现两部分:
①、I2C 主机驱动。
②、I2C 设备驱动。
I2C 主机驱动也就是 SoC 的 I2C 控制器对应的驱动程序,I2C 设备驱动其实就是挂在 I2C总线下的具体设备对应的驱动程序,例如 eeprom、触摸屏 IC、传感器 IC 等;对于主机驱动来说,一旦编写完成就不需要再做修改,其他的 I2C 设备直接调用主机驱动提供的 API 函数完成读写操作即可。这个正好符合 Linux 的驱动分离与分层的思想,因此 Linux 内核也将
I2C 驱动分为两部分。
Linux内核开发者为了让驱动开发工程师在内核中方便的添加自己的I2C设备驱动程序,方便大家更容易的在linux下驱动自己的I2C接口硬件,进而引入了I2C总线框架,我们一般也叫作I2C子系统,Linux下I2C子系统总体框架如下所示:

图40.2.3.1 I2C子系统框架图
从图40.2.3.1可以知道,I2C子系统分为三大组成部分:
1、I2C核心(I2C-core)
I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册、注销方法,I2C通信方法(algorithm)与具体硬件无关的代码,以及探测设备地址的上层代码等;
2、I2C总线驱动(I2C adapter)
I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力。I2C总线驱动由i2c_adapter和i2c_algorithm来描述。I2C适配器是SoC中内置i2c控制器的软件抽象,可以理解为他所代表的是一个I2C主机;
3、I2C设备驱动(I2C client driver)
包括两部分:设备的注册和驱动的注册。
I2C子系统帮助内核统一管理I2C设备,让驱动开发工程师在内核中可以更加容易地添加自己的I2C设备驱动程序。
40.2.1 I2C 总线驱动
首先来看一下I2C总线,在讲platform的时候就说过,platform是虚拟出来的一条总线,目的是为了实现总线、设备、驱动框架。对于I2C而言,不需要虚拟出一条总线,直接使用I2C总线即可。I2C总线驱动重点是I2C适配器(也就是SoC的I2C接口控制器)驱动,这里要用到两个重要的数据结构:i2c_adapter和i2c_algorithm,I2C子系统将SoC的I2C适配器(控制器)抽象成一个i2c_adapter结构体,i2c_adapter结构体定义在include/linux/i2c.h文件中,结构体内容如下:

示例代码40.2.1 i2c_adapter结构体
685 struct i2c_adapter { 
        
686     struct module *owner;
687     unsigned int class;       
688     const struct i2c_algorithm *algo; 
689     void *algo_data;
690
691     /* data fields that are valid for all devices */
692     const struct i2c_lock_operations *lock_ops;
693     struct rt_mutex bus_lock;
694     struct rt_mutex mux_lock;
695
696     int timeout;            		/* in jiffies */
697     int retries;
698     struct device dev;      		/* the adapter device */
699     unsigned long locked_flags; /* owned by the I2C core */
700 #define I2C_ALF_IS_SUSPENDED        	0
701 #define I2C_ALF_SUSPEND_REPORTED    	1
702
703     int nr;
704     char name[48];
705     struct completion dev_released;
706
707     struct mutex userspace_clients_lock;
708     struct list_head userspace_clients;
709
710     struct i2c_bus_recovery_info *bus_recovery_info;
711     const struct i2c_adapter_quirks *quirks;
712
713     struct irq_domain *host_notify_domain;
714 };

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

示例代码40.2.1.2 i2c_algorithm结构体
526 struct i2c_algorithm { 
        
527     /* 528 * If an adapter algorithm can't do I2C-level access, set 529 * master_xfer to NULL. If an adapter algorithm can do SMBus 530 * access, set smbus_xfer. If set to NULL, the SMBus protocol is 531 * simulated using common I2C messages. 532 * 533 * master_xfer should return the number of messages successfully 534 * processed, or a negative value on error 535 */
536     int (*master_xfer)(struct i2c_adapter *adap, 
struct i2c_msg *msgs,
537                int num);
538     int (*master_xfer_atomic)(struct i2c_adapter *adap,
539                    struct i2c_msg *msgs, int num);
540     int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
541               unsigned short flags, char read_write,
542               u8 command, int size, union i2c_smbus_data *data);
543     int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,
544                  unsigned short flags, char read_write,
545                  u8 command, int size, union i2c_smbus_data *data);
546
547     /* To determine what the adapter supports */
548     u32 (*functionality)(struct i2c_adapter *adap);
549
550 #if IS_ENABLED(CONFIG_I2C_SLAVE)
551     int (*reg_slave)(struct i2c_client *client);
552     int (*unreg_slave)(struct i2c_client *client);
553 #endif
554 };

第536行,master_xfer就是I2C适配器的传输函数,可以通过此函数来完成与IIC设备之间的通信。
第540行,smbus_xfer就是SMBUS总线的传输函数。smbus协议是从I2C协议的基础上发展而来的,他们之间有很大的相似度,SMBus与I2C总线之间在时序特性上存在一些差别,应用于移动PC和桌面PC系统中的低速率通讯。
综上所述,I2C总线驱动,或者说I2C适配器驱动的主要工作就是初始化i2c_adapter结构体变量,然后设置i2c_algorithm中的master_xfer函数。完成以后通过i2c_add_numbered_adapter或i2c_add_adapter这两个函数向I2C子系统注册设置好的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总线驱动都是由半导体厂商编写的,比如STM32MP1的I2C适配器驱动ST官方已经编写好了,这个不需要用户去编写。因此I2C总线驱动对我们这些SoC使用者来说是被屏蔽掉的,我们只要专注于I2C设备驱动即可,除非你是在半导体公司上班,工作内容就是写I2C适配器驱动。
40.2.2 I2C总线设备
I2C设备驱动重点关注两个数据结构:i2c_client和i2c_driver,根据总线、设备和驱动模型,I2C总线上一小节已经讲了。还剩下设备和驱动,i2c_client用于描述I2C总线下的设备,i2c_driver则用于描述I2C总线下的设备驱动,类似于platform总线下的platform_device和platform_driver。
1、i2c_client结构体
i2c_client结构体定义在include/linux/i2c.h文件中,内容如下:

示例代码40.2.2.1 i2c_client结构体
313     struct i2c_client { 
        
314         unsigned short flags;       		/* div., see below */
......
328         struct i2c_adapter *adapter;	/* the adapter we sit on */
329         struct device dev;      			/* the device structure */
330         int init_irq;          	/* irq set at initialization */
331         int irq;            		/* irq issued by device */
332         struct list_head detected;
333     #if IS_ENABLED(CONFIG_I2C_SLAVE)
334         i2c_slave_cb_t slave_cb;    /* callback for slave mode */
335     #endif
336     };

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

示例代码40.2.2.2 i2c_driver结构体
253     struct i2c_driver { 
        
254         unsigned int class;
255 
256         /* Standard driver model interfaces */
257         int (*probe)(struct i2c_client *client, 
const struct i2c_device_id *id);
258         int (*remove)(struct i2c_client *client);
259 
260         /* New driver model interface to aid the seamless removal of 261 * the current probe()'s, more commonly unused than used 262 second parameter.*/
263         int (*probe_new)(struct i2c_client *client);
264 
265       /* driver model interfaces that don't relate to enumeration */
266         void (*shutdown)(struct i2c_client *client);
267 
268         /* Alert callback, for example for the SMBus alert protocol. 269 * The format and meaning of the data value depends on the 270 * protocol. For the SMBus alert protocol, there is a single 271 * bit of data passed as the alert response's low bit ("event 272 * flag"). For the SMBus Host Notify protocol, the data 273 * corresponds to the 16-bit payload data reported by the 274 slave device acting as master.*/
275         void (*alert)(struct i2c_client *client, 
enum i2c_alert_protocol protocol,
276                          unsigned int data);
277 
278         /* a ioctl like command that can be used to perform specific 279 * functions with the device. 280 */
281         int (*command)(struct i2c_client *client, unsigned int cmd, 
void *arg);
282 
283         struct device_driver driver;
284         const struct i2c_device_id *id_table;
285 
286         /* Device detection callback for automatic device creation */
287         int (*detect)(struct i2c_client *client, 
struct i2c_board_info *info);
288         const unsigned short *address_list;
289         struct list_head clients;
290 
291         bool disable_i2c_core_irq_mapping;
292     };

第257行,当I2C设备和驱动匹配成功以后probe函数就会执行,和platform驱动一样。
第283行,device_driver驱动结构体,如果使用设备树的话,需要设置device_driver的of_match_table成员变量,也就是驱动的兼容(compatible)属性。
第284行,id_table是传统的、未使用设备树的设备匹配ID表。
对于我们I2C设备驱动编写人来说,重点工作就是构建i2c_driver,构建完成以后需要向I2C子系统注册这个i2c_driver。i2c_driver注册函数为int i2c_register_driver,此函数原型如下:
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是一个宏,定义如下:

示例代码40.2.2.3 i2c_add_driver宏
844     #define i2c_add_driver(driver) \
845         i2c_register_driver(THIS_MODULE, driver)

i2c_add_driver就是对i2c_register_driver做了一个简单的封装,只有一个参数,就是要注册的i2c_driver。
注销I2C设备驱动的时候需要将前面注册的i2c_driver从I2C子系统中注销掉,需要用到i2c_del_driver函数,此函数原型如下:
void i2c_del_driver(struct i2c_driver *driver)
函数参数和返回值含义如下:
driver:要注销的i2c_driver。
返回值:无。
i2c_driver的注册示例代码如下:

示例代码40.2.2.4 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函数里面基本就是标准的字符设备驱动那一套了。

40.2.3 I2C 设备和驱动匹配过程
I2C设备和驱动的匹配过程是由I2C子系统核心层来完成的,drivers/i2c/i2c-core-base.c就是I2C的核心部分,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_bus_type,定义在drivers/i2c/i2c-core-base.c文件,i2c_bus_type内容如下:

示例代码40.2.3.1 i2c_bus_type结构体
492 struct bus_type i2c_bus_type = { 
        
493     .name     	= "i2c",
494     .match    	= i2c_device_match,
495     .probe    	= i2c_device_probe,
496     .remove   	= i2c_device_remove,
497     .shutdown 	= i2c_device_shutdown,
498 };

.match就是I2C总线的设备和驱动匹配函数,在这里就是i2c_device_match这个函数,此函数内容如下:

示例代码40.2.3.2 i2c_device_match函数
93  static int i2c_device_match(struct device *dev, 
struct device_driver *drv)
94  { 
        
95      struct i2c_client   *client = i2c_verify_client(dev);
96      struct i2c_driver   *driver;
97 
98 
99      /* Attempt an OF style match */
100     if (i2c_of_match_device(drv->of_match_table, client))
101         return 1;
102
103     /* Then ACPI style match */
104     if (acpi_driver_match_device(dev, drv))
105         return 1;
106
107     driver = to_i2c_driver(drv);
108
109     /* Finally an I2C match */
110     if (i2c_match_id(driver->id_table, client))
111         return 1;
112
113     return 0;
114 }

第100行,i2c_of_match_device函数用于完成设备树中定义的设备与驱动匹配过程。比较I2C设备节点的compatible属性和of_device_id中的compatible属性是否相等,如果相当的话就表示I2C设备和驱动匹配。
第104行,acpi_driver_match_device函数用于ACPI形式的匹配。
第110行,i2c_match_id函数用于传统的、无设备树的I2C设备和驱动匹配过程。比较I2C设备名字和i2c_device_id的name字段是否相等,相等的话就说明I2C设备和驱动匹配成功。
40.3 STM32MP1 I2C 适配器驱动分析
上一小节我们讲解了Linux下的I2C子系统,重点分为I2C适配器驱动和I2C设备驱动,其中I2C适配器驱动就是SoC的I2C控制器驱动。I2C设备驱动是需要用户根据不同的I2C从设备去编写,而I2C适配器驱动一般都是SoC厂商去编写的,比如ST就已经提供了STM3MP21的I2C适配器驱动程序。在内核源码arch/arm/boot/dts/stm32mp151.dtsi设备树文件中找到STM32MP1的I2C控制器节点,节点内容如下所示:

示例代码40.3.1 I2C1控制器节点
590     i2c1: i2c@40012000 { 
        
591         compatible = "st,stm32mp15-i2c";
592         reg = <0x40012000 0x400>;
593         interrupt-names = "event", "error";
594         interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>,
595                       <&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
596         clocks = <&rcc I2C1_K>;
597         resets = <&rcc I2C1_R>;
598         #address-cells = <1>;
599         #size-cells = <0>;
600         dmas = <&dmamux1 33 0x400 0x80000001>,
601                <&dmamux1 34 0x400 0x80000001>;
602         dma-names = "rx", "tx";
603         power-domains = <&pd_core>;
604         st,syscfg-fmp = <&syscfg 0x4 0x1>;
605         wakeup-source;
606         status = "disabled";
607     };
重点关注i2c1节点的compatible属性值,因为通过compatible属性值可以在Linux源码里面找到对应的驱动文件。这里i2c1节点的compatible属性值“st,stm32mp15-i2c”,在Linux源码中搜索这个字符串即可找到对应的驱动文件。STM32MP1的I2C适配器驱动驱动文件为drivers/i2c/busses/i2c-stm32f7.c,在此文件中有如下内容:
示例代码40.3.2 i2c-stm32f7.c文件代码段
2520    static const struct of_device_id stm32f7_i2c_match[] = { 
        
2521     { 
         .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup},
2522     { 
         .compatible = "st,stm32mp15-i2c", .data = &stm32mp15_setup},
2523     { 
        },
2524    };
2525    MODULE_DEVICE_TABLE(of, stm32f7_i2c_match);
2526
2527    static struct platform_driver stm32f7_i2c_driver = { 
        
2528        .driver = { 
        
2529            .name = "stm32f7-i2c",
2530            .of_match_table = stm32f7_i2c_match,
2531            .pm = &stm32f7_i2c_pm_ops,
2532        },
2533        .probe = stm32f7_i2c_probe,
2534        .remove = stm32f7_i2c_remove,
2535    };
2536
元器件数据手册、IC替代型号,打造电子元器件IC百科大全!
          

相关文章