全面总结Linux内核下的IIC子系统架构

30 篇文章 0 订阅
15 篇文章 1 订阅

​1.IIC简介

IIC是philips提出的外设总线。

IIC只有两条线,一条串行数据线SDA;一条是时钟线SCL。使用SCL,SDA这两条线就实现了设备之间的数据交互。

【文章福利】小编推荐自己的Linux内核源码交流群:【869634926】整理了一些个人觉得比较好的内核学习书籍、视频资料共享在群里面,有需要的可以自行添加哦!

 

2.Linux下的驱动思路

在linux下编写I2C驱动,目前主要有两种方法,一种是把I2C设备当做一个普通字符设备,另一种是利用Linux内核提供的I2C子系统来完成。两种方法有一些区别:

第一种方法:

优点:思路比较直接,不需要花很多时间去了解Linux中复杂的I2C子系统的操作方法。

缺点:要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器(I2C控制器)操作。

要求工程师对I2C的适配器及I2C的设备操作方法都比较熟悉,最重要的写出的程序可移植性差。

对内核资源无法直接使用,因为内核提供的所有I2C设备,及设备驱动都是基于I2C子系统的格式。

第一种方法的优点就是第二种方法的缺点,第一方法的缺点就是第二种方法的优点。

3.I2C架构概述

Linux下的I2C体系结构分为3个组成部分:

I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法,I2C通信方法(algorithm)上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。

I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。

I2C设备驱动:I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。

4.Linux驱动中I2C驱动架构

上图中完整的描述了Linux I2C驱动架构,虽然I2C硬件体系结构比较简单,但是I2C体系结构在Linux中的实现却相当复杂。

那么我们如何编写特定的I2C接口器件的驱动程序?就是说上述架构中有哪些是需要我们完成,而哪些是Linux内核已经完善的或者是芯片厂商提供的。

5.架构层次分类

第一层:提供i2c adapter的硬件驱动,探测,初始化i2c adapter(如申请i2c的io地址和中断号)驱动soc控制的i2c adapter在硬件上产生信号(stop,start,ack)以及处理I2c中断。覆盖图中硬件实现层。

第一层:提供i2c adapter的硬件驱动,探测,初始化i2c adapter(如申请i2c的io地址和中断号)驱动soc控制的i2c adapter在硬件上产生信号(stop,start,ack)以及处理I2c中断。覆盖图中硬件实现层。

第二层:提供i2c adapter 的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter的algo指针。覆盖图中的访问抽象层、i2c核心层。

第三层:实现i2c设备驱动中的i2c_driver接口,用具体的i2c device设备attch_adapter();detach_adapter();方法赋值给i2c_driver的成员函数指针。实现设备device与总线(或者叫adapter)的挂接。覆盖图中driver驱动层。

第四层:实现i2c设备所对应的具体device的驱动,i2c_driver只是实现设备与总线的挂接,而挂接在总线上的设备则是千差万别的,所以要实现具体的设备device的read(),write(),ioctl();等方法,赋值给file_operations,然后注册字符设备(多数是字符设备),覆盖图中的driver驱动层。

第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。

在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成了所有的总线bus,如usb,pci,i2c,等,并且总线bus中的(与特定硬件相关的代码)已经由芯片厂商编写完成,

第三第四层与特定device相关的就需要驱动工程师来实现了。

Linux下I2C体系文件架构

i2c-core.c这个文件实现了I2C核心功能以及/proc/bus/i2c*接口。

i2c-dev.c实现了I2C适配器的设备文件的功能,每个I2c适配器都被分配一个设备。

为0-255.I2C-dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl(),等接口。应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。

busses文件夹,这个文件夹包含了一些I2C总线的驱动,如针对S3C2410、S3C2440等处理器的I2C控制驱动。

algos文件夹实现了一些I2C总线适配器的algorithm。

6.重要的结构体

