3.0.35 platform 设备资源和数据

在结构体 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去获取

     资源和数据,做到了板相关代码和驱动代码的分离,便得驱动具有更好的可扩展性和跨平台性。

原文地址:https://www.cnblogs.com/aqing1987/p/4355197.html