Linux-DTS基礎

設備樹使用手冊

 

  1. Device Tree是一種描述硬件的數據結構,由一系列被命名的結點(node)和屬性(property)組成,而結點本身可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中):CPU的數量和類別,內存基地址和大小,總線和橋,外設連接,中斷控制器和中斷使用情況,GPIO控制器和GPIO使用情況,Clock控制器和Clock使用情況。

  2. 通常由.dts文件以文本方式對系統設備樹進行描述,經過Device Tree Compiler(dtc)將dts文件轉換成二進制文件binary device tree blob(dtb).dtb文件可由Linux內核解析,有了device tree就可以在不改動Linux內核的情況下,對不同的平臺實現無差異的支持,只需更換相應的dts文件,即可滿足,當然這樣會增加內核的體積。

設備樹的存儲格式:

簡單的說,設備樹是一種描述硬件配置的樹形數據結構, 有且僅有一個根節點[4]。它包含了有關CPU、物理內存、總線、串口、PHY以及其他外圍設備信息等。該樹繼承了Open Firmware IEEE 1275設備樹的定義。操作系統能夠在啓動時對此結構進行語法分析,以此配置內核,加載相應的驅動。 
  U-Boot需要將扁平設備樹在內存地址傳給內核。該樹主要由三大部分組成:頭(Header)、結構塊(Structure block)、字符串塊(Strings block)。在內存中分配圖如下:
  1.  ------------------------------
  2. base -> | struct boot_param_header |
  3.         ------------------------------
  4.         | (alignment gap) (*) |
  5.         ------------------------------
  6.         | memory reserve map |
  7.         ------------------------------
  8.         | (alignment gap) |
  9.         ------------------------------
  10.         | |
  11.         | device-tree structure |
  12.         | |
  13.         ------------------------------
  14.         | (alignment gap) |
  15.         ------------------------------
  16.         | |
  17.         | device-tree strings |
  18.         | |
  19. -----> ------------------------------
  20. |
  21. |
    1. --- (base + totalsize)
  1. <1> 頭(header)
  2. 頭主要描述設備樹的一些基本信息,例如設備樹大小,結構塊偏移地址,字符串塊偏移地址等。偏移地址是相對於設備樹頭的起始地址計算的。
  3. struct boot_param_header {
  4.     __be32 magic;                //設備樹魔數,固定爲0xd00dfeed
  5.     __be32 totalsize;            //整個設備樹的大小
  6.     __be32 off_dt_struct;        //保存結構塊在整個設備樹中的偏移
  7.     __be32 off_dt_strings;        //保存的字符串塊在設備樹中的偏移
  8.     __be32 off_mem_rsvmap;        //保留內存區,該區保留了不能被內核動態分配的內存空間
  9.     __be32 version;            //設備樹版本
  10.     __be32 last_comp_version;    //向下兼容版本號
  11.     __be32 boot_cpuid_phys;    //爲在多核處理器中用於啓動的主cpu的物理id
  12.     __be32 dt_strings_size;    //字符串塊大小
  13.     __be32 dt_struct_size;     //結構塊大小
  14. };

  1. <2> 結構塊(struct block)

  1. 設備樹結構塊是一個線性化的結構體,是設備樹的主體,以節點node的形式保存了目標單板上的設備信息。
  2. 在結構塊中以宏OF_DT_BEGIN_NODE標誌一個節點的開始,以宏OF_DT_END_NODE標識一個節點的結束,整個結構塊以宏OF_DT_END結束。一個節點主要由以下幾部分組成。
  3. (1)節點開始標誌:一般爲OF_DT_BEGIN_NODE。
  4. (2)節點路徑或者節點的單元名(version<3以節點路徑表示,version>=0x10以節點單元名錶示)
  5. (3)填充字段(對齊到四字節)
  6. (4)節點屬性。每個屬性以宏OF_DT_PROP開始,後面依次爲屬性值的字節長度(4字節)、屬性名稱在字符串塊中的偏移量(4字節)、屬性值和填充(對齊到四字節)。
  7. (5)如果存在子節點,則定義子節點。
  8. (6)節點結束標誌OF_DT_END_NODE。

  1. <3>字符串塊

  1. 通過節點的定義知道節點都有若干屬性,而不同的節點的屬性又有大量相同的屬性名稱,因此將這些屬性名稱提取出一張表,當節點需要應用某個屬性名稱時直接在屬性名字段保存該屬性名稱在字符串塊中的偏移量。

  1. <4> 設備樹源碼 DTS 表示

  1. 設備樹源碼文件(.dts)以可讀可編輯的文本形式描述系統硬件配置設備樹,支持 C/C++方式的註釋,該結構有一個唯一的根節點“/,每個節點都有自己的名字並可以包含多個子節點。設備樹的數據格式遵循了 Open Firmware IEEE standard 1275。這個設備樹中有很多節點,每個節點都指定了節點單元名稱。每一個屬性後面都給出相應的值。以雙引號引出的內容爲 ASCII 字符串,以尖括號給出的是 32 位的16進制值。這個樹結構是啓動 Linux 內核所需節點和屬性簡化後的集合,包括了根節點的基本模式信息、CPU 和物理內存佈局,它還包括通過/chosen 節點傳遞給內核的命令行參數信息。

  1. <5> machine_desc結構

  1. 內核提供了一個重要的結構體struct machine_desc ,這個結構體在內核移植中起到相當重要的作用,內核通過machine_desc結構體來控制系統體系架構相關部分的初始化。machine_desc結構體通過MACHINE_START宏來初始化,在代碼中, 通過在start_kernel->setup_arch中調用setup_machine_fdt來獲取。
  2. struct machine_desc {
  3.     unsigned int nr; /* architecture number */
  4.     const char *name; /* architecture name */
  5.     unsigned long atag_offset; /* tagged list (relative) */
  6.     const char *const *dt_compat; /* array of device tree* 'compatible' strings */
  7.     unsigned int nr_irqs; /* number of IRQs */
  8.     
  9. #ifdef CONFIG_ZONE_DMA
  10.     phys_addr_t dma_zone_size; /* size of DMA-able area */
  11. #endif
  12.     
  13.     unsigned int video_start; /* start of video RAM */
  14.     unsigned int video_end; /* end of video RAM */
  15.     
  16.     unsigned char reserve_lp0 :1; /* never has lp0 */
  17.     unsigned char reserve_lp1 :1; /* never has lp1 */
  18.     unsigned char reserve_lp2 :1; /* never has lp2 */
  19.     enum reboot_mode reboot_mode; /* default restart mode */
  20.     struct smp_operations *smp; /* SMP operations */
  21.     bool (*smp_init)(void);
  22.     void (*fixup)(struct tag *, char **,struct meminfo *);
  23.     void (*init_meminfo)(void);
  24.     void (*reserve)(void);/* reserve mem blocks */
  25.     void (*map_io)(void);/* IO mapping function */
  26.     void (*init_early)(void);
  27.     void (*init_irq)(void);
  28.     void (*init_time)(void);
  29.     void (*init_machine)(void);
  30.     void (*init_late)(void);
  31. #ifdef CONFIG_MULTI_IRQ_HANDLER
  32.     void (*handle_irq)(struct pt_regs *);
  33. #endif
  34.     void (*restart)(enum reboot_mode, const char *);
  35. };

  1. <6>設備節點結構體

  1. struct device_node {
  2.     const char *name;    //設備name
  3.     const char *type; //設備類型
  4.     phandle phandle;
  5.     const char *full_name; //設備全稱,包括父設備名

  6.     struct property *properties; //設備屬性鏈表
  7.     struct property *deadprops; //removed properties
  8.     struct device_node *parent; //指向父節點
  9.     struct device_node *child; //指向子節點
  10.     struct device_node *sibling; //指向兄弟節點
  11.     struct device_node *next; //相同設備類型的下一個節點
  12.     struct device_node *allnext; //next in list of all nodes
  13.     struct proc_dir_entry *pde; //該節點對應的proc
  14.     struct kref kref;
  15.     unsigned long _flags;
  16.     void *data;
  17. #if defined(CONFIG_SPARC)
  18.     const char *path_component_name;
  19.     unsigned int unique_id;
  20.     struct of_irq_controller *irq_trans;
  21. #endif
  22. };

  1. <7>屬性結構體

  1. struct property {
  2.     char *name;        //屬性名
  3.     int length;        //屬性值長度
  4.     void *value;        //屬性值
  5.     struct property *next; //指向下一個屬性
  6.     unsigned long _flags; //標誌
  7.     unsigned int unique_id;
  8. };




