Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1696699
  • 博文数量: 174
  • 博客积分: 5493
  • 博客等级: 上校
  • 技术积分: 5802
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-05 15:13
个人简介

炼狱,是为追逐光芒

文章分类

全部博文(174)

文章存档

2017年(1)

2016年(3)

2015年(9)

2014年(5)

2013年(23)

2012年(56)

2011年(45)

2010年(32)

分类: LINUX

2011-12-29 15:21:03

platform机制由两部分组成,platform_deviceplatform_driver

Platform驱动与传统的设备驱动模型(即通过driver_register函数进行注册)相比,优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口(即通过platform device提供的标准接口),这样提高了程序可移植性。

platform 是一个虚拟的地址总线,相比 PCIUSBI2C,它主要用于描述 SOC 上的片上资源。比如 S3C2410 上集成的控制器( LCDWatchdogRTC等),platform 所描述的资源有一个共同点:在 CPU 的总线上直接取址。

platform机制开发底层设备驱动流程图。

1.平台设备

(1)描述

  1. struct platform_device {
  2.     const char    * name;//设备名字
  3.     int        id;       //设备编号
  4.     struct device    dev;
  5.     u32        num_resources;
  6.     struct resource    * resource;//设备资源

  7.     struct platform_device_id    *id_entry;

  8.     /* arch specific additions */
  9.     struct pdev_archdata    archdata;
  10. };

(2)分配

  1. struct platform_device *platform_device_alloc(const char *name, unsigned int id)
  2. name:设备名
  3. id:一般为-1

(3)注册

  1. int platform_device_add(struct platform_device *pdev)
  2. {
  3. //增加的platform设备,都以platform_bus(platform设备)为父节点
  4. if (!pdev->dev.parent) pdev->dev.parent = &platform_bus;

  5. //platform类型设备都挂接在platform总线上 /sys/bus/platform/
  6. pdev->dev.bus = &platform_bus_type;
  7. .
  8. .
  9. .
  10. }

(4)资源描述

  1. /*
  2.  * Resources are tree-like, allowing
  3.  * nesting etc..
  4.  */
  5. struct resource {
  6.     resource_size_t start;//资源的起始物理地址
  7.     resource_size_t end;  //资源的结束物理地址
  8.     const char *name;     //资源名称
  9.     unsigned long flags;  //资源类型,MEM,IO,IRQ
  10.     struct resource *parent, *sibling, *child; //资源链表指针
  11. };

比如linux-2.6.32内核自带的s3c2410-wdt设备加载过程如下

  1. /* Watchdog */
  2. #define S3C24XX_VA_WATCHDOG S3C_VA_WATCHDOG
  3. #define S3C2410_PA_WATCHDOG (0x53000000)
  4. #define S3C24XX_SZ_WATCHDOG SZ_1M

  5. #define S3C24XX_PA_WATCHDOG S3C2410_PA_WATCHDOG

  6. static struct resource s3c_wdt_resource[] = {
  7.     [0] = {//硬件寄存器资源
  8.         .start = S3C24XX_PA_WATCHDOG, //0x53000000
  9.         .end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
  10.         .flags = IORESOURCE_MEM,
  11.     },
  12.     [1] = {//中断资源
  13.         .start = IRQ_WDT,
  14.         .end = IRQ_WDT,
  15.         .flags = IORESOURCE_IRQ,
  16.     }

  17. };

  18. struct platform_device s3c_device_wdt = {
  19.     .name         = "s3c2410-wdt",//name要与platform_driver中的name一致
  20.     .id         = -1,
  21.     .num_resources     = ARRAY_SIZE(s3c_wdt_resource),
  22.     .resource     = s3c_wdt_resource,
  23. };
  24. static struct platform_device *smdk2440_devices[] __initdata = {
  25. &s3c_device_usb,
  26. &s3c_device_lcd,
  27. &s3c_device_wdt,//platform_device指针
  28. &s3c_device_i2c0,
  29. &s3c_device_iis,
  30. };
  31. static void __init smdk2440_machine_init(void)
  32. {
  33. s3c24xx_fb_set_platdata(&smdk2440_fb_info);
  34. s3c_i2c0_set_platdata(NULL);
//添加所有platform设备
  1. platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
  2. smdk_machine_init();
  3. }

  4. MACHINE_START(S3C2440, "SMDK2440")
  5. /* Maintainer: Ben Dooks */
  6. .phys_io = S3C2410_PA_UART,
  7. .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
  8. .boot_params = S3C2410_SDRAM_PA + 0x100,

  9. .init_irq = s3c24xx_init_irq,
  10. .map_io = smdk2440_map_io,
  11. .init_machine = smdk2440_machine_init,
  12. .timer = &s3c24xx_timer,
  13. MACHINE_END

