zoukankan      html  css  js  c++  java
  • linux驱动开发—基于Device tree机制的驱动编写

    前言
    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.38-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-0 = <&pinctrl_keypad>;
     27 in-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>, //key_in0
     28 <&gpio2 4 GPIO_ACTIVE_HIGH>, //key_in1
     29 <&gpio2 5 GPIO_ACTIVE_HIGH>; //key_in2
     30 
     31 out-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>, //key_out0
     32 <&gpio2 2 GPIO_ACTIVE_HIGH>, //key_out1
     33 <&gpio2 7 GPIO_ACTIVE_HIGH>, //key_out2
     34 <&gpio4 25 GPIO_ACTIVE_HIGH>, //key_out3
     35 <&gpio4 26 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 = <786432000>;
     48 };
     49 
     50 &tsc {
     51 pinctrl-names = "default";
     52 pinctrl-0 = <&pinctrl_tsc>;
     53 status = "okay";
     54 xnur-gpio = <&gpio1 3 0>;
     55 measure_delay_time = <0xffff>;
     56 pre_charge_time = <0xfff>;
     57 };
     58 
     59 &gpmi {
     60 pinctrl-names = "default";
     61 pinctrl-0 = <&pinctrl_gpmi_nand_1>;
     62 status = "okay";
     63 nand-on-flash-bbt;
     64 };
     65 
     66 &lcdif {
     67 pinctrl-names = "default";
     68 pinctrl-0 = <&pinctrl_lcdif_dat
     69 &pinctrl_lcdif_ctrl>;
     70 lcd_reset = <&gpio3 14 GPIO_ACTIVE_HIGH>;
     71 display = <&display0>;
     72 status = "okay";
     73 
     74 display0: display {
     75 bits-per-pixel = <16>;
     76 bus-width = <8>;
     77 
     78 display-timings {
     79 native-mode = <&timing0>;
     80 timing0: timing0 {
     81 clock-frequency = <9200000>;
     82 hactive = <240>;
     83 vactive = <320>;
     84 hfront-porch = <8>;
     85 hback-porch = <4>;
     86 hsync-len = <41>;
     87 vback-porch = <2>;
     88 vfront-porch = <4>;
     89 vsync-len = <10>;
     90 
     91 hsync-active = <0>;
     92 vsync-active = <0>;
     93 de-active = <1>;
     94 pixelclk-active = <0>;
     95 };
     96 };
     97 };
     98 };
     99 
    100 
    101 
    102 
    103 &iomuxc {
    104 pinctrl-names = "default";
    105 pinctrl-0 = <&pinctrl_uart1>;
    106 imx6ul-evk {
    107 pinctrl_uart1: uart1grp {
    108 fsl,pins = <
    109 MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
    110 MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
    111 >;
    112 };
    113 
    114 pinctrl_tsc: tscgrp {
    115 fsl,pins = <
    116 MX6UL_PAD_GPIO1_IO01__GPIO1_IO01    0xb0
    117 MX6UL_PAD_GPIO1_IO02__GPIO1_IO02    0xb0
    118 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03    0xb0
    119 MX6UL_PAD_GPIO1_IO04__GPIO1_IO04    0xb0
    120 >;
    121 };
    122 
    123 pinctrl_lcdif_dat: lcdifdatgrp {
    124 fsl,pins = <
    125 MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79
    126 MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79
    127 MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79
    128 MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79
    129 MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79
    130 MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79
    131 MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79
    132 MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79
    133 >;
    134 };
    135 
    136 pinctrl_lcdif_ctrl: lcdifctrlgrp {
    137 fsl,pins = <
    138 MX6UL_PAD_LCD_CLK__LCDIF_WR_RWN    0x79
    139 MX6UL_PAD_LCD_ENABLE__LCDIF_RD_E 0x79
    140 MX6UL_PAD_LCD_HSYNC__LCDIF_RS 0x79
    141 MX6UL_PAD_LCD_RESET__LCDIF_CS 0x79
    142 /* used for lcd reset */
    143 MX6UL_PAD_LCD_DATA09__GPIO3_IO14 0x79
    144 >;
    145 };
    146 
    147 pinctrl_keypad: keypadgrp {
    148 fsl,pins = <
    149 MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02 0x70a0
    150 MX6UL_PAD_ENET1_TX_DATA0__GPIO2_IO03 0x70a0
    151 MX6UL_PAD_ENET1_TX_DATA1__GPIO2_IO04 0x70a0
    152 MX6UL_PAD_ENET1_TX_EN__GPIO2_IO05 0x70a0
    153 MX6UL_PAD_ENET1_TX_CLK__GPIO2_IO06 0x70a0
    154 MX6UL_PAD_ENET1_RX_ER__GPIO2_IO07 0x70a0
    155 MX6UL_PAD_CSI_DATA04__GPIO4_IO25 0x70a0
    156 MX6UL_PAD_CSI_DATA05__GPIO4_IO26 0x70a0
    157 >;
    158 };
    159 
    160 pinctrl_gpmi_nand_1: gpmi-nand-1 {
    161 fsl,pins = <
    162 MX6UL_PAD_NAND_CLE__RAWNAND_CLE 0xb0b1
    163 MX6UL_PAD_NAND_ALE__RAWNAND_ALE 0xb0b1
    164 MX6UL_PAD_NAND_WP_B__RAWNAND_WP_B 0xb0b1
    165 MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0xb000
    166 MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B 0xb0b1
    167 MX6UL_PAD_NAND_CE1_B__RAWNAND_CE1_B 0xb0b1
    168 MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B 0xb0b1
    169 MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B 0xb0b1
    170 MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00 0xb0b1
    171 MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01 0xb0b1
    172 MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02 0xb0b1
    173 MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03 0xb0b1
    174 MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04 0xb0b1
    175 MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05 0xb0b1
    176 MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06 0xb0b1
    177 MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07 0xb0b1
    178 >;
    179 };
    180 };
    181 };

    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-0 = <&pinctrl_keypad>;//到iomuxc里面去获取相应IO资源的初始化状态
     5 in-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>, //“in-gpios”字符串可以自己随便定义,主要是为了获取gpio资源的时候匹配用的
     6 <&gpio2 4 GPIO_ACTIVE_HIGH>, //GPIO_ACTIVE_HIGH:逻辑高电平有效
     7 <&gpio2 5 GPIO_ACTIVE_HIGH>; //key_in2
     8 
     9 out-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>, //“out<span style="font-family: Arial, Helvetica, sans-serif;">-gpios”字符串可以自己随便定义,主要是为了获取gpio资源的时候匹配用的</span>
    10 <&gpio2 2 GPIO_ACTIVE_HIGH>, //key_out1
    11 <&gpio2 7 GPIO_ACTIVE_HIGH>, //key_out2
    12 <&gpio4 25 GPIO_ACTIVE_HIGH>, //key_out3
    13 <&gpio4 26 GPIO_ACTIVE_HIGH>; //key_out4
    14 status = "okay";//使能要使用的gpio资源
    15 };
    16 };
    17 &iomuxc {
    18 pinctrl-names = "default";
    19 pinctrl-0 = <&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!
    ");
    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!
    ");
    20 return -EINVAL;
    21 }
    22 
    23 for(i=0; 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=0; 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 < 0)
    21 {
    22 printk("max_keypad_device driver init error!
    ");
    23 return -ENODEV;    
    24 }
    25 return 0;    
    26 }
    27 
    28 static void __exit keypad_module_exit(void)
    29 {
    30 platform_driver_unregister(&max_keypad_device_driver);
    31 }


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

  • 相关阅读:
    CS229 6.4 Neurons Networks Autoencoders and Sparsity
    CS229 6.3 Neurons Networks Gradient Checking
    【Leetcode】【Easy】Min Stack
    【Leetcode】【Easy】Merge Sorted Array
    【Leetcode】【Easy】ZigZag Conversion
    【Leetcode】【Easy】Valid Palindrome
    【Leetcode】【Easy】Reverse Integer
    【Leetcode】【Easy】Palindrome Number
    【Leetcode】【Easy】Length of Last Word
    【Leetcode】【Easy】Remove Nth Node From End of List
  • 原文地址:https://www.cnblogs.com/ricks/p/9993222.html
Copyright © 2011-2022 走看看