i2c_driver

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;//依附i2c_adapter函数指针
 
	/* 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 *);
	int (*suspend)(struct i2c_client *, pm_message_t mesg);
	int (*resume)(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;//该驱动所支持的设备ID表
 
	/* 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;
};

i2c_client

struct i2c_client {
	unsigned short flags;		/* div., see below		*///标志
	unsigned short addr;		/* chip address - NOTE: 7bit	*///低7位为芯片地址
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];                                         //设备名称
	struct i2c_adapter *adapter;	/* the adapter we sit on	*///依附的i2c_adapter
	struct i2c_driver *driver;	/* and our access routines	*///依附的i2c_driver
	struct device dev;		/* the device structure		*///设备结构体
	int irq;			/* irq issued by device		*///链表头
	struct list_head detected;
};

i2c_adapter

struct i2c_adapter {
	struct module *owner;  //所属模块
	unsigned int class;		  /* classes to allow probing for */
	const struct i2c_algorithm *algo; /* the algorithm to access the bus *///总线通信方法指针
	void *algo_data;                                                       //algorithm数据
 
	/* data fields that are valid for all devices	*/
	struct rt_mutex bus_lock;                                              //控制并发访问的自旋锁
 
	int timeout;			/* in jiffies */
	int retries;                                                            //重试次数
	struct device dev;		/* the adapter device */                //适配器设备
 
	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;
};

i2c_algorithm

struct i2c_algorithm {
	/* If an adapter algorithm can't do I2C-level access, set master_xfer
	   to NULL. If an adapter algorithm can do SMBus access, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,//i2c传输函数指针
			   int num);
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,//smbus传输函数指针
			   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 *);//返回适配器支持的功能
};

各结构体的作用与他们之间的关系

i2c_adapter与i2c_algorithm

i2c_adapter对应物理上的一个适配器,也就是指的是CPU内部的I2C控制器,一个CPU内部可能有多个I2c适配器。而i2c_algorithm对应一套通信方法,一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器产生特定的访问周期,缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。

i2c_algorithm

i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start stop ack信号,以i2c_msg(即i2c消息)为单位发送和接收通信数据。

i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体。

struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

i2c_driver和i2c_client

  • i2c_driver对应一套驱动方法,其主要是驱动我们的i2c设备的,比如设备的初始化等等。
  • i2c_client对应真实的i2c屋里设备device,每个i2c设备都需要一个i2c_client来描述。
  • i2c_driver与i2c_client的关系是一对多,一个i2cdriver可以支持多个同类型的i2c_client.但必须是同类型的。

i2c_adapter和i2c_client的关系是i2c硬件体系中适配器和设备的关系,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备,所以i2c_adapter中包含依附于它的i2c_client的链表。

从i2c驱动架构图中可以看出,linux内核对i2c架构抽象了一个核心层core的中间件,它分离了设备驱动device driver和硬件控制的实现细节,core不但为下层的设备驱动提供了封装后的内核注册函数,而且还未硬件事件提供了注册接口(也就是i2c总线注册接口)。可以说core起到了承上启下的作用。

7.i2c驱动和设备的匹配过程

以一个tp驱动来详细说明i2c驱动和设备的注册过程。先说设备的注册过程。

7.1. i2c设备的注册过程

自从内核引入设备树以后,很多设备的注册都是通过设备树来完成的,i2c adapter和i2c设备也不例外。我们来看下dts文件中对i2c adapter的描述。

 "actions,s700-i2c";
                reg = <0 0xe0170000 0 0x1000>;
                interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
                #address-cells = <1>;
                #size-cells = <0>;
                clocks = <&clock CLK_I2C0>;
                clock-names = "i2c0";
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_i2c0_default>;
                status = "disabled";
        };

        i2c1: i2c@e0174000 {
                compatible = "actions,s700-i2c";
                reg = <0 0xe0174000 0 0x1000>;
                interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
                #address-cells = <1>;
                #size-cells = <0>;
                clocks = <&clock CLK_I2C1>;
                clock-names = "i2c1";
                //pinctrl-names = "default";
                //pinctrl-0 = <&pinctrl_i2c1_default>;
                status = "disabled";            
        };

        i2c2: i2c@e0178000 {          

从上面可以看出这个CPU内有四个i2c adapter。关于适配器一端的相关驱动一般是有芯片厂商完成的,在内核中kernel/drivers/i2c/busses$目录下。我用的炬芯的芯片,可以看下它是怎么实现的。

static const struct of_device_id owl_i2c_dt_ids[] = {
	{.compatible = "actions,s900-i2c"},
	{.compatible = "actions,s700-i2c"}, //将会与设备树中的compatible字段匹配。
	{.compatible = "actions,ats3605-i2c"},
	{},
};
MODULE_DEVICE_TABLE(of, owl_i2c_dt_ids);
 
static struct platform_driver owl_i2c_driver = {
	.probe		= owl_i2c_probe,
	.remove		= owl_i2c_remove,
	.driver		= {
		.name	= "i2c-owl",
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(owl_i2c_dt_ids),
	},
};
 
static int __init owl_i2c_init(void)
{
	return platform_driver_register(&owl_i2c_driver); //将本驱动注册为平台设备驱动
}
subsys_initcall(owl_i2c_init);//驱动入口
{.compatible = "actions,s700-i2c"}, //将会与设备树中的compatible字段匹配。
	{.compatible = "actions,ats3605-i2c"},
	{},
};
MODULE_DEVICE_TABLE(of, owl_i2c_dt_ids);

static struct platform_driver owl_i2c_driver = {
	.probe		= owl_i2c_probe,
	.remove		= owl_i2c_remove,
	.driver		= {
		.name	= "i2c-owl",
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(owl_i2c_dt_ids),
	},
};

static int __init owl_i2c_init(void)
{
	return platform_driver_register(&owl_i2c_driver); //将本驱动注册为平台设备驱动
}
subsys_initcall(owl_i2c_init);//驱动入口

由设备树相关知识知道,设备树解析以后会将设备树内的设备添加并注册到设备链表内,本驱动将owl_i2c_driver驱动注册为平台设备后,如果能够在设备链表中匹配到与owl_i2c_driver中of_match_table中complatibale字段相同的名字,那么久去执行owl_i2c_driver的probe函数。

看一下probe函数。

static struct i2c_algorithm owl_i2c_algorithm = {
	.master_xfer    = owl_i2c_xfer,          //芯片厂商提供的i2c_algorithm通信方法,不需要用户去实现。
	.functionality  = owl_i2c_func,
};
owl_i2c_algorithm = {
	.master_xfer    = owl_i2c_xfer,          //芯片厂商提供的i2c_algorithm通信方法,不需要用户去实现。
	.functionality  = owl_i2c_func,
};

static int owl_i2c_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	struct owl_i2c_dev *dev;
	struct i2c_adapter *adap;
	struct resource *res;
	int ret;
	u32 phy_addr;
 
	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return -ENOMEM;
	init_completion(&dev->cmd_complete);
	dev->dev = &pdev->dev;
 
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -ENODEV;
	phy_addr = res->start;
 
	dev->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(dev->base))
		return PTR_ERR(dev->base);
	dev->phys = res->start;
 
	dev->irq = platform_get_irq(pdev, 0);
	if (dev->irq < 0)
		return dev->irq;
 
	ret = devm_request_irq(&pdev->dev, dev->irq, owl_i2c_interrupt, 0,
			 dev_name(dev->dev), dev);
	if (ret) {
		dev_err(dev->dev, "Cannot get irq %d: %d\n", dev->irq, ret);
		return ret;
	}
 
	platform_set_drvdata(pdev, dev);
 
	adap = &dev->adapter;
	snprintf(dev->adapter.name, sizeof(dev->adapter.name),
		"OWL I2C adapter");
	i2c_set_adapdata(adap, dev);
	adap->owner = THIS_MODULE;
	adap->class = I2C_CLASS_HWMON;
	adap->algo = &owl_i2c_algorithm;    //注册一下i2c_algorithm指针
	adap->timeout = OWL_I2C_TIMEOUT;
	adap->dev.parent = dev->dev;
	adap->dev.of_node = pdev->dev.of_node;
	adap->nr = pdev->id;
 
	ret = i2c_add_numbered_adapter(adap);
	if (ret) {
		dev_err(dev->dev, "Adapter %s registration failed\n",
			adap->name);
		clk_disable_unprepare(dev->clk);
		return ret;
	}
 
	of_i2c_register_devices(adap);   //of解析设备树,注册i2c设备,也就是client设备
 
	dev_info(dev->dev, "I2C adapter ready to operate.\n");
 
	return 0;
}
owl_i2c_algorithm;    //注册一下i2c_algorithm指针
	adap->timeout = OWL_I2C_TIMEOUT;
	adap->dev.parent = dev->dev;
	adap->dev.of_node = pdev->dev.of_node;
	adap->nr = pdev->id;

	ret = i2c_add_numbered_adapter(adap);
	if (ret) {
		dev_err(dev->dev, "Adapter %s registration failed\n",
			adap->name);
		clk_disable_unprepare(dev->clk);
		return ret;
	}

	of_i2c_register_devices(adap);   //of解析设备树,注册i2c设备,也就是client设备

	dev_info(dev->dev, "I2C adapter ready to operate.\n");

	return 0;
}

因此,本probe函数要执行四次,因为在设备树dts文件中有四个匹配到的i2c适配器。其中重要的是函数i2c_add_numbered_adapter 和of_i2c_register_devices(adap);

int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
	if (adap->nr == -1) /* -1 means dynamically assign bus id */
		return i2c_add_adapter(adap);
 
