Linux I2C设备驱动中,是通过i2c device id名字进行i2c device和i2c driver匹配的。
例如以下在板级代码定义"twl4030"作为device名字信息。
static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {
{
I2C_BOARD_INFO("twl4030", 0x48),
.flags = I2C_CLIENT_WAKE,
.irq = INT_34XX_SYS_NIRQ,
.platform_data = &omap3evm_twldata,
},
};
在i2c driver驱动中通过以下代码将device和driver进行匹配
static const struct i2c_device_id twl_ids[] = {
{ "twl4030", TWL4030_VAUX2 }, /* "Triton 2" */
{ "twl5030", 0 }, /* T2 updated */
{ "twl5031", TWL5031 }, /* TWL5030 updated */
{ "tps65950", 0 }, /* catalog version of twl5030 */
{ "tps65930", TPS_SUBSET }, /* fewer LDOs and DACs; no charger */
{ "tps65920", TPS_SUBSET }, /* fewer LDOs; no codec or charger */
{ "twl6030", TWL6030_CLASS }, /* "Phoenix power chip" */
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(i2c, twl_ids);
/* One Client Driver , 4 Clients */
static struct i2c_driver twl_driver = {
.driver.name = DRIVER_NAME,
.id_table = twl_ids,
.probe = twl_probe,
.remove = twl_remove,
};
i2c_device_id的原型是:
struct i2c_device_id {
char name[I2C_NAME_SIZE];
kernel_ulong_t driver_data /* Data private to the driver */
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
那么通过看twl_ids和i2c_device_id原型可试想i2c_device_id结构中的第二个成员有什么作用?
例如应该可以将i2c_device_id twl_ids定义为如果下形势:
static const struct i2c_device_id twl_ids[] = {
{ "twl4030", 1}, /* "Triton 2" */
{ "twl5030", 2 }, /* T2 updated */
{ "twl5031", 3}, /* TWL5030 updated */
{ "tps65950", 4 }, /* catalog version of twl5030 */
{ "tps65930", 5}, /* fewer LDOs and DACs; no charger */
{ "tps65920", 6}, /* fewer LDOs; no codec or charger */
{ "twl6030", 7}, /* "Phoenix power chip" */
{ /* end of list */ },
};
那么driver_data为1表示”twl4030“,driver_data为2表示”twl5030“等等。。
回到i2c_device_id twl_ids的原始定义,driver_data应该怎么用呢?很简单,看一下代码。
static int __init
twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int status;
unsigned i;
struct twl4030_platform_data *pdata = client->dev.platform_data;
u8 temp;
if (!pdata) {
dev_dbg(&client->dev, "no platform data?\n");
return -EINVAL;
}
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
dev_dbg(&client->dev, "can't talk I2C?\n");
return -EIO;
}
if (inuse) {
dev_dbg(&client->dev, "driver is already in use\n");
return -EBUSY;
}
for (i = 0; i < TWL_NUM_SLAVES; i++) {
struct twl_client *twl = &twl_modules[i];
twl->address = client->addr + i;
if (i == 0)
twl->client = client;
else {
twl->client = i2c_new_dummy(client->adapter,
twl->address);
if (!twl->client) {
dev_err(&client->dev,
"can't attach client %d\n", i);
status = -ENOMEM;
goto fail;
}
}
mutex_init(&twl->xfer_lock);
}
inuse = true;
if ((id->driver_data) & TWL6030_CLASS) {
twl_id = TWL6030_CLASS_ID;
twl_map = &twl6030_map[0];
} else {
twl_id = TWL4030_CLASS_ID;
twl_map = &twl4030_map[0];
}
/* setup clock framework */
clocks_init(&client->dev, pdata->clock);
/* load power event scripts */
if (twl_has_power() && pdata->power)
twl4030_power_init(pdata->power);
/* Maybe init the T2 Interrupt subsystem */
if (client->irq
&& pdata->irq_base
&& pdata->irq_end > pdata->irq_base) {
if (twl_class_is_4030()) {
twl4030_init_chip_irq(id->name);
status = twl4030_init_irq(client->irq, pdata->irq_base,
pdata->irq_end);
} else {
status = twl6030_init_irq(client->irq, pdata->irq_base,
pdata->irq_end);
}
if (status < 0)
goto fail;
}
/* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.
* Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,
* SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.
*/
if (twl_class_is_4030()) {
twl_i2c_read_u8(TWL4030_MODULE_INTBR, &temp, REG_GPPUPDCTR1);
temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \
I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);
twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
}
status = add_children(pdata, id->driver_data);
usb_gpio_settings();
fail:
if (status < 0)
twl_remove(client);
return status;
}
再看看 TWL6030,它其实是一个宏定义。
#define TWL6030_CLASS BIT(3) /* TWL6030 class */
找i2c driver的probe函数中,以下代码:
if ((id->driver_data) & TWL6030_CLASS) {
twl_id = TWL6030_CLASS_ID;
twl_map = &twl6030_map[0];
} else {
twl_id = TWL4030_CLASS_ID;
twl_map = &twl4030_map[0];
}
就通过 driver_data 来判断driver现在是在服务于哪个设备。