嵌入式linux IIC驱动

1. Linux IIC驱动框架

Linux驱动和设备隔离,驱动分层,IIC驱动框架分为两部分

  • IIC 总线驱动,也就是SOC的IIC控制器驱动,也就适配器驱动
  • IIC 设备驱动,指具体的IIC设备驱动

1.1 IIC总线驱动

platform是虚拟总线,针对没有总线的设备实现,总线、设备、驱动框架,IIC不用虚拟,可直接用总线bus。IIC总线驱动有两个重要数据结构:i2c_adapter, i2c_algorithm。i2c_adapter结构体定义如下:include/linux/i2c.h

struct i2c_adapter {
	。。。
	const struct i2c_algortithm *algo;  /*总线访问算法*/
	。。。
}

通过iic适配器操作设备的API函数都在i2c_algortithm结构体中,i2c_algortithm 定义在 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);

	/* To determine what the adapter supports */
	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
};
  • master_xfer 是i2c适配器的数据传输函数,完成和i2c设备的数据通信
  • smbus_xfer 是smbus的数据传输函数
    I2C总线驱动,主要就是初始化i2c_adapter结构体变量,然后设置master_xfer数据传输函数,完成后通过向系统注册i2c_adapter,注册函数如下:
int i2c_add_adapter(struct i2c_adapter *adapter) //使用动态总线号
int i2c_add_number_adapter(struct i2c_adapter *adap) //使用静态总线号
  • 参数 adapter/adap 表示要注册到系统中的适配器
  • 返回值 0 成功 负值 失败
    删除IIC适配器函数:
void i2c_del_adapter(struct i2c_adapter *adap);

1.2 IIC设备驱动

重点就是两个结构体,i2c_client和i2c_driver。

  • i2c_client 主要描述设备信息,inlcude/linux/i2c.h中
struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct device dev;		/* the device structure		*/
	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
};

每检测到IIC设备就会分配一个i2c_client,这个结构体和设备一一对应

  • i2c_driver结构体描述设备驱动 include/linux/i2c.h
struct i2c_driver {
	unsigned int class;

	/* Notifies the driver that a new bus has appeared. You should avoid
	 * using this, it will be removed in a near future.
	 */
	int (*attach_adapter)(struct i2c_adapter *) __deprecated;

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *);

	/* Alert callback, for example for the SMBus alert protocol.
	 * The format and meaning of the data value depends on the protocol.
	 * For the SMBus alert protocol, there is a single bit of data passed
	 * as the alert response's low bit ("event flag").
	 */
	void (*alert)(struct i2c_client *, unsigned int data);

	/* a ioctl like command that can be used to perform specific functions
	 * with the device.
	 */
	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

	struct device_driver driver;
	const struct i2c_device_id *id_table;

	/* Device detection callback for automatic device creation */
	int (*detect)(struct i2c_client *, struct i2c_board_info *);
	const unsigned short *address_list;
	struct list_head clients;
};

probe: IIC设备和驱动匹配成功以后执行probe函数
device_driver :驱动结构体,如果用设备树,设置of_match_table成员,表示兼容属性
id_table:没有使用设备树,表示设备匹配列表

  • i2c_driver结构体构建完成后,需要向linux内核注册,注册函数:
int i2c_register_driver(struct module *owner,struct i2c_driver *driver)

参数 owner 一般为 THIS_MODULE
driver:需要注册的i2c_driver结构体
返回值 0 成功 负值 失败

  • 注销IIC设备驱动,函数
void i2c_del_driver(struct i2c_driver *driver)

1.3 设备和驱动的匹配过程

IIC设备和驱动的匹配是有IIC核心完成的,drivers/i2c/i2c-core.c,提供了一些和具体硬件无关的API函数,i2c_adapter注册和注销函数,i2c_driver注册和注销函数。IIC设备和驱动的匹配是由I2C总线完成的,结构体i2c_bus_type,定义在drivers/i2c/i2c-core.c

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
};

i2c_device_match 是IIC设备和驱动的匹配函数,定义如下

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;

	if (!client)
		return 0;

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	driver = to_i2c_driver(drv);
	/* match on an id table if there is one */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}

of_driver_match_device : 设备树设备和驱动的匹配,比较I2C设备节点的compatible属性和 of_device_id中的compatible是否相等,相等则匹配
i2c_match_id : 用于传统无设备树的匹配,比较设备名字和i2c_device_id的name字段是否相等,相等则匹配

2. I2C适配器驱动分析

I2C总线驱动其实就是SOC的I2C控制器驱动,一般SOC厂商已经写好。在imx6ull.dtsi文件中找到I.MX6U的I2C1控制节点,

扫描二维码关注公众号,回复: 9254749 查看本文章
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";
};

通过compatible属性可以再linux源码中找到对应的驱动文件,I.MX6U的I2C适配器驱动文件为drivers/i2c/busser/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);
。。。
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);
}
subsys_initcall(i2c_adap_imx_init);

static void __exit i2c_adap_imx_exit(void)
{
	platform_driver_unregister(&i2c_imx_driver);
}
module_exit(i2c_adap_imx_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Darius Augulis");
MODULE_DESCRIPTION("I2C adapter driver for IMX I2C bus");
MODULE_ALIAS("platform:" DRIVER_NAME);

从以上可以看出I2C适配器驱动是个标准的platform驱动,当设备和驱动匹配完成后i2c_imx_probe函数就会执行,主要是完成I2C适配器的初始化。
i2c_imx_probe 函数主要的工作就是一下两点:
①、初始化 i2c_adapter,设置 i2c_algorithm 为 i2c_imx_algo,最后向 Linux 内核注册
i2c_adapter。
②、初始化 I2C1 控制器的相关寄存器。

3. I2C设备驱动编写流程

3.1 未使用设备树,I2C设备信息描述

未使用设备树的时候用i2c_board_info 结构体描述具体的I2C设备,定义如下

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];
	unsigned short	flags;
	unsigned short	addr;
	void		*platform_data;
	struct dev_archdata	*archdata;
	struct device_node *of_node;
	struct fwnode_handle *fwnode;
	int		irq;
};

type : I2C设备名字
addr:I2C器件地址

3.2 使用设备树,I2C设备信息描述

在使用设备树的情况下,I2C设备信息通过创建相应的节点添加,在i2c1节点下创建子节点,描述具体i2c设备的有关信息

3.3 I2C设备数据收发处理流程
发布了4 篇原创文章 · 获赞 1 · 访问量 152

猜你喜欢

转载自blog.csdn.net/m0_46291920/article/details/104370342