platform机制由两部分组成,platform_device和platform_driver。
Platform驱动与传统的设备驱动模型(即通过driver_register函数进行注册)相比,优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口(即通过platform device提供的标准接口),这样提高了程序可移植性。
platform 是一个虚拟的地址总线,相比 PCI、USB、I2C,它主要用于描述 SOC 上的片上资源。比如 S3C2410 上集成的控制器( LCD、Watchdog、RTC等),platform 所描述的资源有一个共同点:在 CPU 的总线上直接取址。
用platform
1.平台设备
(1)描述
- struct platform_device {
- const char * name;//设备名字
- int id; //设备编号
- struct device dev;
- u32 num_resources;
- struct resource * resource;//设备资源
-
- struct platform_device_id *id_entry;
-
- /* arch specific additions */
- struct pdev_archdata archdata;
- };
(2)分配
- struct platform_device *platform_device_alloc(const char *name, unsigned int id)
- name:设备名
- id:一般为-1
(3)注册
- int platform_device_add(struct platform_device *pdev)
- {
- //增加的platform设备,都以platform_bus(platform设备)为父节点
- if (!pdev->dev.parent) pdev->dev.parent = &platform_bus;
- //platform类型设备都挂接在platform总线上 /sys/bus/platform/
- pdev->dev.bus = &platform_bus_type;
- .
- .
- .
- }
(4)资源描述
- /*
- * Resources are tree-like, allowing
- * nesting etc..
- */
- struct resource {
- resource_size_t start;//资源的起始物理地址
- resource_size_t end; //资源的结束物理地址
- const char *name; //资源名称
- unsigned long flags; //资源类型,MEM,IO,IRQ
- struct resource *parent, *sibling, *child; //资源链表指针
- };
比如linux-2.6.32内核自带的s3c2410-wdt设备加载过程如下
- /* Watchdog */
- #define S3C24XX_VA_WATCHDOG S3C_VA_WATCHDOG
- #define S3C2410_PA_WATCHDOG (0x53000000)
- #define S3C24XX_SZ_WATCHDOG SZ_1M
- #define S3C24XX_PA_WATCHDOG S3C2410_PA_WATCHDOG
- static struct resource s3c_wdt_resource[] = {
- [0] = {//硬件寄存器资源
- .start = S3C24XX_PA_WATCHDOG, //0x53000000
- .end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {//中断资源
- .start = IRQ_WDT,
- .end = IRQ_WDT,
- .flags = IORESOURCE_IRQ,
- }
-
- };
-
- struct platform_device s3c_device_wdt = {
- .name = "s3c2410-wdt",//name要与platform_driver中的name一致
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_wdt_resource),
- .resource = s3c_wdt_resource,
- };
- static struct platform_device *smdk2440_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_lcd,
- &s3c_device_wdt,//platform_device指针
- &s3c_device_i2c0,
- &s3c_device_iis,
- };
- static void __init smdk2440_machine_init(void)
- {
- s3c24xx_fb_set_platdata(&smdk2440_fb_info);
- s3c_i2c0_set_platdata(NULL);
- platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
- smdk_machine_init();
- }
- MACHINE_START(S3C2440, "SMDK2440")
- /* Maintainer: Ben Dooks */
- .phys_io = S3C2410_PA_UART,
- .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
- .boot_params = S3C2410_SDRAM_PA + 0x100,
- .init_irq = s3c24xx_init_irq,
- .map_io = smdk2440_map_io,
- .init_machine = smdk2440_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
(5)获取资源方法
- /**
- * 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)
- dev:资源所属设备
- type:获取资源类型
- num:获取的资源index
- 比如:
- platform_get_resource(pdev,IORESOURCE_IRQ,0)//获取中断号
- platform_get_resource(pdev,IORESOURCE_MEM,0)//获取第0个资源
2.平台驱动
(1)描述
- 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 (*resume)(struct platform_device *);
- struct device_driver driver;
- struct platform_device_id *id_table;
- };
(2)注册
- /**
- * platform_driver_register
- * @drv: platform driver structure
- */
- int platform_driver_register(struct platform_driver *drv)
在2.6.32内核s3c2410_wdt.c中实例注册过程
- static struct platform_driver s3c2410wdt_driver = {
- .probe = s3c2410wdt_probe,
- .remove = __devexit_p(s3c2410wdt_remove),
- .shutdown = s3c2410wdt_shutdown,
- .suspend = s3c2410wdt_suspend,
- .resume = s3c2410wdt_resume,
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c2410-wdt",//名字与platform_device中的name一致
- },
- };
- static int __init watchdog_init(void)
- {
- printk(banner);
- return platform_driver_register(&s3c2410wdt_driver);
- }
- static void __exit watchdog_exit(void)
- {
- platform_driver_unregister(&s3c2410wdt_driver);
- }
- module_init(watchdog_init);
- module_exit(watchdog_exit);
(3)probe函数
执行时机:当所加载驱动与相应设备匹配成功
功能:获得设备资源,完成一个设备驱动所有的操作接口
(4)remove函数
执行:设备或驱动移除时
功能:释放probe申请的一切资源
比如s3c2410_wdt.c
- /* device interface */
-
- static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
- {
- struct resource *res;
- struct device *dev;
- unsigned int wtcon;
- int started = 0;
- int ret;
- int size;
-
- DBG("%s: probe=%p\n", __func__, pdev);
-
- dev = &pdev->dev;
- wdt_dev = &pdev->dev;
-
- /* get the memory region for the watchdog timer */
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获得资源,物理地址
- if (res == NULL) {
- dev_err(dev, "no memory resource specified\n");
- return -ENOENT;
- }
-
- size = (res->end - res->start) + 1;
- wdt_mem = request_mem_region(res->start, size, pdev->name);
- if (wdt_mem == NULL) {
- dev_err(dev, "failed to get memory region\n");
- ret = -ENOENT;
- goto err_req;
- }
-
- wdt_base = ioremap(res->start, size);
- if (wdt_base == NULL) {
- dev_err(dev, "failed to ioremap() region\n");
- ret = -EINVAL;
- goto err_req;
- }
-
- DBG("probe: mapped wdt_base=%p\n", wdt_base);
-
- wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);//获得中断号
- if (wdt_irq == NULL) {
- dev_err(dev, "no irq resource specified\n");
- ret = -ENOENT;
- goto err_map;
- }
-
- ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);//注册中断
- if (ret != 0) {
- dev_err(dev, "failed to install irq (%d)\n", ret);
- goto err_map;
- }
-
- wdt_clock = clk_get(&pdev->dev, "watchdog");
- if (IS_ERR(wdt_clock)) {
- dev_err(dev, "failed to find watchdog clock source\n");
- ret = PTR_ERR(wdt_clock);
- goto err_irq;
- }
-
- clk_enable(wdt_clock);
-
- /* see if we can actually set the requested timer margin, and if
- * not, try the default value */
-
- if (s3c2410wdt_set_heartbeat(tmr_margin)) {
- started = s3c2410wdt_set_heartbeat(
- CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
-
- if (started == 0)
- dev_info(dev,
- "tmr_margin value out of range, default %d used\n",
- CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
- else
- dev_info(dev, "default timer value is out of range, "
- "cannot start\n");
- }
-
- ret = misc_register(&s3c2410wdt_miscdev);
- if (ret) {
- dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
- WATCHDOG_MINOR, ret);
- goto err_clk;
- }
-
- if (tmr_atboot && started == 0) {
- dev_info(dev, "starting watchdog timer\n");
- s3c2410wdt_start();
- } else if (!tmr_atboot) {
- /* if we're not enabling the watchdog, then ensure it is
- * disabled if it has been left running from the bootloader
- * or other source */
-
- s3c2410wdt_stop();
- }
-
- /* print out a statement of readiness */
-
- wtcon = readl(wdt_base + S3C2410_WTCON);
-
- dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
- (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
- (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
- (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");
-
- return 0;
-
- err_clk:
- clk_disable(wdt_clock);
- clk_put(wdt_clock);
-
- err_irq:
- free_irq(wdt_irq->start, pdev);
-
- err_map:
- iounmap(wdt_base);
-
- err_req:
- release_resource(wdt_mem);
- kfree(wdt_mem);
-
- return ret;
- }
3.平台总线
(1)描述
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_attrs = platform_dev_attrs,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = &platform_dev_pm_ops,
- };
(2)match方法:当platform总线上有设备或驱动变化时执行一次或多次
- static int platform_match(struct device *dev, struct device_driver *drv)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct platform_driver *pdrv = to_platform_driver(drv);
-
- /* match against the id table first */
- if (pdrv->id_table)
- return platform_match_id(pdrv->id_table, pdev) != NULL;
-
- /* fall-back to driver name match */
- return (strcmp(pdev->name, drv->name) == 0); //比较name成员
- }
4.小结:
(1)当platform总线上有设备或驱动加入时,platform总线的match方法被调用一次或多次,遍历所有设备,一旦name成员与driver匹配成功,调用driver的probe方法,移除时调用remove方法。
(2)platform总线只是提供了一个管理硬件资源与设备的机制,驱动接口真正的实现在probe()函数内完成。
一个简单的platform机制测试代码: platform.rar