2016年5月26日 星期四

Linux kernel - platform_get_resouce 和 platform_get_irq 用法

1. 在寫platform driver的時候,有些硬體相關的資源,如記憶體位址和中斷號,需要透過platform_get_resouce去取得,核心這邊也定義了resource的結構來放這些硬體的資訊,resource的結構如下,我們通常比較關心start、end和flag的定義,

當flag為IORESOURCE_MEM時,start、end表示platform device使用的記憶體開始位址和結束位址‧
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

當flag為IORESOURCE_IRQ時,start、end表示platform device使用的中斷號的開始值和結束值‧
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

2. 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;
};

/*
 * IO resources have these defined flags.
 */
#define IORESOURCE_BITS         0x000000ff      /* Bus-specific bits */

#define IORESOURCE_TYPE_BITS    0x00001f00      /* Resource type */
#define IORESOURCE_IO           0x00000100      /* PCI/ISA I/O ports */
#define IORESOURCE_MEM          0x00000200
#define IORESOURCE_REG          0x00000300      /* Register offsets */
#define IORESOURCE_IRQ          0x00000400
#define IORESOURCE_DMA          0x00000800
#define IORESOURCE_BUS          0x00001000

#define IORESOURCE_PREFETCH     0x00002000      /* No side effects */
#define IORESOURCE_READONLY     0x00004000
#define IORESOURCE_CACHEABLE    0x00008000
#define IORESOURCE_RANGELENGTH  0x00010000
#define IORESOURCE_SHADOWABLE   0x00020000

#define IORESOURCE_SIZEALIGN    0x00040000      /* size indicates alignment */
#define IORESOURCE_STARTALIGN   0x00080000      /* start field is alignment */

#define IORESOURCE_MEM_64       0x00100000
#define IORESOURCE_WINDOW       0x00200000      /* forwarded by bridge */
#define IORESOURCE_MUXED        0x00400000      /* Resource is software muxed */

#define IORESOURCE_EXCLUSIVE    0x08000000      /* Userland may not map this resource */
#define IORESOURCE_DISABLED     0x10000000
#define IORESOURCE_UNSET        0x20000000      /* No address assigned yet */
#define IORESOURCE_AUTO         0x40000000
#define IORESOURCE_BUSY         0x80000000      /* Driver has marked this resource busy */
3. 當resouce中定義了兩個以上的記憶體空間和一個中斷號資源
/* device tree 定義方式 */
  test: test@09100000 {
   compatible = "uio";
   reg = <0x09100000 0x10000>,
         <0x09200000 0x10000>;
   interrupts = <0 29 0x4>;
  };


/* 結構定義方式 */
static struct resource cns3xxx_usb_ehci_resources[] = {
        [0] = {
                .start = CNS3XXX_USB0_BASE,
                .end   = CNS3XXX_USB0_BASE + SZ_16M - 1,
                .flags = IORESOURCE_MEM,
        },
        [1] = {
                .start = CNS3XXX_USB1_BASE,
                .end   = CNS3XXX_USB1_BASE + SZ_16M - 1,
                .flags = IORESOURCE_MEM,
        },
        [2] = {
                .start = IRQ_CNS3XXX_USB_EHCI,
                .flags = IORESOURCE_IRQ,
        },
};
可透過platform_get_resouice第三個參數指定index
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
4. 在使用IRQ資源,platform_get_irq封裝了platform_get_resource,也可以使用platform_get_irq(struct platform_device *dev, unsigned int num);去取得中斷號資源,

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

/**
 * 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)
{
#ifdef CONFIG_SPARC
        /* sparc does not have irqs represented as IORESOURCE_IRQ resources */
        if (!dev || num >= dev->archdata.num_irqs)
                return -ENXIO;
        return dev->archdata.irqs[num];
#else
        struct resource *r;
        if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
                int ret;

                ret = of_irq_get(dev->dev.of_node, num);
                if (ret >= 0 || ret == -EPROBE_DEFER)
                        return ret;
        }

        r = platform_get_resource(dev, IORESOURCE_IRQ, num);
        /*
         * The resources may pass trigger flags to the irqs that need
         * to be set up. It so happens that the trigger flags for
         * IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER*
         * settings.
         */
        if (r && r->flags & IORESOURCE_BITS)
                irqd_set_trigger_type(irq_get_irq_data(r->start),
                                      r->flags & IORESOURCE_BITS);

        return r ? r->start : -ENXIO;
#endif
}
5. 範例
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/platform_device.h>

struct sample_timer {
        void __iomem *base;
        void __iomem *memory;
        int irq;
};

static int sample_probe(struct platform_device *pdev)
{
        struct resource *base_res, *memory_res;
        static struct sample_timer *timer; 

        pr_alert("%s\n", __FUNCTION__);

        timer = devm_kzalloc(&pdev->dev, sizeof(struct sample_timer), GFP_KERNEL);
        if (NULL == timer) {
                dev_err(&pdev->dev, "cannot allocate memory\n");
                return -ENOMEM;
        }

        base_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (NULL == base_res) {
                dev_err(&pdev->dev, "cannot get IORESOUCE_MEM\n");
                return -ENOENT;
        }

        memory_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
        if (NULL == memory_res) {
                dev_err(&pdev->dev, "cannot get IORESOUCE_MEM\n");
                return -ENOENT;
        }

        timer->base = devm_ioremap_resource(&pdev->dev, base_res);
        timer->memory = devm_ioremap_resource(&pdev->dev, memory_res);

        if ((IS_ERR((const void *)(timer->base))) || (IS_ERR((const void *)(timer->memory)))) {
                dev_err(&pdev->dev, "cannot get ioremap\n");
                return -ENOENT;
        }

        timer->irq = platform_get_irq(pdev, 0);
        if (timer->irq < 0) {
                dev_err(&pdev->dev, "no irq specified\n");
                return -ENOENT;
        }

        return 0;
}

static int sample_remove(struct platform_device *pdev)
{
        return 0;
}

#define sample_suspend NULL
#define sample_resume NULL

static const struct of_device_id sample_dt_ids[] = {
        {.compatible = "sample,timer",},
        { }
};

MODULE_DEVICE_TABLE(of, sample_dt_ids);

static struct platform_driver sample_driver = {
        .probe = sample_probe,
        .remove = sample_remove,
        .suspend = sample_suspend,
        .resume = sample_resume,
        .driver = {
                .name = "sample",
                .owner = THIS_MODULE,
                .of_match_table = sample_dt_ids,
        },
};

static int __init sample_init(void)
{
        int ret;

        ret = platform_driver_register(&sample_driver);

        return ret;
}

static void __exit sample_exit(void)
{
        platform_driver_unregister(&sample_driver);
}

module_init(sample_init);
module_exit(sample_exit);

MODULE_AUTHOR("Allen");
MODULE_DESCRIPTION("sample");
MODULE_LICENSE("GPL");
6. 參考來源
platform设备驱动全透析 http://www.bianceng.cn/OS/Linux/201301/34910_3.htm

沒有留言:

張貼留言