日期:2014-05-16  浏览次数:20832 次

linux i2c驱动架构(史上最全) davinc dm368 i2c驱动分析

预备知识

在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinci.c)。标题党请见谅!

其实i2c接口非常的简单,即使用51单片的gpio来模拟i2c,编写一个e2prom或者其他i2c接口的驱动程序,也不是什么难事,几百行代码就能搞定。

但是Linux的i2c驱动体系结构却有相当的复杂度,不管是叫linux i2c驱动还是单片机i2c驱动,其根本还是操作soc芯片内部的i2c模块(也叫i2c adapter)(读写i2c相关的寄存器)来产生start、stop还有ack信号而已。

linux设备驱动到底复杂在什么地方?

假设soc芯片dm368有两个i2c adapter(368内部真正只有一个i2c模块):i2c_adapter1,i2c_adapter1;然后外部有三个i2c接口的设备i2c_device1,i2c_device2,i2c_device3。

现在要求在裸机下写出他们的驱动函数。那么肯定要写出6个不同的驱动函数:

i2c_adapter1_ReadWrite_i2c_device1();
i2c_adapter1_ReadWrite_i2c_device2()
i2c_adapter1_ReadWrite_i2c_device3()
i2c_adapter2_ReadWrite_i2c_device1()
i2c_adapter2_ReadWrite_i2c_device2()
i2c_adapter2_ReadWrite_i2c_device3()
设想一共有m个i2c adapter和n个外设i2c device,那么将需要m*n个驱动。并且这m*n个驱动程序必要会有很大部分重复的代码,而且不利于驱动程序的移植。

如果采用adapter和device分离的思想来写这样的驱动会是怎样呢?


图1

这样分离之后,只需要m+n个驱动,而且Adapter和Device的几乎没有耦合性,增加一个Adapter或者device并不会影响其余的驱动。

这就是分离思想带来的好处。除此之外,linux虽然是C写的,但是大量使用了面向对象的变成方法(可以理解为分层的思想),仅仅分离细想和分层思想的引入,就大大增加了linux设备驱动的复杂度。


linux驱动中 i2c驱动架构


图2

上图完整的描述了linux i2c驱动架构,虽然I2C硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂。那么我们如何编写特定i2c接口器件(比如,ov2715,需要i2c来配置寄存器)的驱动程序?就是说上述架构中的那些部分需要我们完成,而哪些是linux内核已经完善的或者是芯片提供商(TI davinci平台已经做好的)已经提供的?

架构层次分类

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

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

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

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

第一层和第二层又叫i2c总线驱动(bus),第三第四属于i2c设备驱动(device driver)。在linux驱动架构中,几乎不需要驱动开发人员再添加bus,因为linux内核几乎集成所有总线bus,如usb、pci、i2c等等。并且总线bus中的【与特定硬件相关的代码】已由芯片提供商编写完成,例如TI davinci平台i2c总线bus与硬件相关的代码在内核目录/drivers/i2c/buses下的i2c-davinci.c源文件中;而三星的s3c-2440平台i2c总线bus为/drivers/i2c/buses/i2c-s3c2410.c

第三第四层又叫设备驱动层与特定device相干的就需要驱动工程师来实现了。

明确了方向后,再来具体分析。

具体分析

i2c_adapter与i2c_client的关系与i2c硬件体系中设配器与设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备device,所以相应的,i2c_adapter也可以被多个i2c_client依附,在i2c_adapter中包含i2c_client的链表。同一类的i2c设备device对应一个驱动driver。driver与device的关系是一对多的关系。

现在,我们就来看一下这几个重要的结构体,分别是i2c_driver i2c_client i2c_adapter,也可以先忽略他们,待会回过头来看会更容易理解

1、i2c_driver

struct i2c_driver {
	int id;
	unsigned int class;

	int (*attach_adapter)(struct i2c_adapter *);
	int (*detach_adapter)(struct i2c_adapter *);

	int (*detach_client)(struct i2c_client *);

	int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
	struct device_driver driver;
	struct list_head list;
};

2、i2c_client

struct i2c_client {
	unsigned int flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit 	*/
					/* addresses are stored in