1. 程式人生 > >i.mx6ul linux驅動開發—基於Device tree機制的驅動編寫

i.mx6ul 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檔案如下:

    imx6ul/linux-3.14.38-v2$ vim arch/arm/boot/dts/imx6ul-14x14-evk.dts(部分內容)

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

    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手冊定義,不唯一。

    [objc] view plain copy
    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資源的時候匹配用的  
    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. };  
    [objc] view plain copy
    1. &;iomuxc {  
    2.     pinctrl-names = "default";  
    3.     pinctrl-0 = <&;pinctrl_uart1>;  
    4. 。。。。。。。。  
    5. pinctrl_keypad: keypadgrp {  
    6.              fsl,pins = <  
    7.                 MX6UL_PAD_ENET1_RX_EN__GPIO2_IO02       0x70a0  
    8.                 MX6UL_PAD_ENET1_TX_DATA0__GPIO2_IO03    0x70a0  
    9.                 MX6UL_PAD_ENET1_TX_DATA1__GPIO2_IO04    0x70a0  
    10.                 MX6UL_PAD_ENET1_TX_EN__GPIO2_IO05       0x70a0  
    11.                 MX6UL_PAD_ENET1_TX_CLK__GPIO2_IO06      0x70a0  
    12.                 MX6UL_PAD_ENET1_RX_ER__GPIO2_IO07       0x70a0  
    13.                 MX6UL_PAD_CSI_DATA04__GPIO4_IO25        0x70a0  
    14.                 MX6UL_PAD_CSI_DATA05__GPIO4_IO26        0x70a0  
    15.             >;  
    16.      };  

    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中獲取裝置資源: [objc] view plain copy
    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=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函式中註冊裝置: [objc] view plain copy
    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. };  
    [objc] view plain copy
    1. static int __init keypad_module_init(void)  
    2. {  
    3.     int ret;  
    4.     ret = platform_driver_register(&;max_keypad_device_driver);//modify by zengxiany  
    5.     if(ret < 0)  
    6.     {  
    7.         printk("max_keypad_device driver init error!/n");  
    8.         return -ENODEV;   
    9.     }  
    10.     return 0;     
    11. }  
    12.   
    13. static void __exit keypad_module_exit(void)  
    14. {  
    15.     platform_driver_unregister(&;max_keypad_device_driver);  
    16. }  
    OK,這樣就完成了裝置的註冊!
  • 以上是i.mx6ul linux驅動開發—基於Device tree機制的驅動編寫的內容,更多 驅動 編寫 機制 基於 Device 開發 Linux tree UL MX 的內容,請您使用右上方搜尋功能獲取相關資訊。