Freescale imx6 linux platform 驱动模型分析

Freescale imx6 linux platform 驱动模型分析

. 概述
    platform
设备和驱动与linux设备模型密切相关。platformlinux设备模型中,其实就是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内存,中断信号线。现在大多数处理器芯片都是soc,如Freescale imx6,它包括处理器内核和系统的外设(lvds接口,nandflash接口等)。linux在引入了platform机制之后,内核假设所有的这些外设都挂载在platform虚拟总线上,以便进行统一管理。
. platform 总线
   1.
在系统中platform对应的文件drivers/base/platform.c,它不是作为一个模块注册到内核的,关键的注册总线的函数由系统初始化部分,对应/init/main.c中的do_basic_setup函数间接调用。这里可以看出platform非常重要,要在系统其他驱动加载之前注册。下面分析platform总线注册函数

[cpp] view plain copy

1.  int __init platform_bus_init(void)  

2.  {  

3.      int error;  

4.      early_platform_cleanup();  

5.      error = device_register(&platform_bus);  

6.          //总线也是设备,所以也要进行设备的注册  

7.      if (error)  

8.          return error;  

9.      error =  bus_register(&platform_bus_type);  

10.         //注册platform_bus_type总线到内核  

11.     if (error)  

12.         device_unregister(&platform_bus);  

13.     return error;  

14. }  

    这个函数向内核注册了一种总线。他首先由/drivers/base/init.c中的driver_init函数调用,driver_init函数由/init/main.c中的do_basic_setup函数调用,do_basic_setup这个函数由kernel_init调用,所以platform总线是在内核初始化的时候就注册进了内核。
   2. platform_bus_type
总线结构与设备结构
   
1 platform总线设备结构

[cpp] view plain copy

1.  struct device platform_bus = {  

2.      .init_name  = "platform",  

3.  };  

    platform总线也是一种设备,这里初始化一个device结构,设备名称platform,因为没有指定父设备,所以注册后将会在/sys/device/下出现platform目录。
   
2 platform总线总线结构

[cpp] view plain copy

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.  };  

    platform_dev_attrs    设备属性
    platform_match       match
函数,这个函数在当属于platform的设备或者驱动注册到内核时就会调用,完成设备与驱动的匹配工作。
    platform_uevent     
热插拔操作函数
. platform 设备
   1. platform_device
结构

[cpp] view plain copy

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. };  

    1platform_device结构体中有一个struct resource结构,是设备占用系统的资源,定义在ioport.h中,如下

[cpp] view plain copy

1.  struct resource {  

2.      resource_size_t start;  

3.      resource_size_t end;  

4.      const char *name;  

5.      unsigned long flags;  

6.      struct resource *parent, *sibling, *child;  

7.  };  

    2 num_resources 占用系统资源的数目,一般设备都占用两种资源,io内存和中断信号线。这个为两种资源的总和。
   2.
设备注册函数 imx_add_platform_device_dmamask

[cpp] view plain copy

1.  struct platform_device *__initimx_add_platform_device_dmamask(

2.       constchar *name, int id,

3.       conststruct resource *res, unsigned int num_resources,

4.       constvoid *data, size_t size_data, u64 dmamask)

5.  {

6.  int ret= -ENOMEM;

7.  structplatform_device *pdev;

8.   

9.  pdev =platform_device_alloc(name, id);

10. if(!pdev)

11.      gotoerr;

12.  

13. if(dmamask) {

14.      /*

15.       * This memory isn't freed when the device isput,

16.       * I don't have a nice idea for thatthough.  Conceptually

17.       * dma_mask in struct device should not be apointer.

18.       * Seehttp://thread.gmane.org/gmane.linux.kernel.pci/9081

19.       */

20.      pdev->dev.dma_mask=

21.          kmalloc(sizeof(*pdev->dev.dma_mask),GFP_KERNEL);

22.      if(!pdev->dev.dma_mask)

23.          /*ret is still -ENOMEM; */

24.          gotoerr;

25.  

26.      *pdev->dev.dma_mask= dmamask;

27.      pdev->dev.coherent_dma_mask= dmamask;

28. }

29.  

30. if(res) {

31.      ret= platform_device_add_resources(pdev, res, num_resources);

32.      if(ret)

33.          gotoerr;

34. }

35.  

36. if(data) {

37.      ret= platform_device_add_data(pdev, data, size_data);

38.      if(ret)

39.          gotoerr;

40. }

41.  

42. ret =platform_device_add(pdev);

43. if(ret) {

44. err:

45.      if(dmamask)

46.          kfree(pdev->dev.dma_mask);

47.      platform_device_put(pdev);

48.      returnERR_PTR(ret);

49. }

50.  

51. returnpdev;

52. }

    这个函数首先初始化了platform_devicedevice结构,然后调用platform_device_add,这个是注册函数的关键,下面分析platform_device_add

