当前位置:   article > 正文

嵌入式linux platform设备驱动_linux内核驱动panfrost

linux内核驱动panfrost

        对于linux这样一个成熟,庞大,复杂的操作系统,代码的重用性非常重要,否则的话会在linux内核中存在大量无意义的重复代码。尤其是驱动程序,因为驱动程序占用了 Linux内核代码量的大头,如果不对驱动程序加以管理,任由重复的代码肆意增加,那么用不了多久Linux 内核的文件数量就庞大到无法接受的地步。

        分隔和分离:也就是将主机驱动和设备驱动分隔开来,比如I2C,SPI等等都会采样驱动分隔的方法来简化驱动的开发。在实际的驱动开发中,一般I2C主机控制器驱动已经由半导体厂商编写好了,而设备驱动一般也由设备区间的厂家编写好了,我们只需要提供设备信息(设备树中)即可。然后根据获取到的设备信息来初始化设备。这样就相当于驱动只负责驱动,设备只负责设备,想办法将两者匹配即可。

        当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之相匹配的设备,如果有就将两则匹配起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。

        分层:一个公司有很多层,每一层有着处理不同事物的员工,比如第一层负责后端开发,第二层负责前段开发,第三层负责销售,每一层都有自己的职责。在linux驱动往往也是分层的,分层的目的也是为了在不同层处理不同的内容,例如input(输入子系统),input子系统负责管理所有跟输入有关的驱动,包括键盘、鼠标、触摸等,最底层的就是设备原始驱动,负责获取输入设 备的原始值,获取到的输入事件上报给 input 核心层。input 核心层会处理各种 IO 模型,并且提供 file_operations 操作集合。我们在编写输入设备驱动的时候只需要处理好输入事件的上报即 可,至于如何处理这些上报的输入事件那是上层去考虑的,我们不用管。可以看出借助分层模型可以极大的简化我们的驱动编写,对于驱动编写来说非常的友好。

platform总线

        linux系统内核使用bus_type结构体表示总线(虚拟总线),结构体如下

         第10行,match函数,这个函数很重要,单词match就是“匹配”的意思。因此此函数就是完成设备与驱动之间匹配的。两个参数:dev 和 drv,这两个参数分别为 device 和 device_driver 类型,也就是设备和驱动。

        platform总线就是bus_type的一个具体实例。

         platform_bus_type就是platform平台总线,其中platform_match就是匹配函数,下面的图片可以看出是如何进行匹配的。

         四个if代表了四种匹配方式:

        第一种匹配方式,OF类型的匹配,也就是设备树的匹配方式,device_driver结构体中有个名为of_match_table的成员变量,此变量保存着驱动的compatible匹配表,设备树中的每个设备节点的compatible属性会和of_match_table表中的所有成员进行比较,有相同的话匹配成功,匹配成功就会执行probe函数。

        第二种匹配方式,ACPI匹配方式

        第三种匹配方式,id_table匹配,每个platform-driver结构体有一个id_table成员变量,顾名思义,保存了很多id信息,这些id信息存放着这个platform驱动所支持的驱动类型。

        第四种匹配方式,如果id_table不存在的话可以直接比较驱动和设备的name字段,看看是不是相等,相等匹配成功。

platform驱动

        platform_driver结构体表示platform驱动,结构图定义如下:

         第2行,probe函数,当驱动与设备匹配成功以后 probe 函数就会执行,非常重要的函数!!一般驱动的提供者会编写,如果自己要编写一个全新的驱动,那么 probe 就需要自行实现。

        第7行,driver成员,为device_drver结构体变量,name字段和of_match_table都在里面,所以这个就是我们匹配时所注重的。

        第8行id_table 表,也就是platform 总线匹配驱动和设备的时候采用的第三种方法。

 

        当我们定义并初始化好platform_driver结构体变量以后,我们需要在驱动入口函数里面调用platform_driver_register函数向linux内核注册一个platform驱动,函数原型如下: 

         driver:要注册的platform驱动

        返回值:负数代表失败,0代表成功

        还需要在驱动卸载函数里面通过platform_diver_unregister函数卸载platform驱动。