本文將介紹如何爲一個新機器編寫設備樹。我們準備提供一個有關設備樹概念的概述和如何使用這些設備樹來描述一個機器。完整的設備樹數據格式的技術說明書請參考 ePAPR 規範。ePAPR 規範涵蓋了比本文基本主題更豐富的細節,要查閱本文沒有涉及到的高級用法請參考該規範。

 目錄

1. 基本數據格式  

2. 基本概念

2.1 模型機

2.2 初始結構

2.3 中央處理器

2.4 節點名稱

2.5 設備

2.6 理解 compatible 屬性

3. 如何編址

3.1 CPU 編址

3.2 內存映射設備

3.3 非內存映射設備

3.4 範圍(地址轉換)

4. 中斷的工作方式

5. 設備特定數據

6. 特殊的節點

6.1 aliases 節點

6.2 chosen 節點

7. 高級主題

7.1 高級模型機

7.2 PCI  主橋

7.2.1 PCI 總線編號

7.2.2 PCI 地址轉換

7.3 高級中斷映射

8. 附註

 

 基本數據格式

設備樹是一個包含節點和屬性的簡單樹狀結構。屬性就是鍵-值對,而節點可以同時包含屬性和子節點。例如,以下就是一個 .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 {

        };

    };

};

 

這棵樹顯然是沒什麼用的,因爲它並沒有描述任何東西,但它確實體現

了節點的一些屬性:

 

一個單獨的根節點:“/

兩個子節點:“node1”和“node2

兩個 node1 的子節點:“child-node1”和“child-node2

一堆分散在樹裏的屬性。

 

屬性是簡單的鍵-值對,它的值可以爲空或者包含一個任意字節流。雖

然數據類型並沒有編碼進數據結構,但在設備樹源文件中任有幾個基本的數據表示形式。

 

 文本字符串(無結束符)可以用雙引號表示:

string-property = "a string"

 Cells’是32位無符號整數,用尖括號限定:

cell-property = <0xbeef 123 0xabcd1234>

二進制數據用方括號限定:

binary-property = [0x01 0x23 0x45 0x67];

不同表示形式的數據可以使用逗號連在一起:

mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

 逗號也可用於創建字符串列表:
string-list = "red fish", "blue fish";

 

                   基本概念

 

我們將以一個簡單機開始,然後通過一步步的建立一個描述這個簡單機

的設備樹,來了解如何使用設備樹。

2.1模型機

考慮下面這個假想的機器(大致基於ARM Versatile),製造商爲“Acme”,

並命名爲“Coyote's Revenge”:

 

  一個 32 位 ARM CPU    

  處理器本地總線連接到內存映射的串行口、spi 總線控制器、i2c 控制器、中斷控

制器和外部總線橋

   256MB SDRAM 起始地址爲 0

 兩個串口起始地址:0x101F1000 和 0x101F2000

  GPIO 控制器起始地址:0x101F3000

  帶有以下設備的 SPI 控制器起始地址:0x10170000

      MMC 插槽的 SS 管腳連接至 GPIO #1

  外部總線橋掛載以下設備

      SMC SMC91111 以太網設備連接到外部總線,起始地址:0x10100000

      i2c 控制器起始地址:0x10160000,並掛載以下設備

          Maxim DS1338 實時時鐘。響應至從地址 1101000 (0x58)

       64MB NOR 閃存起始地址 0x30000000

 

2.2初始結構

第一步就是要爲這個模型機構建一個基本結構,這是一個有效的設備樹最基本的結構。在這個階段你需要唯一的標識該機器。

/ {

    compatible = "acme,coyotes-revenge";

};

compatible 指定了系統的名稱。它包含了一個“<製造商>,<型號>”形式

