device透過bus來跟系統做communication。系統上有好幾種bus,cpu bus,pci bus等,每種bus的spec不用,因此不會隨便接。platform device是linux下,一種與cpu bus連接的機制,因此其特色就是direct addressing。其與傳統的linux device driver在資源管理上有些優勢: 藉由將自己本身的資源向kernel註冊,由kernel來做統一管理。其方式為使用platform device所提供的api,這樣在可移植性和安全性上也會有一些好處。

下面以framebuffer device做例子,說明如何註冊device與driver。

Platform device

include/linux/platform_device.h

這個檔案描述了一個device的結構,其中比較重要的是name和resource。

struct platform_device {
    const char    * name;  //driver binding時用name來match
    int        id;    //此device有幾個 instance,-1表示只有一個
    struct device    dev;
    u32        num_resources;
    struct resource    * resource;
};

這個name就是driver名字,而resource則是該device相關的訊息,結構如下:

include/linux/ioport.h

struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};

start / end宣告了該device的address space,而flag則指定其類型:

#define IORESOURCE_IO        0x00000100    /* Resource type */
#define IORESOURCE_MEM        0x00000200
#define IORESOURCE_IRQ        0x00000400
#define IORESOURCE_DMA        0x00000800

lcd controller resource的範例如下 (arch/arm/plat-s5pc1xx/devs.c):

static struct resource s3c_lcd_resource[] = {
    [0] = {
        .start = S5PC1XX_PA_LCD,
        .end   = S5PC1XX_PA_LCD + SZ_1M - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = IRQ_LCD1,
        .end   = IRQ_LCD3,
        .flags = IORESOURCE_IRQ,
    }
};

設定了lcd 的resource結構。IORESOURCE_MEM表示是memory resource,其address space是從S5PC1XX_PA_LCD到S5PC1XX_PA_LCD + SZ_1M - 1。 IORESOURCE_IRQ是IRQ resource,其範圍是 IRQ_LCD1到 IRQ_LCD3。

定義了lcd controller的resource後,就可以來宣告其platform device如下:

struct platform_device s3c_device_lcd = {
    .name          = "s3c-lcd",
    .id          = -1,
    .num_resources      = ARRAY_SIZE(s3c_lcd_resource),
    .resource      = s3c_lcd_resource,
    .dev              = {
        .dma_mask        = &s3c_device_lcd_dmamask,
        .coherent_dma_mask    = 0xffffffffUL
    }
};

加入__initdata這樣就可以在boot時initialize這個device (arch/arm/mach-s5pc100/mach-smdkc100.c)

static struct platform_device *smdkc100_devices[] __initdata = {
    &s3c_device_lcd,
#if defined(CONFIG_MTD_ONENAND)
        &s3c_device_onenand,
#endif
    &s3c_device_keypad,

    :

    :

最後呼叫platform_add_devices(smdkc100_devices, ARRAY_SIZE(smdkc100_devices)),將device加入系統。這通常是在版子做初始化完成的事

static void __init smdkc100_machine_init(void)
{

    :

    :

    platform_add_devices(smdkc100_devices, ARRAY_SIZE(smdkc100_devices));
   :

}

Platform driver

到這邊,platform device註冊就完成了,接下來是platform driver:

include/linux/platform_device.h

這個檔案定義了一個lcdc driver所需的function pointer及struct

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*suspend_late)(struct platform_device *, pm_message_t state);
    int (*resume_early)(struct platform_device *);
    int (*resume)(struct platform_device *);
    struct pm_ext_ops *pm;
    struct device_driver driver;
};

driver 實作在driver/video/samsung/s3cfb.c

先宣告一個platform_driver:

static struct platform_driver s3cfb_driver = {
    .probe        = s3cfb_probe,
    .remove        = s3cfb_remove,
#ifdef CONFIG_HAS_WAKELOCK
#ifndef CONFIG_HAS_EARLYSUSPEND
    .suspend    = s3cfb_suspend,
    .resume        = s3cfb_resume,
#endif
#endif
        .driver        = {
        .name    = "s3c-lcd",
        .owner    = THIS_MODULE,
    },
};

註冊driver則是使用platform_driver_register():

int __devinit s3cfb_init(void)
{
    return platform_driver_register(&s3cfb_driver);
}

註冊成功後,會呼叫probe這個function pointer,也就是進入s3cfb_probe()

之後就可以對這個device進行配置。

比如說,配置address space:

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    if (res == NULL) {
        dev_err(&pdev->dev, "failed to get memory registers\n");
        ret = -ENXIO;
        goto dealloc_fb;
    }

    size = (res->end - res->start) + 1;
    info->mem = request_mem_region(res->start, size, pdev->name);

註冊IRQ:

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

    if (res == NULL) {
        dev_err(&pdev->dev, "failed to get irq\n");
        ret = -ENXIO;
        goto release_clock;
    }

    ret = request_irq(res->start, s3cfb_irq, 0, "s3c-lcd", pdev);

而address space 和irq都是宣告在resource裡的。

 

Reference

Linux上Platform device and driver介绍

[原创] 读linux2.6驱动的一点收获

kernel doc--platform

platform _device和platform_driver注册过程

 

kezeodsnx 發表在 痞客邦 PIXNET 留言(0) 人氣()