linux 設備樹【轉】


轉自:http://blog.csdn.net/chenqianleo/article/details/77779439

[-]

  1. linux 設備樹
    1. 為什么要使用設備樹Device Tree
    2. 設備樹的的組成和結構
      1. 1設備樹的組成
        1. 11 DTS和DTSI
        2. 12 DTC
        3. 13 DTB
        4. 14 綁定bingding
        5. 15 Bootloader 使用dtb
      2. 2設備樹框架
    3. 設備樹語法
      1. 下面這個是rk3399-fpgadts
      2. 1根節點兼容性
      3. 2節點名
      4. 3引用
      5. KEY
        1. 1compatible
        2. 2address
        3. 3interrupts
        4. 4gpio
      6. DTB的加載過程
      7. API調用

linux 設備樹


參考地址
http://blog.csdn.net/green1900/article/details/45646095
http://www.cnblogs.com/xiaojiang1025/p/6131381.html
http://blog.csdn.net/21cnbao/article/details/8457546


1.為什么要使用設備樹(Device Tree)?

在以前的內核源碼中,存在大量對板級細節信息描述的代碼,這些代碼充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目錄,對內核而言這些platform設備、resource、i2c_board_info、spi_board_info以及各種硬件的platform_data絕大多數純屬垃圾冗余代碼。為了解決這一問題,ARM內核版本3.x之后引入了原先在Power PC等其他體系架構已經使用的Flattened Device Tree。DTS不是arm的專利

在使用了設備樹后,對於同一SOC的不同主板,只需更換設備樹文件.dtb即可實現不同主板的無差異支持,而無需更換內核文件。


2.設備樹的的組成和結構

設備樹可以描述的信息包括了
1. CPU的數量和類別、
2. 內存基地址和大小、
3. 總線和橋、
4. 外設連接、
5. 中斷控制器和中斷使用情況、
6. GPIO控制器和GPIO使用情況、
7. Clock控制器和Clock使用情況。
需要注意的是,設備樹對於可熱插拔的熱備不進行具體描述,它只描述用於控制該熱插拔設備的控制器

2.1設備樹的組成

設備樹包含了DTC(device tree compiler) , DTS(device tree resource) 和 DTB(device tree blob),簡單來說,dts是源碼,dtc是編譯器,dtb是生成的可執行文件
此處輸入圖片的描述

2.1.1 DTS和DTSI

.dts和.dtsi是一種ASCII文本的設備樹描述,此文本格式非常適合人們閱讀,基本上,一個.dts對應一種ARM設備,放在arch/arm/boot/dts目錄,由於一個soc對應好多個不同的開發板,每個開發板有一個.dts,所以這些dts勢必有共同部分,為了減少代碼的屯余,設備樹將這些共同部分提煉保存在dtsi中,供不同的dts使用,dtsi文件類似於c語言的頭文件

2.1.2 DTC

DTC為編譯工具,它可以將.dts文件編譯成.dtb文件。DTC的源碼位於內核的scripts/dtc目錄,內核選中CONFIG_OF,編譯內核的時候,主機可執行程序DTC就會被編譯出來

2.1.3 DTB

DTB設備由DTC編譯后的二進制格式的設備樹描述,可以由linux內核解析,uboot這樣的bootloader也可以識別.dtb,有兩種使用方式,一種是bootloader啟動內核過程中會先讀取dtb到文件中;第二種是把dtb和zImage打包在一起做成一個印象文件,firefly-3399就是采用這種方式,打包生成了boot.img

2.1.4 綁定(bingding)

對於Device Tree中的結點和屬性具體是如何來描述設備的硬件細節的,一般需要文檔來進行講解,文檔的后綴名一般為.txt。這些文檔位於內核的Documentation/devicetree/bindings目錄,其下又分為很多子目錄

2.1.5 Bootloader 使用dtb

在Uboot中,可以從NAND、SD或者TFTP等任意介質將.dtb讀入內存,假設.dtb放入的內存地址為0x71000000,之后可在Uboot運行命令fdt addr命令設置.dtb的地址,如:
U-Boot> fdt addr 0x71000000
fdt的其他命令就變地可以使用,如fdt resize、fdt print等
對於ARM來講,可以透過bootz kernel_addr initrd_address dtb_address的命令來啟動內核,即dtb_address作為bootz或者bootm的最后一次參數,第一個參數為內核映像的地址,第二個參數為initrd的地址,若不存在initrd,可以用 -代替,第三個就是dtb地址