[cpp] view plain copy

53. int platform_device_add(struct platform_device *pdev)  

54. {  

55.     int i, ret = 0;  

56.   

57.     if (!pdev)  

58.         return -EINVAL;  

59.   

60.     if (!pdev->dev.parent)  

61.         pdev->dev.parent = &platform_bus;  

62.         //可以看出,platform设备的父设备一般都是platform_bus,所以注册后的platform设备都出现在/sys/devices/platform_bus  

63.     pdev->dev.bus = &platform_bus_type;  

64.         //挂到platform总线上  

65.     if (pdev->id != -1)  

66.         dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);  

67.     else  

68.         dev_set_name(&pdev->dev, "%s", pdev->name);  

69.         //设置设备名字,这个名字与/sys/devices/platform_bus下的名字对应  

70.     for (i = 0; i < pdev->num_resources; i++) { //下面操作设备所占用的系统资源  

71.         struct resource *p, *r = &pdev->resource[i];  

72.   

73.         if (r->name == NULL)  

74.             r->name = dev_name(&pdev->dev);  

75.   

76.         p = r->parent;  

77.         if (!p) {  

78.             if (resource_type(r) == IORESOURCE_MEM)  

79.                 p = &iomem_resource;  

80.             else if (resource_type(r) == IORESOURCE_IO)  

81.                 p = &ioport_resource;  

82.         }  

83.   

84.         if (p && insert_resource(p, r)) {  

85.             printk(KERN_ERR  

86.                    "%s: failed to claim resource %d\n",  

87.                    dev_name(&pdev->dev), i);  

88.             ret = -EBUSY;  

89.             goto failed;  

90.         }  

91.     }  

92.        //上面主要是遍历设备所占用的资源,找到对应的父资源,如果没有定义,那么根据资源的类型,分别赋予iomem_resourceioport_resource,然后调用insert_resource插入资源。  

93.        //这样系统的资源就形成了一个树形的数据结构,便于系统的管理  

94.     pr_debug("Registering platform device '%s'. Parent at %s\n",  

95.          dev_name(&pdev->dev), dev_name(pdev->dev.parent));  

96.   

97.     ret = device_add(&pdev->dev);  

98.         //注册到设备模型中  

99.     if (ret == 0)  

100.         return ret;  

101.  failed:  

102.     while (--i >= 0) {  

103.         struct resource *r = &pdev->resource[i];  

104.         unsigned long type = resource_type(r);  

105.         if (type == IORESOURCE_MEM || type == IORESOURCE_IO)  

106.             release_resource(r);  

107.     }  

108.     return ret;  

109. }  

   3. imx6内核注册platform设备过程
   
因为一种soc确定之后,其外设模块就已经确定了,所以注册platform设备就由板级初始化代码来完成,在mx6中是board-mx6q_sabreauto..cmx6_board_init函数中调用imx_add_platform_device_dmamask来完成注册。如lvds,其是先调用imx6q_add_ldb(&ldb_data),然后调用imx_add_platform_device("mxc_ldb",-1,

                     res, ARRAY_SIZE(res),pdata, sizeof(*pdata)),然后调用imx_add_platform_device_dmamask,最后调用platform_device_add(pdev)。这个函数完成imx6的相关platform设备的注册:
   
1 imx_add_platform_device_dmamask函数是platform_device_add的简单封装,它向内核注册一组platform设备
   
2)下面以lvds为例说明,其他的类似。

              platform_deviceimx6中没有直接定义,其只是定义了resource的相关内容,具体形式如下:

struct resource res[] = {

              {

                     .start =data->iobase,

                     .end =data->iobase + data->iosize - 1,