	return __i2c_add_numbered_adapter(adap); //里面调用i2c_register_adapter
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
	int res = 0;
 
	rt_mutex_init(&adap->bus_lock);
	mutex_init(&adap->userspace_clients_lock);
	INIT_LIST_HEAD(&adap->userspace_clients);
 
	/* Set default timeout to 1 second if not already set */
	if (adap->timeout == 0)
		adap->timeout = HZ;
 
	dev_set_name(&adap->dev, "i2c-%d", adap->nr); //设置adapter的name 
	adap->dev.bus = &i2c_bus_type;                //设置adapter的dev,bus为i2c_bus_type
	adap->dev.type = &i2c_adapter_type;
	res = device_register(&adap->dev);            //将adapter注册进内核中的设备链表
	if (res)
		goto out_list;
 
	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
 
#ifdef CONFIG_I2C_COMPAT
	res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
				       adap->dev.parent);
	if (res)
		dev_warn(&adap->dev,
			 "Failed to create compatibility class link\n");
#endif
}

到这里就把i2c_adapter注册进系统中了,接下来看看of_i2c_register_devices(adap);

void of_i2c_register_devices(struct i2c_adapter *adap)
{
	void *result;
	struct device_node *node;
 
	/* Only register child devices if the adapter has a node pointer set */
	if (!adap->dev.of_node)
		return;
	for_each_available_child_of_node(adap->dev.of_node, node) {     //遍历i2c adapter节点下的所有子节点。下文中的dts中adapter下有两个子节点。
		struct i2c_board_info info = {};
		struct dev_archdata dev_ad = {};
		const __be32 *addr;
		int len;
 
		dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
 
		if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
			dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
				node->full_name);
			continue;
		}
 
		addr = of_get_property(node, "reg", &len); //获取i2c设备地址
		if (!addr || (len < sizeof(int))) {
			dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
				node->full_name);
			continue;
		}
 
		info.addr = be32_to_cpup(addr);
		if (info.addr > (1 << 10) - 1) {
			dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
				info.addr, node->full_name);
			continue;
		}
 
		info.irq = irq_of_parse_and_map(node, 0);//获取i2c设备中段
		info.of_node = of_node_get(node);
		info.archdata = &dev_ad;
 
		if (of_get_property(node, "wakeup-source", NULL))
			info.flags |= I2C_CLIENT_WAKE;
 
		request_module("%s%s", I2C_MODULE_PREFIX, info.type);
 
		result = i2c_new_device(adap, &info);                //创建一个i2c设备 ,这个设备也就是下面代码中描述的i2c设备节点
		if (result == NULL) {
			dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
			        node->full_name);
			of_node_put(node);
			irq_dispose_mapping(info.irq);
			continue;
		}
	}
}