2.2設備樹框架

設備樹用樹狀結構描述設備信息,它有以下幾種特性
1. 每個設備樹文件都有一個根節點,每個設備都是一個節點。
2. 節點間可以嵌套,形成父子關系,這樣就可以方便的描述設備間的關系。
3. 每個設備的屬性都用一組key-value對(鍵值對)來描述。
4. 每個屬性的描述用;結束


3. 設備樹語法

設備樹是一顆樹,書上的每個節點由節點和屬性組成,屬性是鍵值對

下面這個是rk3399-fpga.dts

#include "rk3399.dtsi" //包含了公共部分 / { model = "Rockchip RK3399 FPGA Board"; compatible = "rockchip,fpga", "rockchip,rk3399"; //根節點兼容性分析,下面具體分析 chosen { bootargs = "init=/init console=uart,mmio32,0xff1a0000"; }; memory@00000000 { //子節點 memory@00000000節點名 device_type = "memory"; reg = <0x0 0x00000000 0x0 0x20000000>; }; }; &uart2 { //使用了引用 status = "okay"; clocks = <&xin24m>, <&xin24m>; }; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3.1根節點兼容性

compatible = "rockchip,fpga", "rockchip,rk3399";
  • 1
上面是根節點的兼容屬性,定義了整個系統(設備級別)的名稱,通過這個屬性就可以判斷出它啟動的是什么設備。它的組織形式是&lt;manufacture&gt;&lt;model&gt;,在實際中一般包括兩個或兩個以上的兼容字符串,上面第一個是"rockchip,fpga",第二個是"rockchip,rk3399",我們來看第二個,manufacture是板子級別的名字,“rockchip”代表的是瑞芯微公司,model是芯片級別的,“rk3399”是瑞芯微公司一個soc的名稱

我們從源碼中找出rk3399的兩個dts,可以看出第一個兼容字符串的model不同,第二個完全相同

rk3399-firefly-linux.dts compatible = "rockchip,rk3399-firefly-linux", "rockchip,rk3399"; rk3399-fpga.dts compatible = "rockchip,fpga", "rockchip,rk3399";
  • 1
  • 2
  • 3
  • 4

3.2節點名

理論個節點名只要是長度不超過31個字符的ASCII字符串即可,Linux內核還約定設備名應寫成形如[@]的形式,其中name就是設備名,最長可以是31個字符長度。unit_address一般是設備地址,用來唯一標識一個節點
Linux中的設備樹還包括幾個特殊的節點,比如chosen,chosen節點不描述一個真實設備,而是用於firmware傳遞一些數據給OS,比如bootloader傳遞內核啟動參數給內核

chosen{
    bootargs = "console=ttySAC2,115200"; stdout-path=&serial_2; };
  • 1
  • 2
  • 3
  • 4

3.3引用

當我們找一個節點的時候,我們必須書寫完整的節點路徑,這樣當一個節點嵌套比較深的時候就不是很方便,所以,設備樹允許我們用下面的形式為節點標注引用(起別名),借以省去冗長的路徑。這樣就可以實現類似函數調用的效果

3.KEY

在設備樹中,鍵值對是描述屬性的方式,比如,Linux驅動中可以通過設備節點中的”compatible”這個屬性查找設備節點
inux設備樹語法中定義了一些具有規范意義的屬性,包括:compatible, address, interrupt等,這些信息能夠在內核初始化找到節點的時候,自動解析生成相應的設備信息。此外,還有一些Linux內核定義好的,一類設備通用的有默認意義的屬性,這些屬性一般不能被內核自動解析生成相應的設備信息,但是內核已經編寫的相應的解析提取函數,常見的有 “mac_addr”,”gpio”,”clock”,”power”。”regulator” 等等。

3.1.compatible

設備節點中對應的節點信息已經被內核構造成struct platform_device。驅動可以通過相應的函數從中提取信息。主要有三種方法提取信息

    1、compatible屬性是用來查找節點
    2、通過節點名查找指定節點
    3、節點路徑查找指定節點
  • 1
  • 2
  • 3

看一個使用compatible提取屬性的例子

