第12章 Linux设备驱动的软件架构思想之platform设备资源和数据

12.2.3 platform设备资源和数据

在linux/platform_device.h 中结构体platform_device的定义:

struct platform_device {
        const char      *name;
        int             id;
        bool            id_auto;
        struct device   dev;
        u32             num_resources;
        struct resource *resource;

        const struct platform_device_id *id_entry;
        char *driver_override; /* Driver name to force a match */

        /* MFD cell pointer */
        struct mfd_cell *mfd_cell;

        /* arch specific additions */
        struct pdev_archdata    archdata;

};

其中结构体struct resource描述platform_device的资源,其定义如代码清单12.8所示。

代码清单12.8 resource结构体定义

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这3个成员属性,分别标明了资源的开始值、结束值和类型。其中flags可以是IORESOURCE_IO(IO)、IORESOURCE_MEM(内存资源)、IORESOURCE_IRQ(中断号)、IORESOURCE_DMA(DMA)等。

start、end的含义会随着flags而变更,解释如下:

当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;

当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用1个中断号,开始和结束值相同

对于同种类型的资源,可以有多份,例如某设备占据两个内存区域,则可以定义两个IORESOURCE_MEM资源。

对resource的定义通常在BSP的板级文件中进行,在具体的设备驱动中通过platform_get_resource()API来获取,此API的原型为:

<linux/platform_device.h>

extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);

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);

例如在arch/arm/mach-at91/board-sam9261ek.c板级文件中为DM9000网卡定义了如下resouce:

static struct resource dm9000_resource[] = {
        [0] = {
                .start  = AT91_CHIPSELECT_2,
                .end    = AT91_CHIPSELECT_2 + 3,
                .flags  = IORESOURCE_MEM
        },
        [1] = {
                .start  = AT91_CHIPSELECT_2 + 0x44,
                .end    = AT91_CHIPSELECT_2 + 0xFF,
                .flags  = IORESOURCE_MEM
        },
        [2] = {
                .flags = IORESOURCE_IRQ
                                | IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE,
                }

};

在DM9000网卡的驱动中则是通过如下方法获取这3份资源:

db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);

db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

对于IRQ,platform_get_resource()还有一个进行了封装的变体platform_get_irq(),其原型:

<linux/platform_device.h>

extern int platform_get_irq(struct platform_device *, unsigned int);

drivers/base/platform.c

/**
 * 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);

分析:platform_get_irq()实际上调用了“platform_get_resource(dev,IORESOURCE_IRQ,num);”。

设备除了在BSP中定义资源外,还可附加一些数据信息,因为对设备的硬件描述除中断、内存等标准资源外,可能还会有一些配置信息,而这些配置信息也依赖于板,不适宜直接放置在设备驱动上。因此,platform也提供了platform_data的支持,platform_data的形式是由每个驱动自定义的,如对于DM9000网卡,platform_data为一个dm9000_plat_data结构体,完成定义后,就可以将MAC地址、总线宽度、板上有无EEPROM信息等放入platform_data中,如代码清单12.9所示。

static struct dm9000_plat_data dm9000_platdata = {
         .flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,
};

static struct platform_device dm9000_device = {
             .name = "dm9000",
            .id = 0,
             .num_resources = ARRAY_SIZE(dm9000_resource),
             .resource = dm9000_resource,
            .dev = {
                     .platform_data = &dm9000_platdata, //平台数据
             }
};

在DM9000网卡的驱动drivers/net/dm9000.c的probe()中,通过如下方式获取platform_data:

struct dm9000_plat_data *pdata = dev_get_platdata(&pdev->dev);

其中,pdev为platform_device的指针。

<linux/device.h>

static inline void *dev_get_platdata(const struct device *dev)
{
return dev->platform_data;
}

总结:在设备驱动中引入platform的概念好处:

1)使得设备被挂接在一个总线上,符合Linux 2.6以后内核的设备模型。其结果是使配套的sysfs节点、设备电源管理都成为可能。

2)隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体配置信息,在驱动中,只需通过通用API去获取资源和数据,做到了板级相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

3)让一个驱动支持多个设备实例。譬如DM9000的驱动只有一份,但是可以在板级添加多份DM9000的platform_device,它们都可以与唯一的驱动匹配。

在Linux 3.x之后的内核中,DM9000驱动实际上已经可以通过设备树的方法被枚举。

#include <linux/spinlock.h>
#include <linux/crc32.h>
#include <linux/mii.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
#include <linux/ethtool.h>
#include <linux/dm9000.h>
#include <linux/delay.h>
@@ -1351,6+1353,31@@ static const struct net_device_ops dm9000_netdev_ops = {
#endif
};
+static struct dm9000_plat_data *dm9000_parse_dt(struct device *dev)//解析设备树
+{
+ …
+}
+
/*
* Search DM9000board, allocate space and register it
*/
@@ -1366,6+1393,12@@ dm9000_probe(struct platform_device *pdev)
int i;
u32id_val;
+ if (!pdata) {
+ pdata = dm9000_parse_dt(&pdev->dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
/* Init network device */
ndev = alloc_etherdev(sizeof(struct board_info));
if (!ndev)
@@ -1676,11+1709,20@@ dm9000_drv_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id dm9000_of_matches[] = {
+ { .compatible = "davicom,dm9000", },//兼容性属性
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dm9000_of_matches);
+#endif
+
static struct platform_driver dm9000_driver = {
        .driver = {
                .name = "dm9000",
                .owner = THIS_MODULE,
                .pm = &dm9000_drv_pm_ops,//电源管理操作
                + .of_match_table = of_match_ptr(dm9000_of_matches),
        },
        .probe = dm9000_probe,
        .remove = dm9000_drv_remove,

改为设备树后,在板级上添加DM9000网卡的动作就变成了简单地修改dts文件,如

arch/arm/boot/dts/s3c6410-mini6410.dts中:

srom-cs1@18000000 {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        reg = <0x18000000 0x8000000>;
        ranges;
                ethernet@18000000{
                        compatible = "davicom,dm9000";
                        reg = <0x18000000 0x2 0x18000004 0x2>;
                        interrupt-parent = <&gpn>;
                        interrupts = <7IRQ_TYPE_LEVEL_HIGH>;
                        davicom,no-eeprom;
                 };
};

备注:

    关于设备树情况下驱动与设备的匹配,以及驱动如何获取平台属性,在后续章节介绍。


阅读更多
换一批

没有更多推荐了,返回首页