IMX6ULL 芯片的I2C_Adapter驱动分析

参考资料:

  • Linux内核真正的I2C控制器驱动程序

    • IMX6ULL: Linux-4.9.88\drivers\i2c\busses\i2c-imx.c

I2C控制器内部结构

通用的简化结构

../../_images/069_i2c_block_general-1632470464532.png

IMX6ULL的I2C控制器内部结构

../../_images/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 = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
    		clocks = <&clks IMX6UL_CLK_I2C1>;
    		status = "disabled";
    };
    
  2. 入口函数:注册platform_driver,且确定了设备树匹配的of_device_id

    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函数分析

    • 分配适配器

      ../../_images/image-20210924161626950.png

      注意:struct imx_i2c_struct里面已经有了适配器如下:

      ../../_images/image-20210924161441883.png

    • 设置适配器

      ../../_images/image-20210924161558662.png

    • 注册适配器

      ../../_images/image-20210924161812230.png

    当然在probe函数里面还有一些额外的操作,例如设置i2c控制器的时钟等如下:

    ../../_images/image-20210924161906600.png

  4. 算法结构体分析

    ../../_images/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分析: ../../_images/072_master_xfer_imx6ull-1632470464533.png