赞
踩
1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban
对于 I2C 我相信大家都很熟悉,基本上做过单片机开发的朋友都接触过,在电子产品硬件设计当中,I2C 是一种很常见的同步、串行、低速、近距离通信接口,用于连接各种 IC、传感器等器件,它们都会提供 I2C 接口与 SoC 主控相连,比如陀螺仪、加速度计、触摸屏等,其最大优势在于可以在总线上扩展多个外围设备的支持。
Linux 内核开发者为了让驱动开发工程师在内核中方便的添加自己的 I2C 设备驱动程序,更容易的在 linux 下驱动自己的 I2C 接口硬件,进而引入了 I2C 总线框架。与 Linux 下的 platform 虚拟总线不同的是,I2C 是实际的物理总线,所以 I2C 总线框架也是Linux 下总线、设备、驱动模型的产物。
本章我们来学习一下如何在 Linux 下的 I2C 总线框架,以及如何使用 I2C 总线框架编写一个 I2C 接口的外设驱动程序;本章重点是学习 Linux 下的 I2C 总线框架。
28.1 I2C& AP3216C简介
28.1.1 I2C简介
I2C是很常见的一种总线协议,I2C是NXP公司设计的,I2C使用两条线在主控制器和从机之间进行数据通信。一条是SCL(串行时钟线),另外一条是SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候SCL和SDA处于高电平。I2C总线标准模式下速度可以达到100Kb/S,快速模式下可以达到400Kb/S。I2C总线工作是按照一定的协议来运行的,接下来就看一下I2C协议。
I2C是支持多从机的,也就是一个I2C控制器下可以挂多个I2C从设备,这些不同的I2C从设备有不同的器件地址,这样I2C主控制器就可以通过I2C设备的器件地址访问指定的I2C设备了,一个I2C总线连接多个I2C设备如图28.1.1.1所示:
图28.1.1.1 I2C多个设备连接结构图
图28.1.1.1中SDA和SCL这两根线必须要接一个上拉电阻,一般是4.7K。其余的I2C从器件都挂接到SDA和SCL这两根线上,这样就可以通过SDA和SCL这两根线来访问多个I2C设备。
接下来看一下I2C协议有关的术语:
1、起始位
顾名思义,也就是I2C通信起始标志,通过这个起始位就可以告诉I2C从机,“我”要开始进行I2C通信了。在SCL为高电平的时候,SDA出现下降沿就表示为起始位,如图28.1.1.2所示:
图28.1.1.1.2 I2C通信起始位
2、停止位
停止位就是停止I2C通信的标志位,和起始位的功能相反。在SCL位高电平的时候,SDA出现上升沿就表示为停止位,如图28.1.1.3所示:
图28.1.1.3 I2C通信停止位
3、数据传输
I2C总线在数据传输的时候要保证在SCL高电平期间,SDA上的数据稳定,因此SDA上的数据变化只能在SCL低电平期间发生,如图28.1.1.4所示:
图28.1.1.4 I2C数据传输
4、应答信号
当I2C主机发送完8位数据以后会将SDA设置为输入状态,等待I2C从机应答,也就是等待I2C从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完8位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将SDA拉低来表示发出应答信号,表示通信成功,否则表示通信失败。
5、I2C写时序
主机通过I2C总线与从机之间进行通信不外乎两个操作:写和读,I2C总线单字节写时序如图28.1.5所示:
图28.1.1.5 I2C写时序
图28.1.1.5就是I2C写时序,我们来看一下写时序的具体步骤:
1)、开始信号。
2)、发送I2C设备地址,每个I2C器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个I2C器件。这是一个8位的数据,其中高7位是设备地址,最后1位是读写位,为1的话表示这是一个读操作,为0的话表示这是一个写操作。
3)、 I2C器件地址后面跟着一个读写位,为0表示写操作,为1表示读操作。
4)、从机发送的ACK应答信号。
5)、重新发送开始信号。
6)、发送要写写入数据的寄存器地址。
7)、从机发送的ACK应答信号。
8)、发送要写入寄存器的数据。
9)、从机发送的ACK应答信号。
10)、停止信号。
6、I2C读时序
I2C总线单字节读时序如图28.1.1.6所示:
图28.1.1.6 I2C单字节读时序
I2C单字节读时序比写时序要复杂一点,读时序分为4大步,第一步是发送设备地址,第二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是I2C从器件输出要读取的寄存器值,我们具体来看一下这步。
1)、主机发送起始信号。
2)、主机发送要读取的I2C从设备地址。
3)、读写控制位,因为是向I2C从设备发送数据,因此是写信号。
4)、从机发送的ACK应答信号。
5)、重新发送START信号。
6)、主机发送要读取的寄存器地址。
7)、从机发送的ACK应答信号。
8)、重新发送START信号。
9)、重新发送要读取的I2C从设备地址。
10)、读写控制位,这里是读信号,表示接下来是从I2C从设备里面读取数据。
11)、从机发送的ACK应答信号。
12)、从I2C器件里面读取到的数据。
13)、主机发出NO ACK信号,表示读取完成,不需要从机再发送ACK信号了。
14)、主机发出STOP信号,停止I2C通信。
7、I2C多字节读写时序
有时候我们需要读写多个字节,多字节读写时序和单字节的基本一致,只是在读写数据的时候可以连续发送多个自己的数据,其他的控制时序都是和单字节一样的。
28.1.2 RK3568 I2C简介
RK3568支持6个独立I2C: I2C0、I2C1、I2C2、I2C3、I2C4、I2C5。I2C控制器支持以下特性:
①兼容i2c总线
②AMBA APB从接口
③支持I2C总线主模式
④软件可编程时钟频率和传输速率高达400Kbit/sec
⑤支持7位和10位寻址模式
⑥中断或轮询驱动的多字节数据传输
⑦时钟拉伸和等待状态生成
⑧过滤掉SCL和SDA上的故障
关于RK3568 IIC更多详细的介绍,路径:开发板光盘 03、核心板资料核心板板载芯片资料Rockchip RK3568 TRM Part1 V1.1-20210301.pdf(RK3568参考手册1).pdf和Rockchip RK3568 TRM Part2 V1.1-20210301(RK3568参考手册2).pdf。
28.1.3 AP3216C简介
ATK-DLRK3568开发板上通过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结构如图28.1.3.1所示:
图28.1.3.1 AP3216C结构图
AP3216的设备地址为0X1E,同几乎所有的I2C从器件一样,AP3216C内部也有一些寄存器,通过这些寄存器我们可以配置AP3216C的工作模式,并且读取相应的数据。AP3216C我们用的寄存器如表28.1.3.1所示:
寄存器地址 位 寄存器功能 描述
0X00 2:0 系统模式 000:掉电模式(默认)。
001:使能ALS。
010:使能PS+IR。
011:使能ALS+PS+IR。
100:软复位。
101:ALS单次模式。
110:PS+IR单次模式。
111:ALS+PS+IR单次模式。
0X0A 7 IR低位数据 0:IR&PS数据有效,1:无效
1:0 IR最低2位数据。
0X0B 7:0 IR高位数据 IR高8位数据。
0X0C 7:0 ALS低位数据 ALS低8位数据。
0X0D 7:0 ALS高位数据 ALS高8位数据。
0X0E 7 PS低位数据 0,物体在远离;1,物体在接近。
6 0,IR&PS数据有效;1,IR&PS数据无效
3:0 PS最低4位数据。
0X0F 7 PS高位数据 0,物体在远离;1,物体在接近。
6 0,IR&PS数据有效;1,IR&PS数据无效
5:0 PS最低6位数据。
表28.1.3.1 本章使用的AP3216C寄存器表
在表28.1.3.1中,0X00这个寄存器是模式控制寄存器,用来设置AP3216C的工作模式,一般开始先将其设置为0X04,也就是先软件复位一次AP3216C。接下来根据实际使用情况选择合适的工作模式,比如设置为0X03,也就是开启ALS+PS+IR。0X0A~0X0F这6个寄存器就是数据寄存器,保存着ALS、PS和IR这三个传感器获取到的数据值。如果同时打开ALS、PS和IR的读取间隔最少要112.5ms,因为AP3216C完成一次转换需要112.5ms。关于AP3216C的介绍就到这里,如果要想详细的研究此芯片的话,请大家自行查阅其数据手册。
28.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子系统总体框架如下所示:
图28.2.3.1 I2C子系统框架图
从图28.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设备驱动程序。
28.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文件中,结构体内容如下:
示例代码28.2.1 i2c_adapter结构体
672 struct i2c_adapter { 673 struct module *owner; 674 unsigned int class; /* classes to allow probing for */ 675 const struct i2c_algorithm *algo; /* the algorithm to access the bus */ 676 void *algo_data; 677 678 /* data fields that are valid for all devices */ 679 const struct i2c_lock_operations *lock_ops; 680 struct rt_mutex bus_lock; 681 struct rt_mutex mux_lock; 682 683 int timeout; /* in jiffies */ 684 int retries; 685 struct device dev; /* the adapter device */ 686 687 int nr; 688 char name[48]; 689 struct completion dev_released; 690 691 struct mutex userspace_clients_lock; 692 struct list_head userspace_clients; 693 694 struct i2c_bus_recovery_info *bus_recovery_info; 695 const struct i2c_adapter_quirks *quirks; 696 697 struct irq_domain *host_notify_domain; 698 };
第 675 行,i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法。
i2c_algorithm 结构体定义在 include/linux/i2c.h 文件中,内容如下:
示例代码28.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 }; 519 struct i2c_algorithm { 520 /* If an adapter algorithm can't do I2C-level access, set master_xfer 521 to NULL. If an adapter algorithm can do SMBus access, set 522 smbus_xfer. If set to NULL, the SMBus protocol is simulated 523 using common I2C messages */ 524 /* master_xfer should return the number of messages successfully 525 processed, or a negative value on error */ 526 int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, 527 int num); 528 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, 529 unsigned short flags, char read_write, 530 u8 command, int size, union i2c_smbus_data *data); 531 532 /* To determine what the adapter supports */ 533 u32 (*functionality) (struct i2c_adapter *); 534 535 #if IS_ENABLED(CONFIG_I2C_SLAVE) 536 int (*reg_slave)(struct i2c_client *client); 537 int (*unreg_slave)(struct i2c_client *client); 538 #endif 539 };
第526行,master_xfer就是I2C适配器的传输函数,可以通过此函数来完成与IIC设备之间的通信。
第528行,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总线驱动都是由半导体厂商编写的,比如RK3568的I2C适配器驱动RK官方已经编写好了,这个不需要用户去编写。因此I2C总线驱动对我们这些SoC使用者来说是被屏蔽掉的,我们只要专注于I2C设备驱动即可,除非你是在半导体公司上班,工作内容就是写I2C适配器驱动。
28.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文件中,内容如下:
示例代码28.2.2.1 i2c_client结构体
328 struct i2c_client {
329 unsigned short flags; /* div., see below */
330 unsigned short addr; /* chip address - NOTE: 7bit */
331 /* addresses are stored in the */
332 /* _LOWER_ 7 bits */
333 char name[I2C_NAME_SIZE];
334 struct i2c_adapter *adapter; /* the adapter we sit on */
335 struct device dev; /* the device structure */
336 int init_irq; /* irq set at initialization */
337 int irq; /* irq issued by device */
338 struct list_head detected;
339 #if IS_ENABLED(CONFIG_I2C_SLAVE)
340 i2c_slave_cb_t slave_cb; /* callback for slave mode */
341 #endif
342 };
一个I2C设备对应一个i2c_client结构体变量,系统每检测到一个I2C从设备就会给这个设备分配一个i2c_client。
2、i2c_driver结构体
i2c_driver类似platform_driver,是我们编写I2C设备驱动重点要处理的内容,i2c_driver结构体定义在include/linux/i2c.h文件中,内容如下:
示例代码28.2.2.2 i2c_driver结构体
267 struct i2c_driver { 268 unsigned int class; 269 270 /* Standard driver model interfaces */ 271 int (*probe)(struct i2c_client *, const struct i2c_device_id *); 272 int (*remove)(struct i2c_client *); 273 274 /* New driver model interface to aid the seamless removal of the 275 * current probe()'s, more commonly unused than used second parameter. 276 */ 277 int (*probe_new)(struct i2c_client *); 278 279 /* driver model interfaces that don't relate to enumeration */ 280 void (*shutdown)(struct i2c_client *); 281 282 /* Alert callback, for example for the SMBus alert protocol. 283 * The format and meaning of the data value depends on the protocol. 284 * For the SMBus alert protocol, there is a single bit of data passed 285 * as the alert response's low bit ("event flag"). 286 * For the SMBus Host Notify protocol, the data corresponds to the 287 * 16-bit payload data reported by the slave device acting as master. 288 */ 289 void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol, 290 unsigned int data); 291 292 /* a ioctl like command that can be used to perform specific functions 293 * with the device. 294 */ 295 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); 296 297 struct device_driver driver; 298 const struct i2c_device_id *id_table; 299 300 /* Device detection callback for automatic device creation */ 301 int (*detect)(struct i2c_client *, struct i2c_board_info *); 302 const unsigned short *address_list; 303 struct list_head clients; 304 305 bool disable_i2c_core_irq_mapping; 306 }; 307 #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
第271行,当I2C设备和驱动匹配成功以后probe函数就会执行,和platform驱动一样。
第297行,device_driver驱动结构体,如果使用设备树的话,需要设置device_driver的of_match_table成员变量,也就是驱动的兼容(compatible)属性。
第298行,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是一个宏,定义如下:
示例代码28.2.2.3 i2c_add_driver宏
806 #define i2c_add_driver(driver) \
807 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的注册示例代码如下:
示例代码28.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函数里面基本就是标准的字符设备驱动那一套了。
28.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内容如下:
示例代码28.2.3.1 i2c_bus_type结构体
505 struct bus_type i2c_bus_type = {
506 .name = "i2c",
507 .match = i2c_device_match,
508 .probe = i2c_device_probe,
509 .remove = i2c_device_remove,
510 .shutdown = i2c_device_shutdown,
511 };
.match就是I2C总线的设备和驱动匹配函数,在这里就是i2c_device_match这个函数,此函数内容如下:
示例代码28.2.3.2 i2c_device_match函数
103 static int i2c_device_match(struct device *dev,
struct device_driver *drv)
104 { 105 struct i2c_client *client = i2c_verify_client(dev); 106 struct i2c_driver *driver; 107 108 109 /* Attempt an OF style match */ 110 if (i2c_of_match_device(drv->of_match_table, client)) 111 return 1; 112 113 /* Then ACPI style match */ 114 if (acpi_driver_match_device(dev, drv)) 115 return 1; 116 117 driver = to_i2c_driver(drv); 118 119 /* Finally an I2C match */ 120 if (i2c_match_id(driver->id_table, client)) 121 return 1; 122 123 return 0; 124 }
第110行,i2c_of_match_device函数用于完成设备树中定义的设备与驱动匹配过程。比较I2C设备节点的compatible属性和of_device_id中的compatible属性是否相等,如果相当的话就表示I2C设备和驱动匹配。
第114行,acpi_driver_match_device函数用于ACPI形式的匹配。
第120行,i2c_match_id函数用于传统的、无设备树的I2C设备和驱动匹配过程。比较I2C设备名字和i2c_device_id的name字段是否相等,相等的话就说明I2C设备和驱动匹配成功。
28.3 RK3568 I2C 适配器驱动分析
上一小节我们讲解了Linux下的I2C子系统,重点分为I2C适配器驱动和I2C设备驱动,其中I2C适配器驱动就是SoC的I2C控制器驱动。I2C设备驱动是需要用户根据不同的I2C从设备去编写,而I2C适配器驱动一般都是SoC厂商去编写的,比如RK就已经提供了RK3568的I2C适配器驱动程序。在内核源码arch/arm64/boot/dts/rockchip/rk3568.dtsi设备树文件中找到RK3568的I2C控制器节点,节点内容如下所示:
示例代码41.3.1 I2C1控制器节点
2900 i2c1: i2c@fe5a0000 {
2901 compatible = "rockchip,rk3399-i2c";
2902 reg = <0x0 0xfe5a0000 0x0 0x1000>;
2903 clocks = <&cru CLK_I2C1>, <&cru PCLK_I2C1>;
2904 clock-names = "i2c", "pclk";
2905 interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
2906 pinctrl-names = "default";
2907 pinctrl-0 = <&i2c1_xfer>;
2908 #address-cells = <1>;
2909 #size-cells = <0>;
2910 status = "disabled";
2911 };
重点关注i2c1节点的compatible属性值,因为通过compatible属性值可以在Linux源码里面找到对应的驱动文件。这里i2c1节点的compatible属性值“rockchip,rk3399-i2c”,在Linux源码中搜索这个字符串即可找到对应的驱动文件。RK3568的I2C适配器驱动驱动文件为drivers/i2c/busses/i2c-rk3x.c,在此文件中有如下内容:
示例代码28.3.2 i2c-rk3x.c文件代码段
1258 static const struct of_device_id rk3x_i2c_match[] = { 1259 { 1260 .compatible = "rockchip,rv1108-i2c", 1261 .data = &rv1108_soc_data 1262 }, 1263 { 1264 .compatible = "rockchip,rv1126-i2c", 1265 .data = &rv1126_soc_data 1266 }, 1267 { 1268 .compatible = "rockchip,rk3066-i2c", 1269 .data = &rk3066_soc_data 1270 }, 1271 { 1272 .compatible = "rockchip,rk3188-i2c", 1273 .data = &rk3188_soc_data 1274 }, 1275 { 1276 .compatible = "rockchip,rk3228-i2c", 1277 .data = &rk3228_soc_data 1278 }, 1279 { 1280 .compatible = "rockchip,rk3288-i2c", 1281 .data = &rk3288_soc_data 1282 }, 1283 { 1284 .compatible = "rockchip,rk3399-i2c", 1285 .data = &rk3399_soc_data 1286 }, 1287 {}, 1288 };
从示例代码28.3.2可以看出,RK3568的I2C适配器驱动是个标准的platform驱动,由此可以看出,虽然I2C总线为别的设备提供了一种总线驱动框架,但是I2C适配器却是platform驱动。就像你的部门老大是你的领导,你是他的下属,但是放到整个公司,你的部门老大却也是老板的下属。
当设备和驱动匹配成功以后rk3x_i2c_probe函数就会执行,rk3x_i2c_probe函数就会完成I2C适配器初始化工作。
rk3x_i2c_probe函数内容如下所示(有省略):
示例代码28.3.3 rk3x_i2c_probe函数代码段
1291 static int rk3x_i2c_probe(struct platform_device *pdev) 1292 { 1293 struct device_node *np = pdev->dev.of_node; 1294 const struct of_device_id *match; 1295 struct rk3x_i2c *i2c; 1296 struct resource *mem; 1297 int ret = 0; 1298 u32 value; 1299 int irq; 1300 unsigned long clk_rate; 1301 1302 i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL); 1303 if (!i2c) 1304 return -ENOMEM; 1305 1306 match = of_match_node(rk3x_i2c_match, np); 1307 i2c->soc_data = match->data; 1308 1309 /* use common interface to get I2C timing properties */ 1310 i2c_parse_fw_timings(&pdev->dev, &i2c->t, true); 1311 1312 strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name)); 1313 i2c->adap.owner = THIS_MODULE; 1314 i2c->adap.algo = &rk3x_i2c_algorithm; 1315 i2c->adap.retries = 3; 1316 i2c->adap.dev.of_node = np; 1317 i2c->adap.algo_data = i2c; 1318 i2c->adap.dev.parent = &pdev->dev; 1319 1320 i2c->dev = &pdev->dev; 1321 1322 spin_lock_init(&i2c->lock); 1323 init_waitqueue_head(&i2c->wait); 1324 1325 i2c->i2c_restart_nb.notifier_call = rk3x_i2c_restart_notify; 1326 i2c->i2c_restart_nb.priority = 128; 1327 ret = register_pre_restart_handler(&i2c->i2c_restart_nb); 1328 if (ret) { 1329 dev_err(&pdev->dev, "failed to setup i2c restart handler.\n"); 1330 return ret; 1331 } 1332 1333 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1334 i2c->regs = devm_ioremap_resource(&pdev->dev, mem); 1335 if (IS_ERR(i2c->regs)) 1336 return PTR_ERR(i2c->regs); 1337 ...... 1376 1377 /* IRQ setup */ 1378 irq = platform_get_irq(pdev, 0); 1379 if (irq < 0) { 1380 dev_err(&pdev->dev, "cannot find rk3x IRQ\n"); 1381 return irq; 1382 } 1383 1384 ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq, 1385 0, dev_name(&pdev->dev), i2c); 1386 if (ret < 0) { 1387 dev_err(&pdev->dev, "cannot request IRQ\n"); 1388 return ret; 1389 } 1390 1391 platform_set_drvdata(pdev, i2c); 1392 1393 if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) { 1394 /* Only one clock to use for bus clock and peripheral clock */ 1395 i2c->clk = devm_clk_get(&pdev->dev, NULL); 1396 i2c->pclk = i2c->clk; 1397 } else { 1398 i2c->clk = devm_clk_get(&pdev->dev, "i2c"); 1399 i2c->pclk = devm_clk_get(&pdev->dev, "pclk"); 1400 } 1401 1402 if (IS_ERR(i2c->clk)) { 1403 ret = PTR_ERR(i2c->clk); 1404 if (ret != -EPROBE_DEFER) 1405 dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret); 1406 return ret; 1407 } 1408 if (IS_ERR(i2c->pclk)) { 1409 ret = PTR_ERR(i2c->pclk); 1410 if (ret != -EPROBE_DEFER) 1411 dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret); 1412 return ret; 1413 } 1414 1415 ret = clk_prepare(i2c->clk); 1416 if (ret < 0) { 1417 dev_err(&pdev->dev, "Can't prepare bus clk: %d\n", ret); 1418 return ret; 1419 } 1420 ret = clk_prepare(i2c->pclk); 1421 if (ret < 0) { 1422 dev_err(&pdev->dev, "Can't prepare periph clock: %d\n", ret); 1423 goto err_clk; 1424 } 1425 1426 i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb; 1427 ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb); 1428 if (ret != 0) { 1429 dev_err(&pdev->dev, "Unable to register clock notifier\n"); 1430 goto err_pclk; 1431 } 1432 1433 clk_rate = clk_get_rate(i2c->clk); 1434 rk3x_i2c_adapt_div(i2c, clk_rate); 1435 1436 ret = i2c_add_adapter(&i2c->adap); 1437 if (ret < 0) ...... 1449 }
第1302行,RK使用rk3x_i2c结构体来表示RK系列SOC的I2C控制器,这里使用devm_kzalloc函数来申请内存。
第1310行,调用drivers/i2c/i2c-core-base.c下的i2c_parse_fw_timings函数设置I2C频率,如果不设置“clock-frequency”则使用默认值100KHZ,如果设备树节点设置了“clock-frequency”属性的话 I2C 频率就使用 clock-frequency 属性值。
第1314行,rk3x_i2c结构体有个adap的成员变量,adap就是i2c_adapter,这里初始化i2c_adapter。同时设置i2c_adapter的algo成员变量为rk3x_i2c_algorithm,也就是设置i2c_algorithm。
第1333~1334行,调用platform_get_resource函数从设备树中获取I2C1控制器寄存器物理基地址,也就是0xfe5a0000。获取到寄存器基地址以后使用devm_ioremap_resource函数对其进行内存映射,得到可以在Linux中使用的虚拟地址。
第1378行,调用platform_get_irq函数获取中断号。
第1384行,注册I2C控制器的中断。
第1436行,调用i2c_add_adapter函数向Linux内核注册i2c_adapter。
rk3x_i2c_probe函数主要的工作就是一下两点:
①、初始化 i2c_adapter,设置 i2c_algorithm 为 rk3x_i2c_algorithm,最后向 Linux 内核注册i2c_adapter。
②、初始化 I2C1 控制器的相关寄存器。rk3x_i2c_algorithm包含 I2C1 适配器与 I2C 设备的通信函数 master_xfer,rk3x_i2c_algorithm结构体定义如下:
示例代码28.3.4 rk3x_i2c_algorithm结构体
1218 static const struct i2c_algorithm rk3x_i2c_algorithm = {
1219 .master_xfer = rk3x_i2c_xfer,
1220 .functionality = rk3x_i2c_func,
1221 };
我们先来看一下. functionality,functionality用于返回此I2C适配器支持什么样的通信协议,在这里functionality就是rk3x_i2c_func函数,rk3x_i2c_func函数内容如下:
示例代码28.3.5 rk3x_i2c_func函数
1213 static u32 rk3x_i2c_func(struct i2c_adapter *adap)
1214 {
1215 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
1216 }
重点来看一下rk3x_i2c_xfer函数,因为最终就是通过此函数来完成与I2C设备通信的,此函数内容如下:
示例代码28.3.6 rk3x_i2c_xfer函数
1071 static int rk3x_i2c_xfer(struct i2c_adapter *adap, 1072 struct i2c_msg *msgs, int num) 1073 { 1074 struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data; 1075 unsigned long timeout, flags; 1076 u32 val; 1077 int ret = 0; 1078 int i; 1079 1080 if (i2c->suspended) 1081 return -EACCES; 1082 1083 spin_lock_irqsave(&i2c->lock, flags); 1084 1085 clk_enable(i2c->clk); 1086 clk_enable(i2c->pclk); 1087 1088 i2c->is_last_msg = false; 1089 1090 /* 1091 * Process msgs. We can handle more than one message at once (see 1092 * rk3x_i2c_setup()). 1093 */ 1094 for (i = 0; i < num; i += ret) { 1095 ret = rk3x_i2c_setup(i2c, msgs + i, num - i); 1096 1097 if (ret < 0) { 1098 dev_err(i2c->dev, "rk3x_i2c_setup() failed\n"); 1099 break; 1100 } 1101 1102 if (i + ret >= num) 1103 i2c->is_last_msg = true; 1104 1105 rk3x_i2c_start(i2c); 1106 1107 spin_unlock_irqrestore(&i2c->lock, flags); 1108 1109 timeout = wait_event_timeout(i2c->wait, !i2c->busy, 1110 msecs_to_jiffies(WAIT_TIMEOUT)); 1111 1112 spin_lock_irqsave(&i2c->lock, flags); 1113 1114 if (timeout == 0) { 1115 dev_err(i2c->dev, "timeout, ipd: 0x%02x, state: %d\n", 1116 i2c_readl(i2c, REG_IPD), i2c->state); 1117 1118 /* Force a STOP condition without interrupt */ 1119 rk3x_i2c_disable_irq(i2c); 1120 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; 1121 val |= REG_CON_EN | REG_CON_STOP; 1122 i2c_writel(i2c, val, REG_CON); 1123 1124 i2c->state = STATE_IDLE; 1125 1126 ret = -ETIMEDOUT; 1127 break; 1128 } 1129 1130 if (i2c->error) { 1131 ret = i2c->error; 1132 break; 1133 } 1134 } 1135 1136 rk3x_i2c_disable_irq(i2c); 1137 rk3x_i2c_disable(i2c); 1138 1139 clk_disable(i2c->pclk); 1140 clk_disable(i2c->clk); 1141 1142 spin_unlock_irqrestore(&i2c->lock, flags); 1143 1144 return ret < 0 ? ret : num; 1145 }
第1095行调用 rk3x_i2c_setup 函数来进行 I2C 相关的配置和准备工作。如果返回值 ret 小于 0,表示设置失败,函数会打印错误消息并跳出循环。
第1105行,启动函数来启动 I2C 传输,具体内容这里就不分析了,大家感兴趣可以自己阅读代码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。