ARM Linux 3.x的設備樹(Device Tree)【轉】

分類: Linux Driver開發 Linux Kernel開發 2013-01-01 17:32 1043人閱讀 評論(5) 收藏 舉報

目錄(?)[+]

  1. ARM Device Tree起源
  2. Device Tree組成和結構
    1. DTS device tree source
    2. DTC device tree compiler
    3. Device Tree Blob dtb
    4. Binding
    5. Bootloader
  3. Device Tree引發的BSP和驅動變更
  4. 常用OF API
  5. 總結

宋寶華 Barry Song <[email protected]>

1.    ARM Device Tree起源

Linus Torvalds在2011年3月17日的ARM Linux郵件列表宣稱“this whole ARM thing is a f*cking pain in the ass”,引發ARM Linux社區的地震,隨後ARM社區進行了一系列的重大修正。在過去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代碼,相當多數的代碼只是在描述板級細節,而這些板級細節對於內核來講,不過是垃圾,如板上的platform設備、resource、i2c_board_info、spi_board_info以及各種硬件的platform_data。讀者有興趣可以統計下常見的s3c2410、s3c6410等板級目錄,代碼量在數萬行。
社區必須改變這種局面,於是PowerPC等其他體系架構下已經使用的Flattened Device Tree(FDT)進入ARM社區的視野。Device Tree是一種描述硬件的數據結構,它起源於 OpenFirmware (OF)。在Linux 2.6中,ARM架構的板極硬件細節過多地被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx,採用Device Tree後,許多硬件的細節可以直接透過它傳遞給Linux,而不再需要在kernel中進行大量的冗餘編碼。
Device Tree由一系列被命名的結點(node)和屬性(property)組成,而結點本身可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中):

  • CPU的數量和類別
  • 內存基地址和大小
  • 總線和橋
  • 外設連接
  • 中斷控制器和中斷使用情況
  • GPIO控制器和GPIO使用情況
  • Clock控制器和Clock使用情況

它基本上就是畫一棵電路板上CPU、總線、設備組成的樹,Bootloader會將這棵樹傳遞給內核,然後內核可以識別這棵樹,並根據它展開出Linux內核中的platform_device、i2c_client、spi_device等設備,而這些設備用到的內存、IRQ等資源,也被傳遞給了內核,內核會將這些資源綁定給展開的相應的設備。

2.    Device Tree組成和結構

整個Device Tree牽涉面比較廣,即增加了新的用於描述設備硬件信息的文本格式,又增加了編譯這一文本的工具,同時Bootloader也需要支持將編譯後的Device Tree傳遞給Linux內核。

DTS (device tree source)

.dts文件是一種ASCII 文本格式的Device Tree描述,此文本格式非常人性化,適合人類的閱讀習慣。基本上,在ARM Linux在,一個.dts文件對應一個ARM的machine,一般放置在內核的arch/arm/boot/dts/目錄。由於一個SoC可能對應多個machine(一個SoC可以對應多個產品和電路板),勢必這些.dts文件需包含許多共同的部分,Linux內核爲了簡化,把SoC公用的部分或者多個machine共同的部分一般提煉爲.dtsi,類似於C語言的頭文件。其他的machine對應的.dts就include這個.dtsi。譬如,對於VEXPRESS而言,vexpress-v2m.dtsi就被vexpress-v2p-ca9.dts所引用, vexpress-v2p-ca9.dts有如下一行:
/include/ "vexpress-v2m.dtsi"
當然,和C語言的頭文件類似,.dtsi也可以include其他的.dtsi,譬如幾乎所有的ARM SoC的.dtsi都引用了skeleton.dtsi。
.dts(或者其include的.dtsi)基本元素即爲前文所述的結點和屬性:

[plain] view plaincopyprint?

  1. / {  
  2.     node1 {  
  3.         a-string-property = "A string";  
  4.         a-string-list-property = "first string", "second string";  
  5.         a-byte-data-property = [0x01 0x23 0x34 0x56];  
  6.         child-node1 {  
  7.             first-child-property;  
  8.             second-child-property = <1>;  
  9.             a-string-property = "Hello, world";  
  10.         };  
  11.         child-node2 {  
  12.         };  
  13.     };  
  14.     node2 {  
  15.         an-empty-property;  
  16.         a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */  
  17.         child-node1 {  
  18.         };  
  19.     };  
  20. }; 
/ {
    node1 {
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        a-byte-data-property = [0x01 0x23 0x34 0x56];
        child-node1 {
            first-child-property;
            second-child-property = <1>;
            a-string-property = "Hello, world";
        };
        child-node2 {
        };
    };
    node2 {
        an-empty-property;
        a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
        child-node1 {
        };
    };
};

上述.dts文件並沒有什麼真實的用途,但它基本表徵了一個Device Tree源文件的結構:
1個root結點"/";
root結點下面含一系列子結點,本例中爲"node1" 和 "node2";
結點"node1"下又含有一系列子結點,本例中爲"child-node1" 和 "child-node2";
各結點都有一系列屬性。這些屬性可能爲空,如" an-empty-property";可能爲字符串,如"a-string-property";可能爲字符串數組,如"a-string-list-property";可能爲Cells(由u32整數組成),如"second-child-property",可能爲二進制數,如"a-byte-data-property"。
下面以一個最簡單的machine爲例來看如何寫一個.dts文件。假設此machine的配置如下:
1個雙核ARM Cortex-A9 32位處理器;
ARM的local bus上的內存映射區域分佈了2個串口(分別位於0x101F1000 和 0x101F2000)、GPIO控制器(位於0x101F3000)、SPI控制器(位於0x10170000)、中斷控制器(位於0x10140000)和一個external bus橋;
External bus橋上又連接了SMC SMC91111 Ethernet(位於0x10100000)、I2C控制器(位於0x10160000)、64MB NOR Flash(位於0x30000000);
External bus橋上連接的I2C控制器所對應的I2C總線上又連接了Maxim DS1338實時鐘(I2C地址爲0x58)。
其對應的.dts文件爲:

