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介绍
platform _device和platform_driver注册过程
留言列表