下面就是有设备树的匹配方式

  1. //匹配成功后就会执行
  2. static int led_probe(struct platform_device *dev)
  3. {
  4. }
  5. //驱动卸载就会执行
  6. static int led_remove(struct platform_device *dev)
  7. {
  8. }
  9. /* 匹配列表 */
  10. static const struct of_device_id led_of_match[] = {
  11. { .compatible = "atkalpha-gpioled" },
  12. { /* Sentinel */ }
  13. };
  14. /* platform驱动结构体 */
  15. static struct platform_driver led_driver = {
  16. .driver = {
  17. .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */
  18. .of_match_table = led_of_match, /* 设备树匹配表 */
  19. },
  20. .probe = led_probe,
  21. .remove = led_remove,
  22. };
  23. //驱动入口
  24. static int __init leddriver_init(void)
  25. {
  26. return platform_driver_register(&led_driver);
  27. }
  28. //驱动出口
  29. static void __exit leddriver_exit(void)
  30. {
  31. platform_driver_unregister(&led_driver);
  32. }

platform设备

        成熟的platform驱动需要支持有设备树和无设备树的方式,当有设备数的时候,只需要按照上面的程序将设备树中信息提取出来(匹配就行)。当无设备树的时候,我们就需要编写一个设备文件。将设备注册到内核中,这样总线就会从驱动找到与之匹配的驱动。platform_device描述设备信息,设备结构体定义如下:

         第23行,name表示设备名字,与想要匹配的驱动name字段一定要一样,否则就无法匹配成功。

        第27行,num_resources表示资源数量,一般为28行resouce资源的大小

        第28行,resource表示资源,也就是设备信息,比如外设寄存器等,linux内核使用resource结构体表示资源,结构体内容如下:

         start和 end 分别表示资源的起始和终止信息,对于内存类的资源,就表示内存起始和终止地址,name 表示资源名字,flags 表示资源类型    

        在以前不支持设备树的linux版本中,用户需要编写platform_device变量来描述设备,然后使用platform_device_register函数将设备信息注册到linux内核中,原型如下: 

        pdev:要注册的platform设备

        返回值:负数代表失败,0代表成功

        如果不再使用 platform 的话可以通过 platform_device_unregister 函数注销掉相应的 platform设备,platform_device_unregister 函数原型如下:

        我们根据下面的程序发现,我们不使用设备树的时候,就需要知道设备的信息(寄存器地址),在device_struct中也就是资源,当我们在驱动程序中获取到这些资源的只是物理地址,我们还需要转化为虚拟地址。platform_get_resource函数获取资源,对这些寄存器进行配置,也就完成了设备的初始化。

  1. /*
  2. * 寄存器地址定义
  3. */
  4. #define CCM_CCGR1_BASE (0X020C406C)
  5. #define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
  6. #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
  7. #define GPIO1_DR_BASE (0X0209C000)
  8. #define GPIO1_GDIR_BASE (0X0209C004)
  9. #define REGISTER_LENGTH 4
  10. /*
  11. * 设备资源信息,也就是LED0所使用的所有寄存器
  12. */
  13. static struct resource led_resources[] = {
  14. [0] = {
  15. .start = CCM_CCGR1_BASE,
  16. .end = (CCM_CCGR1_BASE + REGISTER_LENGTH - 1),
  17. .flags = IORESOURCE_MEM,
  18. },
  19. [1] = {
  20. .start = SW_MUX_GPIO1_IO03_BASE,
  21. .end = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
  22. .flags = IORESOURCE_MEM,
  23. },
  24. [2] = {
  25. .start = SW_PAD_GPIO1_IO03_BASE,
  26. .end = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1),
  27. .flags = IORESOURCE_MEM,
  28. },
  29. [3] = {
  30. .start = GPIO1_DR_BASE,
  31. .end = (GPIO1_DR_BASE + REGISTER_LENGTH - 1),
  32. .flags = IORESOURCE_MEM,
  33. },
  34. [4] = {
  35. .start = GPIO1_GDIR_BASE,
  36. .end = (GPIO1_GDIR_BASE + REGISTER_LENGTH - 1),
  37. .flags = IORESOURCE_MEM,
  38. },
  39. };
  40. /*
  41. * platform设备结构体
  42. */
  43. static struct platform_device leddevice = {
  44. .name = "imx6ul-led",
  45. .id = -1,
  46. .dev = {
  47. .release = &led_release,
  48. },
  49. .num_resources = ARRAY_SIZE(led_resources),
  50. .resource = led_resources,
  51. };
  52. //驱动入口
  53. static int __init leddevice_init(void)
  54. {
  55. return platform_device_register(&leddevice);
  56. }
  57. //驱动出口
  58. static void __exit leddevice_exit(void)
  59. {
  60. platform_device_unregister(&leddevice);
  61. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/586204
推荐阅读
相关标签
  

闽ICP备14008679号