作者:Sam (甄峰) sam_code@hotmail.com
0. Linux
I2C驱动体系概述:
Linux I2C驱动体系结构由以下三部分组成:
A: I2C Core。
B:I2C总线驱动。 C:I2C设备驱动。
I2C核心是I2C总线驱动和I2C设备驱动的中间枢纽,它以通用的、与平台无关的接口实现了I2C中设备与适配器的沟通
I2C总线驱动填充i2c_adapter和i2c_algorithm结构体。
I2C设备驱动填充i2c_driver和i2c_client结构体。
很显然,I2C Core是Linux Kernel提供好的用来支持的模块。而根据I2C总线和I2C设备的区别,I2C
Driver也分两个层次。
I2C 总线层驱动: 根据核心板的芯片手册,编写总线层驱动。例如 使用Nvidia
K1开发板手册中的I2C总线文档,实现总线层驱动。总线层主要向内核注册一个Adapter,并填充Adapter支持的类型和方法。
I2C
设备层驱动: I2C
Driver设备层,主要针对不同的I2C硬件设备编写驱动,并为用户提供接口。
I2C子系统下设备驱动有两种模式:
A: 用户模式驱动,依赖于I2C子系统中的i2c-dev.
B: 普通Device Driver。
1. Linux
I2C驱动程序目录结构:
1.1:I2C Core:
kernel/drivers/i2c
i2c-boardinfo.c i2c-core.c
i2c-dev.c i2c-mux.c
i2c-slave.c i2c-smbus.c
i2c-stub.c
i2c-core.c
可以看到,模块自我介绍为:I2C-Bus main module
提供(EXPORT_SYMBOL)了很多I2C核心功能。
如:
i2c_new_device()
,i2c_unregister_device(): 增加,删除i2c device---struct
i2c_client
i2c_add_adapter(). i2c_del_adapter(): 增加,删除 i2c adapter--struct
i2c_adapter
i2c_register_driver(), i2c_del_driver(): 增加,删除i2c driver.
i2c_master_send()
同时,它注册了一个 bus_register(&i2c_bus_type);
这里有有一个函数:i2c_device_match().
它来判断哪个设备驱动和I2C设备是匹配的。
它调用: of_driver_match_device(),和i2c_match_id().
i2c-dev.c:
I2C /dev entries driver
实现了 I2C适配器设备文件功能。
/dev/i2c-0, /dev/i2c-1, /dev/i2c-n
创建了字符设备驱动--register_chrdev()
创建了/dev/i2c-n node,device_create()
提供了read, write...等功能。
这样,用户就可以在应用程序层通过读写/dev/i2c-n 来操作I2C设备。
按照Sam当前理解:每个node(/dev/i2c-0.....
/dev/i2c-n)对应一个I2C总线。每个总线可以挂载多个I2C设备。那么打开node,就应该可以和挂载在总线上的设备通讯了。
1.2:I2C总线驱动:
I2C总线驱动和当前目标开发板有关,例如:Sam当前拿的是Nvidia K1开发板。则开发I2C总线驱动,需要详细读取K1
IIC部分文档。 幸好这部分在出厂前会由厂商搞定,(BSP部分)。我们就姑且看之吧。
总线驱动位于kernel/drivers/i2c/busses/目录。这里包含很多常用的I2C控制器驱动。
kernel/drivers/i2c/busses/i2c-tegra.c -----nVidia Tegra2 I2C Bus
Controller driver
它首先注册了一个platform drivert : platform_driver_register()
当系统发现匹配的I2C设备时,会调用:tegra_i2c_probe(), 这里初始化Tegra
I2C总线,设置了clk的频率, 配置了中断处理程序,并调用i2c_add_numbered_adapter()
增加一个adpter。
Adapter是I2C总线驱动的关键,那Adapter到底是什么语义的?
Sam当前理解是:Adapter对应物理上存在的一个I2C总线。一个总线上能够挂载多个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;
int retries;
struct device
dev;
bool cancel_xfer_on_shutdown;
bool atomic_xfer_only;
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总线起了名字:
strlcpy(i2c_dev->adapter.name, "Tegra I2C
adapter",sizeof(i2c_dev->adapter.name));
叫:Tegra I2C adapter
cat /sys/class/i2c-dev/i2c-0/name
Tegra I2C adapter
这里还有一个概念需要注意:
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
*);
};
master_xfer:I2C总线的通讯协议函数。
smbus_xfer:
SMBus总线的通讯协议函数。
i2c_algorithm:作用就是这个I2C Bus的数据传输。如果没有它,这个I2C Bus不能传送数据。
数据格式为: struct i2c_msg。
这部分实现和I2C总线控制器设计有关,它按照芯片手册中的说明,操作寄存器控制数据发送和接收。方便I2C设备驱动使用。
这里还有个知识点:
Linux I2C GPIO
Driver. 当没有I2C总线控制器时,可以使用GPIO来模拟I2C总线,使用2个GPIO分别模拟SDA,
SCL, 并使用驱动,让GPIO模拟I2C总线时序,完成Linux 与I2C设备的通讯过程。
与有I2C控制芯片的驱动不同的是传输算法的实现,GPIO模拟I2C驱动,不再是设置寄存器控制数据发送和接收。而是用程序来控制。代码在drivers/i2c/busses/i2c-gpio.c,时序控制:drivers/i2c/algos/i2c-algo-bit.c
理论上,如果没有I2C控制器,只需要修改总线驱动中master_xfer为GPIO模式即可。
至于一些具体设置,如哪个GPIO对应SDA, 哪个GPIO对应SCL等信息,则在板级代码中写入:
static struct i2c_gpio_platform_data pdata = {
.sda_pin =
AT91_PIN_PA25,
.sda_is_open_drain = 1,
.scl_pin =
AT91_PIN_PA26,
.scl_is_open_drain = 1,
.udelay =
2,
};
(请注意:Linux Device
Tree使用后,这些板级代码可能被合并到DTS文件内了)
如需要使用Linux I2C GPIO Driver, 则需要make menuconfig时加入此modules.
1.3:I2C设备层驱动:
1.3.1: I2C接口硬件设备驱动程序不同的实现方式:
从广义上说,I2C设备层驱动是指为各种I2C接口的硬件提供驱动。在不同纬度上,有不同的实现方式:
A: 用户模式驱动程序。
B: 普通驱动程序。
1.3.2:用户模式驱动程序:
依赖于i2c子系统中的/dev/i2c-0, /dev/i2c-1,
/dev/i2c-n
打开/dev/i2c-n,这个node, 相当于打开了一个物理存在的I2C适配器。按Sam理解,也就是得到了一个I2C
Bus。 后面向不同I2C数据写入读出数据,就是向某个挂载在这个I2C总线上的设备写入和读出。
很难说这就是Driver。但却是实现了向I2C设备发送和接收数据。
以后再填充内容。
1.3.3:普通驱动程序:
不利用i2c-dev.c, 直接写I2C设备驱动
在比较老的Linux
Kernel中,I2C设备Driver也可以使用两种方式。Adapter方式和Probe方式。Kernel推荐使用Probe方式。且在某个Kernel版本后,Adapter方式不再支持了(存疑)
1.3.3.1: Adapter方式:
这个方式的思路是,创建一个i2c_drveir结构体并注册给kernel:
A: I2C Device Driver开发者填充i2c_driver结构体。
最重要的两个成员是函数指针:attach_adapter()--调用时机较复杂,后面再说, detach_client()--脱离适配器时被调用。
B: 注册这个 i2c_driver:
i2c_add_driver(&inv_mod_driver);
如此,整个驱动就完整了。但如何运作的呢?
前面谈到过,I2C 总线驱动会创建几个I2C总线Adapter. 每个Adapter代表一个实际存在的I2C Bus。
当I2C 设备驱动使用i2c_add_drvier()注册了一个设备驱动时,则会依次使用每个Adapter来尝试连接我们的I2C
Device。 这个过程,就是调用attach_adapter(struct i2c_adapter* adapter).
attach_adapter中调用:
i2c_probe(adapter, &addr_data, xxxx_detect);
其中:addr_data很关键:
static unsigned short normal_i2c[] = {
xx,xx,xx,xx,xx,xx,xx,xx
I2C_CLIENT_END };
地址必须是I2C芯片的地址。否则无法探测到。
xxxxx_detect(). 它是在探测到设备后,会被调用的函数。
这个过程很关键,填充i2c_client,创建node均在此处实现。
xxxxx_detect(struct i2c_adapter *adapter, int address,
int kind)
可以
A:创建i2c_client, 填充其内容。利用i2c_attach_client()注册i2c_client。
i2c_client--代表的就是一个I2C Device。
B:创建字符设备node.
C: 实现字符设备的read,write,ioctl等。
其它部分如detach()等,就是反向操作了。
1.3.3.2:Probe方式I2C Device Driver:
这个方式也正是被推荐的方式。思路如下:
A:I2C Device
Driver开发者填充i2c_driver结构体。与Adapter方式不同的主要是i2c_driver中需要被填充的内容。
比较重要的是probe(), remove(), id_table.
static struct i2c_driver inv_mod_driver =
{
.class = I2C_CLASS_HWMON,
.probe = nvi_probe,
.remove = nvi_remove,
.id_table = nvi_mpu_id,
.driver = {
.owner = THIS_MODULE,
.name = "inv_dev",
.of_match_table =
of_match_ptr(nvi_mpu_of_match),
#ifdef CONFIG_PM
.pm = &nvi_pm_ops,
#endif
},
.address_list = normal_i2c,
.shutdown = nvi_shutdown,
};
B:注册Driver:
i2c_add_driver(&inv_mod_driver);
具体创建字符设备Device Node等工作。都在probe中做了。
现在具体分析:
我们知道,i2c_driver对应的是一个i2C
driver. 它的probe()函数,就是当有符合条件的I2C Device出现在I2C
Bus上时被Kernel调用的。但Kernel如何知道哪个I2C
Device复合Driver条件呢?和USB设备一样,使用id_table.
static struct i2c_device_id nvi_mpu_id[] = {
{"itg3500", INV_ITG3500},
{"mpu3050", INV_MPU3050},
{"mpu6050", INV_MPU6050},
{"mpu9150", INV_MPU9150},
{"mpu6500", INV_MPU6500},
{"mpu9250", INV_MPU9250},
{"mpu6xxx", INV_MPU6XXX},
{"mpu9350", INV_MPU9350},
{"mpu6515", INV_MPU6515},
{}
};
系统会调用:i2c_device_match()----i2c_match_id()来判断咱们注册的I2C Driver和Bus
上链接的Device是否匹配。如果匹配,则调用probe.
但Sam在想,USB 设备是因为其可以存储一小块数据,这块数据和
.id_table的内容比对,看是否匹配。但I2C设备理应没有这块区域啊。那i2c_driver中存储的.id_table的数据,和谁比对呢?
Sam认为,
kernel的平台代码(arch/arm/mach-xxxx.
在NV-Tegra平台是:arch/arm/mach-tegra/board-ardbeg-sensors.c)中,会针对将要支持的Device,构建各自的i2c_board_info。
类似:
static struct i2c_board_info __initdata
inv_mpu9250_i2c0_board_info[] = {
{
I2C_BOARD_INFO(MPU_GYRO_NAME,
MPU_GYRO_ADDR),
.platform_data =
&mpu9250_gyro_data,
},
{
I2C_BOARD_INFO(MPU_BMP_NAME,
MPU_BMP_ADDR),
.platform_data =
&mpu_bmp_pdata,
},
{
I2C_BOARD_INFO(MPU_COMPASS_NAME,
MPU_COMPASS_ADDR),
.platform_data =
&mpu_compass_data,
},
};
然后调用 i2c_register_board_info()。
Probe分析:
维护字符设备驱动程序。使用户层程序可以访问Device。
附录1:
这个图标上,可以很清晰的看到,I2C总线驱动部分,I2C设备驱动部分,各自有两个概念:
i2c_adapter, i2c_algorithm. i2c_client, i2c_driver.
咱们总结一下他们各自的用途和语义。
Sam认为;
i2c_adapter:
对应的就是一个I2C总线,物理上存在的I2C
Bus。每个I2C总线上,可以挂载多个I2C设备。
i2c_algorithm:每个I2C总线,都要包含一个i2c_algorithm,它相当于传输协议。没有它,I2C总线无法传输数据。
i2c_driver:
对应一个I2C驱动。驱动程序可以与I2C设备匹配,帮助用户访问I2C设备。
i2c_client: 对应一个物理存在的I2C
设备。