来看一下设备树中是怎么定义的,

        i2c1: i2c@e0174000 {
                clock-frequency = <400000>;
                status = "okay";
                mpu6500@68 {
                compatible = "owl-gyrosensor";
                interrupt-parent = <&gpioa>;
                interrupts = <24>;
                gpios = <&gpioa 24 0>; /* GPIOA24 */
                i2c_adapter_id = <1>;
                /*gyro_vcc = "ldo7";*/
                /*vol_range = <1800000 1810000>;*/
                MASTER_inv6500_position = "3";
                SECONDARY_ak8963c_position = "3";
                };
 
                ft5x06@38 {
                        compatible = "ft5x06";    //非常重要,下文中会用到
                        reg = <0x38>;
                        tp_vcc = "ldo5";
                        reset_gpios = <&gpioc 27 1>; /*GPIOB(3) 0: high, 1: low*/
                        max_points = <10>;
                        x_pixel = <1024>;
                        y_pixel = <600>;
                        x_revert_en = <0>;
                        y_revert_en = <1>;
                        xy_swap_en = <0>;
                        rotate_degree = <270>;  /* 0 90 180 270 */
                interrupt-parent = <&sirq>;
                interrupts =< 0 0x4 >; /*SIRQ0*/
                        vol_range = <3100000 3110000>;
                };
        };
 compatible = "ft5x06";    //非常重要,下文中会用到
                        reg = <0x38>;
                        tp_vcc = "ldo5";
                        reset_gpios = <&gpioc 27 1>; /*GPIOB(3) 0: high, 1: low*/
                        max_points = <10>;
                        x_pixel = <1024>;
                        y_pixel = <600>;
                        x_revert_en = <0>;
                        y_revert_en = <1>;
                        xy_swap_en = <0>;
                        rotate_degree = <270>;  /* 0 90 180 270 */
                interrupt-parent = <&sirq>;
                interrupts =< 0 0x4 >; /*SIRQ0*/
                        vol_range = <3100000 3110000>;
                };
        };

struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
	struct i2c_client	*client;
	int			status;
 
	client = kzalloc(sizeof *client, GFP_KERNEL); //分配一个i2c_client结构体
	if (!client)
		return NULL;
 
	client->adapter = adap;                      //这个client依附在adapter下   
 
	client->dev.platform_data = info->platform_data;
 
	if (info->archdata)
		client->dev.archdata = *info->archdata;
 
	client->flags = info->flags;
	client->addr = info->addr;                    //对i2c client的一些初始化,这些都是dts设备树中定义的值
	client->irq = info->irq;
 
	strlcpy(client->name, info->type, sizeof(client->name));     //client name
 
	/* Check for address validity */
	status = i2c_check_client_addr_validity(client);
	if (status) {
		dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
			client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
		goto out_err_silent;
	}
 
	/* Check for address business */
	status = i2c_check_addr_busy(adap, client->addr);
	if (status)
		goto out_err;
 
	client->dev.parent = &client->adapter->dev;
	client->dev.bus = &i2c_bus_type;         //指定i2c_client->dev.bus为i2c_bus_type,后面会用到
	client->dev.type = &i2c_client_type;
	client->dev.of_node = info->of_node;     //综合上面,构造出了一个client结构体,并对其初始化,这个client就是描述的一个具体的i2c设备。
	ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);      
 
	/* For 10-bit clients, add an arbitrary offset to avoid collisions */
	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
		     client->addr | ((client->flags & I2C_CLIENT_TEN)
				     ? 0xa000 : 0));
	status = device_register(&client->dev);                //将client->dev注册进内核中取。
                    bus_add_device(struct device *dev)
                        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);//Add the device to its bus's list of devices.
                                                                              //也就是将这个设备添加到其所在的bus 的链表中.上文中指定了client->dev.bus = &i2c_bus_type;

