一、概述
???? ARM Device Tree起源于OpenFirmware (OF),在Linux 2.6中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥著大量的垃圾代碼,相當(dāng)多數(shù)的代碼只是在描述板級(jí)細(xì)節(jié),而這些板級(jí)細(xì)節(jié)對(duì)于內(nèi)核來講,不過是垃圾,如板上的platform設(shè)備、resource、i2c_board_info、spi_board_info以及各種硬件platform_data。常見的s3c2410、s3c6410等板級(jí)目錄,代碼量在數(shù)萬行。
???? Linus Torvalds對(duì)于此種情況大發(fā)雷霆,在2011年的ARM Linux郵件列表宣稱this whole ARM thing is a f*cking pain in the ass”。
???? 所以Linux開發(fā)社區(qū)就開始整改,設(shè)備樹最早用于PowerPC等其他體系架構(gòu),ARM架構(gòu)開發(fā)社區(qū)就開始采用設(shè)備樹來描述設(shè)備的信息。
二、設(shè)備樹結(jié)構(gòu)
????? Device Tree是一種描述硬件的數(shù)據(jù)結(jié)構(gòu),由一系列被命名的結(jié)點(diǎn)(node)和屬性(property)組成,而結(jié)點(diǎn)本身可包含子結(jié)點(diǎn)。所謂屬性,其實(shí)就是成對(duì)出現(xiàn)的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中):CPU的數(shù)量和類別,內(nèi)存基地址和大小,總線和橋,外設(shè)連接,中斷控制器和中斷使用情況,GPIO控制器和GPIO使用情況,Clock控制器和Clock使用情況。
????? 通常由.dts文件以文本方式對(duì)系統(tǒng)設(shè)備樹進(jìn)行描述,經(jīng)過Device Tree Compiler(dtc)將dts文件轉(zhuǎn)換成二進(jìn)制文件binary device tree blob(dtb),.dtb文件可由Linux內(nèi)核解析,有了device tree就可以在不改動(dòng)Linux內(nèi)核的情況下,對(duì)不同的平臺(tái)實(shí)現(xiàn)無差異的支持,只需更換相應(yīng)的dts文件,即可滿足。
????? 設(shè)備樹信息被保存在一個(gè)ASCII 文本文件中,適合人類的閱讀習(xí)慣,類似于xml文件, 在ARM Linux中,一個(gè).dts文件對(duì)應(yīng)一個(gè)ARM的machine放置在內(nèi)核的arch/arm/boot/dts/目錄。
三、設(shè)備樹語法
3.1 節(jié)點(diǎn)node

3.2 屬性property
屬性擁有兩種格式:
Property格式1(沒有值) : [label:] property-name;
Property格式2(鍵值對(duì)) : [label:] property-name = value;;