#dts gpio_demo: gpio_demo { status = "okay"; compatible = "firefly,rk3399-gpio"; }; #驅動代碼 static struct of_device_id firefly_match_table[] = { { .compatible = "firefly,rk3399-gpio",}, //完全相同 {}, //最后一個成員一定是空,因為相關的操作API會讀取這個數組直到遇到一個空。 };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.2address

  • #address-cells,用來描述子節點”reg”屬性的地址表中用來描述首地址的cell的數量
  • #size-cells,用來描述子節點”reg”屬性的地址表中用來描述地址長度的cell的數量。
        pinctrl: pinctrl {
                compatible = "rockchip,rk3399-pinctrl"; #address-cells = <0x2>; #size-cells = <0x2>; gpio0: gpio0@ff720000 { compatible = "rockchip,gpio-bank"; reg = <0x0 0xff720000 0x0 0x100>; //前兩個數字表示一個地址0x0 0xff720000 //后兩個數字表示一個地址跨度 0x100 }; ... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.3interrupts

一個計算機系統中大量設備都是通過中斷請求CPU服務的,所以設備節點中就需要在指定中斷

  • interrupt-controller 一個空屬性用來聲明這個node接收中斷信號,即這個node是一個中斷控制器
  • #interrupt-cells,是中斷控制器節點的屬性,用來標識這個控制器需要幾個單位做中斷描述符,用來描述子節點中”interrupts”屬性使用了父節點中的interrupts屬性的具體的哪個值。一般,如果父節點的該屬性的值是3,則子節點的interrupts一個cell的三個32bits整數值分別為:<中斷域 中斷 觸發方式>,如果父節點的該屬性是2,則是<中斷 觸發方式>
  • interrupt-parent,標識此設備節點屬於哪一個中斷控制器,如果沒有設置這個屬性,會自動依附父節點的
  • interrupts,一個中斷標識符列表,表示每一個中斷輸出信號

3.4gpio

  • gpio-controller,用來說明該節點描述的是一個gpio控制器
  • #gpio-cells,用來描述gpio使用節點的屬性一個cell的內容,即 `屬性 = <&引用GPIO節點別名 GPIO標號 工作模式>
firefly-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>; /* GPIO0_B4 */ firefly-irq-gpio = <&gpio4 29 IRQ_TYPE_EDGE_RISING>; /* GPIO4_D5 */ 
  • 1
  • 2

4.DTB的加載過程

參考地址
http://blog.csdn.net/green1900/article/details/45646095
此處輸入圖片的描述
http://blog.csdn.net/lichengtongxiazai/article/details/38941913
此處輸入圖片的描述

總的歸納為:

① kernel入口處獲取到uboot傳過來的.dtb鏡像的基地址

② 通過early_init_dt_scan()函數來獲取kernel初始化時需要的bootargs和cmd_line等系統引導參數。

③ 調用unflatten_device_tree函數來解析dtb文件,構建一個由device_node結構連接而成的單向鏈表,並使用全局變量of_allnodes保存這個鏈表的頭指針。

④ 內核調用OF的API接口,獲取of_allnodes鏈表信息來初始化內核其他子系統、設備等。


5.API調用

#來查找在dtb中的根節點 unsigned long __init of_get_flat_dt_root(void) # 根據deice_node結構的full_name參數,在全局鏈表of_allnodes中,查找合適的device_node struct device_node *of_find_node_by_path(const char *path) #若from=NULL,則在全局鏈表of_allnodes中根據name查找合適的device_node struct device_node *of_find_node_by_name(struct device_node *from,const char *name) #根據設備類型查找相應的device_node struct device_node *of_find_node_by_type(struct device_node *from,const char *type) # 根據compatible字符串查找device_node struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible) #根據節點屬性的name查找device_node struct device_node *of_find_node_with_property(struct device_node *from,const char *prop_name) #根據compat參數與device node的compatible匹配,返回匹配度 int of_device_is_compatible(const struct device_node *device,const char *compat) #獲得父節點的device node struct device_node *of_get_parent(const struct device_node *node) #讀取該設備的第index個irq號 unsigned int irq_of_parse_and_map(struct device_node *dev, int index) #讀取該設備的第index個irq號,並填充一個irq資源結構體 int of_irq_to_resource(struct device_node *dev, int index, struct resource *r) #獲取該設備的irq個數 int of_irq_count(struct device_node *dev)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM