3.1 Linux下IIC驱动开发
3.2.1 IIC总线驱动
在系统开机时,首先装载的是I2C总线驱动。一个总线驱动用于支持一条特定的I2C总线的读写。一个总线驱动通常需要两个模块,一个struct i2c_adapter和一个struct i2c_algorithm来描述:
static struct i2c_adapter pb1550_board_adapter =
这个样例挂接了一个叫做“pb1550 adapter”的驱动。但这个模块并未提供读写函数,具体的读写方法由第二个模块,struct i2c_algorithm提供。
static struct i2c_algorithm au1550_algo =
这个样例给上述总线驱动增加了读写“算法”。通常情况下每个I2C总线驱动都定义一个自己的读写算法,但鉴于有些总线使用相同的算法,因而可以共用同一套读写函数。本例中的驱动定义了自己的读写算法模块,起名叫“Au1550 algorithm”。
全部填妥后,通过调用:
i2c_add_adapter(i2c_adap);
将这两个模块注册到操作系统里,总线驱动就算装上了。对于AMD au1550,这部分已经由AMD提供了。
3.2.2 IIC设备驱动
I2C的设备驱动是通过i2c_add_driver(&my_driver)向i2c-core注册的,my_driver中的核心是detach_client
设备驱动透过I2C总线同具体的设备进行通讯。一个设备驱动有两个模块来描述,struct i2c_driver和struct i2c_client。
当系统开机、I2C总线驱动装入完成后,就可以装入设备驱动了。首先装入如下结构:
static struct i2c_driver zlg7290_driver = {
//
};
i2c_add_driver(&zlg7290_driver);
这个i2c_driver一旦装入完成,其中的attach_adapter函数就会被调用。
如何探测总线上的设备、Linux中i2c设备地址如何标识并引用?
I2C中, 典型的attach_adapter如下所示:
static int my_attach(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, zlg7290_detect);
}
static int zlg7290_detect (struct i2c_adapter *adapter, int address, int kind)
{
}
3.2.2.1 IIC设备探测
注意探测可能会找到多个设备,因而不仅一个I2C总线可以挂多个不同类型的设备,一个设备驱动也可以同时为挂在多个不同I2C总线上的设备服务。
每当设备驱动探测到了一个它能支持的设备,它就创建一个struct i2c_client来标识这个设备:
struct i2c_client *new_client;
……
可见,一个i2c_client代表着位于adapter总线上,地址为address,使用zlg7290_driver来驱动的一个设备。它将总线驱动与设备驱动,以及设备地址绑定在了一起。一个i2c_client就代表着一个I2C设备。
当得到I2C设备后,就可以直接对此设备进行读写:
3.2.2.2 linux下IIC设备地址
addr_data是在 include/linux/i2c.h 中定义的或自己在自己驱动程序中定义的一个i2c_client_address_data结构:
static struct i2c_client_address_data addr_data = {
}
若自己不定义,则用i2c.h中的默认定义。
struct i2c_client_address_data {
};
根据作者自行定义设备地址与否,有两种情形:
a. 采用默认定义,一般是不会work,毕竟大多数i2c-core中是不可能提前知道所接设备地址的,这样通过i2c_probe()探测肯定不可能找到,也不可能建立两者之间的联系;况且,i2c_probe()属于i2c-core中的函数,i2c-core中管理着所有注册过的设备和驱动列表,i2c_probe()中也不能随意传入地址,否则容易导致系统混乱或有潜在的风险,所以i2c-core也不允许这么做!
b. 作者自行定义地址结构
典型例子如下:
若自行定义,则参考如下:
static unsigned short normal_i2c[] = {I2C_KS0127_ADDON>>1,
static unsigned short probe[2] =
static unsigned short ignore[2] =
static struct i2c_client_address_data addr_data = {
};
或者根本就不定义完整的i2c_client_address_data结构,只根据需要定义normal_i2c[],probe[],ignore[],forces[][],然后调用i2c_probe(adapter,&addr_data, &my_probe) 即可。
在my_probe()中把实际的地址赋于i2c_client,调用i2c_set_clientdata()设置i2c_client->dev->drv_data,并调用i2c_attach_client(client)向系统注册设备。
最后,i2c_probe()中探测时的地址优先级:
forces[][], probe[], normal_i2c[](其中忽略ignore[]中的项)。
I2c 设备在实际使用中比较广泛, sensor,rtc,audio, codec,etc. 因设备复杂性不同, Linux 中有些驱动中对地址的定义不在同一文件,这时多数情况都在 arch 中对设备作为 platform_device 进行初始化并注册的代码中。