Linux内核的所有适配器驱动程序都在driver/i2c/busses/目录下,当前高通的驱动是i2c-msm.c,适配器驱动的注册过程如下:
在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号。对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分配一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败。高通的adapter驱动使用了i2c_add_numbered_adapter()注册,总线号最初保存在platform_data中。
I2C adapter以platform_device方式注册进系统,在proble函数中初始化了struct i2c_adapter结构:
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* --- administration stuff. */
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
/* data fields that are valid for all devices */
u8 level; /* nesting level for lockdep */
struct mutex bus_lock;
struct mutex clist_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr; /*该成员描述了总线号*/
struct list_head clients; /* i2c_client结构链表,该结构包含device,driver和
adapter结构*/
char name[48];
struct completion dev_released;
};
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* --- administration stuff. */
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
/* data fields that are valid for all devices */
u8 level; /* nesting level for lockdep */
struct mutex bus_lock;
struct mutex clist_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr; /*该成员描述了总线号*/
struct list_head clients; /* i2c_client结构链表,该结构包含device,driver和
adapter结构*/
char name[48];
struct completion dev_released;
};
其中nr的值是在arch\arm\mach-msm\devices.c中定义的:
struct platform_device msm_device_i2c = {
.name = "msm_i2c",
.id = 0,
.num_resources = ARRAY_SIZE(resources_i2c),
.resource = resources_i2c,
};
struct platform_device msm_device_i2c_2 = {
.name = "msm_i2c",
.id = 2,
.num_resources = ARRAY_SIZE(resources_i2c_2),
.resource = resources_i2c_2,
};
struct platform_device msm_device_i2c = {
.name = "msm_i2c",
.id = 0,
.num_resources = ARRAY_SIZE(resources_i2c),
.resource = resources_i2c,
};
struct platform_device msm_device_i2c_2 = {
.name = "msm_i2c",
.id = 2,
.num_resources = ARRAY_SIZE(resources_i2c_2),
.resource = resources_i2c_2,
};
该结构以参数形式传进i2c_add_numbered_adapter(),下一步将进入
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
INIT_LIST_HEAD(&adap->clients);/*初始化设备链表*/
mutex_lock(&core_lock);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;/*父设备是platform_bus*/
pr_debug("I2C adapter driver [%s] forgot to specify "
"physical device\n", adap->name);
}
dev_set_name(&adap->dev, "i2c-%d", adap->nr);/*设备节点名字*/
adap->dev.release = &i2c_adapter_dev_release;
adap->dev.class = &i2c_adapter_class;
res = device_register(&adap->dev); /*注册adapter这个设备本身*/
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/*以下部分完成i2c设备和驱动的注册*/
if (adap->nr < __i2c_first_dynamic_bus_num)/*主板初始化时的动态总线号,该值已导出符号表*/
i2c_scan_static_board_info(adap);/*完成新类型i2c设备的注册,一般只在主板初始化时*/
/* Notify drivers */
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
i2c_do_add_adapter); /*探测总线上的所有i2c设备驱动,同时完成client、driver、device、adapter的绑定,但driver->address_data非空的情况下有用,而这又意味着只对旧的i2c机制有效*/
out_unlock:
mutex_unlock(&core_lock);
return res;
out_list:
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
INIT_LIST_HEAD(&adap->clients);/*初始化设备链表*/
mutex_lock(&core_lock);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;/*父设备是platform_bus*/
pr_debug("I2C adapter driver [%s] forgot to specify "
"physical device\n", adap->name);
}
dev_set_name(&adap->dev, "i2c-%d", adap->nr);/*设备节点名字*/
adap->dev.release = &i2c_adapter_dev_release;
adap->dev.class = &i2c_adapter_class;
res = device_register(&adap->dev); /*注册adapter这个设备本身*/
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/*以下部分完成i2c设备和驱动的注册*/
if (adap->nr < __i2c_first_dynamic_bus_num)/*主板初始化时的动态总线号,该值已导出符号表*/
i2c_scan_static_board_info(adap);/*完成新类型i2c设备的注册,一般只在主板初始化时*/
/* Notify drivers */
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
i2c_do_add_adapter); /*探测总线上的所有i2c设备驱动,同时完成client、driver、device、adapter的绑定,但driver->address_data非空的情况下有用,而这又意味着只对旧的i2c机制有效*/
out_unlock:
mutex_unlock(&core_lock);
return res;
out_list:
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}
i2c_scan_static_board_info对应的初始化过程在board-msm7x27.c中完成,
i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
6. I2C设备7. 驱动
驱动的编写方法已在《msm7227-I2C设备驱动实现要点.doc》中介绍,此节分析驱动和设备的注册过程。