一、Linux的I2C子系统框架介绍
- I2C总线和I2C协议、SMBus协议
二、I2C子系统的分层介绍
- I2C设备驱动,包括一般的I2C设备驱动和i2c-dev.c,形成两种节点,/dev/xxx和/dev/i2c-N
- I2C核心层,如i2c-core-smbus.c、i2c-core-base.c,给上层提供各种API
- I2C控制器驱动,包含一般的I2C控制器驱动和i2c-gpio.c,后者使用GPIO模拟I2C的传输过程
三、典型的设备树文件
// 普通的I2C设备,挂在I2C0总线下,地址是0x50
&i2c0{
i2c_dev@50 {
compatible = "i2c_dev_compatible";
reg = <0x50>;
};
};
// 一般的I2C控制器
i2c_adap_virt: virtual_i2c_adapter {
compatible = "user,virtual_i2c_adapter";
#address-cells = <1>;
#size-cells = <0>;
... // 其他信息
};
// 使用GPIO模拟的I2C控制器
i2c-gpio-0 {
compatible = "i2c-gpio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c_bitbang>;
gpios = <&GPIOA 7 GPIO_ACTIVE_HIGH>, //SCL
<&GPIOA 8 GPIO_ACTIVE_HIGH>; //SDA
i2c-gpio,sda-open-drain;
i2c-gpio,scl-open-drain;
i2c-gpio,delay-us=<5>; //100K
#address-cells = <1>;
#size-cells = <0>;
... // 其他信息
};
四、I2C的重要结构体
struct i2c_adapter { // 描述I2C控制器
const struct i2c_algorithm *algo; // I2C具体的传输实现
int nr; // 第几个I2C控制器
...
};
struct i2c_algorithm { // 描述I2C的传输
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); // 传输函数
...
u32 (*functionality)(struct i2c_adapter *adap); // 控制器支持哪些功能
};
struct i2c_msg { // 描述I2C传递的消息
__u16 addr; // 从机地址
__u16 flag; // 标志位
__u16 len; // 长度
__u8 *buf; // 指向msg数据的指针
};
struct i2c_driver { // 描述I2C驱动,类似platform_driver
int (*probe_new)(struct i2c_client *client);
int (*remove)(struct i2c_client *client);
struct device_driver driver; // compatible成员在这里
const struct i2c_device_id *id_table;
...
};
struct i2c_client { // 描述I2C设备,类似platform_device
unsigned short flags; // 标志
unsigned short addr; // I2C设备地址
struct i2c_adapter *adapter; // 控制器
struct device dev; // 必备的device结构体
};
五、驱动程序的流程
// I2C设备驱动
1. 入口函数中,使用i2c_add_driver注册一个i2c_driver,出口函数中,使用i2c_del_driver注销一个i2c_driver
2. I2C-BUS和Platform-BUS类似,入口函数注册相应driver,出口函数注销driver,compatible属性匹配时,调用probe函数
3. probe函数中,类似其他字符设备。使用alloc_chrdev_region、cdev_init、cdev_add、class_create、device_create
4. 关键的file_operations结构体中的读写函数,可以借助i2c_smbus_xx或者i2c_transfer函数实现
// I2C控制器驱动
1. 使用平台设备驱动程序的框架,注册I2C控制器驱动
2. probe函数中,分配、设置、注册一个i2c_adapter结构体,使用i2c_add_adapter注册一个控制器的结构体
3. 关键的成员i2c_algorithm需要最少实现master_xfer和functionality函数
六、i2c-dev.c介绍
1. 入口函数
register_chrdev_region // 主设备号89,名称为i2c
class_create // 创建类,辅助创建节点
i2c_for_each_dev(NULL, i2cdev_attach_adapter); // 绑定存在的I2C控制器,形成的/dev/i2c-N表示每个控制器
cdev_init
cdev_device_add
2. 字符设备对应的fops
open
adap = i2c_get_adapter(minor); // 得到控制器
kzalloc // 分配一个i2c_client表示I2C设备
设置I2C设备所属的控制器
release
i2c_put_adapter(client->adapter); // 释放控制器
kfree // 释放i2c_client的空间
read
i2c_master_recv
write
i2c_master_send
ioctl
I2C_SLAVE/I2C_SLAVE_FORCE // 设置client的addr
I2C_TENBIT // 设置client的flag
I2C_PEC // 设置client的flag
I2C_FUNCS // 调用i2c_algorithm的functionality函数获取能力
I2C_RDWR // 构建msg,执行i2c_transfer
I2C_SMBUS // 构建msg,执行i2c_smbus_xfer
...
七、i2c-gpio.c介绍
1. 使用平台设备驱动程序的框架,注册该platform_driver
2. probe函数
解析设备树,获得时钟频率、SCL、SDA是否上拉等信息
构建SCL和SDA对应的gpio_desc结构体
构建adapter结构体对象
i2c_bit_add_numbered_bus(adap)
注册i2c_bit_algo,实现transfer函数和functionality函数
调用add_adapter,注册一个adapter
八、总结
- 可以看到一般的I2C设备驱动和i2c-dev.c的实现类似,只不过两者生成的节点不同,后者对控制器生成了一个单独的节点,同样可以调用i2c协议或smbus协议的xfer函数
- i2c-gpio.c和一般的I2C控制器驱动类似,都使用add_adapter实现控制器的注册,关键之处在于xfer函数,前者使用GPIO做模拟实现I2C协议。