总结
iic_client和iic_driver 加入iic总线的思想和paltform总线的玩法一样
把iic设备和驱动注册到iic总线中 构造出字符设备驱动和设备节点供app进行操作
但是iic硬件设备是挂在iic控制器下面的 所以iic控制器也会有自己的驱动和设备树节点 厂家一般都会帮做好
我们写的iic_driver驱动程序 控制iic硬件的时候 使用iic总线提供的api 来控制iic硬件设备
详细介绍分两部分 一个是iic设备驱动程序 一个是iic控制器驱动介绍
iic控制器驱动
芯片的iic控制器抽象成 一个iic_adapter (一般这个控制器的驱动和设备节点芯片厂商都帮创建好了)
iic_algotithm 也构建好了
linux 上电注册iic总线 drivers/i2c/i2c-core-base.c
static int __init i2c_init(void)
bus_register(&i2c_bus_type); //很熟悉的总线注册
IIC总线匹配规则 也就是看总线上的.match函数
- of_driver_match_device:设备树匹配方式
- 比较 I2C 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性
- acpi_driver_match_device : ACPI 匹配方式
- i2c_match_id:i2c总线传统匹配方式
- 比较 I2C设备名字和 i2c驱动的id_table->name 字段是否相等
构造iic_适配器设备树节点
下面用IIC1控制器来举例子
i2c1: i2c@21a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x21a0000 0x4000>; //reg属性记录了IIC寄存器组的地址
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "disabled";
};
适配器的驱动与之匹配
这个就是厂商写的适配器驱动 但只是在和设备树匹配后
应该是存了个什么东西给 适配器万能驱动调用
plantform_driver.probe
struct resource *res;
void __iomem *base;
of_device_id *of_id = of_match_device(i2c_imx_dt_ids,&pdev->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取resource资源,放到设备树就是reg = <0x21a0000 0x4000>;
base = devm_ioremap_resource(&pdev->dev, res);//对resource物理地址和虚拟地址映射,此时base记录了虚拟地址
phy_addr = (dma_addr_t)res->start;//这里记录物理地址,直接从resource->start拿取
i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);//分配内存
i2c_imx->adapter.algo = &i2c_imx_algo;//初始化运算器,用于直接操作硬件的代码
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);//在linux注册一个i2c_adap
static const struct i2c_algorithm i2c_imx_algo = { //运算器结构体
.master_xfer = i2c_imx_xfer,//产生IIC通讯时序,负责数据收发
.functionality = i2c_imx_func,//查询iic通信协议类型,iic通讯协议有一大类
};
.master_xfer
result = i2c_imx_start(i2c_imx);//发送IIC的启动信号
...
for (i = 0; i < num; i++) { //在for循环里面发送数据
if (i == num - 1)
is_lastmsg = true;
result = i2c_imx_bus_busy(i2c_imx, 1);//判断当前iic总线是否忙
if (msgs[i].flags & I2C_M_RD) //不忙判断当前信息需要读还是写
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);//这里真正控制IIC的寄存器进行发送读取
else {
if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
else
result = i2c_imx_write(i2c_imx, &msgs[i]);
}
IIC核心函数和万能驱动!!! i2c-dev.c
iic设备驱动程序
struct i2c_client
struct device dev; 继承了 struct device 符合linux驱动设备模型
linux根据IIC设备节点自动生成i2c_client节点
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* 7位地址 */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* 继承了 struct device 符合linux驱动设备模型 */
int init_irq; /* irq set at initialization */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
struct i2c_driver {
unsigned int class;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
...
struct device_driver driver;
const struct i2c_device_id *id_table;
...
}