Linux驱动.之I2C,iic驱动层(三)

一、i2c驱动代码分析

在Linux驱动.之I2C,iic驱动层(二),进行了框架分析,这篇,将分析具体device的设备驱动代码

摘抄自,正点原子

I2C主要以两种功能模式运行:标准模式和快速模式。
•在标准模式下,I2C支持高达100 kbits/s的数据传输速率。
•在快速模式下,可以实现高达400 kbits/s的数据传输速率。

在前面讲 platform 的时候就说过,platform 是虚拟出来的一条总线,目的是为了实现总线、设备、驱动框架。对于 I2C 而言,不需要虚拟出一条总线,直接使用 I2C总线即可。I2C 总线驱动重点是 I2C 适配器(也就是 SOC 的 I2C 接口控制器)驱动。

在Linux设计中,将I2C代码框架分为三个部分:I2C总线、I2C核心、I2C驱动。
I2C核心(i2c-core): 主要定义i2c驱动所用到的通用API,高内聚的代码会放到i2c-core.c。

I2C总线驱动(i2c adapter): 根据平台定制的i2c驱动,其中包含i2c传输的算法设计。主要工作负责生成i2c_client,注册适配器,以及i2c_client与i2c_driver的匹配。

I2C设备驱动(i2c client driver): 驱动I2C设备的代码。I2C设备驱动定义了外设的交互方式,与不同的I2C外设需要不同的设备驱动。I2C设备驱动对上和用户应用程序打交道,对下和I2C核心对接。

对于i2c-core而言,不需要我们实现,这个是linux内核中已经有的,属于标准内核代码。
i2c adapter,则是SOC芯片的I2C控制器驱动,这个根据不同的平台,I2C控制器会有差异,所以这部分代码是SOC厂商会实现,也不需要开发人员写,但我们需要了解一下也是可以的。
i2c client driver,则是具体的I2C设备驱动,一般驱动开发人员需要编写这部分代码。

1、I2C总线驱动

我们先来看一下i2c adapter的有关内容。对于i2c adapter相关驱动代码已经有NXP实现,相关内容如下。
I2C控制器的设备树节点如下:

路径: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";
			};

			i2c2: i2c@021a4000 {
   
   
				#address-cells = <1>;
				#size-cells = <0>;
				compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
				reg = <0x021a4000 0x4000>;
				interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
				clocks = <&clks IMX6UL_CLK_I2C2>;
				status = "disabled";
			};

			i2c3: i2c@021a8000 {
   
   
				#address-cells = <1>;
				#size-cells = <0>;
				compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
				reg = <0x021a8000 0x4000>;
				interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
				clocks = <&clks IMX6UL_CLK_I2C3>;
				status = "disabled";
			};

上述代码内容就是IMX6ULL中的3个I2C,I2C1、I2C2和I2C3的地址分别是0x021a0000、0x021a4000、0x021a8000。根据compatible属性,我们可以找到对于的驱动程序,搜索 “fsl,imx6ul-i2c”, “fsl,imx21-i2c”,可找到如下内容:

路径:drivers/i2c/busses/i2c-imx.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);

文件i2c-imx.c中的内容就是NXP原厂针对IMX6ULL等SOC中I2C控制器的驱动i2c adapter。我们不深入研究i2c adapter,因为这是SOC原厂会完成这部分驱动的编写。

i2c adapter用到两个重要的数据结构:i2c_adapter 和 i2c_algorithm,Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter,i2c_adapter 结构体内容如下:

路径:
/*i2c_adapter是用于识别物理i2c总线以及访问该总线所需的访问算法的结构。
*/
struct i2c_adapter {
   
   
	struct module *owner;
	unsigned int class;		/*允许探测的类*/
	const struct i2c_algorithm *algo; /*访问总线的算法*/
	void *algo_data;

/*对所有设备有效的数据字段*/
	struct rt_mutex bus_lock;

	int timeout;			/* in jiffies */
	int retries;
	struct device dev;		/*适配器设备*/

	int nr;
	char name[48];
	struct completion dev_released;

	struct mutex userspace_clients_lock;
	struct list_head userspace_clients;

	struct i2c_bus_recovery_info *bus_recovery_info;
	const struct i2c_adapter_quirks *quirks;
};

第 7 行,i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法。

i2c_algorithm 结构体定义内容如下:

路径:include/linux/i2c.h
struct i2c_algorithm {
   
   
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

/*确定适配器支持的内容*/
	u32 (*functionality) (struct i2c_adapter *);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
	int (*reg_slave)(struct i2c_client *client);
	int (*unreg_slave)(struct i2c_client *client);
#endif
};

第 20 行,master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。
第 22 行,smbus_xfer 就是 SMBUS 总线的传输函数。
重点来看一下 i2c_imx_xfer 函数,因为最终就是通过此函数来完成与 I2C 设备通信的,此函数内容如下:
路径:drivers/i2c/busses/i2c-imx.c

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) {
   
   
			dev_dbg(&i2c_imx->adapter.dev,
				"<%s> repeated start\n", __func__);
			temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
			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;
		}
		dev_dbg(&i2c_imx->adapter.dev,
			"<%s> transfer message: %d\n", __func__, i);
		/* write/read data */
#ifdef CONFIG_I2C_DEBUG_BUS
		temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
		dev_dbg(&i2c_imx->adapter.dev,
			"<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n",
			__func__,
			(temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
			(temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
			(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
		temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
		dev_dbg(&i2c_imx->adapter.dev,
			"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",
			__func__,
			(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
			(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
			(temp & I2SR_SRW ? 1 : 0), (temp 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值