Device Tree

設備樹筆記html

參考資料:http://www.wowotech.net/linux_kenrel/why-dt.htmlnode

 

1、背景linux

  設想一下:bootloader將Linux內核複製到內存中,而後跳到內核的入口點開始執行。此時內核就像運行在處理器上的一個裸機程序。須要配置處理器,設置虛擬內存,向控制檯打印一些信息。可是這些事情如何完成?全部的這些操做都要經過寫寄存器來實現,但Linux內核如何知道這些寄存器的地址?如何知道當前有多少個CPU核可使用?有多少內存能夠訪問?最直接的辦法就是在內核代碼裏爲指定平臺寫好這些代碼,由內核配置參數決定哪些平臺代碼將被啓用。但每一塊ARM芯片都有本身的寄存器地址和不一樣的配置方式以及外設,這致使內核充斥大量垃圾代碼,因此人們但願內核能以某種方式識別硬件並加載驅動,因而設備樹出現了,他用於指明系統所使用的設備及相應的配置信息。數組

  隨着愈來愈多的芯片廠商加入ARM陣營,各個ARM 供應商的SOC 家族的CPU愈來愈多,不一樣廠商的周邊硬件設備又各不相同,加之芯片供應商開發人員爲了更快的開發效率,使得不少SOC 特定的代碼都是經過複製現有代碼並稍做修改就提交到ARM Linux。這致使數據結構

  一、愈來愈多的ARM 平臺相關的代碼被加入到Linux內核, #ifdef充斥在各個源代碼中,讓ARM mach-和plat-目錄下的代碼有些不忍直視。架構

  二、系統充斥大量重複代碼。app

  所以,內核社區成立了一個「ARM 子架構」的團隊,該團隊主要負責協調各個ARM廠商的代碼(not ARM core part),檢查各個子架構維護者提交的代碼,並創建一個ARM 平臺合併樹來維護這些代碼。針對不一樣的SOC共用的IP block(知識產權塊,例如I2C controller),將其驅動代碼從各個arch/arm/mach-xxx中獨立出來,變成通用的模塊移動到kernel/drivers目錄。而如clock control、interrupt control等並非ARM特殊部分,將其驅動放在linux/kernel目錄下,屬於core-Linux-kernel frameworks。此外,對於ARM平臺,須要保存一些和framework交互的代碼,這些代碼叫作ARM SoC core architecture code。總結以下:函數

  一、ARM的核心代碼仍然保存在arch/arm目錄下,ARM SoC core architecture code(與系統內核交互的代碼)也保存在arch/arm目錄下;工具

  二、ARM SOC的周邊外設模塊(如I2C控制器)的驅動保存在drivers目錄下,通用的設備(如中斷控制器)驅動直接集成到系統內核中;佈局

  三、ARM SOC的專有代碼在arch/arm/mach-xxx目錄下;

  四、ARM SOC board specific的代碼被移除,由設備樹機制來負責傳遞硬件拓撲和硬件資源信息。

  本質上,設備樹改變了原來用hardcode方式將硬件配置信息嵌入到內核代碼的方法,改用bootloader傳遞一個DB的形式。對於基於ARM CPU的嵌入式系統,咱們習慣於針對每個平臺進行內核的編譯。可是咱們指望ARM可以像X86那樣用一個kernel image來支持多個平臺。在這種狀況下,若是咱們認爲內核是一個黑盒,那麼其輸入參數應該包括:識別平臺的信息、runtime的配置參數、設備的拓撲結構以及特性。在linux kernel中,設備樹就是爲了把上述的三個參數信息經過bootloader傳遞給kernel,以便kernel能夠有較大的靈活性。

 

 

