阅读aml_i2c的代码时,发现在probe函数直接调用了platform_get_resource获取io内存,但是却没有做任何检测!probe函数怎么知道这块io内存属于这个驱动呢?
后来发现原来在arch目录下的对应目录里面有个board-8726m-refa00.c文件,这个文件里面声明了一个platform设备的资源数组platform_devs,原型如下:
static struct platform_device __initdata *platform_devs[]= {
#if defined(CONFIG_TOUCHSCREEN_ADS7846)
&spi_gpio,
#endif
#if defined(CONFIG_AML_RTC)
&aml_rtc_device,
#endif
#if defined(CONFIG_SUSPEND)
&aml_pm_device,
#endif
...
#if defined(CONFIG_I2C_AML)
&aml_i2c_device,
#endif
...
}
这个数组里面包含了所有platform设备的资源信息。例如:aml_i2c设备的资源声明如下:
#if defined(CONFIG_I2C_AML)
static struct aml_i2c_platform aml_i2c_plat = {
.wait_count = 1000000,
.wait_ack_interval = 5,
.wait_read_interval = 5,
.wait_xfer_interval = 5,
.master_no = AML_I2C_MASTER_B,
.use_pio = 0,
.master_i2c_speed = AML_I2C_SPPED_400K,
.master_b_pinmux = {
.scl_reg = MESON_I2C_MASTER_B_GPIOB_0_REG,
.scl_bit = MESON_I2C_MASTER_B_GPIOB_0_BIT,
.sda_reg = MESON_I2C_MASTER_B_GPIOB_1_REG,
.sda_bit = MESON_I2C_MASTER_B_GPIOB_1_BIT,
}
};
static struct resource aml_i2c_resource[] = {
[0] = {/*master a*/
.start = MESON_I2C_MASTER_A_START,
.end = MESON_I2C_MASTER_A_END,
.flags = IORESOURCE_MEM,
},
[1] = {/*master b*/
.start = MESON_I2C_MASTER_B_START,
.end = MESON_I2C_MASTER_B_END,
.flags = IORESOURCE_MEM,
},
[2] = {/*slave*/
.start = MESON_I2C_SLAVE_START,
.end = MESON_I2C_SLAVE_END,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device aml_i2c_device = {
.name = "aml-i2c",
.id = -1,
.num_resources = ARRAY_SIZE(aml_i2c_resource),
.resource = aml_i2c_resource,
.dev = {
.platform_data = &aml_i2c_plat,
},
};
#endif
那么将这个aml_i2c_device加入platform_devs[] 数组,就能直接在probe函数中用platform_get_resource获取资源了,但是要注意驱动的名称必须和platform_device结构中的名称完全相同。
那为什么加入platform_devs数组后就能直接访问了呢?
在相关体系的machine_desc结构体中(对于每个特定平台都有一个MACHINE_START宏用来定义machine_desc结构体),有一个接口init_machine,这个接口中会调用platform_add_devices添加platform_devs。例如:
platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs));
其中platform_device的资源,资源本身由 resource结构体描述,其定义如下:
resouce结构体定义
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
我们通常关心start、end和flags这3个字段,分别标明资源的开始值、结束值和类型,flags可以为IORESOURCE_IO、 IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA等。
start、end的含义会随着flags而变更,如当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;
当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。
对于同种类型的资源而言,可以有多份,譬如说某设备占据了2个内存区域,则可以定义2个IORESOURCE_MEM资源。
对resource的定义也通常在BSP的板文件中进行,而在具体的设备驱动中透过platform_get_resource()这样的API来获取,此API的原型为:
struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
在aml_i2c的驱动中则是通过如下办法拿到这2份资源:
aml-i2c.c文件中aml_i2c_probe()函数中
i2c->master_no = plat->master_no; /*master a:0 master b:1*/
/*master a or master b*/
res = platform_get_resource(pdev, IORESOURCE_MEM, i2c->master_no); //通过下面可知master_no 为1,即master b
等价于下面两句:
xxx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //master a
yyy_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); //master b
对于IRQ而言,platform_get_resource()还有一个进行了封装的变体platform_get_irq(),其原型为:
int platform_get_irq(struct platform_device *dev, unsigned int num);
它实际上调用了“platform_get_resource(dev, IORESOURCE_IRQ, num);”。
设备除了可以在BSP中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道以外,可能还会有一些配置信息,
而这些配置信息也依赖于板,不适宜直接放置在设备驱动本身,因此,platform也提供了platform_data的支持。platform_data 的形式是自定义的,
如对于aml-i2c而言,platform_data为一个aml_i2c_plat结构体,我们就可以将使用master a还是master b,i2c speed等信息放入platform_data:
static struct aml_i2c_platform aml_i2c_plat = {
.wait_count = 1000000, // i2c wait ack timeout
.wait_ack_interval = 5,
.wait_read_interval = 5,
.wait_xfer_interval = 5,
.master_no = AML_I2C_MASTER_B, //AML_I2C_MASTER_B值为1 即master b
.use_pio = 0, //0: hardware i2c, 1: manual pio i2c
.master_i2c_speed = AML_I2C_SPPED_400K, //AML_I2C_SLAVE_ADDR=0x6c
.master_b_pinmux = {
.scl_reg = MESON_I2C_MASTER_B_GPIOB_0_REG,
.scl_bit = MESON_I2C_MASTER_B_GPIOB_0_BIT,
.sda_reg = MESON_I2C_MASTER_B_GPIOB_1_REG,
.sda_bit = MESON_I2C_MASTER_B_GPIOB_1_BIT,
}
};
static struct platform_device aml_i2c_device = {
.name = "aml-i2c",
.id = -1,
.num_resources = ARRAY_SIZE(aml_i2c_resource),
.resource = aml_i2c_resource,
.dev = {
.platform_data = &aml_i2c_plat,
},
};
而在aml-i2c的驱动中,通过如下方式就拿到了platform_data:
struct aml_i2c_platform *plat = pdev->dev.platform_data;
其中,pdev为platform_device的指针。