                     .flags =IORESOURCE_MEM,

              },

       };

const struct imx_ldb_data imx6q_ldb_data __initconst =

                     imx_ldb_data_entry_single(MX6Q,SZ_4K);

 

#define imx_ldb_data_entry_single(soc, size)      \

       {                                                       \

              .iobase = soc ##_IOMUXC_BASE_ADDR,                  \

              .iosize = size,                                          \

       }  

    可以看出,它占用的资源resource,定义如下:

[cpp] view plain copy

struct resource res[] = {

              {

                     .start = MX6Q_IOMUXC_BASE_ADDR,

                     .end = MX6Q_IOMUXC_BASE_ADDR+ SZ_4K - 1,

                     .flags =IORESOURCE_MEM,

              },

       }; 

   这有一个元素,其资源类型是IORESOURCE_MEM代表io内存,起使地址MX6Q_IOMUXC_BASE_ADDR,这个是lvds寄存器的地址。
. platform设备驱动
   
如果要将所写的驱动程序注册成platform驱动,那么所做的工作就是初始化一个platform_driver,然后调用platform_driver_register进行注册,其源代码在文件ldb.c中。
   1.
基本数据机构platform_driver

[cpp] view plain copy

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.  };  

    这是platform驱动基本的数据结构,在驱动程序中我们要做的就是声明一个这样的结构并初始化。下面是lvds驱动程序对它的初始化:

[cpp] view plain copy

10. static struct platform_driver mxcldb_driver ={

11. .driver= {

12.         .name = "mxc_ldb",

13.         },

14. .probe= ldb_probe,

15. .remove= ldb_remove,

16. .suspend= ldb_suspend,

17. .resume= ldb_resume,

18. }; 

    上面几个函数是我们要实现的,它将赋值给device_driver中的相关成员,probe函数是用来查询特定设备是够真正存在的函数。当设备从系统删除的时候调用remove函数。
   2.
注册函数platform_driver_register

[cpp] view plain copy

1.  int platform_driver_register(struct platform_driver *drv)  

2.  {  

3.      drv->driver.bus = &platform_bus_type;  

4.      if (drv->probe)  

5.          drv->driver.probe = platform_drv_probe;  

6.      if (drv->remove)  

7.          drv->driver.remove = platform_drv_remove;    

8.      if (drv->shutdown)  

9.          drv->driver.shutdown = platform_drv_shutdown;  

10.     return driver_register(&drv->driver);  

11. }  

    这个函数首先使驱动属于platform_bus_type总线,将platform_driver结构中的定义的proberemove,shutdown赋值给device_driver结构中的相应成员,以供linux设备模型核心调用,然后调用driver_regster将设备驱动注册到linux设备模型核心中。
. 各环节的整合
   
前面提到imx6板级初始化程序将它所有的platform设备注册到了linux设备模型核心中,在/sys/devices/platform目录中都有相应的目录表示。platform驱动则是由各个驱动程序模块分别注册到系统中的。但是他们是如何联系起来的呢,这就跟linux设备模型核心有关系了。这里简要说明一下platform实现的方法。每当注册一个platform驱动的时候就会调用driver_register,这个函数的调用会遍历设备驱动所属总线上的所有设备,并对每个设备调用总线的match函数。platform驱动是属于platform_bus_type总线,所以调用platform_match函数。这个函数实现如下:

[cpp] view plain copy

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.    

6.      /* match against the id table first */  

7.      if (pdrv->id_table)  

8.          return platform_match_id(pdrv->id_table, pdev) != NULL;  

9.      /* fall-back to driver name match */  

10.     return (strcmp(pdev->name, drv->name) == 0);  

11. }  

    这个函数将device结构转换为platform_devcie结构,将device_driver结构转换为platform_driver结构,并调用platform_match_id对设备与驱动相关信息进行比较。如果没有比较成功会返回0,以便进行下一个设备的比较,如果比较成功就会返回1,并且将device结构中的driver指针指向这个驱动,本例中driver namemxc_ldb,而device namemxc_ldb,两个相同,故匹配成功。然后调用device_driver中的probe函数,lvds驱动中就是ldb_probe。这个函数是我们要编写的函数。这个函数检测驱动的状态,并且测试能否真正驱动设备,并且做一些初始化工作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值