爲一個外設寫一個設備樹entry(http://blog.csdn.net/klaus_wei/article/details/42915545):

 

一、爲"compatible"賦一個字符串"magicstring",自動生成工具的生成格式通常是:名字+版本。

 

二、在數據手冊裏查看總線上設備的地址分配信息, 寫一條 "reg=" 語句。

 

三、"interrupt-parent=<&gic>"

 

四、中斷號 "interrupt="

 

五、最後加上一些設備的自定義參數

 

 

 

Porting操做系統到硬件平臺:

一、本身撰寫一個bootloader並傳遞適當的參數給kernel。除了傳統的 command line以及tag list之類的,最重要的是申請一個machine type,當拿到屬於本身項目的machine type ID的時候,當時心情雀躍,彷佛本身已是開源社區的一份子了(其實當時是有意願,或者說有目標是想將你們的代碼併入到linux kernel main line的)。

二、在內核的arch/arm目錄下創建mach-xxx目錄,這個目錄下,放入該SOC的相關代碼,例如中斷 controller的代碼,時間相關的代 碼,內存映射,睡眠相關的代碼等等。此外,最重要的是創建一個board specific文件,定義一個machine的宏:

MACHINE_START(project name, "xxx公司的xxx硬件平臺")
    .phys_io    = 0x40000000,
    .boot_params    = 0xa0000100,  
    .io_pg_offst    = (io_p2v(0x40000000) >> 18) & 0xfffc,
    .map_io        = xxx_map_io,
    .init_irq    = xxx_init_irq,
    .timer        = &xxx_timer,
    .init_machine    = xxx_init,
MACHINE_END

在xxx_init函數中,通常會加入不少的platform device。所以,伴隨這個board specific文件中是大量的靜態table,描述了各類硬件設備信息。

三、調通了system level的driver(timer,中斷處理,clock等)以及串口terminal以後,linux kernel基本是能夠起來了,後續各類driver不斷的添加,直到系統軟件支持全部的硬件。

 

2、設備樹

2.1 概念

    若是要使用Device Tree,首先用戶要了解本身的硬件配置和系統運行參數,並把這些信息組織成Device Tree source file。經過DTC(Device Tree Compiler),編譯爲Device Tree binary file(有一個更好聽的名字,DTB,device tree blob)。系統啓動時被加載到內存並將起始地址傳給OS內核。

    另外,設備樹中不用描述全部硬件信息,對於能夠動態探測到的設備沒必要在其中描述,如USB設備、PCI 設備,但usb host controller是沒法動態識別的,PCI bridge若是不能被探測,則須要在device tree中描述。對於同一系列的SOC家族,一般將公共的硬件描述保存在一個單獨的dtsi文件中,方便你們include共用,省去代碼的重複。

2.2 設備樹源文件

    2.2.1 語法

device tree的基本單元是node。這些node被組織成樹狀結構,除了root node,每一個node都只有一個parent。一個device tree文件中只能有一個root node。每一個node中包含了若干的property/value來描述該node的一些特性。在linux kernel中,擴展名是dts的文件就是描述硬件信息的device tree source file,在dts文件中,一個node被定義成:

[label:] node-name[@unit-address] {

   [properties definitions]

   [child nodes]

}

    說明:

    []表示可選項;

label方便在dts文件中引用;

每一個node用節點名字(node name)標識,節點名字的格式是node-name@unit-address;@unit-address的格式和設備掛在哪一個bus上相關,如cpu,其unit-address就是從0開始編址,如以太網控制器,其unit-address就是寄存器地址,若是該node沒有reg屬性(尋址需求屬性),那麼該節點名字中必須不能包括@和unit-address。root node的node name是肯定的,必須是「/」。

屬性(property)值標識了設備的特性,它的值(value)是多種多樣的:

一、多是空,也就是沒有值的定義。

二、多是一個u3二、u64的數值(值得一提的是cell這個術語,在Device Tree表示32bit的信息單位)。例如#address-cells = <1> 。固然,多是一個數組。例如<0x00000000 0x00000000 0x00000000 0x20000000>

三、多是一個字符串。例如device_type = "memory" ,固然也多是一個string list。例如"PowerPC,970"

    child node的格式和node是徹底同樣的。

    2.2.2 節點和屬性

根節點/

節點

屬性

屬性說明

節點說明

 

#address-cells

#是數量的意思,#address-cells屬性用來描述sub node中的reg屬性的地址域特性,也就是說須要用多少個u32的cell來描述該地址域。

若是節點中包含了有尋址需求reg的子節點,則須要定義這兩個屬性,

#size-cells

Compatible

該屬性的值是string list,定義了一系列的modle(每一個string是一個model)。這些字符串列表被操做系統用來選擇用哪個driver來驅動該設備。

model屬性指明瞭該設備屬於哪一個設備生產商的哪個model。通常model賦值「生產商,模型(系列)」 。對於root node,compatible屬性用來匹配machine type,對於普通的HW block的節點,如中斷控制器,屬性被用來匹配適合的driver。

 

interrupt-parent

用於標識能產生中斷的設備鏈接到哪一個中斷控制器

 

chosen { }

bootargs

傳遞命令行參數

描述由系統firmware指定的runtime parameter。若是存在chosen這個node,其parent node必須是根節點。

initrd-start

傳遞initrd的開始地址

aliases { }

 

 

定義了一些別名,方便引用節點時省寫完整路徑

memory { }

device_type

對於memory node,其device_type必須爲memory。

是全部設備樹文件的必備節點,它定義了系統物理內存的佈局

reg屬性定義了訪問該device node的地址信息,

該屬性的值被解析成任意長度的(address,size)數組, address和size在其父節點中定義(#address-cells和#size-cells)。對於device node,reg描述了memory-mapped IO register的offset和length。對於memory node,定義了該memory的起始地址和長度。

interrupt-controller @4a000000{}

#interrupt-cells

用多少個u32(即cells)來標識一個interrupt source

中斷控制器節點,其中包含屬性值。4a000000表示中斷控制器寄存器的起始地址

Serial @50000000{}

interrupts

對於一個能產生中斷的設備,必須定義interrupts這個屬性。也能夠定義interrupt-parent這個屬性,若是不定義,則繼承其parent node的interrupt-parent屬性。

 

status

 

 

 

 

 

Cpus{}

 

對於cpus node,#address-cells 是1,而#size-cells是0。

對於根節點,必須有一個cpus的child node來描述系統中的CPU信息。

2.3 設備樹二進制文件

    設備樹二進制文件的組織格式以下:

   

    說明:

1 DTB header其各個成員解釋以下:

header field name

description

magic

用來識別DTB的。經過這個magic,kernel能夠肯定bootloader傳遞的參數block是一個DTB仍是tag list。

totalsize

DTB的total size

off_dt_struct

device tree structure block的offset

off_dt_strings

device tree strings block的offset

off_mem_rsvmap

offset to memory reserve map。有些系統,咱們也許會保留一些memory有特殊用途(例如DTB或者initrd image),或者在有些DSP+ARM的SOC platform上,有寫memory被保留用於ARM和DSP進行信息交互。這些保留內存不會進入內存管理系統。

version

該DTB的版本。

last_comp_version

兼容版本信息

boot_cpuid_phys

咱們在哪個CPU(用ID標識)上booting

dt_strings_size

device tree strings block的size。和off_dt_strings一塊兒肯定了strings block在內存中的位置

dt_struct_size

device tree structure block的size。和和off_dt_struct一塊兒肯定了device tree structure block在內存中的位置

三、 memory reserve map的格式描述

這個區域包括了若干的reserve memory描述符。每一個reserve memory描述符是由address和size組成。其中address和size都是用U64來描述。

 

四、device tree structure block的格式描述

device tree structure block區域是由若干的分片組成,每一個分片開始位置都是保存了token,以此來描述該分片的屬性和內容。共計有5種token:

(1)FDT_BEGIN_NODE (0x00000001)。該token描述了一個node的開始位置,緊挨着該token的就是node name(包括unit address)

(2)FDT_END_NODE (0x00000002)。該token描述了一個node的結束位置。

(3)FDT_PROP (0x00000003)。該token描述了一個property的開始位置,該token以後是兩個u32的數據,分別是length和name offset。length表示該property value data的size。name offset表示該屬性字符串在device tree strings block的偏移值。length和name offset以後就是長度爲length具體的屬性值數據。

(4)FDT_NOP (0x00000004)。

(5)FDT_END (0x00000009)。該token標識了一個DTB的結束位置。

一個可能的DTB的結構以下:

(1)若干個FDT_NOP(可選)

(2)FDT_BEGIN_NODE

              node name

              paddings

(3)若干屬性定義。

(4)若干子節點定義。(被FDT_BEGIN_NODE和FDT_END_NODE包圍)

(5)若干個FDT_NOP(可選)

(6)FDT_END_NODE

(7)FDT_END

 

五、device tree strings bloc的格式描述

device tree strings bloc定義了各個node中使用的屬性的字符串表。因爲不少屬性會出如今多個node中,所以,全部的屬性字符串組成了一個string block。這樣能夠壓縮DTB的size。

2.4 設備樹數據流

    一、在ARM的彙編啓動代碼中,定義了兩個變量_machine_rach_type(保存了機器類型ID)、_atags_pointer(保存了設備樹/標籤列表指針);

    二、上電後,引導加載程序被加載到內存(ARM沒有BIOS這類固件程序),在將控制權交給內核時,將設備樹指針傳給內核;

    三、將DTB轉換爲樹狀結構,節點用一個結構體標識;

    四、掃描DTB,獲取chosen節點的bootargs、initrd屬性的值,並保存在全局變量中,其中保存了一些系統參數;

    五、內核根據機器類型ID掃描機器描述符列表(機器描述符在編譯時被保存在一個特殊段,並使用一個數據結構標識),肯定機器描述符。

 

3、代碼分析

請參考http://www.wowotech.net/linux_kenrel/dt-code-analysis.html

相關文章
相關標籤/搜索