前言
Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF)。在目前广泛使用的Linux kernel 2.6.x版本中,对于不同平台、不同硬件,往往存在着大量的不同的、移植性差的板级描述代码,以达到对这些不同平台和不同硬件特殊适配的需求。但是过多的平台、过的的不同硬件导致了这样的代码越来越多,最终引发了Linux创始人Linus的不满,以及强烈呼吁改变。Device Tree的引入给驱动适配带来了很大的方便,一套完整的Device Tree可以将一个PCB摆在你眼前。Device Tree可以描述CPU,可以描述时钟、中断控制器、IO控制器、SPI总线控制器、I2C控制器、存储设备等任何现有驱动单位。对具体器件能够描述到使用哪个中断,内存映射空间是多少等等。

关于Device Tree的数据结构和详细使用方法,请大家查看宋宝华老师的一篇博客:

http://blog.csdn.net/airk000/article/details/2

1 基于Device Tree机制内核的驱动开发—实例讲解
这个章节,作者来讲讲基于Linux-3.2.X之后使用device tree机制的内核的驱动开发案例。本文的驱动开发案例是作者工作期间亲自写的键盘驱动代码。CPU平台使用的是NXP(freescale)的i.MX6ul。概要信息描述如下:

硬件平台:NXP(freescale)—i.MX6ul

软件开发平台:Ubuntu-12.04

内核版本:Linux-3.14.38

编译环境:yocto project

1.1 基于Device Tree机制的驱动开发—系统如何加载和解析dtb文件
基于Device Tree机制的驱动开发,在驱动当中所使用到的硬件资源都在对应的CPU平台的dts文件上进行配置,然后编译生成dtb文件,放在u-boot分区之后,内核分区之前。这里顺便讲一下,内核是如何解析dtb文件的。其大致过程如下:

系统上电启动之后,u-boot加载dtb,通过u-boot和Linux内核之间的传参操作将dtb文件传给内核,然后内核解析dtb文件,根据device tree中的配置(dtb文件)去初始化设备的CPU管脚、各个外设的状态。device tree中的配置主要是起到了初始化硬件资源的作用,后期可以在驱动中修改设备的硬件资源的状态,比如在device tree中初始化某个GPIO的管脚为上拉状态,可以在驱动加载之后修改这个管脚的状态。

1.2 基于Device Tree机制的驱动开发—dts文件的配置和编译
本节开始以具体的驱动例子讲解如何在驱动开发中配置dts文件。这里使用i.MX6ul平台下的矩阵键盘驱动中使用到的几个GPIO口讲解如何配置dts文件和编译。本次讲解案例用于编译驱动的内核是Linux-3.14.38。首先我们先来看看如何在内核中找到自己相应CPU平台的dts文件:

