什么是設備樹
-
設備樹(Device Tree)是用來描述板卡上的硬件資源信息的,包括外設控制器和各種外設所使用的硬件資源信息
-
設備樹的源文件名后綴是.dts/.dtsi, dtsi文件一般用來描述一些通用的硬件信息資源,然后會被dts文件引入,這樣dts文件中就會包含dtsi文件中的內容
-
設備樹源文件在內核中的位置:/arch/arm(64)/boot/dts
-
dts源文件不能直接被內核驅動所使用,必須被編譯成二進制文件才能被內核驅動使用,設備樹二進制文件后綴名是dtb,使用dtc工具把dts文件編譯成dtb文件
graph LR dts ----dtc---> dtb
設備樹語法
dts文件布局
/dts-v1/; // 表示版本
[memory reservations] // 格式為: /memreserve/ <address> <length>;
/ {
[property definitions]
[child nodes]
};
以上各項的含義:
| 名稱 | 含義 |
|---|---|
| /dts-v1/ | 設備樹文件的版本 |
| memory reservations | 指定保留內存,內核不會使用保留內存 |
| / | 根節點(使用花括號表示屬於根節點的內容) |
| property definitions | 根節點的屬性,用來描述硬件 |
| child nodes | 子節點(使用花括號表示屬於孩子節點的內容) |
設備節點
設備樹中的基本單元被稱為節點(node),格式為:
[label:] node-name[@unit-address] {
[properties definitions]
[child nodes]
};
label是標號,可以省略,label的作用是為了方便地引用node,比如:
/dts-v1/;
/ {
uart0: uart@fe001000 {
compatible="ns16550";
reg=<0xfe001000 0x100>;
};
};
可以使用下面2種方法來修改uart@fe001000這個node:
// 在根節點之外使用label引用node:
&uart0 {
status = “disabled”;
};
或在根節點之外使用全路徑:
&{/uart@fe001000} {
status = “disabled”;
};
屬性(properties)
每個節點都有不同的屬性,不同的屬性又有不同的內容,屬性都是鍵值對,值可以為空或任意的字節流
屬性格式
- 屬性有值
[label:] property-name = value; - 屬性沒有值
[label:] property-name;
屬性取值可以有四種
- arrays of cells(1個或多個32位數據, 64位數據使用2個32位數據表示)
cell 就是一個32位的數據,一個或多個cell用尖括號括起來,並以空格隔開就可以作為一種合法的屬性值
interrupts = <17 0xc>
64bit數據使用2個cell來表示,一個或多個cell用尖括號包圍起來:
clock-frequency = <0x00000001 0x00000000>
- string(字符串),用雙引號包圍起來
compatible = "simple-bus";
- bytestring(1個或多個字節),用中括號包圍起來
local-mac-address = [00 00 12 34 56 78]; // 每個byte使用2個16進制數來表示
local-mac-address = [000012345678]; // 每個byte使用2個16進制數來表示
- 以上三種值的混合,以逗號隔開
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";
一些常用的屬性
compatible
compatible 屬性也叫做“兼容性”屬性,這是非常重要的一個屬性!compatible 屬性的值是一個字符串列表,compatible 屬性用於將設備和驅動綁定起來。
“compatible”表示“兼容”,對於某個LED,內核中可能有A、B、C三個驅動都支持它,那可以這樣寫:
led {
compatible = “A”, “B”, “C”;
};
內核啟動時,就會為這個LED按這樣的優先順序為它找到驅動程序:A、B、C。
當該屬性位於根節點時,用於指定內核中哪個machine_desc可以支持本設備,即當前設備與哪些平台兼容。其值的格式一般是"manufacturer,model",其中manufacturer表示廠家,model表示型號(廠家的哪型產品)。
當該屬性的值有多個字符串時,從左往右,從最特殊到最一般。舉例來說,compatible = "samsung,smdk2416", "samsung,s3c2416";作為根節點的屬性時,第一個字符串指示了一個具體的開發板型號,而第二個字符串要更一般,只指示了SoC的型號。在linux初始化時,會優先找支持"samsung,smdk2416"的machine_desc用以初始化硬件,找不到時才退而求其次"samsung,s3c2416"。
model
model屬性與compatible屬性有些類似,但是有差別。
compatible屬性是一個字符串列表,表示你的硬件可以兼容A、B、C等驅動;
model用來准確地定義這個硬件是什么。
比如根節點中可以這樣寫:
/ {
compatible = "samsung,smdk2440", "samsung,mini2440";
model = "jz2440_v3";
};
它表示這個單板,可以兼容內核中的“smdk2440”,也兼容“mini2440”。
從compatible屬性中可以知道它兼容哪些板,但是它到底是什么板?用model屬性來明確。
#address-cells、#size-cells
cell指一個32位的數值
address-cells:address要用多少個32位數來表示
size-cells:size要用多少個32位數來表示
比如一段內存,怎么描述它的起始地址和大小?
下例中,address-cells為1,所以reg中用1個數來表示地址,即用0x80000000來表示地址;size-cells為1,所以reg中用1個數來表示大小,即用0x20000000表示大小:
/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
reg = <0x80000000 0x20000000>;
};
};
status
status 屬性是和設備狀態有關的,status 屬性值是字符串
&uart1 {
status = "disabled";
};
status 可選的狀態如下:
| 值 | 含義 |
|---|---|
| “okay” | 表明設備是可操作的。 |
| “disabled” | 表明設備當前是不可操作的,但是在未來可以變為可操作的 |
| “fail” | 發生了嚴重錯誤,需修復 |
| “fail-sss” | 發生了嚴重錯誤,需修復;sss表示錯誤信息 |
phandle
該屬性可以為節點指定一個全局唯一的數字標識符。這個標識符可以被需要引用該節點的另一個節點使用。舉例來說,現有一個中斷控制器:
pic@10000000 {
phandle = <1>;
interrupt-controller;
};
還有一個可以產生中斷的設備,且這個設備的中斷信號線連接到了上述中斷控制器,為了描述清楚這種關系,該設備的設備節點就需要引用中斷控制器的節點:
another-device-node {
interrupt-parent = <1>; /* 數字1就唯一標識了節點pic@10000000 */
};
interrupt-controller
這是一個沒有值的屬性,用在中斷控制器的設備節點中,以表明這個節點描述的是一個中斷控制器。
interrupt-parent
該屬性用於可以產生中斷,且中斷信號連接到某中斷控制器的設備的設備節點,用於表示該設備的中斷信號連接到了哪個中斷控制器。該屬性的值通常是中斷控制器設備節點的數字標識(phandle),具體示例在上文已經出現過了。
reg
reg屬性描述了設備資源在其父總線定義的地址空間內的地址。reg 屬性的值一般是(address,length)對,該屬性使用一對或多對(地址,長度)來描述設備所占的地址空間。至於地址和長度使用多少個cell來表示呢?這取決於父節點的#address-cells、#size-cells屬性的值。
舉個例子,當:
#address-cells = <1>;
#size-cells = <1>;
那么reg = <0x3000 0x20 0xFE00 0x100>,表示該屬性所屬的設備占據了兩塊內存空間,第一塊是以0x3000為起始的32字節內存塊;第二塊是以0xFE00為起始的256字節內存塊。
/aliases
/aliases節點應當作為根節點的孩子節點,用於定義一個或多個別名屬性,每條別名屬性會為一個設備節點的路徑名設置一個別名,別名即為別名屬性的屬性名,屬性值則是設備節點的路徑名。如下面這個例子所示:
aliases { serial0 = "/simple-bus@fe000000/serial@llc500"; ethernet0 = "/simple-bus@fe000000/ethernet@31c000"; };
/chosen
/chosen節點應當用作根節點的孩子節點,有以下可選屬性:
- bootargs
- stdout-path
- stdin-path
顧名思義,該節點可以指定啟動參數、標准輸出和標准輸入,一個例子如下:
/ {
......
chosen {
bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";
};
......
};
向節點追加或修改內容
要向節點中追加或修改節點中的屬性,可以對節點使用引用的方式來處理。
比如原始的i2c1節點是:
i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "disabled";
};
要對i2c1節點做屬性的追加和修改,使用如下方式:
&i2c1 {
/* 要追加或修改的內容 */
};
&i2c1 表示使用引用的方式訪問 i2c1 這個 label 所對應的節點
修改后的設備樹代碼如下:
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
mag3110@0e {
compatible = "fsl,mag3110";
reg = <0x0e>;
position = <2>;
};
fxls8471@1e {
compatible = "fsl,fxls8471";
reg = <0x1e>;
position = <0>;
interrupt-parent = <&gpio5>;
interrupts = <0 8>;
};
};
其中:
clock-frequency 就是新添加的屬性。
status 屬性的值由原來的 disabled 改為 okay
mag3110 和 fxls8471 是新增加的2個子節點
編寫設備樹
在DTS文件中包含其他文件
編寫設備樹文件時,我們通常會把多種設備的共性抽出來,寫在DTSI文件(后綴為.dtsi)中,其語法與DTS文件一樣。比如,多款使用了am335x的板子,因為使用了同一款SoC,描述設備時肯定會有一些相同的部分,可以把這部分抽出來,寫到am335x.dtsi中,然后在具體的某型板子的設備樹中包含相應的DTSI文件,包含的方式有:
/include/ “xxx.dtsi”
#include “xxx.dtsi”
設備樹編譯器還支持c語言的頭文件,因此,如果有需要可以定義一些宏並在設備樹文件中使用。
如何在設備樹文件中描述設備
設備樹寫出來是給驅動程序看的,也就是說驅動程序怎么寫的,相應的設備樹就該怎么寫;或者反過來,先約定好設備樹怎么寫,在相應的設計驅動。驅動和設備樹有着對應的關系,這種對應關系也被稱為bindings。具體的:
對於上游芯片廠商,應當按照devicetree-specification推薦的設備樹寫法,遵守各種約定,確定好如何規范的描述設備,並提供相應的驅動程序。devicetree-specification-v0.3的第四章給出了一些推薦的做法。
對於下游產品廠商,當使用芯片廠商的芯片做產品時,芯片廠商通常會提供驅動程序和設備樹文件編寫的參考文檔,這些文檔位於linux內核源碼樹的Documentation/devicetree/bindings目錄下。如果芯片廠商沒提供相應文檔的話,就要讀驅動的源碼,知道驅動怎么寫的,自然也就知道如何寫設備樹了。
