Linux I2C驱动:i2c_device_id
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,
- },
- };
- 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结构中的第二个成员有什么作用?
为什么twl_ids有些明确使用了"driver_data",有些确默认赋值为0?
通过twl_ids看到,这个驱动和可以适用于多种设备,那么对于驱动开发着来说,要使得此驱动能和多种设备(twl4030/twl630等)兼容,那么就需要知道驱动和哪个设备匹配了。怎么知道呢?就是通过i2c_device_id的第二个成员了,driver_data可以用来方便快捷指示设备的一些特定属性。至于怎么指示则由驱动开发者来设定。
例如应该可以将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;
- }
- #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现在是在服务于哪个设备。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步