I2C驱动框架(三)
参考:I2C子系统之platform_device初始化——smdk2440_machine_init()
I2C驱动框架还应用了另一种总线-设备-驱动模型,平台设备总线platform_bus_type。内核已经注册好了平台总线,驱动程序只需向平台总线添加平台设备和平台驱动。这节主要介绍如何添加平台设备。
/*********"/arch/arm/mach-s3c64xx/mach-smdk6410.c"**********************/ //2./arch/arm/mach-s3c2440/mach-smdk2440.c中的函数:smdk2440_machine_init() arch_initcall级别 static void __init smdk6410_machine_init(void) -->s3c_i2c0_set_platdata(NULL); -->i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); -->i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); -->platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices)); -->platform_device_register(&s3c_device_i2c0); /*注册平台设备*/
s3c_device_i2c0定义如下:
/*********"/arch/arm/plat-samsung/dev-i2c0.c"**********************/ static struct resource s3c_i2c_resource[] = { [0] = { .start = S3C_PA_IIC, .end = S3C_PA_IIC + SZ_4K - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_IIC, .end = IRQ_IIC, .flags = IORESOURCE_IRQ, }, }; /* 内核用adapter来表示主机,I2C驱动框架中的adapter就是指s3c6410的i2c控制器。 内核中通过platform_device来描述。具体如下: */ struct platform_device s3c_device_i2c0 = { .name = "s3c2410-i2c", .id = 0, .num_resources = ARRAY_SIZE(s3c_i2c_resource), .resource = s3c_i2c_resource, /*s3c_device_i2c0.dev.platform_data = &default_i2c_data0;*/ };
通过s3c_set_platdata(NULL)进一步设置s3c_device_i2c0的成员变量
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = { .flags = 0, .slave_addr = 0x10, .frequency = 100*1000, .sda_delay = 100, }; void s3c_i2c0_cfg_gpio(struct platform_device *dev) { s3c_gpio_cfgpin(S3C64XX_GPB(5), S3C64XX_GPB5_I2C_SCL0); s3c_gpio_cfgpin(S3C64XX_GPB(6), S3C64XX_GPB6_I2C_SDA0); s3c_gpio_setpull(S3C64XX_GPB(5), S3C_GPIO_PULL_UP); s3c_gpio_setpull(S3C64XX_GPB(6), S3C_GPIO_PULL_UP); } void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) { struct s3c2410_platform_i2c *npd; if (!pd) pd = &default_i2c_data0; npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); if (!npd->cfg_gpio) npd->cfg_gpio = s3c_i2c0_cfg_gpio; s3c_device_i2c0.dev.platform_data = npd; }
最后调用platform_device_register(&s3c_device_i2c0)注册平台设备,其中s3c_device_i2c0包含了s3c6410上i2c相关寄存器的地址,以及地址资源,还包括i2c时钟线所需的时钟频率,i2c设备的从地址等信息,即获取了系统i2c控制器的信息,后面需要使用这些信息去初始化i2c adapter结构体。具体描述如下图所示:
smdk6410_machine_init(void)中做的另一项工作是通过i2c_register_board_info()函数将i2c从设备的信息收集起来。
int __init i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len) { int status; down_write(&__i2c_board_lock); /* dynamic bus numbers will be assigned after the last static one */ if (busnum >= __i2c_first_dynamic_bus_num) __i2c_first_dynamic_bus_num = busnum + 1; for (status = 0; len; len--, info++) { /* #include <linux/rwsem.h> */ struct i2c_devinfo *devinfo; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); if (!devinfo) { pr_debug("i2c-core: can't register boardinfo!\n"); status = -ENOMEM; break; } devinfo->busnum = busnum; devinfo->board_info = *info; list_add_tail(&devinfo->list, &__i2c_board_list); } up_write(&__i2c_board_lock); return status; }
/* i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); */ struct i2c_board_info { char type[I2C_NAME_SIZE]; unsigned short flags; unsigned short addr; void *platform_data; struct dev_archdata *archdata; struct device_node *of_node; int irq; }; struct i2c_devinfo { struct list_head list; int busnum; struct i2c_board_info board_info; }; #define I2C_BOARD_INFO(dev_type, dev_addr) .type = dev_type, .addr = (dev_addr) static struct i2c_board_info i2c_devs0[] __initdata = { { I2C_BOARD_INFO("24c08", 0x50), }, { I2C_BOARD_INFO("wm8580", 0x1b), }, { I2C_BOARD_INFO("ov965x", 0x30), }, // gjl }; static struct i2c_board_info i2c_devs1[] __initdata = { { I2C_BOARD_INFO("24c128", 0x57), }, /* Samsung S524AD0XD1 */ };
执行完i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));这两个函数后,建立起如下的一个i2c_devinfo结构体类型的链表,链表头为__i2c_board_list,创建i2c_client结构体时会使用这些信息,每一个i2c_devinfo结构体对应一个i2c_client设备: