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现在是在服务于哪个设备。