7.4.5. 设计说明¶
7.4.5.1. 源码说明¶
源代码位于:linux-5.10/drivers/i2c/busses/
I2C的驱动文件如下:
| 文件 | 说明 | 
|---|---|
| i2c-artinchip.h | aic I2C公用头文件,I2C模块的寄存器定义,结构体定义等 | 
| i2c-artinchip-master.c | I2C作为master时的驱动文件 | 
| i2c-artinchip-slave.c | I2C作为slave时的驱动文件 | 
| i2c-artinchip-common.c | I2C一些公用寄存器读写函数的实现,以及plaform_driver的定义 | 
7.4.5.2. 模块架构¶
linux中I2C子系统的体系结构如下图所示
 
在I2C子系统中,SOC厂商需要实现的就是I2C adapter部分的驱动,I2C adapter是对I2C controller的软件抽象。具体到上图,就是实现I2C adapter的algorithm以及特定SOC的I2C代码部分。I2C模块支持master和slave两种模式,所以I2C adapter的驱动实现也就分为两部分:I2C master驱动和I2C slave驱动。
7.4.5.2.1. I2C master¶
I2C作为master时,驱动的实现主要包括4个部分:
- 硬件参数配置:主要是设置I2C工作的主机模式,7bit或10bit寻址,寻址的从机地址设置,FIFO设置以及总线传输速率等。 
- SCL时序参数设置:根据设置的总线传输速率,设置SCL的高低电平时间。 
- i2c_algorithm的实现:作为主机端,主要是master_xfer的实现。在驱动实现中,以message为单位进行数据的收发,数据的传输采用中断的方式。 
- 中断的处理:处理master端的数据收发,并产生相应的start、ack、nack、restart、stop信号。 
7.4.5.2.2. I2C slave¶
I2C作为从机时,需要一个相应的后端软件(对I2C从设备的软件模拟),该后端软件与I2C adapter驱动,组合成具有相应功能的I2C从设备。内核的I2C子系统框架中提供了一个EEPROM的软件后端,与I2C slave驱动一起,可以作为一个具有I2C接口的EEPROM使用。
 
I2C作为slave时,驱动的实现主要包括3个部分:
- 硬件参数配置:设置I2C工作的从模式,FIFO设置等。 
- i2c_algorithm的实现:作为从机端,主要是reg_slave和unreg_slave的实现。reg_slave用于将一个i2c_client注册到从模式的i2c adapter上,unreg_slave的功能与reg_slave相反。 
- 中断的处理:处理I2C从机接收到的各种中断信号,并调用相应的回调函数进行数据的读写。 
综上,I2C模块的驱动实现,主要的工作有:
- 提供I2C控制器的platform驱动,初始化I2C适配器,判断I2C模块工作的主从模式,执行不同的初始化流程。 
- I2C模块作为主机时,提供I2C适配器的algorithm,并用具体适配器的xxx_xfer函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋值给i2c_adapter的algo指针。处理master端时序的设置以及I2C作为主机时的各种中断信号处理。 
- I2C模块作为从机时,提供I2C适配器的algorithm,实现具体适配器的reg_slave和unreg_slave函数,并把i2c_algorithm指针赋值给i2c_adapter的algo指针。处理I2C作为从机时的各种中断信号处理。 
7.4.5.3. 关键流程设计¶
7.4.5.3.3. I2C模块总线信号¶
在I2C总线的数据传输过程,由start/restart/stop作为总线的控制信号。了解I2C模块中start/restart/stop信号的生成方式,有助于了解驱动的源码实现。
7.4.5.3.3.1. master transmitter¶
 
对图中3个关键点的解释:
- I2C作为master transmitter时,当向TXFIFO中写入数据时,I2C模块会自动发出start信号 
- 若stop位未置位,则当TXFIFO中的数据全部发送,TXFIFO为空时,会保持SCL为低电平,直到再次向TXFIFO中写入数据 
- 再次向TXFIFO写入数据时,将stop位置1,则在完成该字节的发送后,master会自动发送stop信号 
7.4.5.3.3.2. master receiver¶
 
