[dts]Device Tree格式解析


轉自:http://blog.csdn.net/airk000/article/details/21345159

 目錄:

1. 作用

2. 基本數據格式

3. 一些基本概念

4. 工作方式

    a. 地址

    b. 中斷

    c. 其他

5. 進階例子

Device Tree常用方法解析

Device Tree在Linux內核驅動中的使用源於2011年3月17日Linus Torvalds在ARM Linux郵件列表中的一封郵件,他宣稱“this whole ARM thing is a f*cking pain in the ass”,並提倡學習PowerPC等其他架構已經成熟使用的Device Tree技術。自此,Device Tree正式進入ARM社區的視野中。

1. 作用

Device Tree是一種用來描述硬件的數據結構,類似板級描述語言,起源於OpenFirmware(OF)。在目前廣泛使用的Linux kernel 2.6.x版本中,對於不同平台、不同硬件,往往存在着大量的不同的、移植性差的板級描述代碼,以達到對這些不同平台和不同硬件特殊適配的需求。但是過多的平台、過的的不同硬件導致了這樣的代碼越來越多,最終引發了Linux創始人Linus的不滿,以及強烈呼吁改變。Device Tree的引入給驅動適配帶來了很大的方便,一套完整的Device Tree可以將一個PCB擺在你眼前。Device Tree可以描述CPU,可以描述時鍾、中斷控制器、IO控制器、SPI總線控制器、I2C控制器、存儲設備等任何現有驅動單位。對具體器件能夠描述到使用哪個中斷,內存映射空間是多少等等。

2. 基本數據格式

Device Tree由節點和屬性構成。屬性為key-value對,節點包括了各種屬性,也可以包含子節點。下邊列舉一個簡單的dts文件:

 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];--------------------/* 屬性:16進制 */
 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 };

 

 

這個文件實際上沒有任何意義,但卻包含了基本所有要素:

  • 1 唯一的根節點 “/”
  • 2 一些節點:node1 node2
  • 3 子節點 node1的子節點child-node1和child-node2
  • 4 一群分散的屬性

屬性都是簡單的key-value對,其中value也可以是空的或包含任意的byte流。以下是一些屬性的基本數據結構:

  • 1 雙引號包含的字符信息

     string-property = "a string";
  • 2 cells單位信息是32位無符號整型數據

     cell-property = <1 2 3 4>;
  • 3 二進制數據流

     binary-property = [0x01 0x02 0x03 0x04];
  • 4 混合數據用逗號隔開

     mixed-property = "a string", [0x01 0x02 0x03 0x04], <0xFF01 412 0x12341283>;
  • 5 字符數組

     string-list = "string test1", "string test2";

3. 一些基本概念

  • 每個完整的dts文件必須擁有一個根節點
  • dtsi文件一般為通用文件(類似C語言的頭文件),可被其他文件include
    后邊的名字涵蓋的范圍更加廣泛,如果可以匹配到,同樣會以這個dts為基礎進行初始化並啟動。
  • 父節點名應該取類型名,而不是IC名。節點名的命名規則一般是 [name]@[address],也可以只有name而沒有@之后的內容,但是要確保name不能重名。如果加了@以及地址,那么name可以相同,只要address不同即可。---如果@后有地址,則@前類型名可相同;如果沒有地址,則類型名必須不能相同
  • 每一個設備節點都要有一個compatible屬性(或者在*.dtsi中,或者在*.dts中)
  • compatible的內容是用來匹配驅動的,組成方式為"[manufacturer], [model]",加入廠商名是為了避免重名。有的時候后邊還會跟一個名字,如:

     compatible = "acme,coyotes-revenge", "acmd-board";

4. 工作方式

a. 地址

設備的地址特性根據一下幾個屬性來控制:

  • reg
  • #address-cells
  • #size-cells
4.a.1.reg

這里還是要說明一點,子節點的reg和range是由父節點的#address-cells和#size-cells決定的

reg意為region,區域。格式為:

reg = <address1 length1 [address2 length2] [address3 length3]>;

父類的address-cells和size-cells決定了子類的相關屬性要包含多少個cell,如果子節點有特殊需求的話,可以自己再定義,這樣就可以擺脫父節點的控制(及可重寫,局部優先級更高)
address-cells決定了address1/2/3包含幾個cell,size-cells決定了length1/2/3包含了幾個cell。本地模塊例如:

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

位於0x10115000的SPI設備申請地址空間,起始地址為0x10115000,長度為0x1000,即屬於這個SPI設備的地址范圍是0x10115000~0x10116000。

實際應用中,有另外一種情況,就是通過外部芯片片選激活模塊。例如,掛載在外部總線上,需要通過片選線工作的一些模塊:

 1 external-bus {-----------------------------------------------------/* 外部總線片選 */
 2     #address-cells = <2>
 3     #size-cells = <1>;
 4 
 5     ethernet@0,0 {-------------------------------------------------/* 一個片選號,一個片選號上偏移量*/
 6         compatible = "smc,smc91c111";
 7         reg = <0 0 0x1000>;----------------------------------------/* 子設備:片選號/偏移量/地址長度 */
 8     };
 9 
10     i2c@1,0 {
11         compatible = "acme,a1234-i2c-bus";
12         #address-cells = <1>;
13         #size-cells = <0>;
14         reg = <1 0 0x1000>;
15         rtc@58 {
16             compatible = "maxim,ds1338";
17             reg = <58>;
18         };
19     };
20 
21     flash@2,0 {
22         compatible = "samsung,k8f1315ebm", "cfi-flash";
23         reg = <2 0 0x4000000>;
24     };
25 };

 

external-bus使用兩個cell來描述地址,一個是片選序號,另一個是片選序號上的偏移量。而地址空間長度依然用一個cell來描述。所以以上的子設備們都需要3個cell來描述地址空間屬性——片選、偏移量、地址長度。在上個例子中,有一個例外,就是i2c控制器模塊下的rtc模塊。因為I2C設備只是被分配在一個地址上,不需要其他任何空間,所以只需要一個address的cell就可以描述完整,不需要size-cells。

4.a.2.range

需要描述的設備不是本地設備時,就需要描述一個從設備地址空間到CPU地址空間的映射關系,這里就需要用到ranges屬性。還是以上邊的external-bus舉例:

 1 #address-cells = <1>;
 2 #size-cells = <1>;
 3 ...
 4 external-bus {
 5     #address-cells = <2>
 6     #size-cells = <1>;
 7     ranges = <0 0  0x10100000   0x10000     // Chipselect 1, Ethernet
 8               1 0  0x10160000   0x10000     // Chipselect 2, i2c controller
 9               2 0  0x30000000   0x1000000>; // Chipselect 3, NOR Flash
10 };

 

ranges屬性為一個地址轉換表。表中的每一行都包含了子地址、父地址、在各自地址空間內的區域大小。他們的大小(包含的cell)分別由子節點的address-cells的值、父節點的address-cells的值和子節點的size-cells來決定。以第一行為例:<這好難知道到底哪個地址有父節點決定,哪個由子節點決定>

  • 0 0 兩個cell,由子節點external-bus的address-cells=<2>決定;
  • 0x10100000 一個cell,由父節點的address-cells=<1>決定;
  • 0x10000 一個cell,由子節點external-bus的size-cells=<1>決定。
    最終第一行說明的意思就是:片選0,偏移0(選中了網卡),被映射到CPU地址空間的0x10100000~0x10110000中,地址長度為0x10000。

理解下來,可以認為ranges的格式為<子節點address 父節點address 子節點size>, 而address長度由對應cells決定

b. 中斷

描述中斷連接需要四個屬性
1. interrupt-controller 一個空屬性用來聲明這個node接收中斷信號(固定為空屬性);
2. #interrupt-cells 這是中斷控制器節點的屬性,用來標識這個控制器需要幾個單位做中斷描述符
3. interrupt-parent 標識此設備節點屬於哪一個中斷控制器,如果沒有設置這個屬性,會自動依附父節點的;
4. interrupts 一個中斷標識符列表,表示每一個中斷輸出信號