至此,内核中的i2c adapter 以及i2c设备已经全部注册完了,这些跟设备树紧密结合。

7.2. i2c驱动的注册过程

我们来一起看下i2c设备驱动是怎么注册进内核中去的,

static struct of_device_id ft5x06_of_match[] = {
	   { .compatible = "ft5x06" },          //重要,在驱动和设备的匹配过程中会用到
	   { }
};
 
MODULE_DEVICE_TABLE(i2c, ft5x06_id);
 
static struct i2c_driver ft5x06_driver = {
	.driver = {
		.owner = THIS_MODULE,
		.name = FT5X06_NAME,
		.of_match_table        = of_match_ptr(ft5x06_of_match),
	},
 
	.class = I2C_CLASS_HWMON,
	.probe = ft5x06_probe,
	.remove = ft5x06_remove,
	.suspend = ft5x06_suspend,
	.resume = ft5x06_resume,
 
	.id_table = ft5x06_id,
};

static int touch_ft5x06_init(void)
{
	int err = 0, i = 0;
	struct i2c_client *client = NULL;
 
	tp_config_init();
 
#if CFG_FT_USE_CONFIG
	err = tp_of_data_get();
	if (err < 0) {
		printk("ft get config err!!!");
		return err;
	}
	tp_info.addr = cfg_dts.i2cAddr;
 
	ft5x06_hw_init();              //i2c设备硬件的一些初始化工作
 
	err = i2c_add_driver(&ft5x06_driver); //重点,添加i2c驱动, 后面会重点分析,
 
	if (err) {
		FT5X06_WARNNING("add i2c driver failed");
		goto out;
	}
	list_for_each_entry(client, &(ft5x06_driver.clients), detected) {
		for (i = 0; i < ARRAY_SIZE(ft5x06_attr); i++) {
			err = device_create_file(&client->dev, &ft5x06_attr[i]);
			if (err) {
				FT5X06_WARNNING("Add device file failed");
				goto out;
			}
		}
	}
out:
	printlf();
	return err;
}

其实tp的驱动中就只调用了个i2c_add_driver函数把i2c设备端的驱动ft5x06_driver注册进去。我们接下来进去看一下。

#define i2c_add_driver(driver) \
	i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;
 
	/* Can't register until after driver model init */
	if (unlikely(WARN_ON(!i2c_bus_type.p)))
		return -EAGAIN;
 
	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;           //指定driver->driver.bus为i2c_bus_type,这个后面会用到
     
	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);      //将ft5x06_driver->driver注册进去
 
	INIT_LIST_HEAD(&driver->clients);
	/* Walk the adapters that are already present */
	i2c_for_each_dev(driver, __process_new_driver);
 
	return 0;
}
EXPORT_SYMBOL(i2c_register_driver);
driver->driver.bus = &i2c_bus_type;           //指定driver->driver.bus为i2c_bus_type,这个后面会用到
     
	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);      //将ft5x06_driver->driver注册进去

	INIT_LIST_HEAD(&driver->clients);
	/* Walk the adapters that are already present */
	i2c_for_each_dev(driver, __process_new_driver);

	return 0;
}
EXPORT_SYMBOL(i2c_register_driver);

接下来我们看下i2c设备和驱动最精彩的匹配过程。

7.3. i2c设备和驱动的匹配过程