[plain] view plaincopyprint?

  1. / {  
  2.     compatible = "acme,coyotes-revenge";  
  3.     #address-cells = <1>;  
  4.     #size-cells = <1>;  
  5.     interrupt-parent = <&intc>;  
  6.     cpus {  
  7.         #address-cells = <1>;  
  8.         #size-cells = <0>;  
  9.         cpu@0 {  
  10.             compatible = "arm,cortex-a9";  
  11.             reg = <0>;  
  12.         };  
  13.         cpu@1 {  
  14.             compatible = "arm,cortex-a9";  
  15.             reg = <1>;  
  16.         };  
  17.     };  
  18.     serial@101f0000 {  
  19.         compatible = "arm,pl011";  
  20.         reg = <0x101f0000 0x1000 >;  
  21.         interrupts = < 1 0 >;  
  22.     };  
  23.     serial@101f2000 {  
  24.         compatible = "arm,pl011";  
  25.         reg = <0x101f2000 0x1000 >;  
  26.         interrupts = < 2 0 >;  
  27.     };  
  28.     gpio@101f3000 {  
  29.         compatible = "arm,pl061";  
  30.         reg = <0x101f3000 0x1000  
  31.                0x101f4000 0x0010>;  
  32.         interrupts = < 3 0 >;  
  33.     };  
  34.     intc: interrupt-controller@10140000 {  
  35.         compatible = "arm,pl190";  
  36.         reg = <0x10140000 0x1000 >;  
  37.         interrupt-controller;  
  38.         #interrupt-cells = <2>;  
  39.     };  
  40.     spi@10115000 {  
  41.         compatible = "arm,pl022";  
  42.         reg = <0x10115000 0x1000 >;  
  43.         interrupts = < 4 0 >;  
  44.     };  
  45.     external-bus {  
  46.         #address-cells = <2>  
  47.         #size-cells = <1>;  
  48.         ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet  
  49.                   1 0  0x10160000   0x10000     // Chipselect 2, i2c controller  
  50.                   2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash  
  51.         ethernet@0,0 {  
  52.             compatible = "smc,smc91c111";  
  53.             reg = <0 0 0x1000>;  
  54.             interrupts = < 5 2 >;  
  55.         };  
  56.         i2c@1,0 {  
  57.             compatible = "acme,a1234-i2c-bus";  
  58.             #address-cells = <1>;  
  59.             #size-cells = <0>;  
  60.             reg = <1 0 0x1000>;  
  61.             interrupts = < 6 2 >;  
  62.             rtc@58 {  
  63.                 compatible = "maxim,ds1338";  
  64.                 reg = <58>;  
  65.                 interrupts = < 7 3 >;  
  66.             };  
  67.         };  
  68.         flash@2,0 {  
  69.             compatible = "samsung,k8f1315ebm", "cfi-flash";  
  70.             reg = <2 0 0x4000000>;  
  71.         };  
  72.     };  
  73. }; 
/ {
    compatible = "acme,coyotes-revenge";
    #address-cells = <1>;
    #size-cells = <1>;
    interrupt-parent = <&intc>;

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        cpu@0 {
            compatible = "arm,cortex-a9";
            reg = <0>;
        };
        cpu@1 {
            compatible = "arm,cortex-a9";
            reg = <1>;
        };
    };

    serial@101f0000 {
        compatible = "arm,pl011";
        reg = <0x101f0000 0x1000 >;
        interrupts = < 1 0 >;
    };

    serial@101f2000 {
        compatible = "arm,pl011";
        reg = <0x101f2000 0x1000 >;
        interrupts = < 2 0 >;
    };

    gpio@101f3000 {
        compatible = "arm,pl061";
        reg = <0x101f3000 0x1000
               0x101f4000 0x0010>;
        interrupts = < 3 0 >;
    };

    intc: interrupt-controller@10140000 {
        compatible = "arm,pl190";
        reg = <0x10140000 0x1000 >;
        interrupt-controller;
        #interrupt-cells = <2>;
    };

    spi@10115000 {
        compatible = "arm,pl022";
        reg = <0x10115000 0x1000 >;
        interrupts = < 4 0 >;
    };

    external-bus {
        #address-cells = <2>
        #size-cells = <1>;
        ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
                  1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
                  2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash

        ethernet@0,0 {
            compatible = "smc,smc91c111";
            reg = <0 0 0x1000>;
            interrupts = < 5 2 >;
        };

        i2c@1,0 {
            compatible = "acme,a1234-i2c-bus";
            #address-cells = <1>;
            #size-cells = <0>;
            reg = <1 0 0x1000>;
            interrupts = < 6 2 >;
            rtc@58 {
                compatible = "maxim,ds1338";
                reg = <58>;
                interrupts = < 7 3 >;
            };
        };

        flash@2,0 {
            compatible = "samsung,k8f1315ebm", "cfi-flash";
            reg = <2 0 0x4000000>;
        };
    };
};

上述.dts文件中,root結點"/"的compatible 屬性compatible = "acme,coyotes-revenge";定義了系統的名稱,它的組織形式爲:<manufacturer>,<model>。Linux內核透過root結點"/"的compatible 屬性即可判斷它啓動的是什麼machine。
在.dts文件的每個設備,都有一個compatible 屬性,compatible屬性用戶驅動和設備的綁定。compatible 屬性是一個字符串的列表,列表中的第一個字符串表徵了結點代表的確切設備,形式爲"<manufacturer>,<model>",其後的字符串表徵可兼容的其他設備。可以說前面的是特指,後面的則涵蓋更廣的範圍。如在arch/arm/boot/dts/vexpress-v2m.dtsi中的Flash結點:

[plain] view plaincopyprint?

  1. flash@0,00000000 {  
  2.      compatible = "arm,vexpress-flash", "cfi-flash";  
  3.      reg = <0 0x00000000 0x04000000>,  
  4.      <1 0x00000000 0x04000000>;  
  5.      bank-width = <4>;  
  6. }; 
flash@0,00000000 {
     compatible = "arm,vexpress-flash", "cfi-flash";
     reg = <0 0x00000000 0x04000000>,
     <1 0x00000000 0x04000000>;
     bank-width = <4>;
 };

compatible屬性的第2個字符串"cfi-flash"明顯比第1個字符串"arm,vexpress-flash"涵蓋的範圍更廣。
再比如,Freescale MPC8349 SoC含一個串口設備,它實現了國家半導體(National Semiconductor)的ns16550 寄存器接口。則MPC8349串口設備的compatible屬性爲compatible = "fsl,mpc8349-uart", "ns16550"。其中,fsl,mpc8349-uart指代了確切的設備, ns16550代表該設備與National Semiconductor 的16550 UART保持了寄存器兼容。
接下來root結點"/"的cpus子結點下面又包含2個cpu子結點,描述了此machine上的2個CPU,並且二者的compatible 屬性爲"arm,cortex-a9"。
注意cpus和cpus的2個cpu子結點的命名,它們遵循的組織形式爲:<name>[@<unit-address>],<>中的內容是必選項,[]中的則爲可選項。name是一個ASCII字符串,用於描述結點對應的設備類型,如3com Ethernet適配器對應的結點name宜爲ethernet,而不是3com509。如果一個結點描述的設備有地址,則應該給出@unit-address。多個相同類型設備結點的name可以一樣,只要unit-address不同即可,如本例中含有cpu@0、cpu@1以及serial@101f0000與serial@101f2000這樣的同名結點。設備的unit-address地址也經常在其對應結點的reg屬性中給出。ePAPR標準給出了結點命名的規範。
可尋址的設備使用如下信息來在Device Tree中編碼地址信息:

  •     reg
  •     #address-cells
  •     #size-cells

其中reg的組織形式爲reg = <address1 length1 [address2 length2] [address3 length3] ... >,其中的每一組address length表明了設備使用的一個地址範圍。address爲1個或多個32位的整型(即cell),而length則爲cell的列表或者爲空(若#size-cells = 0)。address 和 length 字段是可變長的,父結點的#address-cells和#size-cells分別決定了子結點的reg屬性的address和length字段的長度。在本例中,root結點的#address-cells = <1>;和#size-cells = <1>;決定了serial、gpio、spi等結點的address和length字段的長度分別爲1。cpus 結點的#address-cells = <1>;和#size-cells = <0>;決定了2個cpu子結點的address爲1,而length爲空,於是形成了2個cpu的reg = <0>;和reg = <1>;。external-bus結點的#address-cells = <2>和#size-cells = <1>;決定了其下的ethernet、i2c、flash的reg字段形如reg = <0 0 0x1000>;、reg = <1 0 0x1000>;和reg = <2 0 0x4000000>;。其中,address字段長度爲0,開始的第一個cell(0、1、2)是對應的片選,第2個cell(0,0,0)是相對該片選的基地址,第3個cell(0x1000、0x1000、0x4000000)爲length。特別要留意的是i2c結點中定義的 #address-cells = <1>;和#size-cells = <0>;又作用到了I2C總線上連接的RTC,它的address字段爲0x58,是設備的I2C地址。
root結點的子結點描述的是CPU的視圖,因此root子結點的address區域就直接位於CPU的memory區域。但是,經過總線橋後的address往往需要經過轉換才能對應的CPU的memory映射。external-bus的ranges屬性定義了經過external-bus橋後的地址範圍如何映射到CPU的memory區域。

[plain] view plaincopyprint?

  1. ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet  
  2.           1 0  0x10160000   0x10000     // Chipselect 2, i2c controller  
  3.           2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash 
        ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
                  1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
                  2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash

ranges是地址轉換表,其中的每個項目是一個子地址、父地址以及在子地址空間的大小的映射。映射表中的子地址、父地址分別採用子地址空間的#address-cells和父地址空間的#address-cells大小。對於本例而言,子地址空間的#address-cells爲2,父地址空間的#address-cells值爲1,因此0 0  0x10100000   0x10000的前2個cell爲external-bus後片選0上偏移0,第3個cell表示external-bus後片選0上偏移0的地址空間被映射到CPU的0x10100000位置,第4個cell表示映射的大小爲0x10000。ranges的後面2個項目的含義可以類推。
Device Tree中還可以中斷連接信息,對於中斷控制器而言,它提供如下屬性:
interrupt-controller – 這個屬性爲空,中斷控制器應該加上此屬性表明自己的身份;
#interrupt-cells – 與#address-cells 和 #size-cells相似,它表明連接此中斷控制器的設備的interrupts屬性的cell大小。
在整個Device Tree中,與中斷相關的屬性還包括:
interrupt-parent – 設備結點透過它來指定它所依附的中斷控制器的phandle,當結點沒有指定interrupt-parent 時,則從父級結點繼承。對於本例而言,root結點指定了interrupt-parent = <&intc>;其對應於intc: interrupt-controller@10140000,而root結點的子結點並未指定interrupt-parent,因此它們都繼承了intc,即位於0x10140000的中斷控制器。
interrupts – 用到了中斷的設備結點透過它指定中斷號、觸發方法等,具體這個屬性含有多少個cell,由它依附的中斷控制器結點的#interrupt-cells屬性決定。而具體每個cell又是什麼含義,一般由驅動的實現決定,而且也會在Device Tree的binding文檔中說明。譬如,對於ARM GIC中斷控制器而言,#interrupt-cells爲3,它3個cell的具體含義Documentation/devicetree/bindings/arm/gic.txt就有如下文字說明:

[plain] view plaincopyprint?

  1. 01   The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI  
  2. 02   interrupts.  
  3. 03  
  4. 04   The 2nd cell contains the interrupt number for the interrupt type.  
  5. 05   SPI interrupts are in the range [0-987].  PPI interrupts are in the  
  6. 06   range [0-15].  
  7. 07  
  8. 08   The 3rd cell is the flags, encoded as follows:  
  9. 09         bits[3:0] trigger type and level flags.  
  10. 10                 1 = low-to-high edge triggered  
  11. 11                 2 = high-to-low edge triggered  
  12. 12                 4 = active high level-sensitive  
  13. 13                 8 = active low level-sensitive  
  14. 14         bits[15:8] PPI interrupt cpu mask.  Each bit corresponds to each of  
  15. 15         the 8 possible cpus attached to the GIC.  A bit set to '1' indicated  
  16. 16         the interrupt is wired to that CPU.  Only valid for PPI interrupts. 
01   The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
02   interrupts.
03
04   The 2nd cell contains the interrupt number for the interrupt type.
05   SPI interrupts are in the range [0-987].  PPI interrupts are in the
06   range [0-15].
07
08   The 3rd cell is the flags, encoded as follows:
09         bits[3:0] trigger type and level flags.
10                 1 = low-to-high edge triggered
11                 2 = high-to-low edge triggered
12                 4 = active high level-sensitive
13                 8 = active low level-sensitive
14         bits[15:8] PPI interrupt cpu mask.  Each bit corresponds to each of
15         the 8 possible cpus attached to the GIC.  A bit set to '1' indicated
16         the interrupt is wired to that CPU.  Only valid for PPI interrupts.

另外,值得注意的是,一個設備還可能用到多箇中斷號。對於ARM GIC而言,若某設備使用了SPI的168、169號2箇中斷,而言都是高電平觸發,則該設備結點的interrupts屬性可定義爲:interrupts = <0 168 4>, <0 169 4>;
除了中斷以外,在ARM Linux中clock、GPIO、pinmux都可以透過.dts中的結點和屬性進行描述。

DTC (device tree compiler)

將.dts編譯爲.dtb的工具。DTC的源代碼位於內核的scripts/dtc目錄,在Linux內核使能了Device Tree的情況下,編譯內核的時候主機工具dtc會被編譯出來,對應scripts/dtc/Makefile中的“hostprogs-y := dtc”這一hostprogs編譯target。
在Linux內核的arch/arm/boot/dts/Makefile中,描述了當某種SoC被選中後,哪些.dtb文件會被編譯出來,如與VEXPRESS對應的.dtb包括:

[plain] view plaincopyprint?

  1. dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \  
  2.         vexpress-v2p-ca9.dtb \  
  3.         vexpress-v2p-ca15-tc1.dtb \  
  4.         vexpress-v2p-ca15_a7.dtb \  
  5.         xenvm-4.2.dtb 
dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2p-ca5s.dtb \
        vexpress-v2p-ca9.dtb \
        vexpress-v2p-ca15-tc1.dtb \
        vexpress-v2p-ca15_a7.dtb \
        xenvm-4.2.dtb

在Linux下,我們可以單獨編譯Device Tree文件。當我們在Linux內核下運行make dtbs時,若我們之前選擇了ARCH_VEXPRESS,上述.dtb都會由對應的.dts編譯出來。因爲arch/arm/Makefile中含有一個dtbs編譯target項目。

Device Tree Blob (.dtb)

.dtb是.dts被DTC編譯後的二進制格式的Device Tree描述,可由Linux內核解析。通常在我們爲電路板製作NAND、SD啓動image時,會爲.dtb文件單獨留下一個很小的區域以存放之,之後bootloader在引導kernel的過程中,會先讀取該.dtb到內存。

Binding

對於Device Tree中的結點和屬性具體是如何來描述設備的硬件細節的,一般需要文檔來進行講解,文檔的後綴名一般爲.txt。這些文檔位於內核的Documentation/devicetree/bindings目錄,其下又分爲很多子目錄。

Bootloader

Uboot mainline 從 v1.1.3開始支持Device Tree,其對ARM的支持則是和ARM內核支持Device Tree同期完成。
爲了使能Device Tree,需要編譯Uboot的時候在config文件中加入
#define CONFIG_OF_LIBFDT 
在Uboot中,可以從NAND、SD或者TFTP等任意介質將.dtb讀入內存,假設.dtb放入的內存地址爲0x71000000,之後可在Uboot運行命令fdt addr命令設置.dtb的地址,如:
U-Boot> fdt addr 0x71000000
fdt的其他命令就變地可以使用,如fdt resize、fdt print等。
對於ARM來講,可以透過bootz kernel_addr initrd_address dtb_address的命令來啓動內核,即dtb_address作爲bootz或者bootm的最後一次參數,第一個參數爲內核映像的地址,第二個參數爲initrd的地址,若不存在initrd,可以用 -代替。

3.    Device Tree引發的BSP和驅動變更

有了Device Tree後,大量的板級信息都不再需要,譬如過去經常在arch/arm/plat-xxx和arch/arm/mach-xxx實施的如下事情:
1.    註冊platform_device,綁定resource,即內存、IRQ等板級信息。

透過Device Tree後,形如

[cpp] view plaincopyprint?

  1. 90 static struct resource xxx_resources[] = {  
  2. 91         [0] = {  
  3. 92                 .start  = …,  
  4. 93                 .end    = …,  
  5. 94                 .flags  = IORESOURCE_MEM,  
  6. 95         },  
  7. 96         [1] = {  
  8. 97                 .start  = …,  
  9. 98                 .end    = …,  
  10. 99                 .flags  = IORESOURCE_IRQ,  
  11. 100         },  
  12. 101 };  
  13. 102  
  14. 103 static struct platform_device xxx_device = {  
  15. 104         .name           = "xxx",  
  16. 105         .id             = -1,  
  17. 106         .dev            = {  
  18. 107                                 .platform_data          = &xxx_data,  
  19. 108         },  
  20. 109         .resource       = xxx_resources,  
  21. 110         .num_resources  = ARRAY_SIZE(xxx_resources),  
  22. 111 }; 
90 static struct resource xxx_resources[] = {
91         [0] = {
92                 .start  = …,
93                 .end    = …,
94                 .flags  = IORESOURCE_MEM,
95         },
96         [1] = {
97                 .start  = …,
98                 .end    = …,
99                 .flags  = IORESOURCE_IRQ,
100         },
101 };
102
103 static struct platform_device xxx_device = {
104         .name           = "xxx",
105         .id             = -1,
106         .dev            = {
107                                 .platform_data          = &xxx_data,
108         },
109         .resource       = xxx_resources,
110         .num_resources  = ARRAY_SIZE(xxx_resources),
111 };

之類的platform_device代碼都不再需要,其中platform_device會由kernel自動展開。而這些resource實際來源於.dts中設備結點的reg、interrupts屬性。典型地,大多數總線都與“simple_bus”兼容,而在SoC對應的machine的.init_machine成員函數中,調用of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);即可自動展開所有的platform_device。譬如,假設我們有個XXX SoC,則可在arch/arm/mach-xxx/的板文件中透過如下方式展開.dts中的設備結點對應的platform_device:

[cpp] view plaincopyprint?

  1. 18 static struct of_device_id xxx_of_bus_ids[] __initdata = {  
  2. 19         { .compatible = "simple-bus", },  
  3. 20         {},  
  4. 21 };  
  5. 22  
  6. 23 void __init xxx_mach_init(void)  
  7. 24 {  
  8. 25         of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);  
  9. 26 }  
  10. 32  
  11. 33 #ifdef CONFIG_ARCH_XXX  
  12. 38  
  13. 39 DT_MACHINE_START(XXX_DT, "Generic XXX (Flattened Device Tree)")  
  14. 41         …  
  15. 45         .init_machine   = xxx_mach_init,  
  16. 46         …  
  17. 49 MACHINE_END  
  18. 50 #endif 
18 static struct of_device_id xxx_of_bus_ids[] __initdata = {
19         { .compatible = "simple-bus", },
20         {},
21 };
22
23 void __init xxx_mach_init(void)
24 {
25         of_platform_bus_probe(NULL, xxx_of_bus_ids, NULL);
26 }
32
33 #ifdef CONFIG_ARCH_XXX
38
39 DT_MACHINE_START(XXX_DT, "Generic XXX (Flattened Device Tree)")
41         …
45         .init_machine   = xxx_mach_init,
46         …
49 MACHINE_END
50 #endif

