1. 程式人生 > >Linux裝置樹使用

Linux裝置樹使用

本頁面介紹瞭如何為新machine編寫裝置樹檔案。它旨在提供裝置樹概念的概述以及它們如何用於描述machine。

有關裝置樹資料格式的完整技術說明,請參閱ePAPR v1.1規範。ePAPR規範比本頁面介紹的基本主題包含更多詳細資訊,請參閱此頁面以獲取本頁未涵蓋的更高階用法。ePAPR目前正在使用Devicetree規範文件的新名稱進行更新。

1.基本資料格式

裝置樹是由節點和屬性組成的樹型結構。屬性是鍵-值對的形式,節點可以包含屬性和子節點。例如,以下是.dts格式的簡單裝置樹:

/dts-v1/;		//裝置樹的版本號
/ {
    node1{		//節點
        a-string-property = "A string";
        a-string-list-property = "first string", "second string";
        // hex is implied in byte arrays. no '0x' prefix is required
        a-byte-data-property = [01 23 34 56];
        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.基本概念

要了解裝置樹的使用方式,我們將從一臺簡單的機器開始,並構建一個裝置樹來逐步描述它。

考慮以下設想的機器(基於ARM Versatile),由"Acme"製造並命名為"Coyote's Revenge":

一個32位ARM CPU; 處理器本地匯流排連線到記憶體對映的串列埠,spi匯流排控制器,i2c控制器,中斷控制器和外部匯流排橋; 基於0地址的256MB SDRAM; 2個基於0x101F1000和0x101F2000的串列埠; GPIO控制器基地址為0x101F3000; 基於0x10170000地址的SPI控制器,具有以下器件; 帶有SS引腳的MMC插槽連線到GPIO#1; 外部匯流排橋接器具有以下裝置: SMC SMC91111乙太網裝置連線到外部匯流排,基地址為0x10100000; i2c控制器基地址為0x10160000,具有以下裝置: Maxim DS1338實時時鐘。從地址為1101000(0x58); 基地址為0x30000000的64MB NOR快閃記憶體。

初始基本dts框架:

第一步是為machine設定骨架結構。這是有效裝置樹所需的最小結構。在此階段,您需要唯一標識機器。

/ DTS-V1 /;
/ {
    compatible =“acme,coyotes-revenge”;
};

compatible指定系統的名稱。它包含一個"<manufacturer>,<model>"形式的字串。重要的是指定確切的裝置,幷包含製造商名稱以避免名稱空間衝突。由於作業系統將使用該compatible值來決定如何在機器上執行,將正確的資料放入此屬性非常重要。

從理論上講,compatible是作業系統唯一識別機器所需的資料。如果所有機器細節都是硬編碼的,那麼作業系統可能會專匹配對上層的compatible屬性中的"acme,coyotes-revenge"。

CPU的設定:

/ DTS-V1 /;
/ {
    compatible =“acme,coyotes-revenge”;

    cpus {
        cpu @ 0 {
            compatible =“arm,cortex-a9”;
        };
        cpu @ 1 {
            compatible =“arm,cortex-a9”;
        };
    };
};

每個cpu節點中的compatible屬性是一個字串,它指定表單中的確切cpu模型<manufacturer>,<model>,就像頂層的compatible屬性一樣。稍後將向cpu節點新增更多屬性,但我們首先需要討論更多基本概念。

節點名稱:

值得花點時間談談命名約定。每個節點必須有一個名稱,格式為:<name>[@<unit-address>]。<name>是一個簡單的ascii字串,最多可以包含31個字元。通常,節點根據它代表的裝置型別命名。即,3com乙太網介面卡的節點將使用該名稱ethernet,而不是3com509。如果節點描述的是具有地址的裝置,則包括地址。通常,地址是用於訪問裝置的主要地址,並列在節點的reg屬性中。我們將在本文件後面介紹reg屬性。

同級節點名稱必須是唯一的,但只要地址不同(即[email protected][email protected])即可,多個節點使用相同的通用名稱是正常的。有關節點命名的完整詳細資訊,請參閱ePAPR規範的第2.2.1節。

裝置:

系統中的每個裝置都由裝置樹節點表示。下一步是使用每個裝置的節點填充裝置樹檔案。現在,新節點將保持為空,直到我們可以討論如何處理地址範圍和irq。

/dts-v1/;
/{
    compatible = "acme,coyotes-revenge";

    cpus {
        [email protected] {
            compatible = "arm,cortex-a9";
        };
        [email protected] {
            compatible = "arm,cortex-a9";
        };
    };

    [email protected] {
        compatible = "arm,pl011";
    };

    [email protected] {
        compatible = "arm,pl011";
    };

    [email protected] {
        compatible = "arm,pl061";
    };

    [email protected] {
        compatible = "arm,pl190";
    };

    [email protected] {
        compatible = "arm,pl022";
    };

    external-bus {
        [email protected],0 {
            compatible = "smc,smc91c111";
        };

        [email protected],0 {
            compatible = "acme,a1234-i2c-bus";
            [email protected] {
                compatible = "maxim,ds1338";
            };
        };

        [email protected],0 {
            compatible = "samsung,k8f1315ebm", "cfi-flash";
        };
    };
};

在此樹中,已為系統中的每個裝置添加了一個節點,層次結構反映了裝置連線到系統的方式。即,外部總線上的裝置是外部匯流排節點的子裝置,i2c裝置是i2c匯流排控制器節點的子裝置。通常,層次結構從CPU的角度表示系統的檢視。

此樹在此時無效。它缺少有關裝置之間連線的資訊。該資料將在稍後新增。在這棵樹中要注意的一些事情:

每個裝置節點都有一個compatible屬性。 flash節點在相容屬性中有2個字串。請繼續閱讀下一節以瞭解原因。 如前所述,節點名稱反映了裝置的型別,而不是特定的模型。有關應儘可能使用的已定義通用節點名稱列表,請參閱ePAPR規範的第2.2.2節。

compatible屬性詳解:

樹中表示裝置的每個節點都需要具有該compatible屬性。compatible是作業系統用來決定繫結裝置和裝置驅動程式的關鍵。

compatible是一個字串列表。列表中的第一個字串指定節點表示的確切裝置"<manufacturer>,<model>"。以下字串表示裝置相容的其他裝置。

例如,飛思卡爾MPC8349片上系統(SoC)具有序列器件,該器件實現了National Semiconductor ns16550暫存器介面。因此,MPC8349序列裝置的相容屬性應為:compatible = "fsl,mpc8349-uart", "ns16550"。在這種情況下,fsl,mpc8349-uart指定確切的器件,並使用ns16550宣告它與National Semiconductor 16550 UART暫存器級相容。

注意:ns16550由於歷史原因,沒有製造商字首。所有新的相容值都應使用製造商字首。

這種做法允許將現有裝置驅動程式繫結到較新的裝置,同時仍然唯一地標識確切的硬體。

地址是如何工作的:

可定址的裝置使用以下屬性將地址資訊編碼到裝置樹中: reg #address-cells #size-cells

每個可定址的裝置獲取一個reg表格中的地址reg = <address1 length1 [address2 length2] [address3 length3] ...>。每個cell表示裝置使用的地址範圍。每個地址值是一個或多個稱為單元的32位整數的列表。類似地,長度值可以是單元格列表也可以是空的。

由於地址和長度欄位都是可變大小的變數,因此父節點中的#address-cells和#size-cells屬性用於表示每個欄位中有多少個單元格。或者換句話說,正確解釋reg屬性需要父節點的#address-cells和#size-cells值。要了解這一切是如何工作的,我們可以從CPU開始,將地址屬性新增到示例裝置樹中。

CPU地址:

在討論地址時,CPU節點代表了最簡單的情況。為每個CPU分配一個唯一的ID,並且沒有與CPU ID相關聯的大小。

cpus {
	#address-cells = <1>;
	#size-cells = <0>;
	[email protected]{
		compatible =“arm,cortex-a9”;
		reg = <0>;
	};
	[email protected]{
		compatible =“arm,cortex-a9”;
		reg = <1>;
	};
};

在cpus節點中,#address-cells設定為1,並#size-cells設定為0。這意味著子reg值是單個uint32,表示沒有大小欄位。在這種情況下,兩個cpus被分配地址0和1;#size-cells對於cpu節點是0,因為每個cpu僅被分配一個地址。

您還會注意到該reg值與節點名稱中的值匹配。按照慣例,如果節點具有reg屬性,則節點名稱必須包含unit-address,這是reg屬性中的第一個地址值。

記憶體對映裝置:

不是像在cpu節點中找到的單個地址值,而是為記憶體對映裝置分配一系列將響應的地址。#size-cells用於表示每個子reg元組中長度欄位的大小。在以下示例中,每個地址值為1個單元(32位),每個長度值也為1個單元,這在32位系統上是典型的。對於#address-cells和#size-cells,64位機器可以使用值2來獲得裝置樹中的64位定址。

/dts-v1/;
/ {
    #address-cells = <1>;
    #size-cells = <1>;
    ...
    [email protected] {
        compatible = "arm,pl011";
        reg = <0x101f0000 0x1000 >;
    };

    [email protected] {
        compatible = "arm,pl011";
        reg = <0x101f2000 0x1000 >;
    };

    [email protected] {
        compatible = "arm,pl061";
        reg = <0x101f3000 0x1000
               0x101f4000 0x0010>;
    };

    [email protected] {
        compatible = "arm,pl190";
        reg = <0x10140000 0x1000 >;
    };

    [email protected] {
        compatible = "arm,pl022";
        reg = <0x10115000 0x1000 >;
    };
    ...
};

為每個裝置分配一個基址,併為其分配區域的大小。本例中的GPIO裝置地址分配了兩個地址範圍; 0x101f3000 ... 0x101f3fff和0x101f4000..0x101f400f。

一些裝置存在於具有不同定址方案的總線上。例如,可以使用分立的晶片選擇線將器件連線到外部匯流排。由於每個父節點為其子節點定義定址域,因此可以選擇地址對映以最好地描述系統。下面的程式碼顯示了連線到外部匯流排的裝置的地址分配,晶片選擇號編碼到地址中​​。

external-bus {
	#address-cells = <2>;
	#size-cells = <1>;

	[email protected],0 {
		compatible = "smc,smc91c111";
		reg = <0 0 0x1000>;
	};

	[email protected],0 {
		compatible = "acme,a1234-i2c-bus";
		reg = <1 0 0x1000>;
		[email protected] {
			compatible = "maxim,ds1338";
		};
	};

	[email protected],0 {
		compatible = "samsung,k8f1315ebm", "cfi-flash";
		reg = <2 0 0x4000000>;
	};
};

external-bus用2個cell表示地址值;一個用於晶片選擇號,一個用於從晶片選擇的基極偏移。長度欄位保持為單個單元格,因為只有地址的偏移部分需要具有範圍。因此,在這個例子中,每個reg條目包含3個單元格;chipselect號碼,偏移量和長度。

由於地址域包含在節點及其子節點中,因此父節點可以自由定義對匯流排有意義的任何定址方案。直接父節點和子節點之外的節點通常不必關心本地定址域,並且必須對映地址以從一個域到另一個域。

非記憶體對映裝置:

其他裝置未在處理器總線上對映記憶體地址範圍。它們可以具有地址範圍,但CPU無法直接訪問它們。相反,父裝置的驅動程式將代表CPU執行間接訪問。以i2c裝置為例,每個裝置都分配了一個地址,但沒有與之關聯的長度或範圍。這看起來與CPU地址分配大致相同。

[email protected],0 {
	compatible = "acme,a1234-i2c-bus";
	#address-cells = <1>;
	#size-cells = <0>;
	reg = <1 0 0x1000>;
	[email protected] {
		compatible = "maxim,ds1338";
		reg = <58>;
	};
};