从上文中的 driver_register(&driver->driver);说起

int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;
 
	BUG_ON(!drv->bus->p);
 
	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);
 
	other = driver_find(drv->name, drv->bus);         //在bus上查找一下看有无相同名字的driver注册过
	if (other) {
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}
 
	ret = bus_add_driver(drv); //将这个drv添加到对应的bus上      
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);
 
	return ret;
}
EXPORT_SYMBOL_GPL(driver_register);

int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;
 
	bus = bus_get(drv->bus);
	if (!bus)
		return -EINVAL;
 
	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
 
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
	priv->kobj.kset = bus->p->drivers_kset;
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;
 
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	if (drv->bus->p->drivers_autoprobe) { //drv->bus->p->drivers_autoprobe 这个值为1,是在bus_register(struct bus_type *bus)
		error = driver_attach(drv);   //函数中设置的,有兴趣的可以去看bus_register函数。这里就不展开了。
		if (error)                    //接着执行最重要的driver_attach
			goto out_unregister;
	}
	module_add_driver(drv->owner, drv)
}

int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;
 
	if (!bus || !bus->p)
		return -EINVAL;
 
	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)) && !error) //对bus中的中每一个dev 都调用fn,fn也就是参数传递进来的__driver_attach函数。
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
 
	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */
 
	if (!driver_match_device(drv, dev)) //首先执行driver_match_device
		return 0;
 
	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver)
		driver_probe_device(drv, dev);
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);
 
	return 0;
}

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;//如果drv->bus->match函数存在则调用drv->bus->match函数
}

这个函数是存在的,在上文7.2节中指定了driver->driver.bus = &i2c_bus_type;

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

因此drv->bus->match函数就为i2c_device_match函数。

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 */ //用id_table的方式来
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;
 
	return 0;
}

接下来是一连串的函数调用,最终通过比较compatible字段来进行匹配,我们一步一步看看。

static inline int of_driver_match_device(struct device *dev,
					 const struct device_driver *drv)
{
	return of_match_device(drv->of_match_table, dev) != NULL; //drv->of_match_table在上文中有提到,他的.compatible字段为 = "ft5x06" 
}
const struct of_device_id *of_match_device(const struct of_device_id *matches,
					   const struct device *dev)
{
	if ((!matches) || (!dev->of_node))
		return NULL;
	return of_match_node(matches, dev->of_node); //将dev的of_node传进去,看下of_node的定义,struct device_node  *of_node; /* associated device tree node */
}                                                    //也就是这个设备在设备树中的节点,下文在匹配的时候会用到
const struct of_device_id *of_match_node(const struct of_device_id *matches,
					 const struct device_node *node)
{
	const struct of_device_id *match;
	unsigned long flags;
 
	raw_spin_lock_irqsave(&devtree_lock, flags);
	match = __of_match_node(matches, node); //执行,
	raw_spin_unlock_irqrestore(&devtree_lock, flags);
	return match;
}
 
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
					   const struct device_node *node)
{
	if (!matches)
		return NULL;
 
	while (matches->name[0] || matches->type[0] || matches->compatible[0]) { //循环
		int match = 1;
		if (matches->name[0])
			match &= node->name
				&& !strcmp(matches->name, node->name);
		if (matches->type[0])
			match &= node->type
				&& !strcmp(matches->type, node->type);
		if (matches->compatible[0]) //如果matches的compatible[0]存在,这个是存在的
			match &= __of_device_is_compatible(node,           //matches->compatible与node比较
							   matches->compatible);
		if (match)
			return matches;
		matches++;
	}
	return NULL;
}
 
static int __of_device_is_compatible(const struct device_node *device,
				     const char *compat)
{
	const char* cp;
	int cplen, l;
 
	cp = __of_get_property(device, "compatible", &cplen); //从设备树中获取本节点的compatible字段,
	if (cp == NULL)
		return 0;
	while (cplen > 0) {
		if (of_compat_cmp(cp, compat, strlen(compat)) == 0)  //设备树总的compatible字段和驱动中的compatible字段比较,相等返回1.
			return 1;                                    //比较结束
		l = strlen(cp) + 1;
		cp += l;
		cplen -= l;
	}
 
	return 0;
}