2.    註冊i2c_board_info,指定IRQ等板級信息。

形如

[cpp] view plaincopyprint?

  1. 145 static struct i2c_board_info __initdata afeb9260_i2c_devices[] = {  
  2. 146         {  
  3. 147                 I2C_BOARD_INFO("tlv320aic23", 0x1a),  
  4. 148         }, {  
  5. 149                 I2C_BOARD_INFO("fm3130", 0x68),  
  6. 150         }, {  
  7. 151                 I2C_BOARD_INFO("24c64", 0x50),  
  8. 152         },  
  9. 153 }; 
145 static struct i2c_board_info __initdata afeb9260_i2c_devices[] = {
146         {
147                 I2C_BOARD_INFO("tlv320aic23", 0x1a),
148         }, {
149                 I2C_BOARD_INFO("fm3130", 0x68),
150         }, {
151                 I2C_BOARD_INFO("24c64", 0x50),
152         },
153 };

之類的i2c_board_info代碼,目前不再需要出現,現在只需要把tlv320aic23、fm3130、24c64這些設備結點填充作爲相應的I2C controller結點的子結點即可,類似於前面的

[cpp] view plaincopyprint?

  1. i2c@1,0 {  
  2.       compatible = "acme,a1234-i2c-bus";  
  3.       …  
  4.       rtc@58 {  
  5.           compatible = "maxim,ds1338";  
  6.           reg = <58>;  
  7.           interrupts = < 7 3 >;  
  8.       };  
  9.   }; 
      i2c@1,0 {
            compatible = "acme,a1234-i2c-bus";
            …
            rtc@58 {
                compatible = "maxim,ds1338";
                reg = <58>;
                interrupts = < 7 3 >;
            };
        };

Device Tree中的I2C client會透過I2C host驅動的probe()函數中調用of_i2c_register_devices(&i2c_dev->adapter);被自動展開。

3.    註冊spi_board_info,指定IRQ等板級信息。

形如

[cpp] view plaincopyprint?

  1. 79 static struct spi_board_info afeb9260_spi_devices[] = {  
  2. 80         {       /* DataFlash chip */
  3. 81                 .modalias       = "mtd_dataflash",  
  4. 82                 .chip_select    = 1,  
  5. 83                 .max_speed_hz   = 15 * 1000 * 1000,  
  6. 84                 .bus_num        = 0,  
  7. 85         },  
  8. 86 }; 
79 static struct spi_board_info afeb9260_spi_devices[] = {
80         {       /* DataFlash chip */
81                 .modalias       = "mtd_dataflash",
82                 .chip_select    = 1,
83                 .max_speed_hz   = 15 * 1000 * 1000,
84                 .bus_num        = 0,
85         },
86 };

之類的spi_board_info代碼,目前不再需要出現,與I2C類似,現在只需要把mtd_dataflash之類的結點,作爲SPI控制器的子結點即可,SPI host驅動的probe函數透過spi_register_master()註冊master的時候,會自動展開依附於它的slave。

4.    多個針對不同電路板的machine,以及相關的callback。

過去,ARM Linux針對不同的電路板會建立由MACHINE_START和MACHINE_END包圍起來的針對這個machine的一系列callback,譬如:

[cpp] view plaincopyprint?

  1. 373 MACHINE_START(VEXPRESS, "ARM-Versatile Express")  
  2. 374         .atag_offset    = 0x100,  
  3. 375         .smp            = smp_ops(vexpress_smp_ops),  
  4. 376         .map_io         = v2m_map_io,  
  5. 377         .init_early     = v2m_init_early,  
  6. 378         .init_irq       = v2m_init_irq,  
  7. 379         .timer          = &v2m_timer,  
  8. 380         .handle_irq     = gic_handle_irq,  
  9. 381         .init_machine   = v2m_init,  
  10. 382         .restart        = vexpress_restart,  
  11. 383 MACHINE_END 
373 MACHINE_START(VEXPRESS, "ARM-Versatile Express")
374         .atag_offset    = 0x100,
375         .smp            = smp_ops(vexpress_smp_ops),
376         .map_io         = v2m_map_io,
377         .init_early     = v2m_init_early,
378         .init_irq       = v2m_init_irq,
379         .timer          = &v2m_timer,
380         .handle_irq     = gic_handle_irq,
381         .init_machine   = v2m_init,
382         .restart        = vexpress_restart,
383 MACHINE_END

這些不同的machine會有不同的MACHINE ID,Uboot在啓動Linux內核時會將MACHINE ID存放在r1寄存器,Linux啓動時會匹配Bootloader傳遞的MACHINE ID和MACHINE_START聲明的MACHINE ID,然後執行相應machine的一系列初始化函數。

引入Device Tree之後,MACHINE_START變更爲DT_MACHINE_START,其中含有一個.dt_compat成員,用於表明相關的machine與.dts中root結點的compatible屬性兼容關係。如果Bootloader傳遞給內核的Device Tree中root結點的compatible屬性出現在某machine的.dt_compat表中,相關的machine就與對應的Device Tree匹配,從而引發這一machine的一系列初始化函數被執行。

[cpp] view plaincopyprint?

  1. 489 static const char * const v2m_dt_match[] __initconst = {  
  2. 490         "arm,vexpress",  
  3. 491         "xen,xenvm",  
  4. 492         NULL,  
  5. 493 };  
  6. 495 DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")  
  7. 496         .dt_compat      = v2m_dt_match,  
  8. 497         .smp            = smp_ops(vexpress_smp_ops),  
  9. 498         .map_io         = v2m_dt_map_io,  
  10. 499         .init_early     = v2m_dt_init_early,  
  11. 500         .init_irq       = v2m_dt_init_irq,  
  12. 501         .timer          = &v2m_dt_timer,  
  13. 502         .init_machine   = v2m_dt_init,  
  14. 503         .handle_irq     = gic_handle_irq,  
  15. 504         .restart        = vexpress_restart,  
  16. 505 MACHINE_END 
489 static const char * const v2m_dt_match[] __initconst = {
490         "arm,vexpress",
491         "xen,xenvm",
492         NULL,
493 };
495 DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
496         .dt_compat      = v2m_dt_match,
497         .smp            = smp_ops(vexpress_smp_ops),
498         .map_io         = v2m_dt_map_io,
499         .init_early     = v2m_dt_init_early,
500         .init_irq       = v2m_dt_init_irq,
501         .timer          = &v2m_dt_timer,
502         .init_machine   = v2m_dt_init,
503         .handle_irq     = gic_handle_irq,
504         .restart        = vexpress_restart,
505 MACHINE_END

