在结构体 platform_device 的定义(include/linux/platform_device.h)中:
struct platform_device { const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; const struct platform_device_id *id_entry; /* MFD cell pointer */ struct mfd_cell *mfd_cell; /* arch specific additions */ struct pdev_archdata archdata;};
描述了platform_device 的资源,资源本身由结构体 resource 描述,其定义(include/linux/ioport.h)如下示:
/* * Resources are tree-like, allowing * nesting etc.. */struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child;};
我们通常关心 start, end, flags 这三个字段,分别表示资源的开始值、结束值和类型。
类型字段也在ioport.h中有定义,支持 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 使用的中断号的开始值和结束值。
对 resource 的定义通常在BSP的板文件中进行,并通过platform_device_add_resources()将资源文件注册到台设备中,而在具体的设
备驱动中通过platform_get_resource() 来获取,其定义(drivers/base/platform.c),如下:
/** * platform_get_resource - get a resource for a device * @dev: platform device * @type: resource type * @num: resource index */struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num){ int i; for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; if (type == resource_type(r) && num-- == 0) return r; } return NULL;}EXPORT_SYMBOL_GPL(platform_get_resource);
例如在 MY.IMx6 开发板的板文件中为 ECSPI 定义(arch/arm/plat-mxc/devices/platform-spi_imx.c)了如下的resource:
struct platform_device *__init imx_add_spi_imx( const struct imx_spi_imx_data *data, const struct spi_imx_master *pdata){ struct resource res[] = { { .start = data->iobase, .end = data->iobase + data->iosize - 1, .flags = IORESOURCE_MEM, }, { .start = data->irq, .end = data->irq, .flags = IORESOURCE_IRQ, }, }; return imx_add_platform_device(data->devid, data->id, res, ARRAY_SIZE(res), pdata, sizeof(*pdata));}
通过 imx_add_platform_device() 将资源文件添加,其定义(arch/arm/plat-mxc/include/mach/devices-common.h)如下:
static inline struct platform_device *imx_add_platform_device( const char *name, int id, const struct resource *res, unsigned int num_resources, const void *data, size_t size_data){ return imx_add_platform_device_dmamask( name, id, res, num_resources, data, size_data, 0);}
imx_add_platform_device_dmamask() ,其定义(arch/arm/plat-mxc/devices.c)如下:
struct platform_device *__init imx_add_platform_device_dmamask( const char *name, int id, const struct resource *res, unsigned int num_resources, const void *data, size_t size_data, u64 dmamask){ int ret = -ENOMEM; struct platform_device *pdev; pdev = platform_device_alloc(name, id); if (!pdev) goto err; if (dmamask) { /* * This memory isn't freed when the device is put, * I don't have a nice idea for that though. Conceptually * dma_mask in struct device should not be a pointer. * See http://thread.gmane.org/gmane.linux.kernel.pci/9081 */ pdev->dev.dma_mask = kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL); if (!pdev->dev.dma_mask) /* ret is still -ENOMEM; */ goto err; *pdev->dev.dma_mask = dmamask; pdev->dev.coherent_dma_mask = dmamask; } if (res) { ret = platform_device_add_resources(pdev, res, num_resources); if (ret) goto err; } if (data) { ret = platform_device_add_data(pdev, data, size_data); if (ret) goto err; } ret = platform_device_add(pdev); if (ret) {err: if (dmamask) kfree(pdev->dev.dma_mask); platform_device_put(pdev); return ERR_PTR(ret); } return pdev;}
而最终调用了 platform_device_add_resources() 其定义(drivers/base/platform.c)如下:
/** * platform_device_add_resources - add resources to a platform device * @pdev: platform device allocated by platform_device_alloc to add resources to * @res: set of resources that needs to be allocated for the device * @num: number of resources * * Add a copy of the resources to the platform device. The memory * associated with the resources will be freed when the platform device is * released. */int platform_device_add_resources(struct platform_device *pdev, const struct resource *res, unsigned int num){ struct resource *r = NULL; if (res) { r = kmemdup(res, sizeof(struct resource) * num, GFP_KERNEL); if (!r) return -ENOMEM; } kfree(pdev->resource); pdev->resource = r; pdev->num_resources = num; return 0;}EXPORT_SYMBOL_GPL(platform_device_add_resources);
在IMx6 的SPI驱动中则是通过如下方法得到这2份资源:
具体是在 drivers/spi/spi_imx.c 的 spi_imx_probe() 方法中
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);spi_imx->irq = platform_get_irq(pdev, 0);
而 platform_get_irq 的定义如下:
/** * platform_get_irq - get an IRQ for a device * @dev: platform device * @num: IRQ number index */int platform_get_irq(struct platform_device *dev, unsigned int num){ struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num); return r ? r->start : -ENXIO;}EXPORT_SYMBOL_GPL(platform_get_irq);
设备除了可以在BSP中定义资源文件外,还可以附加一些数据信息,因为对设备的硬件描述除了中断,内存,DMA外,可能还会有
一些配置信息,而这些配置信息也依赖于板,不适宜直接放置在驱动本身,因此,platform 也提供了 platform_data 的支持。
设备驱动中引入platform 的概念至少有如下两个好处:
1)使得设备被挂接到一个总线上,文便管理
2)隔离了BSP和驱动。在BSP中定义platform设备和设备使用的资源,设备的具体信息,而在驱动中,只需要通过通用API去获取
资源和数据,做到了板相关代码和驱动代码的分离,便得驱动具有更好的可扩展性和跨平台性。