1.dts文件位于内核的arch/arm/boot/dts/$(board).dts,其中的$(board)指的是对应的CPU平台,比如i.MX6ul平台的dts文件如下:

  1. imx6ul/linux-3.14.-v2$ vim arch/arm/boot/dts/imx6ul-14x14-evk.dts(部分内容)
  2.  
  3. #include <dt-bindings/input/input.h>
  4. #include "imx6ul.dtsi"
  5.  
  6. / {
  7. model = "Freescale i.MX6 UltraLite NewLand Board";
  8. compatible = "fsl,imx6ul-14x14-evk", "fsl,imx6ul";
  9.  
  10. chosen {
  11. stdout-path = &uart1;
  12. };
  13.  
  14. memory {
  15. reg = <0x80000000 0x20000000>;
  16. };
  17.  
  18. pxp_v4l2 {
  19. compatible = "fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2";
  20. status = "okay";
  21. };
  22.  
  23. keyboard {
  24. compatible = "max-keypad";
  25. pinctrl-names = "default";
  26. pinctrl- = <&pinctrl_keypad>;
  27. in-gpios = <&gpio2 GPIO_ACTIVE_HIGH>, //key_in0
  28. <&gpio2 GPIO_ACTIVE_HIGH>, //key_in1
  29. <&gpio2 GPIO_ACTIVE_HIGH>; //key_in2
  30.  
  31. out-gpios = <&gpio2 GPIO_ACTIVE_HIGH>, //key_out0
  32. <&gpio2 GPIO_ACTIVE_HIGH>, //key_out1
  33. <&gpio2 GPIO_ACTIVE_HIGH>, //key_out2
  34. <&gpio4 GPIO_ACTIVE_HIGH>, //key_out3
  35. <&gpio4 GPIO_ACTIVE_HIGH>; //key_out4
  36. status = "okay";
  37. };
  38. };
  39.  
  40. &cpu0 {
  41. arm-supply = <&reg_arm>;
  42. soc-supply = <&reg_soc>;
  43. };
  44.  
  45. &clks {
  46. assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>;
  47. assigned-clock-rates = <>;
  48. };
  49.  
  50. &tsc {
  51. pinctrl-names = "default";
  52. pinctrl- = <&pinctrl_tsc>;
  53. status = "okay";
  54. xnur-gpio = <&gpio1 >;
  55. measure_delay_time = <0xffff>;
  56. pre_charge_time = <0xfff>;
  57. };
  58.  
  59. &gpmi {
  60. pinctrl-names = "default";
  61. pinctrl- = <&pinctrl_gpmi_nand_1>;
  62. status = "okay";
  63. nand-on-flash-bbt;
  64. };
  65.  
  66. &lcdif {
  67. pinctrl-names = "default";
  68. pinctrl- = <&pinctrl_lcdif_dat
  69. &pinctrl_lcdif_ctrl>;
  70. lcd_reset = <&gpio3 GPIO_ACTIVE_HIGH>;
  71. display = <&display0>;
  72. status = "okay";
  73.  
  74. display0: display {
  75. bits-per-pixel = <>;
  76. bus-width = <>;
  77.  
  78. display-timings {
  79. native-mode = <&timing0>;
  80. timing0: timing0 {
  81. clock-frequency = <>;
  82. hactive = <>;
  83. vactive = <>;
  84. hfront-porch = <>;
  85. hback-porch = <>;
  86. hsync-len = <>;
  87. vback-porch = <>;
  88. vfront-porch = <>;
  89. vsync-len = <>;
  90.  
  91. hsync-active = <>;
  92. vsync-active = <>;
  93. de-active = <>;
  94. pixelclk-active = <>;
  95. };
  96. };
  97. };
  98. };
  99.  
  100. &iomuxc {
  101. pinctrl-names = "default";
  102. pinctrl- = <&pinctrl_uart1>;
  103. imx6ul-evk {
  104. pinctrl_uart1: uart1grp {
  105. fsl,pins = <
  106. MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
  107. MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
  108. >;
  109. };
  110.  
  111. pinctrl_tsc: tscgrp {
  112. fsl,pins = <
  113. MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0
  114. MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0
  115. MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0
  116. MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0
  117. >;
  118. };
  119.  
  120. pinctrl_lcdif_dat: lcdifdatgrp {
  121. fsl,pins = <
  122. MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79
  123. MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79
  124. MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79
  125. MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79
  126. MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79
  127. MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79
  128. MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79
  129. MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79
  130. >;
  131. };
  132.  
  133. pinctrl_lcdif_ctrl: lcdifctrlgrp {
  134. fsl,pins = <
  135. MX6UL_PAD_LCD_CLK__LCDIF_WR_RWN 0x79
  136. MX6UL_PAD_LCD_ENABLE__LCDIF_RD_E 0x79
  137. MX6UL_PAD_LCD_HSYNC__LCDIF_RS 0x79
  138. MX6UL_PAD_LCD_RESET__LCDIF_CS 0x79
  139. /* used for lcd reset */
  140. MX6UL_PAD_LCD_DATA09__GPIO3_IO14 0x79
  141. >;
  142. };
  143.  
  144. pinctrl_keypad: keypadgrp {
  145. fsl,pins = <
  146. MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02 0x70a0
  147. MX6UL_PAD_ENET1_TX_DATA0__GPIO2_IO03 0x70a0
  148. MX6UL_PAD_ENET1_TX_DATA1__GPIO2_IO04 0x70a0
  149. MX6UL_PAD_ENET1_TX_EN__GPIO2_IO05 0x70a0
  150. MX6UL_PAD_ENET1_TX_CLK__GPIO2_IO06 0x70a0
  151. MX6UL_PAD_ENET1_RX_ER__GPIO2_IO07 0x70a0
  152. MX6UL_PAD_CSI_DATA04__GPIO4_IO25 0x70a0
  153. MX6UL_PAD_CSI_DATA05__GPIO4_IO26 0x70a0
  154. >;
  155. };
  156.  
  157. pinctrl_gpmi_nand_1: gpmi-nand- {
  158. fsl,pins = <
  159. MX6UL_PAD_NAND_CLE__RAWNAND_CLE 0xb0b1
  160. MX6UL_PAD_NAND_ALE__RAWNAND_ALE 0xb0b1
  161. MX6UL_PAD_NAND_WP_B__RAWNAND_WP_B 0xb0b1
  162. MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0xb000
  163. MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B 0xb0b1
  164. MX6UL_PAD_NAND_CE1_B__RAWNAND_CE1_B 0xb0b1
  165. MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B 0xb0b1
  166. MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B 0xb0b1
  167. MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00 0xb0b1
  168. MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01 0xb0b1
  169. MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02 0xb0b1
  170. MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03 0xb0b1
  171. MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04 0xb0b1
  172. MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05 0xb0b1
  173. MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06 0xb0b1
  174. MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07 0xb0b1
  175. >;
  176. };
  177. };
  178. };

