7.4.5. 设计说明

7.4.5.1. 源码说明

源代码位于:linux-5.10/drivers/i2c/busses/

I2C的驱动文件如下:

文件

说明

i2c-zx.h

aic I2C公用头文件,I2C模块的寄存器定义,结构体定义等

i2c-zx-master.c

I2C作为master时的驱动文件

i2c-zx-slave.c

I2C作为slave时的驱动文件

i2c-zx-common.c

I2C一些公用寄存器读写函数的实现,以及plaform_driver的定义

7.4.5.2. 模块架构

linux中I2C子系统的体系结构如下图所示

../../../_images/subsystem_arch.png

在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个部分:

  1. 硬件参数配置:主要是设置I2C工作的主机模式,7bit或10bit寻址,寻址的从机地址设置,FIFO设置以及总线传输速率等。

  2. SCL时序参数设置:根据设置的总线传输速率,设置SCL的高低电平时间。

  3. i2c_algorithm的实现:作为主机端,主要是master_xfer的实现。在驱动实现中,以message为单位进行数据的收发,数据的传输采用中断的方式。

  4. 中断的处理:处理master端的数据收发,并产生相应的start、ack、nack、restart、stop信号。

7.4.5.2.2. I2C slave

I2C作为从机时,需要一个相应的后端软件(对I2C从设备的软件模拟),该后端软件与I2C adapter驱动,组合成具有相应功能的I2C从设备。内核的I2C子系统框架中提供了一个EEPROM的软件后端,与I2C slave驱动一起,可以作为一个具有I2C接口的EEPROM使用。

../../../_images/twi_slave.png

I2C作为slave时,驱动的实现主要包括3个部分:

  1. 硬件参数配置:设置I2C工作的从模式,FIFO设置等。

  2. i2c_algorithm的实现:作为从机端,主要是reg_slave和unreg_slave的实现。reg_slave用于将一个i2c_client注册到从模式的i2c adapter上,unreg_slave的功能与reg_slave相反。

  3. 中断的处理:处理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.1. 初始化流程

I2C模块驱动的初始化流程如下:

../../../_images/design_1.png

7.4.5.3.2. 传输流程

在I2C master驱动中,数据的传输由i2c_xfer发起,可以完成多个i2c_msg的传输。传输流程如下:

../../../_images/design_2.png

7.4.5.3.3. I2C模块总线信号

在I2C总线的数据传输过程,由start/restart/stop作为总线的控制信号。了解I2C模块中start/restart/stop信号的生成方式,有助于了解驱动的源码实现。

7.4.5.3.3.1. master transmitter

../../../_images/design_3.png

对图中3个关键点的解释:

  1. I2C作为master transmitter时,当向TXFIFO中写入数据时,I2C模块会自动发出start信号

  2. 若stop位未置位,则当TXFIFO中的数据全部发送,TXFIFO为空时,会保持SCL为低电平,直到再次向TXFIFO中写入数据

  3. 再次向TXFIFO写入数据时,将stop位置1,则在完成该字节的发送后,master会自动发送stop信号

7.4.5.3.3.2. master receiver

../../../_images/design_4.png

对图中3个关键点的解释:

  1. I2C作为master receiver时,当向TXFIFO写入读命令(即向I2C_DATA_CMD写入读命令)时,I2C模块会自动发送start信号

  2. 当接收到slave端发送的数据后,只有再次发送一次读命令,才会对本次收到的数据恢复ACK确认信号

  3. 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. 中断流程

../../../_images/design_5.png

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:执行成功

注意事项