如果有兩個,第一個是中斷號,第二個是中斷類型,如高電平、低電平、邊緣觸發等觸發特性。對於給定的中斷控制器,應該仔細閱讀相關文檔來確定其中斷標識該如何解析。

c. 其他

除了以上規則外,也可以自己加一些自定義的屬性和子節點,但是一定要符合以下的幾個規則

  1. 新的設備屬性一定要以廠家名字做前綴,這樣就可以避免他們會和當前的標准屬性存在命名沖突問題;
  2. 新加的屬性具體含義以及子節點必須加以文檔描述,這樣設備驅動開發者就知道怎么解釋這些數據了。描述文檔中必須特別說明compatible的value的意義,應該有什么屬性,可以有哪個(些)子節點,以及這代表了什么設備。每個獨立的compatible都應該由單獨的解釋。
  3. 新添加的這些要發送到devicetree-discuss@lists.ozlabs.org郵件列表中進行review,並且檢查是否會在將來引發其他的問題。

5. 進階例子

 1 pci@0x10180000 {
 2         compatible = "arm,versatile-pci-hostbridge", "pci";
 3         reg = <0x10180000 0x1000>;
 4         interrupts = <8 0>;
 5         bus-ranges = <0 0>;
 6 
 7         #address-cells = <3>
 8         #size-cells = <2>;
 9         ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
10                   0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
11                   0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
12 };

 

像之前描述過的本地總線一樣,PCI地址空間與CPU地址空間是完全分離的,所以這里需要通過定義ranges屬性進行地址轉化
#address-cells定義PCI使用3個cell,並且PCI的地址范圍通過兩個單位就可以解讀。所以,首先的問題就是,為什么需要用3個32位的cell來描述一個PCI地址。

這三個cell分別代表物理地址高位、中位、低位

  • 1 phys.high cell : npt000ss bbbbbbbb dddddfff rrrrrrrr
  • 2 phys.mid cell : hhhhhhh hhhhhhhh hhhhhhhh hhhhhhh
  • 3 phys.low cell : llllllll llllllll llllllll llllllll

PCI地址為64位寬度,編碼在phys.mid和phys.low中。真正重要的東西在於phys.high這一位空間中:

n:代表重申請空間標志(這里沒有使用)
p:代表預讀空間(緩存)標志
t:別名地址標志(這里沒有使用)
ss:空間代碼
00: 設置空間
01:IO空間
10:32位存儲空間
11:64位存儲空間

bbbbbbbb: PCI總線號。PCI有可能是層次性架構,所以我們可能需要區分一些子-總線
ddddd:設備號,通常由初始化設備選擇信號IDSEL連接時申請。
fff:功能序號,有些多功能PCI設備可能用到。
rrrrrrrr:注冊號,在設置周期使用。

ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000 0x02000000 0 0xa0000000 0xa0000000 0 0x10000000 0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;

回頭再看這個ranges分表代表了什么。父節點address-cells為1,子節點address-cells為3, 子節點size-cells為2。則第一行可以這樣划分(怎么理解?)

0x42000000 0 0x80000000 子節點地址| 0x80000000 父節點地址| 0 0x20000000 地址空間長度|

0x42000000為phys.high,第一位為01000010,則p為1,ss為10,即申請32位存儲空間為緩存空間。phys.mid為0,phys.low為0x80000000,他們共同組成了PCI地址,即表示從PCI總線的0x80000000地址處申請出一個32位的存儲空間作為緩存。后邊的那個cell 0x80000000 0 0x20000000代表到CPU空間后的參數,申請的地址被映射到CPU空間的0x80000000地址處,大小共計0x20000000(512MB)。

 

 

相關資料及引用:

http://blog.csdn.net/21cnbao/article/details/8457546
http://devicetree.org/Device_Tree_Usage


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM