目錄
基本數據格式
設備樹組成:節點和屬性。屬性是鍵值對,並且節點可以同時包含屬性和子節點。
.dts簡單樹
/dts-v1/;
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
// hex is implied in byte arrays. no '0x' prefix is required
a-byte-data-property = [01 23 34 56];
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 {
};
};
};
- 此簡單樹沒有實際用處,僅顯示節點和屬性結構。
- 一個根節點:"/"。
- 兩個子節點:"node1"和"node2"。
- node1節點的子節點:"child-node1"和"child-node2"。
- 以及一隊散落在樹上的屬性。
- 屬性是簡單的鍵值對,值可以爲空或者任意字節流。
- 文本字符串以雙引號表示:a-string-property = "A string";
- 單元是由尖括號分割的32位無符號整數:a-cell-property = <0X1 0X2222 3 0X12345678>;
- 二進制數據用方括號分割:a-byte-data-property = [01 23 34 56];
- 可以通過逗號將不同的表示形式的數據連接起來:mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
- 逗號還用於創建字符串列表:a-string-list-property = "first string", "second string";
基本概念:
假設ABC公司製造的名爲"Poseidon"的設備,設備基本信息如下:
- 1個32位ARM CPU
- 處理器本地總線連接到內存映射的串行端口,spi總線控制器,i2c控制器,中斷控制器和外部總線橋
- 256MB SDRAM基於0地址
- 2個基於0x101F1000和0x101F2000的串行端口
- 基於0x101F3000的GPIO控制器
- 基於0x10170000且具有以下器件的SPI控制器
- MMC插槽,SS引腳連接到GPIO#1
- 帶有以下設備的外部總線橋:SMC SMC91111以太網設備連接到基於0x10100000的外部總線
- 基於以下設備的基於0x10160000的i2c控制器:Maxim DS1338實時時鐘。響應從地址1101000(0x58)
- 基於0x30000000的64MB NOR閃存
第一步:構建框架結構。
需要唯一的機器標識,是設備樹中的最小結構。
compatible指定系統名稱,包含了格式爲" <manufacturer>,<model>"的字符串,指定確切的設備和生產商可以避免命名空間上衝突,linux操作系統會根據compatible值來決定如何在機器上運行。
/dts-v1/;
/{
compatible = "ABC, Poseidon";
};
第二步:CPU進行詳細描述。
爲每一個cpu添加一個名爲"cpus"的容器節點以及一個節點。本測試系統是雙核的Cortex a9,故要描述兩個cpu。cpu節點中的每個compatible屬性都是一個字符串,該屬性通過字符串的形式指定確切的cpu模型,和頂層屬性一樣。
/dts-v1/;
/{
compatible = "ABC, Poseidon";
cpus{
cpu@0{
compatible = "arm, cortex-a9";
};
cpu@1{
compatible = "arm, cortex-a9";
};
};
};
補充:節點名稱
- 每一個節點的名稱都必須以<name>@<unit-address>的形式。
- name是一個簡單的ascii字符串,最大長度爲31個字符。通常是根據代表的設備類型命名。例如3ca以太網適配器節點將使用名稱ethernet,而不是3ca。
- 如果節點使用地址描述設備,則包含unit-address屬性,單元地址是用於訪問設備的住地址,並在節點的reg屬性中列出。
- 兄弟節點必須具有唯一的名稱,但是多個節點使用相同的通用名稱也是允許的,只要地址不同。例如(serial@10001001和serial@1000ffff)。
第三步:設備進行描述。
- 系統中的每個設備都有一個設備樹節點表示,暫且沒有使用地址範圍和中斷處理的時候,暫時不填寫。
- 每一個設備節點都要有一個compatible屬性。
- flash節點屬性包含2個字符串,在後續說明。
- 節點的名稱反映的是設備的類型,而不是特定的型號,儘可能使用已定義的通用節點名稱。
/dts-v1/;
/{
compatible = "ABC, Poseidon";
cpus{
cpu@0{
compatible = "arm, cortex-a9";
};
cpu@1{
compatible = "arm, cortex-a9";
};
};
serial@10001001{
compatible = "arm, p1011";
};
serial@10001001{
compatible = "arm, p1012";
};
gpio@101f30000{
compatible = "arm, p1060";
}
interrupt-controller@10140000{
compatible = "arm, p1190";
}
spi@10115000{
compatible = "arm, p1022";
};
external-bus{
ethernet@0,0{
compatible = "smc, smc91c111";
};
i2c@1,0{
compatible = "acme, a1234-i2c-bus";
rtc@58{
compatible = "maxin, ds1222";
};
};
flash@2,0{
compatible = "samsung, k8f1315ebm", "cfi-flash";
};
};
};
compatible屬性詳解
- compatible是每個節點必須有的屬性,作用是操作系統決定將某個驅動程序綁定到設備的鍵值。
- compatible是字符串列表。列表中的第一個字符串指定節點以什麼形式表示確切的設備"<manufacturer>, <model>"。
- 這種做法允許將現有設備驅動程序綁定到較新的設備,同時仍然唯一地標識確切的硬件。
- 請勿使用通配符兼容的值,例如“ fsl,mpc83xx-uart”或類似的值。
例如,飛思卡爾MPC8349片上系統(SoC)具有ns16550寄存器接口的串行設備。因此,MPC8349串行設備的兼容屬性應爲:compatible = "fsl, mpc8349-uart", "ns16550"。在這種情況下,fsl,mpc8349-uart指定確切的設備,並使用ns16550聲明該設備與美國國家半導體16550 UART的寄存器級別兼容。
尋址方式
可尋址的設備使用以下屬性將地址信息放入到設備樹中:
- reg
- #address-cells
- #size-cells
- 每個可尋址設備都會獲得一個reg,它是形式的元組列表reg = <address1 length1 [address2 length2] [address3 length3] ... >。每個元組代表設備使用的地址範圍。每個地址值是一個或多個稱爲單元的 32位整數的列表。同樣,長度值可以是單元格列表,也可以是空值。
- 由於地址和長度字段都是可變大小的變量,因此父節點中的#address-cells和#size-cells屬性用於說明每個字段中有多少個單元格。換句話說,正確解釋reg屬性需要父節點的#address-cells和#size-cells值。
CPU尋址
CPU節點是最簡單的情形,每個CPU都有分配一個唯一的ID,並且沒有與CPU 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節點中,#address-cells爲1,#size-cells爲0。這意味着子reg值是單個uint32,表示沒有限制字段的地址。在這種情況下,兩個#size-cellscpu的地址分別爲0和1。對於cpu節點,其值爲0,因爲每個cpu僅分配了一個地址。
- 該reg值與節點名稱中的值匹配。按照慣例,如果節點具有reg屬性,則節點名稱必須包含unit-address,即該reg屬性中的第一個地址值。
內存映射的設備
與在cpu節點中找到的單個地址值不同的是,爲內存映射的設備分配了它將響應的地址範圍。 #size-cells用於說明每個子reg元組中的length字段有多大。在下面的示例中,每個地址值是1個單元(32位),每個長度值也是1個單元,這在32位系統上是典型的。64位機器可以爲#address-cells和#size-cells使用值2,以在設備樹中獲得64位尋址。
/dts-v1/;
/{
#address-cells = <1>;
#size-cells = <1>;
...
serial@0x101f0000{
compatible = "arm, p1011";
reg = <0x101f0000 0x1000>;
};
serial@0x101f2000{
compatible = "arm, p1011";
reg = <0x101f2000 0x1000>;
};
gpio@101f3000{
compatible = "arm, p1061";
reg = <0x101f3000 0x1000
0x101f4000 0x0010>;
};
interrupt-controller@10140000{
compatible = "arm, p1190";
reg = <0x10140000 0x1000>;
};
spi@10115000{
compatible = "arm, p1022";
reg = <0x10115000 0x1000>;
};
...
};
- 每個設備都分配有一個基地址,並分配了它的區域大小。在此示例中,爲GPIO設備地址分配了兩個地址範圍;即:0x101f3000 ..0x101f3fff和0x101f4000..0x101f400f。
- 有些設備使用不同的尋址方案在總線上。例如,設備可以通過分立的芯片選擇線連接到外部總線。由於每個父節點都爲其子節點定義了尋址域,因此可以選擇地址映射來最好地描述系統。下面的代碼顯示了連接到外部總線的設備的地址分配,芯片選擇號已編碼到該地址中。
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";
reg = <2 0 0x40000000>;
};
};
- 將external-bus2個單元格用作地址值,一個代表芯片選擇編號,另一個代表相對於芯片選擇基數的偏移量。長度字段保持爲單個單元,因爲僅地址的偏移部分需要具有範圍。因此,在此示例中,每個reg條目包含3個單元格。芯片選擇號,偏移量和長度。
- 由於地址域包含在節點及其子節點中,因此父節點可以自由定義對總線有意義的任何尋址方案。直接父級和子級節點之外的節點通常不必關心本地尋址域,並且必須將地址映射爲從一個域到達另一個域。
非內存映射設備
其他設備未在處理器總線上映射到內存。它們可以具有地址範圍,但不能由CPU直接訪問。相反,父設備的驅動程序將代表CPU執行間接訪問。以i2c設備爲例,爲每個設備分配了一個地址,但是沒有與之關聯的長度或範圍。這看起來與CPU地址分配幾乎相同。
i2c@1,0{
compatible = "acme, a1234-i2c-bus";
reg = <1 0 0x1000>;
rtc@58{
compatible = "maxim, ds1338";
};
};
地址範圍
- 上面已經討論瞭如何爲設備分配地址,但是目前這些地址僅在設備節點本地。它尚未描述如何從這些地址映射到CPU可以使用的地址。
- 根節點始終描述CPU的地址空間視圖。根的子節點已經在使用CPU的地址域,因此不需要任何顯式映射。例如,serial @ 101f0000設備直接分配了地址0x101f0000。 不是根的直接子節點的節點不使用CPU的地址域。爲了獲得內存映射的地址,設備樹必須指定如何將地址從一個域轉換爲另一個域。該ranges屬性用於此目的。這是添加了ranges屬性的示例設備樹。
/dts-v1/
/{
compatible = "ABC, Poseidon";
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000 //chip select 1,Ethernet
1 0 0x10160000 0x10000 //chip select 2,i2c controller
2 0 0x30000000 0x10000000>; //chip select 3,NOR Flash
ethernet@0,0 {
compatible = "smc,smc91c111";
reg = <0 0 0x1000>;
};
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>;
};
flash@2,0 {
compatible = "samsung,k8f1315ebm", "cfi-flash";
reg = <2 0 0x4000000>;
};
};
ranges是地址翻譯的列表。範圍表中的每個條目都是一個元組,其中包含子地址,父地址以及子地址空間中區域的大小。每個字段的大小取決於孩子的#address-cells值,父母的#address-cells值和孩子的#size-cells值。對於本例中的外部總線,子地址爲2個單元,父地址爲1個單元,大小也爲1個單元。三個列表對應翻譯爲:
- 片選0的偏移量0映射到地址範圍0x10100000..0x1010ffff
- 片選1的偏移量0映射到地址範圍0x10160000..0x1016ffff
- 片選2的偏移量0映射到地址範圍0x30000000..0x30ffffff
如果父級和子級地址空間相同,則節點可以添加一個空ranges屬性。空範圍屬性的存在意味着子地址空間中的地址被1:1映射到父地址空間。
爲什麼都可以使用1:1映射來編寫地址轉換呢?一些總線(如PCI)具有完全不同的地址空間,其詳細信息需要向操作系統公開。其他一些具有DMA引擎,需要知道總線上的真實地址。有時需要將設備分組在一起,因爲它們都共享相同的軟件可編程物理地址映射。是否應使用1:1映射在很大程度上取決於操作系統所需的信息以及硬件設計。
您還應該注意,rangesi2c @ 1,0節點中沒有任何屬性。其原因是,與外部總線不同,i2c總線上的設備未在CPU的地址域上進行內存映射。相反,CPU通過i2c @ 1,0設備間接訪問rtc @ 58設備。缺少ranges屬性意味着除父設備以外的任何設備都不能直接訪問該設備。
中斷如何使用
- 與遵循樹的自然結構的地址範圍轉換不同,中斷信號可以源自機器中的任何設備,也可以在機器中的任何設備上終止。與設備樹中自然表示的設備尋址不同,中斷信號表示爲獨立於樹的節點之間的鏈接。四個屬性用於描述中斷連接:
- interrupt-controller -空屬性,將節點聲明爲接收中斷信號的設備
- #interrupt-cells-這是中斷控制器節點的屬性。它指出此中斷控制器的中斷說明符中有多少個單元(類似於#address-cells和#size-cells)。
- interrupt-parent-設備節點的屬性,該設備節點包含與其相連的中斷控制器的虛擬對象。沒有中斷父級屬性的節點也可以從其父節點繼承該屬性。
- interrupts-設備節點的屬性,其中包含一系列中斷說明符,每個中斷說明符對應一個設備上的中斷輸出信號。
- 一箇中斷符是數據的一個或多個單元(如通過#中斷單元指定的),該其中斷輸入所述裝置被附接到指定。多數設備只有一箇中斷輸出,如下例所示,但設備上可能有多箇中斷輸出。中斷說明符的含義完全取決於中斷控制器設備的綁定。每個中斷控制器可以決定需要多少個單元來唯一定義一箇中斷輸入。
/dts-v1/
/{
compatible = compatible = "ABC, Poseidon";
#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@0x101f0000{
compatible = "arm, p1011";
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>;
};
};
- 設備具有單箇中斷控制器,interrupt-controller@10140000。
- 標籤'intc:'已添加到中斷控制器節點,並且該標籤用於爲根節點中的interrupt-parent屬性分配一個虛擬對象。此中斷父級值成爲系統的默認值,因爲除非明確覆蓋所有子節點,否則所有子節點都將繼承它。
- 每個設備使用中斷屬性來指定不同的中斷輸入線。
- #interrupt-cells爲2,因此每個中斷說明符都有2個單元。此示例使用以下通用模式:使用第一個單元對中斷線號進行編碼,使用第二個單元對諸如高電平有效對低電平有效或邊沿電平對電平敏感的標誌進行編碼。對於任何給定的中斷控制器,請參閱控制器的綁定文檔以瞭解說明符的編碼方式。
設備特定數據
除了通用屬性,還可以將任意屬性和子節點添加到節點。只要遵循某些規則,就可以添加操作系統所需的任何數據。
- 首先,新的特定於設備的屬性名稱應使用製造商前綴,以使其與現有的標準屬性名稱不衝突。
- 其次,必須在綁定中記錄屬性和子節點的含義,以便設備驅動程序作者知道如何解釋數據。綁定記錄了特定的兼容值的含義,應具有的屬性,其可能具有的子節點以及所代表的設備。每個唯一compatible值應具有其自己的綁定(或聲明與另一個兼容值的兼容性)。新設備的綁定記錄在此Wiki中。有關文檔格式和審閱過程的說明,請參見主頁。
- 第三,將新的綁定發佈到[email protected]郵件列表中以進行檢查。審查新的綁定會發現很多常見的錯誤,這些錯誤將來會引起問題。
特殊節點
aliases節點
完整路徑通常引用一個特定的節點,例如/external-bus/ethernet@0,0,但是當用戶真正想知道的是“哪個設備是eth0?”時,這變得很麻煩。該aliases節點可用於將短別名分配給完整的設備路徑。例如:
aliases{
ethernet0 = ð0;
serial0 = &serial0;
};
chosen節點
該chosen節點並不代表真實的設備,而是充當在固件和操作系統之間傳遞數據的地方,例如引導參數。所選節點中的數據不代表硬件。通常,所選節點在.dts源文件中保留爲空,並在引導時進行填充。在我們的示例系統中,固件可能將以下內容添加到所選節點:
chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};