kernel I2C框架

kernel中的I2C驱动框架,笼统的概括,可以分为三个部分:core、adapter、client。其中core负责维护i2c_bus,匹配已经存在的adapter/driver/client;adapter负责master控制器,提供通信API;client_device描述从设备的属性;client_driver即从设备的驱动。

一个平台上需要支持I2C,必须先要实现I2C adapter驱动。而基于这个平台的开发者,一般只需要实现I2C框架的从设备,也就是client device和client device。

adapter驱动
adapter驱动属于平台代码,它负责对I2C控制器进行操作。一般我们也实现一个platform_driver,在probe函数中注册adapter。先关的API:

struct i2c_adapter; //adapter结构体
struct i2c_algorithm;//I2C算法结构体

i2c_add_numbered_adapter(...)
of_i2c_register_devices(...)

其中的关键就是我们需要自己实现一个algorithm结构体的master_xfer和functionality回调。他们会被core使用来发送数据。

adapter中我们会注册相关的中断函数,并且在xfer回调中根据传入的struct i2c_msg参数来启动传输数据。并在xfer中等待一帧数据传输完毕才返回,并且设置一个wait queue来做等待超时情况的处理。

i2c device驱动
I2C子系统上的逻辑设备仍然是由client driver本身或由平台相关的代码静态地创建的。
有两种方法创建I2C逻辑设备, 一是直接调用i2c_new_device()创建, 这种方式需要提供struct i2c_adapter作为参数。所以这种方式必须在i2c_adapter设备加载来才能使用,一般是在client driver代码中来创建,而非平台代码。一些第三方提供的client driver会使用这种方式。
另外一种方式就是在平台初始化时静态登记,调用i2c_register_board_info预先登记设备信息, 但真正不创建设备。往后当有对应bus号的adapter注册时,由core负责创建这些设备。

其实我们更可能用的一种方式是dts配置的方法。上一节中提到一个API叫做of_i2c_register_devices,其实它就是解析dts节点来创建client设备的。根据它的实现,其实它其中也是调用了i2c_new_device()函数的,所以of_i2c_register_devices只能在i2c_add_numbered_adapter之后被调用使用。

client驱动主要围绕着struct i2c_driver、struct i2c_client结构, 前者对外提供诸如设备枚举、电源管理等回调,后者由core在probe时提供给driver,描述了关联的设备的诸如名称、地址、IRQ等操作从设备的必要信息。驱动完成设备枚举后,拿到设备对应的i2c_client和i2c_adapter结构, 就可以调用core的API访问从设备了。

int i2c_master_send(const struct i2c_client *client, const char *buf, int count);
int i2c_master_recv(const struct i2c_client *client, char *buf, int count);
// 这2个是基本的传输函数,用于在一个传输周期内(START-STOP)传输slave地址+RW+一段数据。
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
// 更加强大并通用的传输函数, 可以由多个struct i2c_msg拼凑出复杂的传输. 其中的i2c_msg定义如下:
struct i2c_msg {
    __u16 addr; /* slave address            */
    __u16 flags;
#define I2C_M_TEN       0x0010  /* this is a ten bit chip address */
#define I2C_M_RD        0x0001  /* read data, from slave to master */
#define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */
    __u16 len;      /* msg length               */
    __u8 *buf;      /* pointer to msg data          */
};
// i2c_transfer顺序处理*msgs指向的所有的i2c_msg, 每个i2c_msg执行前都会发START状态,
// 当最后一个i2c_msg执行完成后会发STOP状态.
s32 i2c_smbus_read_byte(const struct i2c_client *client);
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value);
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value);
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16 value);
s32 i2c_smbus_read_word_swapped(const struct i2c_client *client, u8 command);
s32 i2c_smbus_write_word_swapped(const struct i2c_client *client, u8 command, u16 value);
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values);
s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values);
s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, u8 *values);
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values);
// 这些是smbus协议的常用函数, 方便兼容smbus协议的client使用.
// smbus协议是i2c协议的一个子集, 常见的i2c slave器件的寄存器读写都符合smbus协议,
// 这样的话直接使用这些函数会比使用i2c_transfer方便很多.

smbus协议请参考内核文档android/kernel/Documentation/i2c/smbus-protocol
关于I2C框架的使用, 内核有相关的文档:
android/kernel/Documentation/i2c

gpio模拟I2C控制器
上面介绍的adapter一般都是I2C控制器驱动,但是假如平台上的控制器不够用,我们可以使用gpio来模拟I2C的两线协议来发送数据。

内核中已经有了完整的一套机制,他被叫做bitbing。它主要包括两个内核代码文件i2c-gpio.c和i2c-algo-bit.c。详细内容自阅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值