15.3 Linux I 2 C适配器驱动
15.3.1 I 2 C适配器驱动的注册与注销
由于I 2 C总线控制器通常在内存上,所以I 2 C总线控制器本身也连接在platform总线上,要通过platform_driver和platform_device的匹配来执行。因此尽管I 2 C适配器给别人提供了总线,I 2 C适配器自己也被认为是接在platform总线上的一个客户。Linux的总线、设备和驱动模型实际上是一个树形结构,每个节点虽然可能成为别人的总线控制器,但是自己也被认为是从上一级总线枚举出来的。
通常在与I 2 C适配器所对应的platform_driver的probe()函数中完成两个工作。
1、初始化I 2 C适配器所使用的硬件资源,如申请I/O地址、中断号、时钟等。
2、通过i2c_add_adapter()添加i2c_adapter的数据结构,这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针所初始化。
通常在platform_driver的remove()函数中完成与加载函数相反的工作。
1、释放I 2 C适配器所使用的硬件资源,如释放I/O地址、中断号、时钟等。
2、通过i2c_del_adapter()删除i2c_adapter的数据结构。
代码清单15.9 I 2 C适配器驱动的注册和注销模板。
static int xxx_i2c_probe(struct platform_device *pdev)
{
struct i2c_adapter *adap;
...
xxx_adpater_hw_init();//与具体的CPU和I 2 C适配器硬件直接相关
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;
rc = i2c_add_adapter(adap);
...
}
static int xxx_i2c_remove(struct platform_device *pdev)
{
...
xxx_adpater_hw_free();//与具体的CPU和I 2 C适配器硬件直接相关
i2c_del_adapter(&dev->adapter);
return 0;
}
static const struct of_device_id xxx_i2c_of_match[] = {
{.compatible = "vendor,xxx-i2c",},
{},
};
MODULE_DEVICE_TABLE(of, xxx_i2c_of_match);
static struct platform_driver xxx_i2c_driver = {
.driver = {
.name = "xxx-i2c",
.owner = THIS_MODULE,
.of_match_table = xxx_i2c_of_match,
},
.probe = xxx_i2c_probe,
.remove = xxx_i2c_remove,
};
module_platform_driver(xxx_i2c_driver);
备注:
上述代码中的xxx_adpater_hw_init()和xxx_adpater_hw_free()函数的实现都与具体的CPU和I 2 C适
配器硬件直接相关。15.3.2 I 2 C总线的通信方法
需要为特定的I 2 C适配器实现通信方法,主要是实现i2c_algorithm的functionality()函数和master_xfer()函数。
functionality()函数非常简单,用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、
I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。
master_xfer()函数在I 2 C适配器上完成传递给该函数的i2c_msg数组中的每个I 2 C消息,代码清单15.10所
示为xxx设备的master_xfer()函数模板。
// Generic master transfer entrypoint.
static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
...
for (i = 0; i < num; i++) {
i2c_adapter_xxx_start(); /* 产生开始位 */
if (msgs[i]->flags &I2C_M_RD) {/* 读消息 */
i2c_adapter_xxx_setaddr((msg->addr << 1) | 1); /* 发送从设备读地址 */
i2c_adapter_xxx_wait_ack(); /* 获得从设备的 ack */
i2c_adapter_xxx_readbytes(msgs[i]->buf, msgs[i]->len); /* 从从设备读数据:读取 msgs[i] ->len长的数据到 msgs[i]->buf */
} else { /* 写消息 */
i2c_adapter_xxx_setaddr(msg->addr << 1); /* 发送从设备写地址 */
i2c_adapter_xxx_wait_ack(); /* 获得从设备的 ack */
i2c_adapter_xxx_writebytes(msgs[i]->buf, msgs[i]->len); /* 向从设备写数据:写入msgs[i]->len长的数据到 msgs[i]->buf */
}
}
i2c_adapter_xxx_stop(); /* 产生停止位 */
}
分析:
上述代码给出一个master_xfer()函数处理I 2 C消息数组的流程,对于数组中的每个消息,先判断消息类型,若为读消息,则赋从设备地址为(msg->addr<<1)|1,否则为msg->addr<<1。对每个消息产生一个开始位,紧接着传送从设备地址,然后开始数据的发送或接收,且对最后的消息还需产生一个停止位。
图15.3所示为整个master_xfer()完成的时序(时间顺序)。
图15.3 master_xfer()完成的时序
master_xfer()函数模板中的i2c_adapter_xxx_start()、i2c_adapter_xxx_setaddr()、
i2c_adapter_xxx_wait_ack()、i2c_adapter_xxx_readbytes()、i2c_adapter_xxx_writebytes()和
i2c_adapter_xxx_stop()函数用于完成适配器的底层硬件操作,与I 2 C适配器和CPU的具体硬件直接相
关,由工程师根据芯片的数据手册(data sheet)来实现。
i2c_adapter_xxx_readbytes()用于从从设备上接收一串数据,i2c_adapter_xxx_writebytes()用于向从设备写入一串数据,这两个函数的内部也会涉及I 2 C总线协议中的ACK应答。
master_xfer()函数的实现形式有多种,多数驱动以中断方式来完成这个流程,比如发起硬件操作请求后,将自己调度出去,因此中间会伴随着睡眠的动作。
多数I 2 C总线驱动会定义一个xxx_i2c结构体,作为i2c_adapter的algo_data(类似“私有数据”),其中绑定
I 2 C消息指针、包含I 2 C消息索引及I 2 C适配器Algorithm访问控制用的自旋锁、等待队列等,而
master_xfer()函数在完成i2c_msg数组中消息的处理时,也经常需要访问xxx_i2c结构体的成员以获取寄存器基地址、锁等信息。代码清单15.11所示为一个典型的xxx_i2c结构体的定义,与图15.2中的xxx_i2c是对应的,具体的实现因硬件而异。
代码清单15.11 xxx_i2c结构体模板
struct xxx_i2c {
spinlock_t lock; //自旋锁
wait_queue_head_t wait;//等待队列
struct i2c_msg *msg; // i2c_msg数组
unsigned int msg_num;// i2c_msg数组个数
unsigned int msg_idx;//i2c_msg数组索引
unsigned int msg_ptr;
...
struct i2c_adapter adap;
};