Device Tree (DTS) 簡介
Device Tree 的存在,是為了把在 Platform Device 架構中,用來描述硬體資源的部份給抽出來,因為 Platform Device 的檔案是用 C 語言描述的,但概念上,硬體資源的描述應和程式無關也與 Linux 版本無關。
用 C 去描述的話,只要重新定義硬體資源就需要重新編譯核心,很浪費時間,如果不熟 C 語言,也無法修改,因為可能會導致編譯失敗。
Device Tree架構,解決了上述這些問題,不用懂 C 語言也能定義硬體資源,如此硬體工程師也可以做了,因為 pin 腳的功能,硬體工程師最清楚。
硬體資源: 包含 SoC-level (.dtsi)及 board level (.dts)
描述 CPU、RAM size。
描述 Memory-mapped 上的元件、PWM、UART、I2C、SPI 等晶片內部元件的描述,如暫存器位址及大小,中斷號碼、DMA、pinctrl。
描述外部連接裝置,如裝置規格,以 flash 來說,包含 page size、flash size 等資訊,通常寫 device driver 會從這裡取得裝置資訊。
外部裝置通常使用 device tree overlay (產生.dtbo) 的方式而不要直接修改.dts。
Device Tree 的優點:
1. 不用重新 compile source code 就可以更改系統的 configuration。
2. 當硬體只有小小的更改,只需要 dts 檔小改、然後再重新編譯出 dtb 即可。
3. 可以重複使用已存在的 dts 檔,也可以覆蓋過去定義的功能。
4. 從 C 語言中分離出來,使得硬體的描述架構更清楚。
dts vs. dtc vs. dtb
.dts(Device Tree Source) 檔案是一種 ASCII 文字格式的 Device Tree 描述,文字格式非常人性化,適合人類的閱讀習慣。
基本上,在 ARM Linux,一個 .dts 檔案對應一個 ARM 的 machine,一般放置在核心的 arch/arm64/boot/dts/ 目錄。
由於一個 SoC 可能對應多個 machines(一個 SoC 可以對應多個產品和電路板),這些 .dts 檔案可能會包含許多共同的部分,Linux 核心為了簡化,把 SoC 公用的部分或者多個 machines 共同的部分一般提煉為 .dtsi,類似於 C 語言的標頭檔案。其他的 machines 對應的 .dts 就 include 這些 .dtsi。
DTC(device tree compiler) 是將 .dts 編譯為 .dtb 的工具。
DTC 的原始碼位於核心的 scripts/dtc 目錄,在 Linux 核心採用 Device Tree 功能的情況下,編譯核心的時候,主機工具 dtc 會被編譯出來,對應 scripts/dtc/Makefile 中的 “hostprogs-y := dtc” 這一 hostprogs 編譯 target。
在 Linux 核心的 arch/arm64/boot/dts/Makefile 中,描述了當某種 SoC 被選中後,哪些 .dtb 檔案會被編譯出來。
.dtb(Device Tree Blob) 是 .dts 被 DTC 編譯後的二進位制格式的 Device Tree 描述,可由 Linux 核心解析。
通常在我們為電路板製作 NAND、SD 啟動 image 時,會為 .dtb 檔案單獨留下一個很小的區域以存放,之後 bootloader 在引導 kernel 的過程中,會先讀取該 .dtb 到記憶體。
為了驗証 DTS 是否有修改正確,可以將 dtb 反編譯成 dts 來做確認,指令為:
$ dtc -I dtb -O dts dtb.dtb -o dts.dts
DTS 中相關符號的含義
/ - 根節點。
@ - 如果設備有地址,則由此符號指定。
& - 引用節點。
: - 冒號前的label是為了方便引用給節點起的別名,此 label 的一般引用方式為 &label
, - 屬性名稱中可以包含逗號。如 compatible 屬性的名字 組成方式為 ”[manufacturer], [model]” ,加入廠商名是為了避免重名。自定義屬性名中通常也要有廠商名,並以逗號分隔。
# - #並不表示注釋。如 #address-cells ,#size-cells 用來決定 reg 屬性的格式。
- 空屬性並不一定表示沒有賦值。如 interrupt-controller 一個空屬性用來聲明這個 node 接收中斷信號數據類型
”” - 引號中的為字符串,字符串數組:”strint1”,”string2”,”string3”
< > - 尖括號中的為 32位整數字,整數組 <12 3 4>
[ ] - 方括號中的為 32位十六進制數,十六機制數據 [0x11 0x12 0x13] 其中 0x 可省略
可尋址的設備
可尋址的設備使用如下信息來在 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 字段的長度。
舉例來說:
/ {
#address-cells = <0x2>; // 在 root node 下使用 2 個 u32 來代表 address
#size-cells = <0x2>; // 在 root node 下使用 2 個 u32 來代表 size
...
memory { // memory device
...
reg = <0x90000000 00000000 0x800000 00000000>;
// 0x90000000 00000000 是存取 memory 的 address
// 0x800000 00000000 是 memory 的 size
...
};
...
}
I2C slave address
在 Device Tree 中,要指定 device 的 I2C slave address,通常在 device 的 datasheet 會有說明 default I2C slave address,在 reg 欄位中指定。
範例如下,此 device 的 I2C slave address 為 0x23 (7-bit):
&i2c2 {
pinctrl-0 = <&i2c2_pins>;
pinctrl-names = "default";
status = "okay";
tca9554_led@23 {
reg = <0x23>;
status = "okay";
compatible = "tca9554_led";
};
};
通常來說,I2C device 後面接 @ 的值,會與 reg 指定的值相同。
如何從執行中的檔案系統截取出 DTS
在已執行的系統中,其檔案系統格式的 DTS 位於:
/sys/firmware/devicetree/base/
而 /proc/device-tree 只是一個 soft link 連結到上述的位置。
另一個為 DTB 格式的 DTS 則是位於:
/sys/firmware/fdt
FDT 全名為 Flat device tree。
知道 DTS 在檔案系統的具體位置及格式後,要轉換成 DTS 就簡單了,同樣是透過執行 dtc 指令來進行轉換。
將 檔案系統格式的 DTS 轉換為 DTS 檔案的指令為:
$ dtc -I fs -O dts /proc/device-tree -o device_tree.dts
將 FDT 轉換為 DTS 檔案的指令為:
$ dtc -I dtb -O dts /sys/firmware/fdt -o device_tree.dts
如何動態調整 DTS
在 Linux Kernel 載入之前,可以在 U-boot 階段透過 fdt 的指令來調整 DTS。
使用 fdt 指令的第一個步驟,需要先指定 dts 在 memory 的位址,後續 fdt 指令才能對依此位置的 DTS 進行讀取、修改、刪除…等的操作。
U-Boot> fdt addr ${fdtaddr}
更多 fdt 相關的用法,可以在 u-boot 下,執行 fdt 後進行查詢。
U-Boot> fdt
Reference
Linux DTS(Device Tree Source)设备树详解之二(dts匹配及发挥作用的流程篇)
device tree --- #address-cells and #size-cells property
文字內容 或 影像內容 部份參考、引用自網路,如有侵權,請告知,謝謝。