的字符串。重要的是要指定一個確切的設備,並且包括製造商的名子,以避免命名空間衝突。由於操作系統會使用 compatible 的值來決定如何在機器上運行,所以正確的設置這個屬性變得非常重要。理論上講,兼容性(compatible)就是操作系統需要的所有數據都唯一標識一個機器。如果機器的所有細節都是硬編碼的,那麼操作系統則可以在頂層的compatible屬性中具體查看“acme,coyotes-revenge”。

 

2.3中央處理器

接下來就應該描述每個 CPU 了。先添加一個名爲“cpus”的容器節點,然後爲每個CPU分別添加子節點。具體到我們的情況是一個ARM的 雙核Cortex A9系統。

 

/ {

    compatible = "acme,coyotes-revenge";

 

    cpus {

        cpu@0 {

            compatible = "arm,cortex-a9";

        };

        cpu@1 {

            compatible = "arm,cortex-a9";

        };

    };

};

 

每個 cpu 節點的compatible屬性是一個“<製造商>,<型號>”形式的字符串,並指定了確切的cpu,就像頂層的compatible屬性一樣。稍後將會有更多的屬性添加進cpu節點,但我們先得討論一些更過的基本概念。

 

2.4節點名稱

現在應該花點時間來討論命名約定了。每個節點必須有一個“<名稱>[@<

設備地址>]”形式的名字。 <名稱> 就是一個不超過31位的簡單 ascii 字符串。通常,節點的命名應該根據它所體現的是什麼樣的設備。比如一個 3com 以太網適配器的節點就應該命名爲 ethernet,而不應該是 3com509。

如果該節點描述的設備有一個地址的話就還應該加上設備地址(unit-address)。通常,設備地址就是用來訪問該設備的主地址,並且該地址也在節點的 reg 屬性中列出。本文檔中我們將在稍後涉及到 reg 屬性。同級節點命名必須是唯一的,但只要地址不同,多個節點也可以使用一樣的通用名稱(例如 serial@101f1000 和 serial@101f2000)。關於節點命名的更多細節請參考 ePAPR 規範 2.2.1 節。

2.5設備

系統中每個設備都表示爲一個設備樹節點。所以接下來就應該爲這個設備樹填充設備節點。現在,知道我們討論如何進行尋址和中斷請求如何處理之前這些新節點將一直爲空。

 

/ {

    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 視角的系統視圖。

現在這棵樹還是無效的,因爲它缺少關於設備之間互聯的信息。稍後將添加這些信息。

在這顆樹中,應該注意這些事情:

  每個設備節點都擁有一個 compatible 屬性。

  閃存(flash)節點的 compatible 屬性由兩個字符串構成。欲知爲何,請閱讀下一節。

  正如前面所述,節點的命名應當反映設備的類型而不是特定的型號。請查閱 ePAPR 規範第 2.2.2 節裏定義的通用節點名,應當優先使用這些節點名。

 

2.6理解 compatible 屬性

樹中每個表示一個設備的節點都需要一個 compatible 屬性。compatible 屬性是操作系統用來決定使用哪個設備驅動來綁定到一個設備上的關鍵因素。

compatible 是一個字符串列表,之中第一個字符串指定了這個節點所表示的確切的設備,該字符串的格式爲:"<製造商>,<型號>"。剩下的字符串的則表示其它與之相兼容的設備。

例如,Freescale MPC8349 片上系統(SoC)擁有一個實現了美國國家半導體 ns16550 的寄存器接口的串行設備,那麼 MPC8349 的串行設備的 compatible 屬性就應該是:compatible = "fsl,mpc8349-uart", "ns16550"。在這裏,mpc8349-uart 指定了確切的設備,而 ns16550 則說明這是與美國國家半導體 ns16550 UART 的寄存器級兼容。

注:ns16550 並沒有製造商前綴,這僅僅是歷史原因造成的。所有的新 compatible 值都應該使用製造商前綴。

這種做法可以使現有的設備驅動能夠綁定到新設備上,並仍然唯一的指定確切的設備。

警告:不要使用帶通配符的 compatible 值,比如“fsl,mpc83xx-uart”或類似情況。芯片提供商無不會做出一些能夠輕易打破你通配符猜想的變化,這時候在修改已經爲時已晚了。相反,應該選擇一個特定的芯片然後是所有後續芯片都與之兼容。


如何編址

 

可編址設備使用以下屬性將地址信息編碼進設備樹:

  reg

  #address-cells

 #size-cells

 

每個可編址設備都有一個元組列表的 reg,元組的形式爲:reg = <地址1長度1 [地址長度2] [地址長度3] ... >。每個元組都表示一個該設備使用的地址範圍。每個地址值是一個或多個 32 位整型數列表,稱爲cell。同樣,長度值也可以是一個cell列表或者爲空。

由於地址和長度字段都是可變大小的變量,那麼父節點的 #address-cells #size-cells屬性就用來聲明各個字段的cell的數量。換句話說,正確解釋一個reg屬性需要用到父節點的#address-cells#size-cells的值。要知道這一切是如何運作的,我們將給模型機添加編址屬性,就從CPU開始。

3.1  CPU 編址

CPU 節點表示了一個關於編址的最簡單的例子。每個 CPU 都分配了一個唯一的 ID,並且沒有 CPU id 相關的大小信息。

    

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 分配一個單獨的地址。

你可能還會注意到 reg 的值和節點名字是相同的。按照慣例,如果一個節點有 reg 屬性,那麼該節點的名字就必須包含設備地址,這個設備地址就是 reg 屬性裏第一個地址值。

 

3.2  內存映射設備

與 cpu 節點裏單一地址值不同,應該分配給內存映射設備一個地址範圍。#size-cells 聲明每個子節點的 reg 元組中長度字段的大小。在接下來的例子中,每個地址值是 1 cell(32 位),每個長度值也是 1 cell,這是典型的 32 位系統。64 位的機器則可以使用值爲 2 的 #address-cells 和 #size-cells 來獲得在設備樹中的 64 位編址。

 

/ {

    #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 >;

    };

 

    ...

};

 

每個設備都被分配了一個基址以及該區域的大小。這個例子中爲 GPIO 分配了兩個地址範圍:0x101f3000...0x101f3fff 和 0x101f4000..0x101f400f。

一些掛在總線上的設備有不同的編址方案。例如一個帶獨立片選線的設備也可以連接至外部總線。由於父節點會爲其子節點定義地址域,所以可以選擇不同的地址映射來最恰當的描述該系統。下面的代碼展示了設備連接至外部總線並將其片選號編碼進地址的地址分配。

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:片選號、偏移量和長度。

由於地址域是包含於一個節點及其子節點的,所以父節點可以自由的定義任何對於該總線來說有意義的編址方案。那些在直接父節點和子節點以外的節點通常不關心本地地址域,而地址應該從一個域映射到另一個域。

3.3  非內存映射設備

其他的設備沒有被映射到處理機總線上。雖然這些設備可以有一個地址範圍,但他們並不是由 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>;

            };

        };

 

3.4  範圍(地址轉換)

我們已經討論瞭如何給設備分配地址,但目前來說這些地址還只是設備節點的本地地址,我們還沒有描述如何將這些地址映射成 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 是一個地址轉換列表。ranges 表中的每一項都是一個包含子地址、父地址和在子地址空間中區域大小的元組。每個字段的值都取決於子節點的 #address-cells 、父節點的 #address-cells 和子節點的 #size-cells。以本例中的外部總線來說,子地址是 2 cell、父地址是 1 cell、區域大小也是 1 cell。那麼三個 ranges 被翻譯爲:

從片選 0 開始的偏移量 0 被映射爲地址範圍:

0x10100000..0x1010ffff

從片選 0 開始的偏移量 1 被映射爲地址範圍:

0x10160000..0x1016ffff

從片選 0 開始的偏移量 2 被映射爲地址範圍:

0x30000000..0x10000000

另外,如果父地址空間和子地址空間是相同的,那麼該節點可以添加一個空的 range 屬性。一個空的 range 屬性意味着子地址將被 1:1 映射到父地址空間。

你有可能會問當全都可以設計成 1:1 映射的時候爲何還要使用地址轉換。答案就是,有一些具有完全不同地址空間的總線(比如 PCI),而它們的細節需要暴露給操作系統。另外一些帶有 DMA 引擎的設備需要知道總線上的真實地址。有時有需要將設備組合到一塊,因爲他們共享相同的軟件可編程物理地址映射。是否應該使用 1:1 映射在很大程度上取決於來自操作系統的信息以及硬件設計。

你還應該注意到在 i2c@1,0 節點中並沒有 range 屬性。不同於外部總線,這裏的原因是 i2c 總線上的設備並沒有被內存映射到 CPU 的地址域。相反,CPU 將通過 i2c@1,0 設備間接訪問 rtc@58 設備。缺少 ranges 屬性意味着這個設備將不能被出他的父設備之外的任何設備直接訪問。

 

中斷如何工作

與遵循樹的自然結構而進行的地址轉換不同,機器上的任何設備都可以發起和終止中斷信號。另外地址的編址也不同於中斷信號,前者是設備樹的自然表示,而後者者表現爲獨立於設備樹結構的節點之間的鏈接。描述中斷連接需要四個屬性

  interrupt-controller -一個空的屬性定義該節點作爲一個接收中斷信號的設備

  #interrupt-cells - 這是一箇中斷控制器節點的屬性。它聲明瞭該中斷控制器的中斷指示符中 cell 的個數(類似於 #address-cells 和 #size-cells)。

■  interrupt-parent -這是一個設備節點的屬性,包含一個指向該設備連接的中斷控制器的 phandle。那些沒有 interrupt-parent 的節點則從它們的父節點中繼承該屬性。

  interrupts - 一個設備節點屬性,包含一箇中斷指示符的列表,對應於該設備上的每個中斷輸出信號。

 

