# IMX6ULL 芯片的I2C_Adapter驱动分析 参考资料: * Linux内核真正的I2C控制器驱动程序 * IMX6ULL: `Linux-4.9.88\drivers\i2c\busses\i2c-imx.c` ## I2C控制器内部结构 ### 通用的简化结构 ![](media/069_i2c_block_general-1632470464532.png) ### IMX6ULL的I2C控制器内部结构 ![](media/070_i2c_block_imx6ull-1632470464533.png) ## I2C控制器操作方法 * 使能时钟、设置时钟 * 发送数据: * 把数据写入**tx_register**,等待中断发生 * 中断发生后,判断状态:是否发生错误、是否得到回应信号(ACK) * 把下一个数据写入**tx_register**,等待中断:如此循环 * 接收数据: * 设置**controller_register**,进入接收模式,启动接收,等待中断发生 * 中断发生后,判断状态,读取**rx_register**得到数据 * 如此循环 ## 分析代码 1. 设备树:IMX6ULL: `arch/arm/boot/dts/imx6ull.dtsi` ``` i2c1: i2c@021a0000 { #address-cells = <1>; #size-cells = <0>; compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c"; reg = <0x021a0000 0x4000>; interrupts = ; clocks = <&clks IMX6UL_CLK_I2C1>; status = "disabled"; }; ``` 2. 入口函数:注册platform_driver,且确定了设备树匹配的of_device_id ```c static struct platform_device_id imx_i2c_devtype[] = { { .name = "imx1-i2c", .driver_data = (kernel_ulong_t)&imx1_i2c_hwdata, }, { .name = "imx21-i2c", .driver_data = (kernel_ulong_t)&imx21_i2c_hwdata, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, imx_i2c_devtype); static const struct of_device_id i2c_imx_dt_ids[] = { { .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, }, { .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, }, { .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids); static struct platform_driver i2c_imx_driver = { .probe = i2c_imx_probe, .remove = i2c_imx_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = i2c_imx_dt_ids, .pm = IMX_I2C_PM, }, .id_table = imx_i2c_devtype, }; static int __init i2c_adap_imx_init(void) { return platform_driver_register(&i2c_imx_driver); } ``` 3. probe函数分析 - 分配适配器 ![](media/image-20210924161626950.png) 注意:struct imx_i2c_struct里面已经有了适配器如下: ![](media/image-20210924161441883.png) - 设置适配器 ![](media/image-20210924161558662.png) - 注册适配器 ![](media/image-20210924161812230.png) 当然在probe函数里面还有一些额外的操作,例如设置i2c控制器的时钟等如下: ![](media/image-20210924161906600.png) 4. 算法结构体分析 ![](media/image-20210924162110686.png) 如上图所示,具体的i2c读写数据就在i2c_imx_xfer里面。如下: ``` static int i2c_imx_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { unsigned int i, temp; int result; bool is_lastmsg = false; struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__); /* Start I2C transfer */ result = i2c_imx_start(i2c_imx); if (result) goto fail0; /* read/write data */ for (i = 0; i < num; i++) { if (i == num - 1) is_lastmsg = true; if (i) { temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);// 1.发起开始信号 temp |= I2CR_RSTA; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); result = i2c_imx_bus_busy(i2c_imx, 1); if (result) goto fail0; } /* write/read data */ if (msgs[i].flags & I2C_M_RD) result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); 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]); } if (result) goto fail0; } fail0: /* Stop I2C transfer */ i2c_imx_stop(i2c_imx); return (result < 0) ? result : num; } ``` 函数`i2c_imx_xfer`读操作`i2c_imx_read`分析: ![](media/072_master_xfer_imx6ull-1632470464533.png)