(5)获取资源方法

  1. /**
  2.  * platform_get_resource - get a resource for a device
  3.  * @dev: platform device
  4.  * @type: resource type
  5.  * @num: resource index
  6.  */
  7. struct resource *platform_get_resource(struct platform_device *dev,
  8.                  unsigned int type, unsigned int num)
  9. dev:资源所属设备
  10. type:获取资源类型
  11. num:获取的资源index
  12. 比如:
  13. platform_get_resource(pdev,IORESOURCE_IRQ,0)//获取中断号
  14. platform_get_resource(pdev,IORESOURCE_MEM,0)//获取第0个资源

2.平台驱动

(1)描述

  1. struct platform_driver {
  2.     int (*probe)(struct platform_device *);
  3.     int (*remove)(struct platform_device *);
  4.     void (*shutdown)(struct platform_device *);
  5.     int (*suspend)(struct platform_device *, pm_message_t state);
  6.     int (*resume)(struct platform_device *);
  7.     struct device_driver driver;
  8.     struct platform_device_id *id_table;
  9. };

(2)注册

  1. /**
  2.  * platform_driver_register
  3.  * @drv: platform driver structure
  4.  */
  5. int platform_driver_register(struct platform_driver *drv)

在2.6.32内核s3c2410_wdt.c中实例注册过程

  1. static struct platform_driver s3c2410wdt_driver = {
  2.     .probe        = s3c2410wdt_probe,
  3.     .remove        = __devexit_p(s3c2410wdt_remove),
  4.     .shutdown    = s3c2410wdt_shutdown,
  5.     .suspend    = s3c2410wdt_suspend,
  6.     .resume        = s3c2410wdt_resume,
  7.     .driver        = {
  8.         .owner    = THIS_MODULE,
  9.         .name    = "s3c2410-wdt",//名字与platform_device中的name一致
  10.     },
  11. };
  12. static int __init watchdog_init(void)
  13. {
  14.     printk(banner);
  15.     return platform_driver_register(&s3c2410wdt_driver);
  16. }

  17. static void __exit watchdog_exit(void)
  18. {
  19.     platform_driver_unregister(&s3c2410wdt_driver);
  20. }
  21. module_init(watchdog_init);
  22. module_exit(watchdog_exit);

(3)probe函数

执行时机:当所加载驱动与相应设备匹配成功

功能:获得设备资源,完成一个设备驱动所有的操作接口

(4)remove函数

执行:设备或驱动移除时

功能:释放probe申请的一切资源

