Device Tree常用方法解析
Device Tree在Linux內(nèi)核驅(qū)動中的使用源于2011年3月17日Linus Torvalds在ARM Linux郵件列表中的一封郵件,他宣稱“this whole ARM thing is a f*cking pain in the ass”,并提倡學(xué)習(xí)PowerPC等其他架構(gòu)已經(jīng)成熟使用的Device Tree技術(shù)。自此,Device Tree正式進(jìn)入ARM社區(qū)的視野中。
1. 作用
Device Tree是一種用來描述硬件的數(shù)據(jù)結(jié)構(gòu),類似板級描述語言,起源于OpenFirmware(OF)。在目前廣泛使用的Linux kernel 2.6.x版本中,對于不同平臺、不同硬件,往往存在著大量的不同的、移植性差的板級描述代碼,以達(dá)到對這些不同平臺和不同硬件特殊適配的需求。但是過多的平臺、過的的不同硬件導(dǎo)致了這樣的代碼越來越多,最終引發(fā)了Linux創(chuàng)始人Linus的不滿,以及強(qiáng)烈呼吁改變。Device Tree的引入給驅(qū)動適配帶來了很大的方便,一套完整的Device Tree可以將一個(gè)PCB擺在你眼前。Device
Tree可以描述CPU,可以描述時(shí)鐘、中斷控制器、IO控制器、SPI總線控制器、I2C控制器、存儲設(shè)備等任何現(xiàn)有驅(qū)動單位。對具體器件能夠描述到使用哪個(gè)中斷,內(nèi)存映射空間是多少等等。
2. 基本數(shù)據(jù)格式
Device Tree由節(jié)點(diǎn)和屬性構(gòu)成。屬性為key-value對,節(jié)點(diǎn)包括了各種屬性,也可以包含子節(jié)點(diǎn)。下邊列舉一個(gè)簡單的dts文件:
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
a-byte-data-property = [0x01 0x23 0x34 0x56];
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 {
};
};
};
這個(gè)文件實(shí)際上沒有任何意義,但卻包含了基本所有要素:
- 1 唯一的根節(jié)點(diǎn) “/”
- 2 一些節(jié)點(diǎn):node1 node2
- 3 子節(jié)點(diǎn) node1的子節(jié)點(diǎn)child-node1和child-node2
- 4 一群分散的屬性
屬性都是簡單的key-value對,其中value也可以是空的或包含任意的byte流。以下是一些屬性的基本數(shù)據(jù)結(jié)構(gòu):
-
1 雙引號包含的字符信息
string-property = "a string";
-
2 cells單位信息是32位無符號整型數(shù)據(jù)
cell-property = <0xFF01 412 0x12341283>;
-
3 二進(jìn)制數(shù)據(jù)流
binary-property = [0x01 0x02 0x03 0x04];
-
4 混合數(shù)據(jù)用逗號隔開
mixed-property = "a string", [0x01 0x02 0x03 0x04], <0xFF01 412 0x12341283>;
-
5 字符列表
string-list = "string test1", "string test2";
3. 一些基本概念
- 每個(gè)完整的dts文件必須擁有一個(gè)根節(jié)點(diǎn)
- dtsi文件一般為通用文件(類似C語言的頭文件),可被其他文件include
后邊的名字涵蓋的范圍更加廣泛,如果可以匹配到,同樣會以這個(gè)dts為基礎(chǔ)進(jìn)行初始化并啟動。 - 父節(jié)點(diǎn)名應(yīng)該取類型名,而不是IC名。節(jié)點(diǎn)名的命名規(guī)則一般是 [name]@[address],也可以只有name而沒有@之后的內(nèi)容,但是要確保name不能重名。如果加了@以及地址,那么name可以相同,只要address不同即可。
- 每一個(gè)設(shè)備節(jié)點(diǎn)都要有一個(gè)compatible屬性
-
compatible的內(nèi)容是用來匹配驅(qū)動的,組成方式為"[manufacturer], [model]",加入廠商名是為了避免重名。有的時(shí)候后邊還會跟一個(gè)名字,如:
compatible = "acme,coyotes-revenge", "acmd-board";
4. 工作方式
a. 地址
設(shè)備的地址特性根據(jù)一下幾個(gè)屬性來控制:
- reg
- #address-cells
- #size-cells
reg意為region,區(qū)域。格式為:
reg = <address1 length1 [address2 length2] [address3 length3]>;
父類的address-cells和size-cells決定了子類的相關(guān)屬性要包含多少個(gè)cell,如果子節(jié)點(diǎn)有特殊需求的話,可以自己再定義,這樣就可以擺脫父節(jié)點(diǎn)的控制。
address-cells決定了address1/2/3包含幾個(gè)cell,size-cells決定了length1/2/3包含了幾個(gè)cell。本地模塊例如:
spi@10115000 {
compatible = "arm,pl022";
reg = <0x10115000 0x1000 >;
};
位于0x10115000的SPI設(shè)備申請地址空間,起始地址為0x10115000,長度為0x1000,即屬于這個(gè)SPI設(shè)備的地址范圍是0x10115000~0x10116000。
實(shí)際應(yīng)用中,有另外一種情況,就是通過外部芯片片選激活模塊。例如,掛載在外部總線上,需要通過片選線工作的一些模塊:
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";
#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>;
};
};
external-bus使用兩個(gè)cell來描述地址,一個(gè)是片選序號,另一個(gè)是片選序號上的偏移量。而地址空間長度依然用一個(gè)cell來描述。所以以上的子設(shè)備們都需要3個(gè)cell來描述地址空間屬性——片選、偏移量、地址長度。在上個(gè)例子中,有一個(gè)例外,就是i2c控制器模塊下的rtc模塊。因?yàn)镮2C設(shè)備只是被分配在一個(gè)地址上,不需要其他任何空間,所以只需要一個(gè)address的cell就可以描述完整,不需要size-cells。
當(dāng)需要描述的設(shè)備不是本地設(shè)備時(shí),就需要描述一個(gè)從設(shè)備地址空間到CPU地址空間的映射關(guān)系,這里就需要用到ranges屬性。還是以上邊的external-bus舉例:
#address-cells = <1>;
#size-cells = <1>;
...
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
};
ranges屬性為一個(gè)地址轉(zhuǎn)換表。表中的每一行都包含了子地址、父地址、在自地址空間內(nèi)的區(qū)域大小。他們的大?。ò腸ell)分別由子節(jié)點(diǎn)的address-cells的值、父節(jié)點(diǎn)的address-cells的值和子節(jié)點(diǎn)的size-cells來決定。以第一行為例:
- 0 0 兩個(gè)cell,由子節(jié)點(diǎn)external-bus的address-cells=<2>決定;
- 0x10100000 一個(gè)cell,由父節(jié)點(diǎn)的address-cells=<1>決定;
- 0x10000 一個(gè)cell,由子節(jié)點(diǎn)external-bus的size-cells=<1>決定。
最終第一行說明的意思就是:片選0,偏移0(選中了網(wǎng)卡),被映射到CPU地址空間的0x10100000~0x10110000中,地址長度為0x10000。
b. 中斷
描述中斷連接需要四個(gè)屬性:
1. interrupt-controller 一個(gè)空屬性用來聲明這個(gè)node接收中斷信號;
2. #interrupt-cells 這是中斷控制器節(jié)點(diǎn)的屬性,用來標(biāo)識這個(gè)控制器需要幾個(gè)單位做中斷描述符;
3. interrupt-parent 標(biāo)識此設(shè)備節(jié)點(diǎn)屬于哪一個(gè)中斷控制器,如果沒有設(shè)置這個(gè)屬性,會自動依附父節(jié)點(diǎn)的;
4. interrupts 一個(gè)中斷標(biāo)識符列表,表示每一個(gè)中斷輸出信號。
如果有兩個(gè),第一個(gè)是中斷號,第二個(gè)是中斷類型,如高電平、低電平、邊緣觸發(fā)等觸發(fā)特性。對于給定的中斷控制器,應(yīng)該仔細(xì)閱讀相關(guān)文檔來確定其中斷標(biāo)識該如何解析。
c. 其他
除了以上規(guī)則外,也可以自己加一些自定義的屬性和子節(jié)點(diǎn),但是一定要符合以下的幾個(gè)規(guī)則:
- 新的設(shè)備屬性一定要以廠家名字做前綴,這樣就可以避免他們會和當(dāng)前的標(biāo)準(zhǔn)屬性存在命名沖突問題;
- 新加的屬性具體含義以及子節(jié)點(diǎn)必須加以文檔描述,這樣設(shè)備驅(qū)動開發(fā)者就知道怎么解釋這些數(shù)據(jù)了。描述文檔中必須特別說明compatible的value的意義,應(yīng)該有什么屬性,可以有哪個(gè)(些)子節(jié)點(diǎn),以及這代表了什么設(shè)備。每個(gè)獨(dú)立的compatible都應(yīng)該由單獨(dú)的解釋。
- 新添加的這些要發(fā)送到devicetree-discuss@lists.ozlabs.org郵件列表中進(jìn)行review,并且檢查是否會在將來引發(fā)其他的問題。
5. 進(jìn)階例子
pci@0x10180000 {
compatible = "arm,versatile-pci-hostbridge", "pci";
reg = <0x10180000 0x1000>;
interrupts = <8 0>;
bus-ranges = <0 0>;
#address-cells = <3>
#size-cells = <2>;
ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
};
像之前描述過的本地總線一樣,PCI地址空間與CPU地址空間是完全分離的,所以這里需要通過定義ranges屬性進(jìn)行地址轉(zhuǎn)化。
#address-cells定義PCI使用3個(gè)cell,并且PCI的地址范圍通過兩個(gè)單位就可以解讀。所以,首先的問題就是,為什么需要用3個(gè)32位的cell來描述一個(gè)PCI地址。
這三個(gè)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:代表重申請空間標(biāo)志(這里沒有使用)
p:代表預(yù)讀空間(緩存)標(biāo)志
t:別名地址標(biāo)志(這里沒有使用)
ss:空間代碼
00: 設(shè)置空間
01:IO空間
10:32位存儲空間
11:64位存儲空間
bbbbbbbb: PCI總線號。PCI有可能是層次性架構(gòu),所以我們可能需要區(qū)分一些子-總線
ddddd:設(shè)備號,通常由初始化設(shè)備選擇信號IDSEL連接時(shí)申請。
fff:功能序號,有些多功能PCI設(shè)備可能用到。
rrrrrrrr:注冊號,在設(shè)置周期使用。
ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;
回頭再看這個(gè)ranges分表代表了什么。父節(jié)點(diǎn)address-cells為1,子節(jié)點(diǎn)address-cells為3, 子節(jié)點(diǎn)size-cells為2。則第一行可以這樣劃分:
0x42000000 0 0x80000000 子節(jié)點(diǎn)地址| 0x80000000 父節(jié)點(diǎn)地址| 0 0x20000000 地址空間長度|
0x42000000為phys.high,第一位為01000010,則p為1,ss為10,即申請32位存儲空間為緩存空間。phys.mid為0,phys.low為0x80000000,他們共同組成了PCI地址,即表示從PCI總線的0x80000000地址處申請出一個(gè)32位的存儲空間作為緩存。后邊的那個(gè)cell 0x80000000 0 0x20000000代表到CPU空間后的參數(shù),申請的地址被映射到CPU空間的0x80000000地址處,大小共計(jì)0x20000000(512MB)。
相關(guān)資料及引用:
http://blog.csdn.net/21cnbao/article/details/8457546
http:///Device_Tree_Usage
|