中斷指示符是一個或多個 cell 的數據(由 #interrupt-cells 指定),這些數據指定了該設備連接至哪些輸入中斷。在以下的例子中,大部分設備都只有一個輸出中斷,但也有可能在一個設備上有多個輸出中斷。一箇中斷指示符的意義完全取決於與中斷控制器設備的 binding。每個中斷控制器可以決定使用幾個 cell 來唯一的定義一個輸入中斷。

下面的代碼爲我們 Coyote's Revenge 模型機添加了中斷連接:

 

/ {

    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 文檔以瞭解指示符如何編碼。


設備特定數據

 

除了通用屬性以外,一個節點中可以添加任何屬性和子節點。只要遵循一些規則,可以添加任何操作系統所需要的數據。

首先,新的設備特定屬性的名字都應該使用製造商前綴,以避免和現有標準屬性名相沖突。

其次,屬性和子節點的含義必須存檔在 binding 文檔中,以便設備驅動程序的程序員知道如何解釋這些數據。一個 binding 記錄了一個特定 compatible 值的意義、應該包含什麼樣的屬性、有可能包含那些子節點、以及它代表了什麼樣的設備。每個特別的 compatible 值都應該有一個它自己的 binding(或者要求與其他 compatible 值兼容)。新設備的 binding 存檔在本 wiki 中。請查看主頁上的文檔格式描述和審覈流程。

第三,使用郵件列表 [email protected] 發送新的 binding 以進行審覈。新 binding 的審覈可以捕獲很多可能在以後導致問題的常見錯誤。

  

特殊節點

6.1  aliases 節點

引用一個特定的節點通常使用全路徑

如 /external-bus/ethernet@0,0,但當用戶真真想知道的只是“那個設備是 eth0?”時,這樣的全路徑就變得很冗長。這時,aliases 節點就可以用於指定一個設備全路徑的別名。例如:

    aliases {

        ethernet0 = ð0;

        serial0 = &serial0;

    };

當給一個設備分配一個識別符是操作系統將非常樂意使用別名。

在這裏你會發現一個新語法。property = &label;,將作爲字符串屬性並通過引用標籤來指定一個節點的全路徑。這與之前的 phandle = < &label >; 形式不同,這是把一個 phandle 值插入進一個 cell。

6.2  chosen 節點

chosen 節點並不代表一個真正的設備,只是作爲一個爲固件和操作系統之間傳遞數據的地方,比如引導參數。chosen 節點裏的數據也不代表硬件。通常,chosen 節點在 .dts 源文件中爲空,並在啓動時填充。

在我們的示例系統中,固件可以往 chosen 節點添加以下信息:

    chosen {

        bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200";

    };


 

高級主題

7.1  高級模型機

現在,我們已經掌握了基本的定義,接下來讓我們往模型機裏添加一些硬件,以討論一些更復雜的用例。

高級模型機添加了一個 PCI 主橋,其控制寄存器映射到內存 0x10180000,並且 BARs 編程至以地址 0x80000000 爲起始。

既然關於設備樹我們已經有所瞭解了,那麼我們就從以下所示新增加的節點來介紹 PCI 主橋。

        pci@10180000 {

            compatible = "arm,versatile-pci-hostbridge", "pci";

            reg = <0x10180000 0x1000>;

            interrupts = <8 0>;

        };

7.2  PCI  主橋

本節介紹 Host/PCI 橋節點。

注,本節將假定讀者瞭解 PCI 的一些基本知識。本文並不是 PCI 教程,想要了解更深入的信息,請閱讀 [1]。

你也可以參考 ePAPR 或 PCI Bus Binding to Open Firmware(http://playground.sun.com/1275/bindings/pci/pci2_1.pdf)。還可以訪問 http://devicetree.org/MPC5200:PCI,這裏可以找到 Freescale MPC5200 的一個完整工作的例子。

7.2.1  PCI 總線編號

每個 PCI 總線段都是唯一編號的,並且總線的編號是通過使用 bus-ranges 屬性在 pci 節點中暴露出來的,這個屬性有兩個 cell。第一個 cell 給出分配給該節點的總線號;第二個 cell 給出任何次級 PCI 總線最大總線號。

模型機只有一個 pci 總線,所以兩個 cell 都是 0。

        pci@0x10180000 {

            compatible = "arm,versatile-pci-hostbridge", "pci";

            reg = <0x10180000 0x1000>;

            interrupts = <8 0>;

            bus-ranges = <0 0>;

        };

7.2.2  PCI 地址轉換

類似於前面所描述的本地總線,PCI 地址空間和 CPU 地址空間是完全分離的,所以需要一個從 PCI 地址到 CPU 地址的轉換。同樣,完成這樣的轉換將使用 ranges、#address-cells 和 #size-cells 屬性

        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 地址)使用 3 個 cell,同時 PCI rangs 被編碼爲 2 個 cell。那麼第一個問題就可能是,爲什麼我們要用三個 32 位 cell 去指定一個 PCI 地址?這三個 cell 分別標記了 phys.hi、phys.mid 和 phys.low[2]。

  phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr

  phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh

■ phys.low cell: llllllll llllllll llllllll llllllll

 

PCI 地址是 64 位的,並編碼進了 phys.mid 和 phys.low。然而真正有意思的是在 phys.high 裏棉面,這是一個位域。

 

  n:重定位區域標誌(在這裏不起作用)

  p:預取(可緩存)區標誌

 t:地址別名標誌(在這裏不起作用)

  ss:空間代碼

      00:配置空間

      01:I/O 空間

      10:32 位內存空間

      11:64 位內存空間

  bbbbbbbb:PCI 總線號。PCI 可以是分層結構,所以我們可能有 PCI/PCI 橋,這可以定義子總線。

  ddddd:設備號,通常與 IDSEL 型號相關聯。

  fff:功能號。用於多功能 PCI 設備。

 rrrrrrrr:寄存器號,用於配置週期。

對於 PCI 地址轉換來說,p 和 ss 是最重要的字段。在 phys.hi 裏的 p 和 ss 的值決定了訪問哪個 PCI 地址空間。因此,通過查找 ranges 屬性,我們將得到三個區域。

 以 PCI 地址 0x80000000 開始的一個 512 MByte 32 位預取存儲區,該區域將映射到主機 CPU 地址 0x80000000。

 以 PCI 地址 0xa0000000 開始的一個 265 MByte 32 位非預取存儲區,該區域將映射到主機 CPU 地址 0xa0000000。

■ 以 PCI 地址 0x00000000 開始的一個 16 MByte I/O 區,該區域將映射到主機 CPU 地址 0xb0000000。

 

爲阻止這些工作,phys.hi 位域的存在就意味着操作系統必須知道該節點代表了一個 PCI 橋,這樣操作系統才能爲了地址轉換而忽略那些不相關的字段。爲了判斷應該掩碼哪些額外的字段,操作系統需要在 PCI 總線節點中尋找“pci”字符串。

 

7.3  高級中斷映射

現在我們來到了最有趣的部分,PCI 中斷映射。一個 PCI 設備可以使用引線 #INTA、#INTB、#INTC 和 #INTD 來觸發中斷。如果我們沒有多功能 PCI 設備,那麼設備中斷必須使用 #INTA。然而,每個 PCI 插槽或設備通常會連接到中斷控制器上不同的輸入端。所以設備樹需要一種能將各個 PCI 中斷信號映射到中斷控制器的途徑。#interrupt-cells、interrupt-map 和 interrupt-map-mask 屬性就被用來描述這個中斷映射。

這裏所描述的中斷映射並不僅僅侷限於 PCI 總線,事實上,任何節點都可以指定複雜的中斷映射,但 PCI 是最常見的情況。

        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>;

 

            #interrupt-cells = <1>;

            interrupt-map-mask = <0xf800 0 0 7>;

            interrupt-map = <0xc000 0 0 1 &intc  9 3 // 1st slot

                             0xc000 0 0 2 &intc 10 3

                             0xc000 0 0 3 &intc 11 3

                             0xc000 0 0 4 &intc 12 3

 

                             0xc800 0 0 1 &intc 10 3 // 2nd slot

                             0xc800 0 0 2 &intc 11 3

                             0xc800 0 0 3 &intc 12 3

                             0xc800 0 0 4 &intc  9 3>;

        };

 

首先你會發現,PCI 中斷號只使用了一個 cell,不像系統中斷控制器,它使用兩個 cell,一個用於中斷號,另一個用於標誌。PCI 中斷只使用了一個 cell,因爲 PCI 中斷確定爲始終是低電平觸發

在這個示例板上,我們有 2 個分別包含 4 箇中斷線的 PCI 插槽,所以我們需要映射 8 箇中斷線到中斷控制器上。這已經在 interrupt-map 屬性中完成了。關於中斷映射的具體步驟請參考 [3]。

因爲要區分單一 PCI 總線上的若干 PCI 設備中斷號(#INA 等)是不夠用的,所以我們還需要指出是哪個 PCI 設備觸發了中斷線。幸運的是我們還可以使用每個設備所擁有的唯一設備號。爲了區分這些 PCI 設備,我們需要一個元組,該元組由 PCI 設備號和 PCI 中斷號組成。通俗的說,我們構造了由四個 cell 組成的設備中斷指示符

  三個 #address-cells 由 phys.hi、phys.mid、phys.low 組成,然後

  一個 #interrupt-cell(#INTA、#INTB、#INTC、#INTD)

 

因爲我們只需要 PCI 地址中的設備號部分,所以 interrupt-map-mask 發揮了作用。interrupt-map-mask 也是 4 元組,就像設備中斷指示符一樣。掩碼的第一部分指出我們應該考慮設備中斷指示符中哪一部分。在本例中,我們可以看到在 phys.hi 中只需要設備號部分,另外我們還需要 3 位來區分四個中斷線(PCI 中斷線是從 1 開始計數的,不是 0!)。

現在。我們可以構建 interrupt-map 屬性了。該屬性是一個表,這個表的每一項都由一個子(PCI 總線)設備中斷指示符、一個父句柄(用於中斷服務的中斷控制器)和一個父設備中斷指示符組成。因此,在第一行中我們可以知道 PCI 中斷 #INTA 將被映射到中斷控制器的 IRQ 9,並且是低電平有效。[4]

目前爲止,唯一沒有討論的就是 PCI 總線設備中斷指示符裏古怪的數字了。來自 phys.hi 位域的設備號是設備中斷指示符中的重要組成部分。設備號是平臺特定的,並取決於 PCI 主控制器如何激活各個設備的 IDSEL 管腳。在本例中,PCI slot 1 分配設備 id 24(0x18),PCI slot 2 分配設備 id 25(0x19)。每個 slot 的 phys.hi 值是通過將設備號左移 11 位至位域的 ddddd 段得到的,就像下面:

  slot 1 的 phys.hi 就是 0xC000, 並且

 slot 2 的 phys.hi 就是 0xC800。

把這些放在一起之後,interrupt-map 屬性就顯示爲:

  在主中斷控制器上 slot 1 的 #INTA 是 IRQ9,低電平觸發

  在主中斷控制器上 slot 1 的 #INTB 是 IRQ10,低電平觸發

  在主中斷控制器上 slot 1 的 #INTC 是 IRQ11,低電平觸發

  在主中斷控制器上 slot 1 的 #INTD 是 IRQ12,低電平觸發

  在主中斷控制器上 slot 2 的 #INTA 是 IRQ10,低電平觸發

  在主中斷控制器上 slot 2 的 #INTA 是 IRQ11,低電平觸發

  在主中斷控制器上 slot 2 的 #INTA 是 IRQ12,低電平觸發

在主中斷控制器上 slot 2 的 #INTA 是 IRQ9,低電平觸發

 

屬性 interrupts = <8 0>; 描述了主控制器或 PCI 橋控制器本身有可能觸發中斷。不要與 PCI 設備觸發的中斷(使用 INTA,INTB,...)告混了。

最後需要注意的事。就像 interrupt-parent 屬性一樣,節點中 interrupt-map 屬性的存在將改變子節點和孫節點的默認中斷控制器。在這個 PCI 示例中,這意味着 PCI 主橋變成了默認中斷控制器。如果一個通過 PCI 總線連接的設備同時還直接連接至另一箇中斷控制器,這時就需要指定它自己的 interrupt-parent 屬性。

 

附註

[1] Tom Shanley / Don Anderson: PCI System Architecture. Mindshare Inc.

[2] PCI Bus Bindings to Open Firmware.

[3] Open Firmware Recommended Practice: Interrupt Mapping

[4] PCI 中斷總是低電平觸發。

    

 

 

QQ交流:EMMC-UFS Community 581375017,歡迎加入!


設備樹使用手冊

 

  1. Device Tree是一種描述硬件的數據結構,由一系列被命名的結點(node)和屬性(property)組成,而結點本身可包含子結點。所謂屬性,其實就是成對出現的name和value。在Device Tree中,可描述的信息包括(原先這些信息大多被hard code到kernel中):CPU的數量和類別,內存基地址和大小,總線和橋,外設連接,中斷控制器和中斷使用情況,GPIO控制器和GPIO使用情況,Clock控制器和Clock使用情況。

  2. 通常由.dts文件以文本方式對系統設備樹進行描述,經過Device Tree Compiler(dtc)將dts文件轉換成二進制文件binary device tree blob(dtb).dtb文件可由Linux內核解析,有了device tree就可以在不改動Linux內核的情況下,對不同的平臺實現無差異的支持,只需更換相應的dts文件,即可滿足,當然這樣會增加內核的體積。

設備樹的存儲格式:

簡單的說,設備樹是一種描述硬件配置的樹形數據結構, 有且僅有一個根節點[4]。它包含了有關CPU、物理內存、總線、串口、PHY以及其他外圍設備信息等。該樹繼承了Open Firmware IEEE 1275設備樹的定義。操作系統能夠在啓動時對此結構進行語法分析,以此配置內核,加載相應的驅動。 
  U-Boot需要將扁平設備樹在內存地址傳給內核。該樹主要由三大部分組成:頭(Header)、結構塊(Structure block)、字符串塊(Strings block)。在內存中分配圖如下:
  1.  ------------------------------
  2. base -> | struct boot_param_header |
  3.         ------------------------------
  4.         | (alignment gap) (*) |
  5.         ------------------------------
  6.         | memory reserve map |
  7.         ------------------------------
  8.         | (alignment gap) |
  9.         ------------------------------
  10.         | |
  11.         | device-tree structure |
  12.         | |
  13.         ------------------------------
  14.         | (alignment gap) |
  15.         ------------------------------
  16.         | |
  17.         | device-tree strings |
  18.         | |
  19. -----> ------------------------------
  20. |
  21. |
    1. --- (base + totalsize)
  1. <1> 頭(header)
  2. 頭主要描述設備樹的一些基本信息,例如設備樹大小,結構塊偏移地址,字符串塊偏移地址等。偏移地址是相對於設備樹頭的起始地址計算的。
  3. struct boot_param_header {
  4.     __be32 magic;                //設備樹魔數,固定爲0xd00dfeed
  5.     __be32 totalsize;            //整個設備樹的大小
  6.     __be32 off_dt_struct;        //保存結構塊在整個設備樹中的偏移
  7.     __be32 off_dt_strings;        //保存的字符串塊在設備樹中的偏移
  8.     __be32 off_mem_rsvmap;        //保留內存區,該區保留了不能被內核動態分配的內存空間
  9.     __be32 version;            //設備樹版本
  10.     __be32 last_comp_version;    //向下兼容版本號
  11.     __be32 boot_cpuid_phys;    //爲在多核處理器中用於啓動的主cpu的物理id
  12.     __be32 dt_strings_size;    //字符串塊大小
  13.     __be32 dt_struct_size;     //結構塊大小
  14. };

  1. <2> 結構塊(struct block)

  1. 設備樹結構塊是一個線性化的結構體,是設備樹的主體,以節點node的形式保存了目標單板上的設備信息。
  2. 在結構塊中以宏OF_DT_BEGIN_NODE標誌一個節點的開始,以宏OF_DT_END_NODE標識一個節點的結束,整個結構塊以宏OF_DT_END結束。一個節點主要由以下幾部分組成。
  3. (1)節點開始標誌:一般爲OF_DT_BEGIN_NODE。
  4. (2)節點路徑或者節點的單元名(version<3以節點路徑表示,version>=0x10以節點單元名錶示)
  5. (3)填充字段(對齊到四字節)
  6. (4)節點屬性。每個屬性以宏OF_DT_PROP開始,後面依次爲屬性值的字節長度(4字節)、屬性名稱在字符串塊中的偏移量(4字節)、屬性值和填充(對齊到四字節)。
  7. (5)如果存在子節點,則定義子節點。
  8. (6)節點結束標誌OF_DT_END_NODE。

  1. <3>字符串塊

  1. 通過節點的定義知道節點都有若干屬性,而不同的節點的屬性又有大量相同的屬性名稱,因此將這些屬性名稱提取出一張表,當節點需要應用某個屬性名稱時直接在屬性名字段保存該屬性名稱在字符串塊中的偏移量。

  1. <4> 設備樹源碼 DTS 表示

  1. 設備樹源碼文件(.dts)以可讀可編輯的文本形式描述系統硬件配置設備樹,支持 C/C++方式的註釋,該結構有一個唯一的根節點“/,每個節點都有自己的名字並可以包含多個子節點。設備樹的數據格式遵循了 Open Firmware IEEE standard 1275。這個設備樹中有很多節點,每個節點都指定了節點單元名稱。每一個屬性後面都給出相應的值。以雙引號引出的內容爲 ASCII 字符串,以尖括號給出的是 32 位的16進制值。這個樹結構是啓動 Linux 內核所需節點和屬性簡化後的集合,包括了根節點的基本模式信息、CPU 和物理內存佈局,它還包括通過/chosen 節點傳遞給內核的命令行參數信息。

  1. <5> machine_desc結構

  1. 內核提供了一個重要的結構體struct machine_desc ,這個結構體在內核移植中起到相當重要的作用,內核通過machine_desc結構體來控制系統體系架構相關部分的初始化。machine_desc結構體通過MACHINE_START宏來初始化,在代碼中, 通過在start_kernel->setup_arch中調用setup_machine_fdt來獲取。
  2. struct machine_desc {
  3.     unsigned int nr; /* architecture number */
  4.     const char *name; /* architecture name */
  5.     unsigned long atag_offset; /* tagged list (relative) */
  6.     const char *const *dt_compat; /* array of device tree* 'compatible' strings */
  7.     unsigned int nr_irqs; /* number of IRQs */
  8.     
  9. #ifdef CONFIG_ZONE_DMA
  10.     phys_addr_t dma_zone_size; /* size of DMA-able area */
  11. #endif
  12.     
  13.     unsigned int video_start; /* start of video RAM */
  14.     unsigned int video_end; /* end of video RAM */
  15.     
  16.     unsigned char reserve_lp0 :1; /* never has lp0 */
  17.     unsigned char reserve_lp1 :1; /* never has lp1 */
  18.     unsigned char reserve_lp2 :1; /* never has lp2 */
  19.     enum reboot_mode reboot_mode; /* default restart mode */
  20.     struct smp_operations *smp; /* SMP operations */
  21.     bool (*smp_init)(void);
  22.     void (*fixup)(struct tag *, char **,struct meminfo *);
  23.     void (*init_meminfo)(void);
  24.     void (*reserve)(void);/* reserve mem blocks */
  25.     void (*map_io)(void);/* IO mapping function */
  26.     void (*init_early)(void);
  27.     void (*init_irq)(void);
  28.     void (*init_time)(void);
  29.     void (*init_machine)(void);
  30.     void (*init_late)(void);
  31. #ifdef CONFIG_MULTI_IRQ_HANDLER
  32.     void (*handle_irq)(struct pt_regs *);
  33. #endif
  34.     void (*restart)(enum reboot_mode, const char *);
  35. };

  1. <6>設備節點結構體

  1. struct device_node {
  2.     const char *name;    //設備name
  3.     const char *type; //設備類型
  4.     phandle phandle;
  5.     const char *full_name; //設備全稱,包括父設備名

  6.     struct property *properties; //設備屬性鏈表
  7.     struct property *deadprops; //removed properties
  8.     struct device_node *parent; //指向父節點
  9.     struct device_node *child; //指向子節點
  10.     struct device_node *sibling; //指向兄弟節點
  11.     struct device_node *next; //相同設備類型的下一個節點
  12.     struct device_node *allnext; //next in list of all nodes
  13.     struct proc_dir_entry *pde; //該節點對應的proc
  14.     struct kref kref;
  15.     unsigned long _flags;
  16.     void *data;
  17. #if defined(CONFIG_SPARC)
  18.     const char *path_component_name;
  19.     unsigned int unique_id;
  20.     struct of_irq_controller *irq_trans;
  21. #endif
  22. };

  1. <7>屬性結構體

  1. struct property {
  2.     char *name;        //屬性名
  3.     int length;        //屬性值長度
  4.     void *value;        //屬性值
  5.     struct property *next; //指向下一個屬性
  6.     unsigned long _flags; //標誌
  7.     unsigned int unique_id;
  8. };




本文將介紹如何爲一個新機器編寫設備樹。我們準備提供一個有關設備樹概念的概述和如何使用這些設備樹來描述一個機器。完整的設備樹數據格式的技術說明書請參考 ePAPR 規範。ePAPR 規範涵蓋了比本文基本主題更豐富的細節,要查閱本文沒有涉及到的高級用法請參考該規範。

 目錄

1. 基本數據格式  

2. 基本概念

2.1 模型機

2.2 初始結構

2.3 中央處理器

2.4 節點名稱

2.5 設備

2.6 理解 compatible 屬性

3. 如何編址

3.1 CPU 編址

3.2 內存映射設備

3.3 非內存映射設備

3.4 範圍(地址轉換)

4. 中斷的工作方式

5. 設備特定數據

6. 特殊的節點

6.1 aliases 節點

6.2 chosen 節點

7. 高級主題

7.1 高級模型機

7.2 PCI  主橋

7.2.1 PCI 總線編號

7.2.2 PCI 地址轉換

7.3 高級中斷映射

8. 附註

 

 基本數據格式

設備樹是一個包含節點和屬性的簡單樹狀結構。屬性就是鍵-值對,而節點可以同時包含屬性和子節點。例如,以下就是一個 .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 {

        };

    };

};

 

