i2c adapter
i2c 适配器框架分析
i2c适配器驱动走的是平台总线驱动那一套东西,过程如下:
设备树在根节点提供i2c控制器的设备节点如下:
/ { …… i2c-bus-virtual { compatible = "flyrobot,i2c-bus-virtual"; }; …… };
编写平台总线驱动
提供自己的平台驱动结构体,里面有probe和remove函数
static const struct of_device_id i2c_bus_virtual_dt_ids[] = { { .compatible = "flyrobot,i2c-bus-virtual", },// 和设备树匹配 { /* sentinel */ } }; static struct platform_driver i2c_bus_virtual_driver = { .driver = { .name = "i2c-gpio", .of_match_table = of_match_ptr(i2c_bus_virtual_dt_ids), }, .probe = i2c_bus_virtual_probe, .remove = i2c_bus_virtual_remove, };
入口函数把自己的
i2c_bus_virtual_driver注册平台驱动总线上,当和设备树匹配成功后,会调用probe函数static int __init i2c_bus_virtual_init(void) { int ret; ret = platform_driver_register(&i2c_bus_virtual_driver); if (ret) printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret); return ret; }
probe函数
分配适配器
g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL);
设置适配器
g_adapter->owner = THIS_MODULE; g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; g_adapter->nr = -1; g_adapter->algo = &i2c_bus_virtual_algo; // 算法结构体,很重要
注意:
i2c_bus_virtual_algo和重要下面会分析。注册适配器
ret = i2c_add_adapter(adap); //不管adap->nr是什么,都动态设置adap->nr ret = i2c_add_numbered_adapter(adap); //如果adap->nr == -1 则动态分配nr; 否则使用该nr
整个probe函数如下:
static int i2c_bus_virtual_probe(struct platform_device *pdev) { /* get info from device tree, to set i2c_adapter/hardware */ /* alloc, set, register i2c_adapter */ g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL); g_adapter->owner = THIS_MODULE; g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; g_adapter->nr = -1; snprintf(g_adapter->name, sizeof(g_adapter->name), "i2c-bus-virtual"); g_adapter->algo = &i2c_bus_virtual_algo; i2c_add_adapter(g_adapter); // i2c_add_numbered_adapter(g_adapter); return 0; }
remove函数如下
static int i2c_bus_virtual_remove(struct platform_device *pdev) { i2c_del_adapter(g_adapter); // 反注册适配器 return 0; }
出口函数,unregister平台驱动,
static void __exit i2c_bus_virtual_exit(void) { platform_driver_unregister(&i2c_bus_virtual_driver); }
算法结构体分析:
上面介绍了在probe函数中分配设置i2c_adapter结构体,该结构体内如如下:

从图中可以看到里面有个
i2c_algorithm结构体。内容如下:
image-20210303121043020master_xfer:这是最重要的函数,它实现了一般的I2C传输,用来传输一个或多个i2c_msg
master_xfer_atomic:
可选的函数,功能跟master_xfer一样,在
atomic context环境下使用,比如在关机之前、所有中断都关闭的情况下,用来访问电源管理芯片smbus_xfer:实现SMBus传输,如果不提供这个函数,SMBus传输会使用master_xfer来模拟
smbus_xfer_atomic:
可选的函数,功能跟smbus_xfer一样,在
atomic context环境下使用,比如在关机之前、所有中断都关闭的情况下,用来访问电源管理芯片functionality:返回所支持的flags:各类I2C_FUNC_*
reg_slave/unreg_slave:
有些I2C Adapter也可工作与Slave模式,用来实现或模拟一个I2C设备
有上面的分析可知我们构建我们自己的算法结构体
i2c_bus_virtual_algo即可,如下:static u32 i2c_bus_virtual_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_PROTOCOL_MANGLING; } const struct i2c_algorithm i2c_bus_virtual_algo = { .master_xfer = i2c_bus_virtual_master_xfer, // i2c 硬件实现读写数据 .functionality = i2c_bus_virtual_func, // 提供该i2c驱动支持的功能 };
下面就是重要的
i2c_bus_virtual_master_xfer函数,里面如果是模拟的i2c,就会通过io拉时序,如果是硬件i2c,就会操作i2c寄存器,然后写或者读数据。大致的操作如下:static int i2c_bus_virtual_master_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { for (i = 0; i < num; i++) { struct i2c_msg *msg = msgs[i]; { // 1. 設置控制寄存器 发出S信号: 设置寄存器发出S信号 CTLREG = S; // 2. 根据Flag发出设备地址和R/W位: 把这8位数据写入某个DATAREG即可发出信号 // 判断是否有ACK if (!ACK) return ERROR; else { // 3. read / write if (read) { STATUS = XXX; // 这决定读到一个数据后是否发出ACK给对方 val = DATAREG; // 这会发起I2C读操作 } else if(write) { DATAREG = val; // 这会发起I2C写操作 val = STATUS; // 判断是否收到ACK if (!ACK) return ERROR; } } // 4. 設置控制寄存器 发出P信号 CTLREG = P; } } return i; }
整个驱动框架代碼如下:
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
//#include <linux/i2c-algo-bit.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
//#include <linux/platform_data/i2c-gpio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
static struct i2c_adapter *g_adapter;
static int i2c_bus_virtual_master_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
// 這裏面和硬件有关系,具体的硬件自己添加 实现通过i2c控制器读写功能
}
static u32 i2c_bus_virtual_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_PROTOCOL_MANGLING;
}
const struct i2c_algorithm i2c_bus_virtual_algo = {
.master_xfer = i2c_bus_virtual_master_xfer,
.functionality = i2c_bus_virtual_func,
};
static int i2c_bus_virtual_probe(struct platform_device *pdev)
{
/* get info from device tree, to set i2c_adapter/hardware */
/* alloc, set, register i2c_adapter */
g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL);
g_adapter->owner = THIS_MODULE;
g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
g_adapter->nr = -1;
snprintf(g_adapter->name, sizeof(g_adapter->name), "i2c-bus-virtual");
g_adapter->algo = &i2c_bus_virtual_algo;
i2c_add_adapter(g_adapter); // i2c_add_numbered_adapter(g_adapter);
return 0;
}
static int i2c_bus_virtual_remove(struct platform_device *pdev)
{
i2c_del_adapter(g_adapter);
return 0;
}
static const struct of_device_id i2c_bus_virtual_dt_ids[] = {
{ .compatible = "100ask,i2c-bus-virtual", },
{ /* sentinel */ }
};
static struct platform_driver i2c_bus_virtual_driver = {
.driver = {
.name = "i2c-gpio",
.of_match_table = of_match_ptr(i2c_bus_virtual_dt_ids),
},
.probe = i2c_bus_virtual_probe,
.remove = i2c_bus_virtual_remove,
};
static int __init i2c_bus_virtual_init(void)
{
int ret;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
ret = platform_driver_register(&i2c_bus_virtual_driver);
if (ret)
printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);
return ret;
}
module_init(i2c_bus_virtual_init);
static void __exit i2c_bus_virtual_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&i2c_bus_virtual_driver);
}
module_exit(i2c_bus_virtual_exit);
MODULE_AUTHOR("www.100ask.net");
MODULE_LICENSE("GPL");