Linux倡導針對多個SoC、多個電路板的通用DT machine,即一個DT machine的.dt_compat表含多個電路板.dts文件的root結點compatible屬性字符串。之後,如果的電路板的初始化序列不一樣,可以透過int of_machine_is_compatible(const char *compat) API判斷具體的電路板是什麼。

    譬如arch/arm/mach-exynos/mach-exynos5-dt.c的EXYNOS5_DT machine同時兼容"samsung,exynos5250"和"samsung,exynos5440":

[cpp] view plaincopyprint?

  1. 158 static char const *exynos5_dt_compat[] __initdata = {  
  2. 159         "samsung,exynos5250",  
  3. 160         "samsung,exynos5440",  
  4. 161         NULL  
  5. 162 };  
  6. 163  
  7. 177 DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")  
  8. 178         /* Maintainer: Kukjin Kim <[email protected]> */
  9. 179         .init_irq       = exynos5_init_irq,  
  10. 180         .smp            = smp_ops(exynos_smp_ops),  
  11. 181         .map_io         = exynos5_dt_map_io,  
  12. 182         .handle_irq     = gic_handle_irq,  
  13. 183         .init_machine   = exynos5_dt_machine_init,  
  14. 184         .init_late      = exynos_init_late,  
  15. 185         .timer          = &exynos4_timer,  
  16. 186         .dt_compat      = exynos5_dt_compat,  
  17. 187         .restart        = exynos5_restart,  
  18. 188         .reserve        = exynos5_reserve,  
  19. 189 MACHINE_END 
158 static char const *exynos5_dt_compat[] __initdata = {
159         "samsung,exynos5250",
160         "samsung,exynos5440",
161         NULL
162 };
163
177 DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")
178         /* Maintainer: Kukjin Kim <[email protected]> */
179         .init_irq       = exynos5_init_irq,
180         .smp            = smp_ops(exynos_smp_ops),
181         .map_io         = exynos5_dt_map_io,
182         .handle_irq     = gic_handle_irq,
183         .init_machine   = exynos5_dt_machine_init,
184         .init_late      = exynos_init_late,
185         .timer          = &exynos4_timer,
186         .dt_compat      = exynos5_dt_compat,
187         .restart        = exynos5_restart,
188         .reserve        = exynos5_reserve,
189 MACHINE_END

     它的.init_machine成員函數就針對不同的machine進行了不同的分支處理:

[cpp] view plaincopyprint?

  1. 126 static void __init exynos5_dt_machine_init(void)  
  2. 127 {  
  3. 128         …  
  4. 149  
  5. 150         if (of_machine_is_compatible("samsung,exynos5250"))  
  6. 151                 of_platform_populate(NULL, of_default_bus_match_table,  
  7. 152                                      exynos5250_auxdata_lookup, NULL);  
  8. 153         else if (of_machine_is_compatible("samsung,exynos5440"))  
  9. 154                 of_platform_populate(NULL, of_default_bus_match_table,  
  10. 155                                      exynos5440_auxdata_lookup, NULL);  
  11. 156 } 
126 static void __init exynos5_dt_machine_init(void)
127 {
128         …
149
150         if (of_machine_is_compatible("samsung,exynos5250"))
151                 of_platform_populate(NULL, of_default_bus_match_table,
152                                      exynos5250_auxdata_lookup, NULL);
153         else if (of_machine_is_compatible("samsung,exynos5440"))
154                 of_platform_populate(NULL, of_default_bus_match_table,
155                                      exynos5440_auxdata_lookup, NULL);
156 }

使用Device Tree後,驅動需要與.dts中描述的設備結點進行匹配,從而引發驅動的probe()函數執行。對於platform_driver而言,需要添加一個OF匹配表,如前文的.dts文件的"acme,a1234-i2c-bus"兼容I2C控制器結點的OF匹配表可以是:

[cpp] view plaincopyprint?

  1. 436 static const struct of_device_id a1234_i2c_of_match[] = {  
  2. 437         { .compatible = "acme,a1234-i2c-bus ", },  
  3. 438         {},  
  4. 439 };  
  5. 440 MODULE_DEVICE_TABLE(of, a1234_i2c_of_match);  
  6. 441  
  7. 442 static struct platform_driver i2c_a1234_driver = {  
  8. 443         .driver = {  
  9. 444                 .name = "a1234-i2c-bus ",  
  10. 445                 .owner = THIS_MODULE,  
  11. 449                 .of_match_table = a1234_i2c_of_match,  
  12. 450         },  
  13. 451         .probe = i2c_a1234_probe,  
  14. 452         .remove = i2c_a1234_remove,  
  15. 453 };  
  16. 454 module_platform_driver(i2c_a1234_driver); 
436 static const struct of_device_id a1234_i2c_of_match[] = {
437         { .compatible = "acme,a1234-i2c-bus ", },
438         {},
439 };
440 MODULE_DEVICE_TABLE(of, a1234_i2c_of_match);
441
442 static struct platform_driver i2c_a1234_driver = {
443         .driver = {
444                 .name = "a1234-i2c-bus ",
445                 .owner = THIS_MODULE,
449                 .of_match_table = a1234_i2c_of_match,
450         },
451         .probe = i2c_a1234_probe,
452         .remove = i2c_a1234_remove,
453 };
454 module_platform_driver(i2c_a1234_driver);

對於I2C和SPI從設備而言,同樣也可以透過of_match_table添加匹配的.dts中的相關結點的compatible屬性,如sound/soc/codecs/wm8753.c中的:

[cpp] view plaincopyprint?

  1. 1533 static const struct of_device_id wm8753_of_match[] = {  
  2. 1534         { .compatible = "wlf,wm8753", },  
  3. 1535         { }  
  4. 1536 };  
  5. 1537 MODULE_DEVICE_TABLE(of, wm8753_of_match);  
  6. 1587 static struct spi_driver wm8753_spi_driver = {  
  7. 1588         .driver = {  
  8. 1589                 .name   = "wm8753",  
  9. 1590                 .owner  = THIS_MODULE,  
  10. 1591                 .of_match_table = wm8753_of_match,  
  11. 1592         },  
  12. 1593         .probe          = wm8753_spi_probe,  
  13. 1594         .remove         = wm8753_spi_remove,  
  14. 1595 };  
  15. 1640 static struct i2c_driver wm8753_i2c_driver = {  
  16. 1641         .driver = {  
  17. 1642                 .name = "wm8753",  
  18. 1643                 .owner = THIS_MODULE,  
  19. 1644                 .of_match_table = wm8753_of_match,  
  20. 1645         },  
  21. 1646         .probe =    wm8753_i2c_probe,  
  22. 1647         .remove =   wm8753_i2c_remove,  
  23. 1648         .id_table = wm8753_i2c_id,  
  24. 1649 }; 
