關注、星標嵌入式客棧,精彩及時送達
[導讀] 新版的U-Boot以及內核都引入了設備樹,那麼這究竟是棵什麼樣的樹呢?長啥樣?有啥用?爲啥弄個這樣的樹?本文基於對設備樹標準的理解,來學習整理一下相關的要點,供大家參考。
Linux爲啥要設備樹?
在Linux3.x之前的內核源碼中,存在大量對板級細節信息描述的代碼。這些代碼充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目錄,而且更嚴重的問題是,由於ARM商業生態模式,基於ARM IP授權模式,產生越來越多ARM核芯片。如此一來這類辣雞代碼越來越多,維護變得愈加困難。於是在2011年3月17這天,Linux之父Linus Torvalds飆了,郵件中罵到:“this whole ARM thing is a f*cking pain in the ass”。
自此之後,Linux內核引入了設備樹機制以描述計算機板機底層硬件信息。
啥是設備樹?
設備樹(device tree)是一種描述特定計算機的硬件組件的數據結構,以便操作系統的內核或者引導程序可以使用和管理那些組件,包括一個或多個CPU,內存,總線和外圍設備 。爲什麼說這個數據結構是樹呢?
設備樹是通過Open Firmware項目從基於SPARC的工作站和服務器發展而來,由https://www.devicetree.org/組織維護,目前的發展至V0.3版。
來看看設備樹標準中的例子:該圖顯示了一個簡單的設備樹的示例表示,該樹幾乎完整到足以啓動一個簡單的操作系統,描述了平臺類型,CPU,內存和1個UART。看這個圖,正是一種樹形數據結構。
節點
設備樹數據結構,本質上由一系列帶屬性的節點組成,節點由節點名、單元地址以及屬性組成。描述節點的語法爲:
node-name@unit-address
node-name 由1到31個ASCII字符描述,字符可取0-9,a-z,A-Z,逗號(,),點(.),加減號(+ -),以及下劃線(_)組成
unit-address:節點所在的總線類型。
/ :表示樹的根節點
那麼上圖的節點是哪些呢?移除掉屬性簡化一下:
節點的單元地址特定於節點所在的總線類型。單元地址必須與節點的r一般與reg屬性中指定的第一個地址匹配。如果節點不具有reg屬性,則必須省略@ unit-address,並且節點名稱僅會將節點與樹中同一級別的其他節點區分開。特定總線的綁定可以爲reg和unit-address的格式指定其他更具體的要求。
節點名稱,一般按照功能描述進行命令,以提升可讀性,比如:
adc
atm
audio-codec
audio-controller
backlight
bluetooth
bus
.......
路徑名path name
通過指定從根節點到所有後代節點到所需節點的完整路徑,可以唯一標識設備樹中的節點。按照下述約定進行描述:
/node-name-1/node-name-2/node-name-N
比如上圖中CPU1的路徑爲:
/cpus/cpu@1
屬性語法
屬性用於描述節點的特徵,由屬性名及值組成。
屬性名,可由下表中字符組成:
字符 | 描述 |
---|---|
0-9 | 數字 |
a-z A-Z | 大小寫字母 |
, | 逗號 |
. | 點 |
_ | 下劃線 |
+ - | 加減號 |
? | 問號 |
# | #字符 |
值
<empty> 空,無值
<u32> <u64> ,大端16進制數,所謂大端模式就是高字節存在低地址
<string> 以'\0'字符串結尾的字符串
<phandle> 引用設備樹中另一個節點的方法。見標準屬性中舉例。
<stringlist> 字符串表
標準屬性
compatible, 其值爲<stringlist>,兼容的屬性值包含一個或多個字符串,這些字符串定義了設備的編程模型。使用此字符串列表選擇設備驅動程序。該屬性值由具有空終止字符串的串聯列表組成。它們使設備可以表達與一系列類似設備的兼容性,從而可能使單個設備驅動程序與多個設備匹配。比如
compatible = "fsl,mpc8641", "ns16550";
model, <string> 其值爲字符串,用於指定設備製造商的型號。如上例中的mpc8641/ns16550
phandle,<u32>phandle屬性爲設備樹內唯一的節點指定一個數字標識符。phandle屬性值由其他需要引用與該屬性關聯的節點的節點使用。比如:
pic@10000000 {
phandle = <1>;
interrupt-controller;
};
定義的phandle值爲1。另一個設備節點可以使用一個phandle值1引用pic節點:
another-device-node {
interrupt-parent = <1>;
};
status,<string> status屬性指示設備的運行狀態。取值範圍定義如下表:
值 | 描述 |
---|---|
"okay" | 表示設備使能正運行 |
"disabled" | 表示該設備目前禁用,但將來可能會使能 |
"reserved" | 表示該設備正在運行,但不應使用。 |
"fail" | 表示設備無法運行。發現了一個嚴重的錯誤,不進行修理就不可工作。 |
"fail-sss" | 與fail的區別是sss字段表示檢測到的錯誤 |
#address-cells 及 #size-cells,可以在設備樹層次結構中具有子節點的任何設備節點中使用#address-cells和#size-cells屬性,它們描述如何尋址子設備節點。
#address-cells屬性指示在reg屬性中需要多少個單元(即32位值)來形成address字段。
#size-cells屬性定義子節點的reg屬性中size字段需要多少個單元(即32位值)
reg, <prop-encoded-array>, ( address , length ) 對。
virtual-reg,<u32> virtual-reg屬性指定一個有效地址,該地址映射到設備節點的reg屬性中指定的第一個物理地址。此屬性使引導程序可以爲客戶端程序提供已設置的虛擬到物理的映射。
ranges,<empty> 或 <prop-encoded-array>任意數量的(child-bus-address,parent-bus-address,length)三元組。ranges屬性提供了一種定義總線的地址空間(子地址空間)和總線節點的父節點的地址空間(父地址空間)之間的映射或轉換的方法。
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0xe0000000 0x00100000>;
serial@4600 {
device_type = "serial";
compatible = "ns16550";
reg = <0x4600 0x100>;
clock-frequency = <0>;
interrupts = <0xA 0x8>;
interrupt-parent = <&ipic>;
};
};
其中:
<0x0 0xe0000000 0x00100000>;
此屬性值指定對於1024 KB的地址空間範圍,以物理0x0尋址的子節點映射到物理0xe0000000的父地址。通過這種映射,可以通過地址爲0xe0004600的加載或存儲,偏移量0x4600(在reg中指定)和在range中指定的0xe0000000映射來尋址串行設備節點。
dma-ranges,<empty>或<prop-encoded-array>形式的任意數量的(child-bus-address,parent-bus-address,length)三元組。dma-ranges屬性用於描述內存映射總線的直接內存訪問(DMA)結構,該總線可以從源於該總線的DMA操作訪問其設備樹父對象。它提供了一種定義總線的物理地址空間和總線父級的物理地址空間之間的映射或轉換的方法。
child-bus-address是子總線地址空間內的物理地址。表示地址的寬度取決於總線,並且可以從此節點(出現dma-ranges屬性的節點)的#address-cells中確定。
parent-bus-address是父總線地址空間內的物理地址。代表父級地址的cell數量取決於總線,可以從定義父級地址空間的節點的#address-cells屬性中確定。
length指定了子地址空間中範圍的大小。可以從節點(定義了dma-ranges屬性的節點)的size-cells來確定表示大小的cell數。
總結一下
本文總結了Linux設備樹出現的緣由,以及設備樹節點常規概念:節點、屬性,以及標準屬性。設備樹還有中斷以及中斷映射、設備綁定、二進制格式等概念,後續有時間在學習整理。
辛苦原創,如喜歡請點贊/在看/分享,不勝感激!
—END—
往期精彩推薦,點擊即可閱讀