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,
	},
};
在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结构中的第二个成员有什么作用?
为什么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;
}
再看看 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现在是在服务于哪个设备。



你可能感兴趣的:(Linux驱动)