這棵樹顯然是沒什麼用的,因爲它並沒有描述任何東西,但它確實體現

了節點的一些屬性:

 

一個單獨的根節點:“/

兩個子節點:“node1”和“node2

兩個 node1 的子節點:“child-node1”和“child-node2

一堆分散在樹裏的屬性。

 

屬性是簡單的鍵-值對,它的值可以爲空或者包含一個任意字節流。雖

然數據類型並沒有編碼進數據結構,但在設備樹源文件中任有幾個基本的數據表示形式。

 

 文本字符串(無結束符)可以用雙引號表示:

string-property = "a string"

 Cells’是32位無符號整數,用尖括號限定:

cell-property = <0xbeef 123 0xabcd1234>

二進制數據用方括號限定:

binary-property = [0x01 0x23 0x45 0x67];

不同表示形式的數據可以使用逗號連在一起:

mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;

 逗號也可用於創建字符串列表:
string-list = "red fish", "blue fish";

 

                   基本概念

 

我們將以一個簡單機開始,然後通過一步步的建立一個描述這個簡單機

的設備樹,來了解如何使用設備樹。

2.1模型機

考慮下面這個假想的機器(大致基於ARM Versatile),製造商爲“Acme”,

並命名爲“Coyote's Revenge”:

 

  一個 32 位 ARM CPU    

  處理器本地總線連接到內存映射的串行口、spi 總線控制器、i2c 控制器、中斷控

