在上一節迅為IMX6ULL開發板已經對DTS的語法做了比較詳細的介紹,在本節中根據前面講解的語法,從頭到尾編寫一個小型的設備樹文件。我們會以一個虛擬的設備作為參考,提前假設一些外部設備和功能。當然這個虛擬的設備沒有任何的意思,只是為了復習掌握前面學習的設備樹語法。在實際產品的開發過程中,我們不需要從頭編寫一個dts設備樹文件,一般都是使用soc廠商提供的dts文件,我們只需要根據自己的實際情況修改添加自己的內容即可。 下面這個假設的設備,制造商為“Acme”,并命名為“Coyote's Revenge”,具體功能如下: l 處理器本地總線連接到內存映射的串行口、spi 總線控制器、i2c 控制器、中斷控制器和外部總線橋 l 256MB SDRAM起始地址為0 l 兩個串口起始地址:0x101F1000和0x101F2000 l GPIO控制器起始地址:0x101F3000 l 帶有一下設備的SPI控制器起始地址:0x10170000 n MMC插槽的SS管腳連接至GPIO #1 l 外部總線橋掛載一下設備 n SMC SMC91111 以太網,起始地址:0x10100000 l i2c控制器起始地址:0x10160000,并掛載一下設備 n Maxim DS1338實時時鐘,響應至從地址11010000(0x58) n 64MB NOR閃存起始地址:0x30000000 1、初始結構 第一步就是要給這個虛擬的設備構建一個基本結構,這是一個有效的設備樹的最基本的結構,在這個階段需要唯一的標識該設備: / { compatible = "acme,coyotes-revenge"; }; 2、添加CPU處理器 接著就是描述每個CPU了,先添加一個名為“cpus”的容器節點,然后為每個CPU分別添加子節點,具體到我們的情況就是一個ARM的雙核Cortex A9系統。 / { compatible = "acme,coyotes-revenge"; cpus { cpu@0 { compatible = "arm,cortex-a9"; }; cpu@1 { compatible = "arm,cortex-a9"; }; }; }; 1、添加設備 系統中的每個設備都表示為一個設備樹節點,所以接下來就應該為這個設備樹填充設備節點,我們先簡單的形成一個框架,設備節點中的某些屬性在后面不斷添加。 / { compatible = "acme,coyotes-revenge"; cpus { cpu@0 { compatible = "arm,cortex-a9"; }; cpu@1 { compatible = "arm,cortex-a9"; }; }; serial@101F0000 { compatible = "arm,pl011"; }; serial@101F2000 { compatible = "arm,pl011"; }; gpio@101F3000 { compatible = "arm,pl061"; }; interrupt-controller@10140000 { compatible = "arm,pl190"; }; spi@10115000 { compatible = "arm,pl022"; }; external-bus { ethernet@0,0 { compatible = "smc,smc91c111"; }; i2c@1,0 { compatible = "acme,a1234-i2c-bus"; rtc@58 { compatible = "maxim,ds1338"; }; }; flash@2,0 { compatible = "samsung,k8f1315ebm", "cfi-flash"; }; }; }; 在此樹中,已經為系統中的每個設備添加了節點,而且這個層次結構也反映了設備與系統的連接方式。例如,外部總線上的設備就是外部總線節點的子節點,i2c 設備就是 i2c 總線節點的子節點。通常,這個層次結構表現的是 CPU 視角的系統視圖。 2、CPU編址 現在這個樹還是無效的,因為他缺少關于設備之間的關聯信息,接下來我們添加這些屬性信息。 cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { compatible = "arm,cortex-a9"; reg = <0>; }; cpu@1 { compatible = "arm,cortex-a9"; reg = <1>; }; }; 在 cpu 節點中,#address-cells 設置為 1,#size-cells 設置為 0。這意味著子節點的 reg 值是一個單一的 uint32,這是一個不包含大小字段的地址,為這兩個 cpu 分配的地址是 0 和 1。cpu 節點的 #size-cells 為 0 是因為只為每個 cpu 分配一個單獨的地址。 3、內存映射設備 與 cpu 節點里單一地址值不同,應該分配給內存映射設備一個地址范圍。#size-cells 聲明每個子節點的 reg 元組中長度字段的大小。在接下來的例子中,每個地址值是 1 cell(32 位),每個長度值也是 1 cell,這是典型的 32 位系統。 / { #address-cells = <1>; #size-cells = <1>; ... serial@101f0000 { compatible = "arm,pl011"; reg = <0x101f0000 0x1000 >; }; serial@101f2000 { compatible = "arm,pl011"; reg = <0x101f2000 0x1000 >; }; gpio@101f3000 { compatible = "arm,pl061"; reg = <0x101f3000 0x1000 0x101f4000 0x0010>; }; interrupt-controller@10140000 { compatible = "arm,pl190"; reg = <0x10140000 0x1000 >; }; spi@10115000 { compatible = "arm,pl022"; reg = <0x10115000 0x1000 >; }; ... }; 一些掛在總線上的設備有不同的編址方案。例如一個帶獨立片選線的設備也可以連接至外部總線。由于父節點會為其子節點定義地址域,所以可以選擇不同的地址映射來最恰當的描述該系統。 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 0x4000000>; }; }; 外部總線的地址值使用了兩個 cell,一個用于片選號;另一個則用于片選基址的偏移量。而長度字段則還是單個 cell,這是因為只有地址的偏移部分才需要一個范圍量。所以,在這個例子中,每個 reg 項都有三個 cell:片選號、偏移量和長度。 4、非內存映射設備 其他的設備沒有被映射到處理機總線上。雖然這些設備可以有一個地址范圍,但他們并不是由 CPU 直接訪問。取而代之的是,父設備的驅動程序會代表 CPU 執行簡介訪問。 以 i2c 設備為例,每個設備都分配了一個地址,但并沒有與之關聯的長度或范圍信息。這看起來和 CPU 的地址分配很像: 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>; }; };
5、地址轉換 前面描述了如何給設備分配地址,但目前來說這些地址還只是設備節點的本地地址,最終要將這些地址映射成 CPU 可使用的地址 根節點始終描述的是 CPU 視角的地址空間。根節點的子節點已經使用的是 CPU 的地址域,所以它們不需要任何直接映射。例如,serial@101f0000 設備就是直接分配的 0x101f0000 地址。 那些非根節點直接子節點的節點就沒有使用 CPU 地址域。為了得到一個內存映射地址,設備樹必須指定從一個域到另一個域地址轉換地方法,而 ranges 屬性就為此而生。 下面就是一個添加了 ranges 屬性的示例設備樹。 / { compatible = "acme,coyotes-revenge"; #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 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 被翻譯為: 從片選 0 開始的偏移量 0 被映射為地址范圍: 0x10100000..0x1010ffff 從片選 0 開始的偏移量 1 被映射為地址范圍: 0x10160000..0x1016ffff 從片選 0 開始的偏移量 2 被映射為地址范圍: 0x30000000..0x10000000 另外,如果父地址空間和子地址空間是相同的,那么該節點可以添加一個空的 range 屬性。一個空的 range 屬性意味著子地址將被 1:1 映射到父地址空間。 8、添加中斷 與遵循樹的自然結構而進行的地址轉換不同,機器上的任何設備都可以發起和終止中斷信號。另外地址的編址也不同于中斷信號,前者是設備樹的自然表示,而后者者表現為獨立于設備樹結構的節點之間的鏈接。描述中斷連接需要四個屬性: ■ interrupt-controller -一個空的屬性定義該節點作為一個接收中斷信號的設備。 ■ #interrupt-cells - 這是一個中斷控制器節點的屬性。它聲明了該中斷控制器的中斷指示符中 cell 的個數(類似于 #address-cells 和 #size-cells)。 ■ interrupt-parent -這是一個設備節點的屬性,包含一個指向該設備連接的中斷控制器的 phandle。那些沒有 interrupt-parent 的節點則從它們的父節點中繼承該屬性。 ■ interrupts - 一個設備節點屬性,包含一個中斷指示符的列表,對應于該設備上的每個中斷輸出信號。 中斷指示符是一個或多個 cell 的數據(由 #interrupt-cells 指定),這些數據指定了該設備連接至哪些輸入中斷。在以下的例子中,大部分設備都只有一個輸出中斷,但也有可能在一個設備上有多個輸出中斷。一個中斷指示符的意義完全取決于與中斷控制器設備的 binding。每個中斷控制器可以決定使用幾個 cell 來唯一的定義一個輸入中斷。 / { compatible = "acme,coyotes-revenge"; #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@101f0000 { compatible = "arm,pl011"; 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。 ■ 中斷控制器節點上添加了‘inc:’標簽,該標簽用于給根節點的 interrupt-parent 屬性分配一個 phandle。這個 interrupt-parent 將成為本系統的默認值,因為所有的子節點都將繼承它,除非顯示覆寫這個屬性。 ■每個設備使用 interrupts 屬性來不同的中斷輸入線。 ■ #interrupt-cells 是 2,所以每個中斷指示符都有 2 個 cell。本例使用一種通用的模式,也就是用第一個 cell 來編碼中斷線號;然后用第二個 cell 編碼標志位,比如高電平/低電平有效,或者邊緣/水平觸發。對于任何給定的中斷控制器,請參考該控制器的 binding 文檔以了解指示符如何編碼。 經過上面幾步后,一個較為完整的設備樹文件就形成了,當然這個設備樹文件只是作為一個實驗,讓我們在熟悉一遍關于設備樹的知識,對于不同平臺的設備,設備樹文件會有所不同,需要根據具體的soc平臺手冊具體修改。
|