2.根据自己的开发需求配置dts文件,本文矩阵键盘驱动所使用到的GPIO管脚资源为:gpio2-2、gpio2-3、gpio2-4、gpio2-5、gpio2-6、gpio2-7、gpio4-25、gpio4-26。dts文件配置如下:

~/yangfile/imx6ul/linux-3.14.38-v2$ vim arch/arm/boot/dts/imx6ul-newland.dts

2.1 在dts文件中添加一个设备节点,比如我们是矩阵键盘驱动,那么就添加一个名为”keyboard“的设备节点;

2.2 compatible属性用于of_find_node_compatible函数获取设备节点用的,这个函数的通过”max-keypad“字符串去遍历device tree,查找匹配的设备节点;

2.3 pinctrl-0 = <&pinctrl_keypad>主要用于说明设备硬件资源在哪里获取,比如这里就是到iomuxc里面去获取IO资源

2.4 iomuxc设备节点里面定义了CPU所有的IO资源,包括每个IO口的初始化状态都定义好了,比如:MX6UL_PAD_ENET1_RX_EN_GPIO2_IO02  0x70a0,这里的MX6UL_PAD_ENET1_RX_EN_GPIO2_IO02宏表示的是GPIO2-2这个IO口的寄存器组(IO复用寄存器、IO方向控制寄存器、IO输入输出值设置寄存器),0x70a0这个值根据自己的驱动开发需求,查阅CPU手册定义,不唯一。

  1. keyboard {
  2. compatible = "max-keypad";
  3. pinctrl-names = "default";//这个设置成默认default就可以了,没什么特别要求
  4. pinctrl- = <&pinctrl_keypad>;//到iomuxc里面去获取相应IO资源的初始化状态
  5. in-gpios = <&gpio2 GPIO_ACTIVE_HIGH>, //“in-gpios”字符串可以自己随便定义,主要是为了获取gpio资源的时候匹配用的
  6. <&gpio2 GPIO_ACTIVE_HIGH>, //GPIO_ACTIVE_HIGH:逻辑高电平有效
  7. <&gpio2 GPIO_ACTIVE_HIGH>; //key_in2
  8.  
  9. out-gpios = <&gpio2 GPIO_ACTIVE_HIGH>, //“out<span style="font-family: Arial, Helvetica, sans-serif;">-gpios”字符串可以自己随便定义,主要是为了获取gpio资源的时候匹配用的</span>
  10. <&gpio2 GPIO_ACTIVE_HIGH>, //key_out1
  11. <&gpio2 GPIO_ACTIVE_HIGH>, //key_out2
  12. <&gpio4 GPIO_ACTIVE_HIGH>, //key_out3
  13. <&gpio4 GPIO_ACTIVE_HIGH>; //key_out4
  14. status = "okay";//使能要使用的gpio资源
  15. };
  16. };
  17. &iomuxc {
  18. pinctrl-names = "default";
  19. pinctrl- = <&pinctrl_uart1>;
  20. 。。。。。。。。
  21. pinctrl_keypad: keypadgrp {
  22. fsl,pins = <
  23. MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02 0x70a0
  24. MX6UL_PAD_ENET1_TX_DATA0__GPIO2_IO03 0x70a0
  25. MX6UL_PAD_ENET1_TX_DATA1__GPIO2_IO04 0x70a0
  26. MX6UL_PAD_ENET1_TX_EN__GPIO2_IO05 0x70a0
  27. MX6UL_PAD_ENET1_TX_CLK__GPIO2_IO06 0x70a0
  28. MX6UL_PAD_ENET1_RX_ER__GPIO2_IO07 0x70a0
  29. MX6UL_PAD_CSI_DATA04__GPIO4_IO25 0x70a0
  30. MX6UL_PAD_CSI_DATA05__GPIO4_IO26 0x70a0
  31. >;
  32. };

3.编译dts文件,在内核根目录下执行以下命令:

~/yangfile/imx6ul/linux-3.14.38-v2$ make ARCH=arm CROSS_COMPILE=arm-linux-gcc imx6ul-newland.dtb

(这里的arm-Linux-gcc只是个代表交叉编译器的标识,具体的根据实际情况而定)

4.将配置、编译后的dtb文件烧录到设备flash(或者SD卡)的dtb分区中。

2 驱动代码中如何注册dts文件中的设备
接触了device tree机制的驱动开发后,其实device tree机制就是Linux-2.6.x中的platform 总线机制的优化版本。OK,我们来说说基于device tree机制的驱动开发中注册设备的过程,这里以我写的矩阵键盘驱动代码的设备注册过程为例:
1.在probe函数中调用of_get_**或者of_find_**函数从dtb中获取设备资源:

  1. static int max_keypad_probe(struct platform_device *pdev)
  2. {
  3. int i,ret;
  4. struct device *dev;
  5. struct device_node *dev_node = NULL; //add by zengxiany
  6.  
  7. dev = &pdev->dev;
  8. 。。。。。。
  9. //省略部分代码
  10. dev_node = of_find_compatible_node(NULL,NULL,"fsl,imx6ul-gpio");
  11. if(!of_device_is_compatible(dev_node,"fsl,imx6ul-gpio"))
  12. {
  13. printk("get keypad device node error!\n");
  14. return -EINVAL;
  15. }
  16. dev_node = of_find_compatible_node(dev_node,NULL,"max-keypad");
  17. if(!of_device_is_compatible(dev_node,"max-keypad"))
  18. {
  19. printk("failure to find max-keypad device node!\n");
  20. return -EINVAL;
  21. }
  22.  
  23. for(i=; i< KEYPAD_ROWS; i++)
  24. {
  25. gpio_map_rowkey[i] = of_get_named_gpio(dev_node,"in-gpios",i);
  26. set_key_input(gpio_map_rowkey[i]);
  27. }
  28.  
  29. for(i=; i< KEYPAD_COLS; i++)
  30. {
  31. gpio_map_colkey0[i] = of_get_named_gpio(dev_node,"out-gpios",i);
  32. set_key_input(gpio_map_colkey0[i]);
  33. }
  34. }

2.在init函数中注册设备:

  1. //add by zengxiany for platform device register
  2. static struct of_device_id max_keypad_of_match[] = {
  3. { .compatible = "max-keypad", },
  4. { },
  5. };
  6.  
  7. static struct platform_driver max_keypad_device_driver = {
  8. .probe = max_keypad_probe,
  9. .remove = max_keypad_remove,
  10. .driver = {
  11. .name = "max-keypad",
  12. .owner = THIS_MODULE,
  13. .of_match_table = of_match_ptr(max_keypad_of_match),
  14. }
  15. };
  16. static int __init keypad_module_init(void)
  17. {
  18. int ret;
  19. ret = platform_driver_register(&max_keypad_device_driver);//modify by zengxiany
  20. if(ret < )
  21. {
  22. printk("max_keypad_device driver init error!\n");
  23. return -ENODEV;
  24. }
  25. return ;
  26. }
  27.  
  28. static void __exit keypad_module_exit(void)
  29. {
  30. platform_driver_unregister(&max_keypad_device_driver);
  31. }

OK,这样就完成了设备的注册

linux驱动开发—基于Device tree机制的驱动编写的更多相关文章

  1. 【转载】Linux设备树(Device Tree)机制

    转:Linux设备树(Device Tree)机制   目录 1. 设备树(Device Tree)基本概念及作用2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DT ...

  2. The Linux usage model for device tree data

    Linux and the Device Tree The Linux usage model for device tree data Author: Grant Likely grant.like ...

  3. [转] Linux 3.10 ARM Device Tree 的初始化

    [转] Linux 3.10 ARM Device Tree 的初始化 本文代码均来自标准 linux kernel 3.10,可以到这里下载 https://www.kernel.org/     ...

  4. 转:Linux设备树(Device Tree)机制

    目录 1. 设备树(Device  Tree)基本概念及作用 2. 设备树的组成和使用 2.1. DTS和DTSI 2.2. DTC 2.3. DTB 2.4. Bootloader 3. 设备树中d ...

  5. Linux 内核中的 Device Mapper 机制

    本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

  6. [dts]Device Tree机制

    转自:http://blog.csdn.net/machiner1/article/details/47805069 ------------------Based on linux 3.10.24 ...

  7. [dts]Device Tree机制【转】

    转自:https://www.cnblogs.com/aaronLinux/p/5496559.html 转自:http://blog.csdn.net/machiner1/article/detai ...

  8. [转] Linux 内核中的 Device Mapper 机制

    本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...

  9. 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)

    这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...