四、設(shè)備樹實(shí)例
下述例子譯自: http://elinux.org/Device_Tree_Usage
下面給出一個(gè)例子,從0開始描述如何寫一個(gè)完整的設(shè)備樹?,F(xiàn)在有個(gè)arm平臺(tái)的板子,假設(shè)制造商為“acme”我們給他命名為“Coyote's Revenge”首先,假設(shè)我們有如下的硬件平臺(tái):
一顆 32bit ARM CPU
處理器本地總線上映射了串口SPI控制器、I2C控制器、中斷控制器以及外部總線橋。
基地址為0,大小為256MB的SDRAM
基地址分別為0x101F1000 和0x101F2000的2個(gè)串口
基地址為0x101F3000 GPIO控制器
基地址為0x10170000的SPI 控制器上有如下的設(shè)備:
MMC 卡槽的SS引腳連接到了GPIO #1
外部總線橋上有如下的設(shè)備:
SMC公司生產(chǎn)的SMC91111 Ethernet,其基地址為0x10100000。
在基地址為0x10160000的 i2c 控制器有如下設(shè)備:
美信公司的DS1338 實(shí)時(shí)時(shí)鐘芯片. 從站相應(yīng)地址為0x58
大小為64MB 的 NOR flash 其基地址為0x30000000
下面將按照上面的描述信息,寫一個(gè)DTS文件。
初始化結(jié)構(gòu)體
首先先給整個(gè)設(shè)備樹寫一個(gè)框架如下所示:
/dts-v1/;
/ {
compatible ="acme,coyotes-revenge";
};
上面首先在根節(jié)點(diǎn)下寫了一個(gè)屬性compatible,該屬性是系統(tǒng)用來識(shí)別不同板級(jí)設(shè)備的重要依據(jù),其一般由廠商名和樣板名兩部分組成,比如在上面我們的制造商為acme板子的名字叫做coyotes-revenge。
加入CPU
假如我們的CPU是一顆雙核A9的CPU,我們添加一個(gè)叫”cpus”的子節(jié)點(diǎn),具體如下:
/dts-v1/;
/ {
compatible ="acme,coyotes-revenge";
cpus {
cpu@0 {
compatible = "arm,cortex-a9";
};
cpu@1 {
compatible = "arm,cortex-a9";
};
};
};
cpu的compatible設(shè)置與頂層的類似,有兩部分組成。Arm描述了制造商,cortex-a9指明了型號(hào)。
系統(tǒng)中每個(gè)設(shè)備都是依靠設(shè)備樹節(jié)點(diǎn)來描述的,接下來就要添加一些設(shè)備節(jié)點(diǎn)來描述每一個(gè)設(shè)備:
/dts-v1/;
/ {
compatible = "acme,coyotes-revenge";
cpus {
cpu@0 {
compatible = "arm,cortex-a9";
};
cpu@1 {
compatible = "arm,cortex-a9";
};
};
serial@101F0000{
compatible = "arm,pl011";
};
serial@101F2000 {
compatible = "arm,pl011";
};
gpio@101F3000 {
compatible = "arm,pl061";
};
interrupt-controller@10140000 {
compatible = "arm,pl190";
};
spi@10115000 {
compatible = "arm,pl022";
};
external-bus {
ethernet@0,0 {
compatible = "smc,smc91c111";
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
rtc@58 {
compatible ="maxim,ds1338";
};
};
flash@2,0 {
compatible = "samsung,k8f1315ebm","cfi-flash";
};
};
};
通過上面的列表可以看出來,通過dts的層次關(guān)系,可以看到具體的板級(jí)的設(shè)備連接關(guān)系,比如外部總線上有ethercat、i2c和flash三個(gè)設(shè)備。
但是上面描述的這課樹還不是一個(gè)完整有效的設(shè)備樹,因此他缺少必要的連接方式、地址信息等。稍后會(huì)加上這些信息。
理解Compatible
每個(gè)設(shè)備節(jié)點(diǎn)都會(huì)有compatible屬性, 設(shè)備與驅(qū)動(dòng)之間的結(jié)合就依賴這個(gè)屬性的匹配。
Compatible屬性是由字符串列表組成,就像上面列出的flash節(jié)點(diǎn),compatible有兩個(gè)屬性值,以一個(gè)字符串確切的表示該設(shè)備的信息,第二個(gè)字符串描述的與該節(jié)點(diǎn)描述符相兼容的設(shè)備。
設(shè)備如何尋址
設(shè)備尋址地址在設(shè)備樹中通過如下的屬性信息來表示:
reg
address-cells
size-cells
每一個(gè)可尋址設(shè)都會(huì)有一個(gè)reg屬性,該屬性有一個(gè)或多個(gè)元素組成,其基本格式為:
reg =<address1 length1 [address2 length2] [address3 length3] ... >
上面的每一個(gè)元素都代表設(shè)備的尋址地址及其尋址大小,每一個(gè)元素中的address值可以是一個(gè)或者多個(gè)無符號(hào)32位整形數(shù)據(jù)類型cell來表示,元素中的length可以為空也可以使一個(gè)或者多個(gè)無符號(hào)32位整形數(shù)據(jù)類型cell。
由于每個(gè)可尋址設(shè)備都會(huì)有reg屬性可設(shè)置,而且reg屬性元素也是靈活可選擇的,那么誰來制定reg屬性元素中每個(gè)元素也就是address和length的個(gè)數(shù)呢?
在這里,要關(guān)注到期父節(jié)點(diǎn)的兩個(gè)屬性,其中#address-cells表示reg中address元素的個(gè)數(shù),#size-cells用來表示length元素的個(gè)數(shù)。
為了展示剛剛接手實(shí)行的作用,那么現(xiàn)在做一個(gè)演示,首先從cpu節(jié)點(diǎn)開始演示:
CPU 尋址地址
對(duì)于尋址地址的編寫,cpu節(jié)點(diǎn)是最簡單的一個(gè)例子,之前介紹,而每個(gè)cpu節(jié)點(diǎn)都包含一個(gè)標(biāo)記ID,但是沒有其他的描述信息,這里填充一些其他的屬性:
cpus {
#address-cells= <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg= <0>;
};
cpu@1 {
compatible = "arm,cortex-a9";
reg= <1>;
};
};
在cpus節(jié)點(diǎn) #address-cells 賦予 1, #size-cells 賦予 0.。這意味著在其子節(jié)點(diǎn)的reg只有一個(gè)地址元素值,沒有長度元素值。在這個(gè)案例當(dāng)中,兩顆cpu核的地址分別唄分配成0和1。因?yàn)槊總€(gè)cpu只分配了地址,所以節(jié)點(diǎn) #size-cells元素被設(shè)置為 0。
內(nèi)存映射設(shè)備
需要內(nèi)存映射的設(shè)備不同于上面的cpu節(jié)點(diǎn),這類的設(shè)備需要一段內(nèi)存而不是單一的內(nèi)存地址,因此不近需要包含內(nèi)存的基地址還而且還需要映射地址的長度,因此需要使用 #size-cells屬性來表示reg屬性元素中表示地址長度元素的個(gè)數(shù)。在下面的例子中,每一個(gè)節(jié)點(diǎn)的address值有一個(gè)32位無符號(hào)整形數(shù)據(jù)而且length值也是用一個(gè)32位無符號(hào)整形數(shù)據(jù)來表示。因此在32的系統(tǒng)中 #address-cells 和#size-cells都要設(shè)置為1,但是在64位系統(tǒng)中 #address-cells就要設(shè)置成2了。具體設(shè)置如下:
/dts-v1/;
/ {
#address-cells= <1>;
#size-cells = <1>;
...
serial@101f0000{
compatible= "arm,pl011";
reg =<0x101f0000 0x1000 >;
};
serial@101f2000{
compatible= "arm,pl011";
reg =<0x101f2000 0x1000 >;
};
gpio@101f3000 {
compatible= "arm,pl061";
reg =<0x101f3000 0x1000
0x101f4000 0x0010>;
};
interrupt-controller@10140000 {
compatible= "arm,pl190";
reg =<0x10140000 0x1000 >;
};
spi@10115000 {
compatible= "arm,pl022";
reg =<0x10115000 0x1000 >;
};
...
上面的例子中reg屬性都有address元素和length屬性,值的注意的是,例子中的GPIO被分配了兩個(gè)地址范圍,分別是 0x101f3000...0x101f3fff 以及0x101f4000..0x101f400f。
有一些設(shè)備可能有不同的尋址方案,比如一個(gè)設(shè)備掛載到總線上連接一個(gè)片選信號(hào)線,可以通過片選信號(hào)選擇不同的設(shè)備。由于父節(jié)點(diǎn)可以定義了其子節(jié)點(diǎn)的地址映射域,所以可以選擇最適合的一項(xiàng)來描述硬件設(shè)備。下面的代碼就是把片選號(hào)碼編入地址碼掛載到外部總線上的一個(gè)設(shè)備。
external-bus {
#address-cells= <2>
#size-cells = <1>;
ethernet@0,0 {
compatible = "smc,smc91c111";
reg= <0 0 0x1000>;
};
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
reg = <1 0 0x1000>;
rtc@58{
compatible = "maxim,ds1338";
};
};
flash@2,0 {
compatible= "samsung,k8f1315ebm", "cfi-flash";
compatible= <2 0 0x4000000>;
};
};
上面的代碼中, #address-cells 屬性為2,則表示reg屬性的address有兩個(gè)地址域,其中一個(gè)表示片選號(hào),另一個(gè)表示設(shè)備到片選基地址的偏移量,#size-cells為1,其地址范圍量的個(gè)數(shù)還是一個(gè)32位的無符號(hào)整數(shù)。所以最后reg有三個(gè)屬性值,分別表示片選號(hào)、偏移量、地址范圍。
無內(nèi)存映射設(shè)備
其他的一些設(shè)備,他們?cè)谔幚砥骺偩€瓶沒有內(nèi)存映射。他們擁有地址范圍但是他們不被cpu直接的訪問,而是被父設(shè)備驅(qū)動(dòng)替代cpu進(jìn)行訪問。
舉個(gè)例子,對(duì)于i2c設(shè)備,每個(gè)設(shè)備都會(huì)有一個(gè)指定的訪問地址,但是這些設(shè)備不會(huì)有相關(guān)聯(lián)的范圍或者地址長度,這有點(diǎn)類似cpu節(jié)點(diǎn)地址分配。如下是具體代碼的例子:
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells= <1>;
#size-cells = <0>;
reg =<1 0 0x1000>;
rtc@58{
compatible = "maxim,ds1338";
reg= <58>;
};
};
參考:
https://blog.csdn.net/sgmenghuo/article/details/45071615
https://blog.csdn.net/woshidahuaidan2011/article/details/52948732
設(shè)備樹使用文檔