1533 static const struct of_device_id wm8753_of_match[] = {
1534         { .compatible = "wlf,wm8753", },
1535         { }
1536 };
1537 MODULE_DEVICE_TABLE(of, wm8753_of_match);
1587 static struct spi_driver wm8753_spi_driver = {
1588         .driver = {
1589                 .name   = "wm8753",
1590                 .owner  = THIS_MODULE,
1591                 .of_match_table = wm8753_of_match,
1592         },
1593         .probe          = wm8753_spi_probe,
1594         .remove         = wm8753_spi_remove,
1595 };
1640 static struct i2c_driver wm8753_i2c_driver = {
1641         .driver = {
1642                 .name = "wm8753",
1643                 .owner = THIS_MODULE,
1644                 .of_match_table = wm8753_of_match,
1645         },
1646         .probe =    wm8753_i2c_probe,
1647         .remove =   wm8753_i2c_remove,
1648         .id_table = wm8753_i2c_id,
1649 };

不過這邊有一點需要提醒的是,I2C和SPI外設驅動和Device Tree中設備結點的compatible 屬性還有一種弱式匹配方法,就是別名匹配。compatible 屬性的組織形式爲<manufacturer>,<model>,別名其實就是去掉compatible 屬性中逗號前的manufacturer前綴。關於這一點,可查看drivers/spi/spi.c的源代碼,函數spi_match_device()暴露了更多的細節,如果別名出現在設備spi_driver的id_table裏面,或者別名與spi_driver的name字段相同,SPI設備和驅動都可以匹配上:

[cpp] view plaincopyprint?

  1. 90 static int spi_match_device(struct device *dev, struct device_driver *drv)  
  2. 91 {  
  3. 92         const struct spi_device *spi = to_spi_device(dev);  
  4. 93         const struct spi_driver *sdrv = to_spi_driver(drv);  
  5. 94  
  6. 95         /* Attempt an OF style match */
  7. 96         if (of_driver_match_device(dev, drv))  
  8. 97                 return 1;  
  9. 98  
  10. 99         /* Then try ACPI */
  11. 100         if (acpi_driver_match_device(dev, drv))  
  12. 101                 return 1;  
  13. 102  
  14. 103         if (sdrv->id_table)  
  15. 104                 return !!spi_match_id(sdrv->id_table, spi);  
  16. 105  
  17. 106         return strcmp(spi->modalias, drv->name) == 0;  
  18. 107 }  
  19. 71 static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,  
  20. 72                                                 const struct spi_device *sdev)  
  21. 73 {  
  22. 74         while (id->name[0]) {  
  23. 75                 if (!strcmp(sdev->modalias, id->name))  
  24. 76                         return id;  
  25. 77                 id++;  
  26. 78         }  
  27. 79         return NULL;  
  28. 80 } 
90 static int spi_match_device(struct device *dev, struct device_driver *drv)
91 {
92         const struct spi_device *spi = to_spi_device(dev);
93         const struct spi_driver *sdrv = to_spi_driver(drv);
94
95         /* Attempt an OF style match */
96         if (of_driver_match_device(dev, drv))
97                 return 1;
98
99         /* Then try ACPI */
100         if (acpi_driver_match_device(dev, drv))
101                 return 1;
102
103         if (sdrv->id_table)
104                 return !!spi_match_id(sdrv->id_table, spi);
105
106         return strcmp(spi->modalias, drv->name) == 0;
107 }
71 static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
72                                                 const struct spi_device *sdev)
73 {
74         while (id->name[0]) {
75                 if (!strcmp(sdev->modalias, id->name))
76                         return id;
77                 id++;
78         }
79         return NULL;
80 }

4.    常用OF API

在Linux的BSP和驅動代碼中,還經常會使用到Linux中一組Device Tree的API,這些API通常被冠以of_前綴,它們的實現代碼位於內核的drivers/of目錄。這些常用的API包括:

int of_device_is_compatible(const struct device_node *device,const char *compat);

判斷設備結點的compatible 屬性是否包含compat指定的字符串。當一個驅動支持2個或多個設備的時候,這些不同.dts文件中設備的compatible 屬性都會進入驅動 OF匹配表。因此驅動可以透過Bootloader傳遞給內核的Device Tree中的真正結點的compatible 屬性以確定究竟是哪一種設備,從而根據不同的設備類型進行不同的處理。如drivers/pinctrl/pinctrl-sirf.c即兼容於"sirf,prima2-pinctrl",又兼容於"sirf,prima2-pinctrl",在驅動中就有相應分支處理:

[cpp] view plaincopyprint?

  1. 1682 if (of_device_is_compatible(np, "sirf,marco-pinctrl"))  
  2. 1683      is_marco = 1; 
1682 if (of_device_is_compatible(np, "sirf,marco-pinctrl"))
1683      is_marco = 1;

struct device_node *of_find_compatible_node(struct device_node *from,

         const char *type, const char *compatible);

根據compatible屬性,獲得設備結點。遍歷Device Tree中所有的設備結點,看看哪個結點的類型、compatible屬性與本函數的輸入參數匹配,大多數情況下,from、type爲NULL。

int of_property_read_u8_array(const struct device_node *np,

                     const char *propname, u8 *out_values, size_t sz);

int of_property_read_u16_array(const struct device_node *np,

                      const char *propname, u16 *out_values, size_t sz);

int of_property_read_u32_array(const struct device_node *np,

                      const char *propname, u32 *out_values, size_t sz);

int of_property_read_u64(const struct device_node *np, const char

*propname, u64 *out_value);

讀取設備結點np的屬性名爲propname,類型爲8、16、32、64位整型數組的屬性。對於32位處理器來講,最常用的是of_property_read_u32_array()。如在arch/arm/mm/cache-l2x0.c中,透過如下語句讀取L2 cache的"arm,data-latency"屬性:

[cpp] view plaincopyprint?

  1. 534         of_property_read_u32_array(np, "arm,data-latency",  
  2. 535                                    data, ARRAY_SIZE(data)); 
534         of_property_read_u32_array(np, "arm,data-latency",
535                                    data, ARRAY_SIZE(data));

在arch/arm/boot/dts/vexpress-v2p-ca9.dts中,含有"arm,data-latency"屬性的L2 cache結點如下:

