1. Overview
2. Data Structure
3. Adapter
4. I2C-core
5. Slave Device
1. Overview
1.1 Definition
I2C Inter-Integrated Circuit
SMBUS System Management Bus, the I2C subset
1.2 Characteristics
The amount of data exchanged is small.
The required data transfer rate is low.
1.3 Speed
Fast speed 400 kbps
Full speed 100 kbps
1.4 Topology
2 Data Structure
理解对理解整个驱动程序子系统是很重要的。I2C的主要有两结构,struct i2c_client 和 struct i2c_adapter。
2.1 i2c_client
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device (or -1) */
char driver_name[KOBJ_NAME_LEN];
struct list_head list; /* DEPRECATED */
struct completion released;
};
struct i2c_client代表一个挂载到i2c总线上的i2c从设备,该设备所需要的数据结构,其中包括
该i2c从设备所依附的i2c主设备 struct i2c_adapter*adapter
该i2c从设备的驱动程序struct i2c_driver *driver
作为i2c从设备所通用的成员变量,比如addr, name等
该i2c从设备驱动所特有的数据,依附于dev->driver_data下
2.2 i2c_adapter
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class;
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
... ...
};
struct i2c_adapter代表主芯片所支持的一个i2c主设备,该设备所需要的数据结构,
其中,struct i2c_algorithm *algo是该i2c主设备传输数据的一种,或者说是在i2c总线上完成主从设备间数据通信的一种能力。
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 *);
};
接下来,要实现整个i2c子系统的驱动,便围绕这两个数据结构展开,其主要步骤可总结为以下三步,
实现i2c主设备驱动 (drivers/i2c/bus/*)
注册i2c从设备的i2c_client (drivers/i2c/i2c-core)
实现i2c从设备驱动
3Adapter
内核目录drivers/i2c下有两个文件夹,algorithm和bus,其中bus存放i2c主设备的驱动,主设备驱动完成两大任务,
提供该i2c主设备与从设备间完成数据通信的能力
完成该i2c_adapter和所有已知的i2c_client的注册
以i2c-pxa.c为例,
/* drivers/i2c/bus/i2c-pxa.c */
static int __init i2c_adap_pxa_init(void)
{
return platform_driver_register(&i2c_pxa_driver);
}
static struct platform_driver i2c_pxa_driver = {
.probe = i2c_pxa_probe,
... ...
.id_table = i2c_pxa_id_table,
};
static int i2c_pxa_probe(struct platform_device *dev)
{
struct pxa_i2c *i2c;
i2c->adap.algo = i2c_pxa_algorithm; // 提供该i2c主设备与从设备间完成数据通信的能力
i2c_add_numbered_adapter(&i2c->adap); // 调用i2c-core.c中的接口函数,完成该i2c_adapter和i2c_client的注册
... ...
}
static const struct i2c_algorithm i2c_pxa_algorithm = {
.master_xfer = i2c_pxa_xfer, // 根据pxa具体芯片的要求,完成i2c数据传输 .functionality = i2c_pxa_functionality,
};
4 I2C-core
内核目录drivers/i2c下的i2c-core.c,顾名思义,是内核为I2C提供的统一系统接口。
看看i2c_add_numbered_adapter做了些什么,
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{... ...
status = i2c_register_adapter(adap);
return status;
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
... ...device_register(&adap->dev); //完成I2C主设备adapter的注册,即注册object和发送uevent等i2c_scan_static_board_info(adap);
... ...
}
i2c_scan_static_board_info(adap),此函数为整个I2C子系统的核心,它会去遍历一个由I2C从设备组成的双向循环链表,并完成所有I2C从设备的i2c_client的注册,具体过程如下,
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo; //已经建立好了的I2C从设备链表
list_for_each_entry(devinfo, &__i2c_board_list, list) {
i2c_new_device(adapter,&devinfo->board_info);
... ...
}
}
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, structi2c_board_infoconst *info)
{
... ...
i2c_attach_client(client);
... ...
}
int i2c_attach_client(struct i2c_client *client)
{
... ...
device_register(&client->dev); //完成I2C从设备client的注册
... ...
}
那么,这个I2C从设备组成的双向循环链表,是什么时候通过什么方式建立起来的呢?
以某重力感应设备为例,
/* /arch/arm/mach-pxa/starwood_p1.c */
static void __init saar_init(void)
{
... ...
i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_bma220_info));
... ...
}
static struct i2c_board_info saar_i2c_bma220_info[] = {
{
.driver_name = "bma220",
.addr = 0x0B,
.irq = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO15)),
},
};
/* drivers/i2c/i2c-boardinfo.c */
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
{
... ...
struct i2c_devinfo *devinfo;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); //将I2C从设备加入该链表中
... ...
}
所以,在系统初始化的过程中,我们可以通过 i2c_register_board_info,将所需要的I2C从设备加入一个名为__i2c_board_list双向循环链表,系统在成功加载I2C主设备adapt后,就会对这张链表里所有I2C从设备逐一地完成 i2c_client的注册。
5 Slave Driver
如果说硬件方面,I2C主设备已经集成在主芯片内,软件方面,linux也为我们提供了相应的驱动程序,位于drivers/i2c/bus下,那么接下来I2C从设备驱动就变得容易得多。既然系统加载I2C主设备驱动时已经注册了i2c_adapter和i2c_client,那么I2C从设备主要完成三大任务,
系统初始化时添加以i2c_board_info为结构的I2C从设备的信息
在I2C从设备驱动程序里使用i2c_adapter里所提供的算法,即实现I2C通信。
将I2C从设备的特有数据结构挂在到i2c_client.dev->driver_data下。
以重力感应装置为例,
static int __init BMA220_init(void)
{
return i2c_add_driver(&bma220_driver);
}
static struct i2c_driver bma220_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "bma220",
},
.class = I2C_CLASS_HWMON,
.probe = bma220_probe,
.remove = bma220_remove,
};
static int bma220_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct bma220_data *data;
i2c_check_functionality(client->adapter, I2C_FUNC_I2C)
i2c_smbus_read_word_data(client, 0x00); // i2c-core提供的接口,利用i2c_adapter的算法实现I2C通信
i2c_set_clientdata(bma220_client, data); // 将设备的数据结构挂到i2c_client.dev->driver_data下
misc_register(&bma_device);
request_irq(client->irq, bma220_irq_handler, IRQF_TRIGGER_RISING, "bma220", &data->bma220);
bma220_set_en_tt_xyz(0);
bma220_reset_int();
... ...
}