我们来看一下设备树和驱动中对compatible字段都是如何定义的。

因此肯定能比较成功。经过层层返回,最终

driver_match_device(drv, dev) //返回1

	if (!driver_match_device(drv, dev)) //不成立
		return 0;
 
	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver)
		driver_probe_device(drv, dev); //执行
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;
 
	if (!device_is_registered(dev))
		return -ENODEV;
 
	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	printk("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
 
	pm_runtime_barrier(dev);
	ret = really_probe(dev, drv); //也不能说是真正的probe ,进去看看最激动人心的probe函数
	pm_request_idle(dev);
 
	return ret;
}

static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;
	int local_trigger_count = atomic_read(&deferred_trigger_count);
 
	atomic_inc(&probe_count);
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	WARN_ON(!list_empty(&dev->devres_head));
 
	dev->driver = drv;          //设置dev->driver 为当前的drv,下文中i2c_device_probe函数会用到,通过dev-driver找到i2c驱动的driver
 
	/* If using pinctrl, bind pins now before probing */
	ret = pinctrl_bind_pins(dev);
	if (ret)
		goto probe_failed;
 
	if (driver_sysfs_add(dev)) {
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}
 
	if (dev->bus->probe) { //如果dev->bus->probe函数存在,则执行dev->bus-probe函数,在上文的i2c_new_device函数中指定了
		ret = dev->bus->probe(dev);  //dev->bus为i2c_bus_type,因此,这个dev->bus->probe就是i2c_bus_type结构中的probe函数,
		if (ret)                     //即i2c_device_probe函数,因此这里回去执行i2c_device_probe,  
			goto probe_failed;
	} else if (drv->probe) {            //如果dev->bus->probe不存在则直接执行drv->probe,
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}
 
	driver_bound(dev);  //驱动和设备绑定
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;
 
}

static int i2c_device_probe(struct device *dev)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;
	int status;
	if (!client)
		return 0;
 
	driver = to_i2c_driver(dev->driver); //里面调用的是container_of(d, struct i2c_driver, driver),这个函数是通过一个结构
	if (!driver->probe || !driver->id_table)  //的成员返回该结构的首地址,不熟悉的自行百度。这样就找到了我们i2c 驱动。
		return -ENODEV;
	client->driver = driver;                  //设置client->driver 
	if (!device_can_wakeup(&client->dev))
		device_init_wakeup(&client->dev,
					client->flags & I2C_CLIENT_WAKE);
	dev_dbg(dev, "probe\n");
 
	status = dev_pm_domain_attach(&client->dev, true);
	if (status != -EPROBE_DEFER) {
 
   status = driver->probe(client, i2c_match_id(driver->id_table,//真正的probe函数,这里调用的就是ft5x06_driver结构的中的probe函数。
			client));              //即ft5x06_probe(struct i2c_client *client,const struct i2c_device_id *id)
		if (status) {                   //在此函数对i2c设备进行初始化等等一些操作,完全是用户自己决定,这才是真正的probe函数。
			client->driver = NULL;   //走到这里真是激动啊,转了720度的圈才转回来。真是佩服内核的作者。
			i2c_set_clientdata(client, NULL);
			dev_pm_domain_detach(&client->dev, true);
		}
	}
 
	return status;
}

总结

至此,已经大概明白了内核下的IIC架构,真是有点复杂,其中包含了总线,设备,驱动模型、设备树等相关的知识。其中有很多细节还是没有去深究,只是大概了解了框架。我估计平台设备总线模型,SPI设备总线模型应该是跟IIC模型是类似的,后面再分析一下SPI设备总线模型。内核真是精彩啊!!!

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值