制器和外部總線橋

   256MB SDRAM 起始地址爲 0

 兩個串口起始地址:0x101F1000 和 0x101F2000

  GPIO 控制器起始地址:0x101F3000

  帶有以下設備的 SPI 控制器起始地址:0x10170000

      MMC 插槽的 SS 管腳連接至 GPIO #1

  外部總線橋掛載以下設備

      SMC SMC91111 以太網設備連接到外部總線,起始地址:0x10100000

      i2c 控制器起始地址:0x10160000,並掛載以下設備

          Maxim DS1338 實時時鐘。響應至從地址 1101000 (0x58)

       64MB NOR 閃存起始地址 0x30000000

 

2.2初始結構

第一步就是要爲這個模型機構建一個基本結構,這是一個有效的設備樹最基本的結構。在這個階段你需要唯一的標識該機器。

/ {

    compatible = "acme,coyotes-revenge";

};

compatible 指定了系統的名稱。它包含了一個“<製造商>,<型號>”形式

的字符串。重要的是要指定一個確切的設備,並且包括製造商的名子,以避免命名空間衝突。由於操作系統會使用 compatible 的值來決定如何在機器上運行,所以正確的設置這個屬性變得非常重要。理論上講,兼容性(compatible)就是操作系統需要的所有數據都唯一標識一個機器。如果機器的所有細節都是硬編碼的,那麼操作系統則可以在頂層的compatible屬性中具體查看“acme,coyotes-revenge”。

 