[cpp] view plaincopyprint?

  1. 137         L2: cache-controller@1e00a000 {  
  2. 138                 compatible = "arm,pl310-cache";  
  3. 139                 reg = <0x1e00a000 0x1000>;  
  4. 140                 interrupts = <0 43 4>;  
  5. 141                 cache-level = <2>;  
  6. 142                 arm,data-latency = <1 1 1>;  
  7. 143                 arm,tag-latency = <1 1 1>;  
  8. 144         } 
137         L2: cache-controller@1e00a000 {
138                 compatible = "arm,pl310-cache";
139                 reg = <0x1e00a000 0x1000>;
140                 interrupts = <0 43 4>;
141                 cache-level = <2>;
142                 arm,data-latency = <1 1 1>;
143                 arm,tag-latency = <1 1 1>;
144         }


有些情況下,整形屬性的長度可能爲1,於是內核爲了方便調用者,又在上述API的基礎上封裝出了更加簡單的讀單一整形屬性的API,它們爲int of_property_read_u8()、of_property_read_u16()等,實現於include/linux/of.h:

[cpp] view plaincopyprint?

  1. 513 static inline int of_property_read_u8(const struct device_node *np,  
  2. 514                                        const char *propname,  
  3. 515                                        u8 *out_value)  
  4. 516 {  
  5. 517         return of_property_read_u8_array(np, propname, out_value, 1);  
  6. 518 }  
  7. 519  
  8. 520 static inline int of_property_read_u16(const struct device_node *np,  
  9. 521                                        const char *propname,  
  10. 522                                        u16 *out_value)  
  11. 523 {  
  12. 524         return of_property_read_u16_array(np, propname, out_value, 1);  
  13. 525 }  
  14. 526  
  15. 527 static inline int of_property_read_u32(const struct device_node *np,  
  16. 528                                        const char *propname,  
  17. 529                                        u32 *out_value)  
  18. 530 {  
  19. 531         return of_property_read_u32_array(np, propname, out_value, 1);  
  20. 532 } 
513 static inline int of_property_read_u8(const struct device_node *np,
514                                        const char *propname,
515                                        u8 *out_value)
516 {
517         return of_property_read_u8_array(np, propname, out_value, 1);
518 }
519
520 static inline int of_property_read_u16(const struct device_node *np,
521                                        const char *propname,
522                                        u16 *out_value)
523 {
524         return of_property_read_u16_array(np, propname, out_value, 1);
525 }
526
527 static inline int of_property_read_u32(const struct device_node *np,
528                                        const char *propname,
529                                        u32 *out_value)
530 {
531         return of_property_read_u32_array(np, propname, out_value, 1);
532 }


int of_property_read_string(struct device_node *np, const char

*propname, const char **out_string);

int of_property_read_string_index(struct device_node *np, const char

    *propname, int index, const char **output);

前者讀取字符串屬性,後者讀取字符串數組屬性中的第index個字符串。如drivers/clk/clk.c中的of_clk_get_parent_name()透過of_property_read_string_index()遍歷clkspec結點的所有"clock-output-names"字符串數組屬性。

[cpp] view plaincopyprint?

  1. 1759 const char *of_clk_get_parent_name(struct device_node *np, int index)  
  2. 1760 {  
  3. 1761         struct of_phandle_args clkspec;  
  4. 1762         const char *clk_name;  
  5. 1763         int rc;  
  6. 1764  
  7. 1765         if (index < 0)  
  8. 1766                 return NULL;  
  9. 1767  
  10. 1768         rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,  
  11. 1769                                         &clkspec);  
  12. 1770         if (rc)  
  13. 1771                 return NULL;  
  14. 1772  
  15. 1773         if (of_property_read_string_index(clkspec.np, "clock-output-names",  
  16. 1774                                   clkspec.args_count ? clkspec.args[0] : 0,  
  17. 1775                                           &clk_name) < 0)  
  18. 1776                 clk_name = clkspec.np->name;  
  19. 1777  
  20. 1778         of_node_put(clkspec.np);  
  21. 1779         return clk_name;  
  22. 1780 }  
  23. 1781 EXPORT_SYMBOL_GPL(of_clk_get_parent_name); 
1759 const char *of_clk_get_parent_name(struct device_node *np, int index)
1760 {
1761         struct of_phandle_args clkspec;
1762         const char *clk_name;
1763         int rc;
1764
1765         if (index < 0)
1766                 return NULL;
1767
1768         rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
1769                                         &clkspec);
1770         if (rc)
1771                 return NULL;
1772
1773         if (of_property_read_string_index(clkspec.np, "clock-output-names",
1774                                   clkspec.args_count ? clkspec.args[0] : 0,
1775                                           &clk_name) < 0)
1776                 clk_name = clkspec.np->name;
1777
1778         of_node_put(clkspec.np);
1779         return clk_name;
1780 }
1781 EXPORT_SYMBOL_GPL(of_clk_get_parent_name);


static inline bool of_property_read_bool(const struct device_node *np,

                                         const char *propname);

如果設備結點np含有propname屬性,則返回true,否則返回false。一般用於檢查空屬性是否存在。

void __iomem *of_iomap(struct device_node *node, int index);

通過設備結點直接進行設備內存區間的 ioremap(),index是內存段的索引。若設備結點的reg屬性有多段,可通過index標示要ioremap的是哪一段,只有1段的情況,index爲0。採用Device Tree後,大量的設備驅動通過of_iomap()進行映射,而不再通過傳統的ioremap。

 

unsigned int irq_of_parse_and_map(struct device_node *dev, int index);

透過Device Tree或者設備的中斷號,實際上是從.dts中的interrupts屬性解析出中斷號。若設備使用了多箇中斷,index指定中斷的索引號。

還有一些OF API,這裏不一一列舉,具體可參考include/linux/of.h頭文件。

5.    總結

ARM社區一貫充斥的大量垃圾代碼導致Linus盛怒,因此社區在2011年到2012年進行了大量的工作。ARM Linux開始圍繞Device Tree展開,Device Tree有自己的獨立的語法,它的源文件爲.dts,編譯後得到.dtb,Bootloader在引導Linux內核的時候會將.dtb地址告知內核。之後內核會展開Device Tree並創建和註冊相關的設備,因此arch/arm/mach-xxx和arch/arm/plat-xxx中大量的用於註冊platform、I2C、SPI板級信息的代碼被刪除,而驅動也以新的方式和.dts中定義的設備結點進行匹配。

 

轉自:http://blog.csdn.net/21cnbao/article/details/8457546

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章