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; //设备编号
- ;
- ;
- * resource;//设备资源
- *id_entry;
- /* arch specific additions */
- ;
- };
(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..
- */
- {
- ;//资源的起始物理地址
- end; //资源的结束物理地址
- const char *name; //资源名称
- ; //资源类型,MEM,IO,IRQ
- *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
- [] = {
- [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,
- }
- };
- = {
- .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设备
- 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
- */
- *platform_get_resource(struct platform_device *dev,
- 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 *);
- (*shutdown)(struct platform_device *);
- int (*suspend)(struct platform_device *, pm_message_t state);
- int (*resume)(struct platform_device *);
- ;
- *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,
- //名字与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
1. /* device interface */
2.
3. int __devinit s3c2410wdt_probe(struct platform_device *pdev)
4. {
5. *res;
6. *dev;
7. int wtcon;
8. int started = 0;
9. int ret;
10. int size;
11.
12. ("%s: probe=%p\n", __func__, pdev);
13.
14. = &pdev->dev;
15. = &pdev->dev;
16.
17. /* get the memory region for the watchdog timer */
18.
19. = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获得资源,物理地址
20. if (res == NULL) {
21. (dev, "no memory resource specified\n");
22. -ENOENT;
23. }
24.
25. = (res->end - res->start) + 1;
26. = request_mem_region(res->start, size, pdev->name);
27. if (wdt_mem == NULL) {
28. (dev, "failed to get memory region\n");
29. = -ENOENT;
30. ;
31. }
32.
33. = ioremap(res->start, size);
34. if (wdt_base == NULL) {
35. (dev, "failed to ioremap() region\n");
36. = -EINVAL;
37. ;
38. }
39.
40. ("probe: mapped wdt_base=%p\n", wdt_base);
41.
42. = platform_get_resource(pdev, IORESOURCE_IRQ, 0);//获得中断号
43. if (wdt_irq == NULL) {
44. (dev, "no irq resource specified\n");
45. = -ENOENT;
46. ;
47. }
48.
49. = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);//注册中断
50. if (ret != 0) {
51. (dev, "failed to install irq (%d)\n", ret);
52. ;
53. }
54.
55. = clk_get(&pdev->dev, "watchdog");
56. if (IS_ERR(wdt_clock)) {
57. (dev, "failed to find watchdog clock source\n");
58. = PTR_ERR(wdt_clock);
59. ;
60. }
61.
62. (wdt_clock);
63.
64. /* see if we can actually set the requested timer margin, and if
65. * not, try the default value */
66.
67. if (s3c2410wdt_set_heartbeat(tmr_margin)) {
68. = s3c2410wdt_set_heartbeat(
69. );
70.
71. if (started == 0)
72. (dev,
73. "tmr_margin value out of range, default %d used\n",
74. );
75. else
76. (dev, "default timer value is out of range, "
77. "cannot start\n");
78. }
79.
80. = misc_register(&s3c2410wdt_miscdev);
81. if (ret) {
82. (dev, "cannot register miscdev on minor=%d (%d)\n",
83. , ret);
84. ;
85. }
86.
87. if (tmr_atboot && started == 0) {
88. (dev, "starting watchdog timer\n");
89. ();
90. } else if (!tmr_atboot) {
91. /* if we're not enabling the watchdog, then ensure it is
92. * disabled if it has been left running from the bootloader
93. * or other source */
94.
95. ();
96. }
97.
98. /* print out a statement of readiness */
99.
100. = readl(wdt_base + S3C2410_WTCON);
101.
102. (dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
103. (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
104. (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
105. (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");
106.
107. ;
108.
109. :
110. (wdt_clock);
111. (wdt_clock);
112.
113. :
114. (wdt_irq->start, pdev);
115.
116. :
117. (wdt_base);
118.
119. :
120. (wdt_mem);
121. (wdt_mem);
122.
123. ;
124. }
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)
- {
- *pdev = to_platform_device(dev);
- *pdrv = to_platform_driver(drv);
- /* match against the id table first */
- if (pdrv->id_table)
- (pdrv->id_table, pdev) != NULL;
- /* fall-back to driver name match */
- (strcmp(pdev->name, drv->name) == 0); //比较name成员
- }
4.小结:
(1)当platform总线上有设备或驱动加入时,platform总线的match方法被调用一次或多次,遍历所有设备,一旦name成员与driver匹配成功,调用driver的probe方法,移除时调用remove方法。
(2)platform总线只是提供了一个管理硬件资源与设备的机制,驱动接口真正的实现在probe()函数内完成。
一个简单的platform机制测试代码: platform.rar
实验结果: