1. 程式人生 > >Device Tree常用方法解析

Device Tree常用方法解析

如何 單獨 範圍 沒有 lds 導致 節點和 nal include

http://blog.csdn.net/airk000/article/details/21345159

Device Tree常用方法解析

Device Tree在Linux內核驅動中的使用源於2011年3月17日Linus Torvalds在ARM Linux郵件列表中的一封郵件,他宣稱“this whole ARM thing is a f*cking pain in the ass”,並提倡學習PowerPC等其他架構已經成熟使用的Device Tree技術。自此,Device Tree正式進入ARM社區的視野中。

1. 作用

Device Tree是一種用來描述硬件的數據結構,類似板級描述語言,起源於OpenFirmware(OF)。在目前廣泛使用的Linux kernel 2.6.x版本中,對於不同平臺、不同硬件,往往存在著大量的不同的、移植性差的板級描述代碼,以達到對這些不同平臺和不同硬件特殊適配的需求。但是過多的平臺、過的的不同硬件導致了這樣的代碼越來越多,最終引發了Linux創始人Linus的不滿,以及強烈呼籲改變。Device Tree的引入給驅動適配帶來了很大的方便,一套完整的Device Tree可以將一個PCB擺在你眼前。Device Tree可以描述CPU,可以描述時鐘、中斷控制器、IO控制器、SPI總線控制器、I2C控制器、存儲設備等任何現有驅動單位。對具體器件能夠描述到使用哪個中斷,內存映射空間是多少等等。

2. 基本數據格式

Device Tree由節點和屬性構成。屬性為key-value對,節點包括了各種屬性,也可以包含子節點。下邊列舉一個簡單的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 {
        };
    };
};

這個文件實際上沒有任何意義,但卻包含了基本所有要素:

  • 1 唯一的根節點 “/”
  • 2 一些節點:node1 node2
  • 3 子節點 node1的子節點child-node1和child-node2
  • 4 一群分散的屬性

屬性都是簡單的key-value對,其中value也可以是空的或包含任意的byte流。以下是一些屬性的基本數據結構:

  • 1 雙引號包含的字符信息

      string-property = "a string";
  • 2 cells單位信息是32位無符號整型數據

      cell-property = <0xFF01 412 0x12341283>;
  • 3 二進制數據流

      binary-property = [0x01 0x02 0x03 0x04];
  • 4 混合數據用逗號隔開

      mixed-property = "a string", [0x01 0x02 0x03 0x04], <0xFF01 412 0x12341283>;
  • 5 字符列表

      string-list = "string test1", "string test2";

3. 一些基本概念

  • 每個完整的dts文件必須擁有一個根節點
  • dtsi文件一般為通用文件(類似C語言的頭文件),可被其他文件include
    後邊的名字涵蓋的範圍更加廣泛,如果可以匹配到,同樣會以這個dts為基礎進行初始化並啟動。
  • 父節點名應該取類型名,而不是IC名。節點名的命名規則一般是 [name]@[address],也可以只有name而沒有@之後的內容,但是要確保name不能重名。如果加了@以及地址,那麽name可以相同,只要address不同即可。
  • 每一個設備節點都要有一個compatible屬性
  • compatible的內容是用來匹配驅動的,組成方式為"[manufacturer], [model]",加入廠商名是為了避免重名。有的時候後邊還會跟一個名字,如:

      compatible = "acme,coyotes-revenge", "acmd-board";

4. 工作方式

a. 地址

設備的地址特性根據一下幾個屬性來控制:

  • reg
  • #address-cells
  • #size-cells

reg意為region,區域。格式為:

reg = <address1 length1 [address2 length2] [address3 length3]>;

父類的address-cells和size-cells決定了子類的相關屬性要包含多少個cell,如果子節點有特殊需求的話,可以自己再定義,這樣就可以擺脫父節點的控制。
address-cells決定了address1/2/3包含幾個cell,size-cells決定了length1/2/3包含了幾個cell。本地模塊例如:

spi@10115000 {
        compatible = "arm,pl022";
        reg = <0x10115000 0x1000 >;
};

位於0x10115000的SPI設備申請地址空間,起始地址為0x10115000,長度為0x1000,即屬於這個SPI設備的地址範圍是0x10115000~0x10116000。

實際應用中,有另外一種情況,就是通過外部芯片片選激活模塊。例如,掛載在外部總線上,需要通過片選線工作的一些模塊:

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使用兩個cell來描述地址,一個是片選序號,另一個是片選序號上的偏移量。而地址空間長度依然用一個cell來描述。所以以上的子設備們都需要3個cell來描述地址空間屬性——片選、偏移量、地址長度。在上個例子中,有一個例外,就是i2c控制器模塊下的rtc模塊。因為I2C設備只是被分配在一個地址上,不需要其他任何空間,所以只需要一個address的cell就可以描述完整,不需要size-cells。

當需要描述的設備不是本地設備時,就需要描述一個從設備地址空間到CPU地址空間的映射關系,這裏就需要用到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屬性為一個地址轉換表。表中的每一行都包含了子地址、父地址、在自地址空間內的區域大小。他們的大小(包含的cell)分別由子節點的address-cells的值、父節點的address-cells的值和子節點的size-cells來決定。以第一行為例:

  • 0 0 兩個cell,由子節點external-bus的address-cells=<2>決定;
  • 0x10100000 一個cell,由父節點的address-cells=<1>決定;
  • 0x10000 一個cell,由子節點external-bus的size-cells=<1>決定。
    最終第一行說明的意思就是:片選0,偏移0(選中了網卡),被映射到CPU地址空間的0x10100000~0x10110000中,地址長度為0x10000。
b. 中斷

描述中斷連接需要四個屬性:
1. interrupt-controller 一個空屬性用來聲明這個node接收中斷信號;
2. #interrupt-cells 這是中斷控制器節點的屬性,用來標識這個控制器需要幾個單位做中斷描述符;
3. interrupt-parent 標識此設備節點屬於哪一個中斷控制器,如果沒有設置這個屬性,會自動依附父節點的;
4. interrupts 一個中斷標識符列表,表示每一個中斷輸出信號。

如果有兩個,第一個是中斷號,第二個是中斷類型,如高電平、低電平、邊緣觸發等觸發特性。對於給定的中斷控制器,應該仔細閱讀相關文檔來確定其中斷標識該如何解析。

c. 其他

除了以上規則外,也可以自己加一些自定義的屬性和子節點,但是一定要符合以下的幾個規則:

  1. 新的設備屬性一定要以廠家名字做前綴,這樣就可以避免他們會和當前的標準屬性存在命名沖突問題;
  2. 新加的屬性具體含義以及子節點必須加以文檔描述,這樣設備驅動開發者就知道怎麽解釋這些數據了。描述文檔中必須特別說明compatible的value的意義,應該有什麽屬性,可以有哪個(些)子節點,以及這代表了什麽設備。每個獨立的compatible都應該由單獨的解釋。
  3. 新添加的這些要發送到[email protected]郵件列表中進行review,並且檢查是否會在將來引發其他的問題。

5. 進階例子

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屬性進行地址轉化。
#address-cells定義PCI使用3個cell,並且PCI的地址範圍通過兩個單位就可以解讀。所以,首先的問題就是,為什麽需要用3個32位的cell來描述一個PCI地址。

這三個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:代表重申請空間標誌(這裏沒有使用)
p:代表預讀空間(緩存)標誌
t:別名地址標誌(這裏沒有使用)
ss:空間代碼
00: 設置空間
01:IO空間
10:32位存儲空間
11:64位存儲空間

bbbbbbbb: PCI總線號。PCI有可能是層次性架構,所以我們可能需要區分一些子-總線
ddddd:設備號,通常由初始化設備選擇信號IDSEL連接時申請。
fff:功能序號,有些多功能PCI設備可能用到。
rrrrrrrr:註冊號,在設置周期使用。

ranges = <0x42000000 0 0x80000000 0x80000000 0 0x20000000
          0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
          0x01000000 0 0x00000000 0xb0000000 0 0x01000000>;

回頭再看這個ranges分表代表了什麽。父節點address-cells為1,子節點address-cells為3, 子節點size-cells為2。則第一行可以這樣劃分:

0x42000000 0 0x80000000 子節點地址| 0x80000000 父節點地址| 0 0x20000000 地址空間長度|

0x42000000為phys.high,第一位為01000010,則p為1,ss為10,即申請32位存儲空間為緩存空間。phys.mid為0,phys.low為0x80000000,他們共同組成了PCI地址,即表示從PCI總線的0x80000000地址處申請出一個32位的存儲空間作為緩存。後邊的那個cell 0x80000000 0 0x20000000代表到CPU空間後的參數,申請的地址被映射到CPU空間的0x80000000地址處,大小共計0x20000000(512MB)。

相關資料及引用:

http://blog.csdn.net/21cnbao/article/details/8457546
http://devicetree.org/Device_Tree_Usage

Device Tree常用方法解析