对图中3个关键点的解释:
- I2C作为master receiver时,当向TXFIFO写入读命令(即向I2C_DATA_CMD写入读命令)时,I2C模块会自动发送start信号 
- 当接收到slave端发送的数据后,只有再次发送一次读命令,才会对本次收到的数据恢复ACK确认信号 
- master在接收到最后一个数据后,回复NACK,slave端才会结束数据的传送。在发送最后一个读命令时,同时将stop位置位,则master在接收到slave发送的数据后,I2C模块会自动发送NACK信号 
注解
I2C模块的数据传输,无论是transmitter还是receiver,都会用到TXFIFO,transmitter时用来发送数据,receiver时用来发送命令。所以,中断处理中,触发TXFIFO_EMPTY中断的,可能是read msg,也可能是write msg
7.4.5.3.4. 中断流程¶
 
7.4.5.4. 数据结构设计¶
管理I2C控制器资源的顶层结构体
struct aic_i2c_dev {
        struct device *dev;
        void __iomem *base;
        struct i2c_adapter adap;
        struct completion cmd_complete;
        struct clk *clk;
        struct reset_control *rst;
        int irq;
        enum aic_i2c_speed i2c_speed;
        u16 scl_hcnt;
        u16 scl_lcnt;
        u32 abort_source;
        struct i2c_msg *msg;
        enum aic_msg_status msg_status;
        int buf_write_idx;
        int buf_read_idx;
        bool is_first_message;
        bool is_last_message;
        int msg_err;
        struct i2c_timings timings;
        u32 master_cfg;
        u32 slave_cfg;
        struct i2c_client *slave;
};
部分变量说明:
- cmd_complete:完成量,用于指示一个message是否传输完成 
- scl_hcnt:SCL时钟高电平时钟数 
- scl_lcnt:SCL时钟低电平时钟数 
- msg:指向当前传输的message 
- buf_write_idx:当前message为write msg时,buf_write_idx为写数据的计数。当前message为read msg时,buf_write_idx为写命令的计数(I2C模块需要每次写read命令,才能读出数据)。 
- buf_read_idx:读数据的计数 
- is_first_message:是否是第一个message 
- is_last_message:是否是最后一个message 
7.4.5.5. 接口设计¶
7.4.5.5.1. i2c_handle_tx_abort¶
| 函数原型 | int i2c_handle_tx_abort(struct aic_i2c_dev *i2c_dev) | 
|---|---|
| 功能说明 | 打印i2c发生abort的原因,并返回相应的error值 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 根据不同的abort原因,返回不同的error值 | 
| 注意事项 | 
7.4.5.5.2. i2c_scl_cnt¶
| 函数原型 | int i2c_scl_cnt(u32 ic_clk, enum aic_i2c_speed aic_speed, u16 *hcnt, u16 *lcnt) | 
|---|---|
| 功能说明 | 根据i2c模块工作的时钟频率和i2c的传输速率,返回需要设置的hcnt值和lcnt值 | 
| 参数定义 | ic_clk:i2c模块工作的时钟频率,以KHz为单位 aic_speed:表示i2c的传输速率,是标准模式还是快速模式 hcnt:指向需要设置的hcnt值的指针 lcnt:指向需要设置的lcnt值的指针 | 
| 返回值 | 0:函数执行成功 -EINVAL:hcnt或lcnt为空指针 | 
| 注意事项 | 
7.4.5.5.3. i2c_set_timmings_master¶
| 函数原型 | static int i2c_set_timmings_master(struct aic_i2c_dev *i2c_dev) | 
|---|---|
| 功能说明 | 设置master产生SCL时钟的时序参数 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 0:函数执行成功 -EINVAL:参数非法 | 
| 注意事项 | 若在DTS中有设置i2c的时序参数scl_raise_ns或scl_fall_ns,则会由i2c_parse_fw_timings对DTS进行解析,在此函数中不再调用i2c_scl_cnt进行设置 | 
7.4.5.5.4. i2c_init_master¶
| 函数原型 | static void i2c_init_master(struct aic_i2c_dev *i2c_dev) | 
|---|---|
| 功能说明 | 初始化master模式下的参数设置,写入hcnt和lcnt,配置TXFIFO和RXFIFO阈值,将i2c配置为主模式 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 无 | 
| 注意事项 | 
7.4.5.5.5. i2c_xfer_msg_init¶
| 函数原型 | static void i2c_xfer_msg_init(struct aic_i2c_dev *i2c_dev) | 
|---|---|
| 功能说明 | 在传输每个msg前进行的初始化,主要是将指示每个msg状态的变量设置为初始值,并设置传输的从地址设置,使能中断 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 无 | 
| 注意事项 | 
7.4.5.5.6. i2c_xfer_msg¶
| 函数原型 | static int i2c_xfer_msg(struct aic_i2c_dev *i2c_dev, struct i2c_msg *msg, bool is_first, bool is_last) | 
|---|---|
| 功能说明 | 单个msg的传输函数,若当前msg是第一个msg,则会等待总线空闲,然后执行i2c_xfer_msg_init进行msg传输前的初始化工作,等待当前msg传输完成 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 msg:指向当前将要传输的msg is_first:指示当前msg是否是第一个msg is_last:指示当前msg是否是最后一个msg | 
| 返回值 | 0:执行成功 <0:执行过程中发生错误 | 
| 注意事项 | 
7.4.5.5.7. i2c_xfer¶
| 函数原型 | static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) | 
|---|---|
| 功能说明 | i2c的传输函数,该函数可完成多个msg的传输,使用该函数完成对i2c_algorithm中master_xfer函数指针的填充 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 msgs:指向msg数组的指针 num:需要传输的msg个数 | 
| 返回值 | 0:执行成功 <0:执行过程中发生错误 | 
| 注意事项 | 
7.4.5.5.8. i2c_handle_read¶
| 函数原型 | static void i2c_handle_read(struct aic_i2c_dev *i2c_dev) | 
|---|---|
| 功能说明 | 当触发RX_FULL中断时,调用该函数读取接收到的数据,若完成当前msg的接收,则释放完成量。该函数在中断中调用 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 无 | 
| 注意事项 | 
7.4.5.5.9. i2c_handle_write¶
| 函数原型 | static void i2c_handle_write(struct aic_i2c_dev *i2c_dev) | 
|---|---|
| 功能说明 | 当触发TX_EMPTY中断时,调用该函数。若是读msg,则调用该函数发送读命令,若是写msg,则调用该函数发送数据 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 无 | 
| 注意事项 | 
7.4.5.5.10. i2c_init_slave¶
| 函数原型 | static void i2c_init_slave(struct aic_i2c_dev *i2c_dev) | 
|---|---|
| 功能说明 | 初始化slave模式下的参数设置,配置TXFIFO和RXFIFO阈值,设置i2c为slave模式 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 无 | 
| 注意事项 | 
7.4.5.5.11. i2c_reg_slave¶
| 函数原型 | static int i2c_reg_slave(struct i2c_client *slave) | 
|---|---|
| 功能说明 | 初始化slave模式下的参数设置,配置slave是10bit寻址还是7bit寻址,设置从机的地址 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 0:执行成功 -EBUSY:忙等待 | 
| 注意事项 | 
7.4.5.5.12. i2c_unreg_slave¶
| 函数原型 | static int i2c_unreg_slave(struct i2c_client *slave) | 
|---|---|
| 功能说明 | 与i2c_reg_slave功能相反 | 
| 参数定义 | i2c_dev:指向自定义的struct aic_i2c_dev结构体 | 
| 返回值 | 0:执行成功 | 
| 注意事项 | 
 