2.3中央處理器

接下來就應該描述每個 CPU 了。先添加一個名爲“cpus”的容器節點,然後爲每個CPU分別添加子節點。具體到我們的情況是一個ARM的 雙核Cortex A9系統。

 

/ {

    compatible = "acme,coyotes-revenge";

 

    cpus {

        cpu@0 {

            compatible = "arm,cortex-a9";

        };

        cpu@1 {

            compatible = "arm,cortex-a9";

        };

    };

};

 

每個 cpu 節點的compatible屬性是一個“<製造商>,<型號>”形式的字符串,並指定了確切的cpu,就像頂層的compatible屬性一樣。稍後將會有更多的屬性添加進cpu節點,但我們先得討論一些更過的基本概念。

 

2.4節點名稱

現在應該花點時間來討論命名約定了。每個節點必須有一個“<名稱>[@<

設備地址>]”形式的名字。 <名稱> 就是一個不超過31位的簡單 ascii 字符串。通常,節點的命名應該根據它所體現的是什麼樣的設備。比如一個 3com 以太網適配器的節點就應該命名爲 ethernet,而不應該是 3com509。

如果該節點描述的設備有一個地址的話就還應該加上設備地址(unit-address)。通常,設備地址就是用來訪問該設備的主地址,並且該地址也在節點的 reg 屬性中列出。本文檔中我們將在稍後涉及到 reg 屬性。同級節點命名必須是唯一的,但只要地址不同,多個節點也可以使用一樣的通用名稱(例如 serial@101f1000 和 serial@101f2000)。關於節點命名的更多細節請參考 ePAPR 規範 2.2.1 節。

2.5設備

系統中每個設備都表示爲一個設備樹節點。所以接下來就應該爲這個設備樹填充設備節點。現在,知道我們討論如何進行尋址和中斷請求如何處理之前這些新節點將一直爲空。

 

/ {

    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";

    };

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章