随机推荐

  1. 03C++语言对C的增强——实用性、变量检测、struct类型、C++中所有变量和函数都必须有类型、bool类型、三目运算符

    1.“实用性”增强 C语言中的变量都必须在作用域开始的位置定义,C++中更强调语言的“实用性”,所有的变量都可以在需要使用时再定义. 2.C++对c语言register的增强 register关键字 ...

  2. 【VBA研究】VBA自己定义函数參数类型不符的错误

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/iamlaosong/article/details/36871769 作者:iamlaosong 1 ...

  3. 主机-配件-接口-整机-3c-1

    standby 待机 hibernate 休眠(睡眠) power-off 关机 usb端口能给外部设备充电在低压状态(standby,hibernate,power-off),如果系统运行在batt ...

  4. tomcat中配置https

    HTTPS配置中分为单向连接和双向连接,单向连接只需要服务器安装证书,客户端不需要,双向连接需要服务器和客户端都安装证书: 一.Keytool命令: 1.生成密钥对: keytool -genkey ...

  5. mysql之 CentOS系统针对mysql参数优化

    内核相关参数(/etc/sysctl.conf)  以下参数可以直接放到sysctl.conf文件的末尾: net.core.somaxconn = 65535 net.core.netdev_max ...

  6. webpack 入口:entry

    定义一个入口点就生成一个chunk.如果你只是用字符串的方式定义了一个入口点,其就被命名为main.如果你用对象的方式定义多个入口点,其就被命名为入口对象中的键值.下面两个例子是等价的: entry: ...

  7. Spring Cloud Feign Ribbon 配置

    由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...

  8. 写了一个hiero检查任务渲染结果的脚本

    基本思路是写了一个时间判断函数(postSequence_check)来对比transcode任务提交时间和目标文件夹内文件的修改时间来确定渲染是否成功执行,然后通过Hiero提供的postSeque ...

  9. Python3.6+Scrapy爬取知名技术文章网站

    爬取分析 伯乐在线已经提供了所有文章的接口,还有下一页的接口,所有我们可以直接爬取一页,再翻页爬. 环境搭建 Windows下安装Python: http://www.cnblogs.com/0bug ...

  10. 为什么.net 4.6.1装了却没看到

    今天在做SignalR网站,需要在发布到的云服务器安装.net4.6.1 从网上下载了安装包,安装完之后,到Windows文件夹的 Microsoft.NET文件夹中却找不到4.6.1的文件夹. 云服 ...