比如s3c2410_wdt.c

  1. /* device interface */

  2. static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
  3. {
  4.     struct resource *res;
  5.     struct device *dev;
  6.     unsigned int wtcon;
  7.     int started = 0;
  8.     int ret;
  9.     int size;

  10.     DBG("%s: probe=%p\n", __func__, pdev);

  11.     dev = &pdev->dev;
  12.     wdt_dev = &pdev->dev;

  13.     /* get the memory region for the watchdog timer */

  14.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获得资源,物理地址
  15.     if (res == NULL) {
  16.         dev_err(dev, "no memory resource specified\n");
  17.         return -ENOENT;
  18.     }

  19.     size = (res->end - res->start) + 1;
  20.     wdt_mem = request_mem_region(res->start, size, pdev->name);
  21.     if (wdt_mem == NULL) {
  22.         dev_err(dev, "failed to get memory region\n");
  23.         ret = -ENOENT;
  24.         goto err_req;
  25.     }

  26.     wdt_base = ioremap(res->start, size);
  27.     if (wdt_base == NULL) {
  28.         dev_err(dev, "failed to ioremap() region\n");
  29.         ret = -EINVAL;
  30.         goto err_req;
  31.     }

  32.     DBG("probe: mapped wdt_base=%p\n", wdt_base);

  33.     wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);//获得中断号
  34.     if (wdt_irq == NULL) {
  35.         dev_err(dev, "no irq resource specified\n");
  36.         ret = -ENOENT;
  37.         goto err_map;
  38.     }

  39.     ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);//注册中断
  40.     if (ret != 0) {
  41.         dev_err(dev, "failed to install irq (%d)\n", ret);
  42.         goto err_map;
  43.     }

  44.     wdt_clock = clk_get(&pdev->dev, "watchdog");
  45.     if (IS_ERR(wdt_clock)) {
  46.         dev_err(dev, "failed to find watchdog clock source\n");
  47.         ret = PTR_ERR(wdt_clock);
  48.         goto err_irq;
  49.     }

  50.     clk_enable(wdt_clock);

  51.     /* see if we can actually set the requested timer margin, and if
  52.      * not, try the default value */

  53.     if (s3c2410wdt_set_heartbeat(tmr_margin)) {
  54.         started = s3c2410wdt_set_heartbeat(
  55.                     CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

  56.         if (started == 0)
  57.             dev_info(dev,
  58.              "tmr_margin value out of range, default %d used\n",
  59.              CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
  60.         else
  61.             dev_info(dev, "default timer value is out of range, "
  62.                             "cannot start\n");
  63.     }

  64.     ret = misc_register(&s3c2410wdt_miscdev);
  65.     if (ret) {
  66.         dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
  67.             WATCHDOG_MINOR, ret);
  68.         goto err_clk;
  69.     }

  70.     if (tmr_atboot && started == 0) {
  71.         dev_info(dev, "starting watchdog timer\n");
  72.         s3c2410wdt_start();
  73.     } else if (!tmr_atboot) {
  74.         /* if we're not enabling the watchdog, then ensure it is
  75.          * disabled if it has been left running from the bootloader
  76.          * or other source */

  77.         s3c2410wdt_stop();
  78.     }

  79.     /* print out a statement of readiness */

  80.     wtcon = readl(wdt_base + S3C2410_WTCON);

  81.     dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
  82.          (wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
  83.          (wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
  84.          (wtcon & S3C2410_WTCON_INTEN) ? "" : "en");

  85.     return 0;

  86.  err_clk:
  87.     clk_disable(wdt_clock);
  88.     clk_put(wdt_clock);

  89.  err_irq:
  90.     free_irq(wdt_irq->start, pdev);

  91.  err_map:
  92.     iounmap(wdt_base);

  93.  err_req:
  94.     release_resource(wdt_mem);
  95.     kfree(wdt_mem);

  96.     return ret;
  97. }

3.平台总线

(1)描述

  1. struct bus_type platform_bus_type = {
  2.     .name        = "platform",
  3.     .dev_attrs    = platform_dev_attrs,
  4.     .match        = platform_match,
  5.     .uevent        = platform_uevent,
  6.     .pm        = &platform_dev_pm_ops,
  7. };

(2)match方法:当platform总线上有设备或驱动变化时执行一次或多次

  1. static int platform_match(struct device *dev, struct device_driver *drv)
  2. {
  3.     struct platform_device *pdev = to_platform_device(dev);
  4.     struct platform_driver *pdrv = to_platform_driver(drv);

  5.     /* match against the id table first */
  6.     if (pdrv->id_table)
  7.         return platform_match_id(pdrv->id_table, pdev) != NULL;

  8.     /* fall-back to driver name match */
  9.     return (strcmp(pdev->name, drv->name) == 0); //比较name成员
  10. }

4.小结:

(1)当platform总线上有设备或驱动加入时,platform总线的match方法被调用一次或多次,遍历所有设备,一旦name成员与driver匹配成功,调用driver的probe方法,移除时调用remove方法。

(2)platform总线只是提供了一个管理硬件资源与设备的机制,驱动接口真正的实现在probe()函数内完成。

一个简单的platform机制测试代码: platform.rar   

实验结果:



阅读(3155) | 评论(0) | 转发(8) |
给主人留下些什么吧!~~