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,这样就完成了设备的注册

  • 相关阅读:
    仿新浪首页、主题、详情页,纯html静态页面
    hdoj 4790 Just Random 【数学】
    Codeforces 97B Superset 平面分治
    HDU 1017 A Mathematical Curiosity【看懂题意+穷举法】
    Codeforces Round #221 (Div. 2) D
    一个bug在redmine中的诞生到终结
    hi3531 SDK已编译文件系统制作jffs2文件系统镜像并解决这个问题 .
    js前端3des加密 后台java解密
    进程经常使用小知识汇总
    泛型集合的使用
  • 原文地址:https://www.cnblogs.com/ricks/p/9993222.html
Copyright © 2011-2022 走看看