一、設備樹的基本知識
1、什么是設備樹?為什么會有設備樹?
????????2011年,Linux之父Linus Torvalds發現這個問題后,就通過郵件向ARM-Linux開發社區發了一封郵件,不禁的發出了一句“This whole ARM thing is a f*cking pain in the ass”。之后ARM Linux社區引入了設備樹。為什么LinusTorvalds會爆粗口呢?
????????平臺總線模型是把驅動分成了倆個部分,一部分是device,一部分是driver,設備信息和驅動分離這個設計非常的好。device部分是描述硬件的。一般device部分的代碼會放在內核源碼中arch/arm/plat-xxx和arch/arm/mach-xxx下面。但是隨著Linux支持的硬件越來越多,在內核源碼下關于硬件描述的代碼也越來越多。并且每修改一下就要編譯一次內核。長此以往Linux內核里面就存在了大量“垃圾代碼”,而且非常多,這里說的“垃圾代碼”是關于對硬件描述的代碼。從長遠看,這些代碼對Linux內核本身并沒有幫助,所以相當于Linux內核是“垃圾代碼”。但是并不是說平臺總線這種方法不好。
????????為了解決這個問題,設備樹就被引入到了Linux上。使用設備樹來剔除相對內核來說的“垃圾代碼”,既用設備樹來描述硬件信息,用來替代原來的device部分的代碼。雖然用設備樹替換了原來的device部分,但是平臺總線模型的匹配和使用基本不變。并且對硬件修改以后不必重新編譯內核。直接需要將設備樹文件編譯成二進制文件,在通過bootloader傳遞給內核即可。
????????所以設備樹就是用來描述硬件資源的文件。
2、設備樹的基本概念
①為什么叫設備樹?
設備樹是描述硬件的文本文件,因為語法結構像樹一樣。所以叫設備樹.
如下圖:
②基本名詞解釋
<1>DT:DeviceTree //設備樹
<2>FDT:Flattened Device Tree //開放設備樹,起源于OpenFirmware(OF)
<3>DTS:DeviceTree source的縮寫 //設備樹源碼
<4>DTSI:DeviceTree sourcein Clude的縮寫 //通用的設備樹源碼
<5>DTB:DeviceTree Blob的縮寫 //編譯設備樹源碼得到的文件
<6>DTC:DeviceTree Compiler的縮寫 //設備樹編譯器
DTS、DTSI、DTC、DTB之間的關系:
Linux內核通過讀取設備樹的描述文件(通常是.dtb或.dts文件),來構建一個內存中的表示,內核和驅動程序則使用這個表示來識別和配置硬件設備。設備樹文件在編譯時從設備樹源文件(.dts)轉換成二進制形式(.dtb),然后通常被包含在內核映像或者作為獨立文件提供給引導加載程序
補充:
dts????
dts文件是一種ASCII文本格式的設備樹描述文件,此文件適合人類閱讀,主要是給用戶看的。?硬件的相應信息都會寫在.dts為后綴的文件中,每一款硬件可以單獨寫一份xxxx.dts,一般在Linux源碼中存在大量的dts文件,對于?arm 架構可以在arch/arm/boot/dts找到相應的dts。對于rk3399開發板arch/arm/boot/dts/rk3399-nanopi4-common.dtsi中一般會包含一個公共部分的dtsi文件,如下:#include "rk3399-nanopi4-rkisp1.dtsi"
dtsi????
對于一些相同的dts配置可以抽象到dtsi文件中,然后類似于?C 語言的方式可以include到dts文件中
二、編譯設備樹?(DTC編譯器的使用)
DTC編譯器命令格式:
編譯設備樹:dtc -I?dts -O dtb -o?xxx.dtb xxx.dts
反編譯設備樹:dtc -I dtb -O?dts -o?xxx.dts xxx.dtb
三、設備樹的基本語法
1、設備樹根節點
根節點是設備樹必須要包含的節點。根節點的名字是/。
/dts-v1/;? ? ? //第一行表示dts文件的版本
/{????????????????//根節點
};
2、設備樹子節點格式
格式:
label:?node-name?@unit-address{
????????properties definitions
????????child nodes};
舉例:
nodel{????????????????????????//子節點,節點名稱為nodel
????????nodel_child{????????????????//子子節點,節點名稱為nodel_child????????}
};
注意:同級節點下節點名稱不能相同。不通級節點名稱可以相同??
3、節點名稱
在對節點進行命名的時候,一般要體現設備的類型,比如網口一般命名成ethernet,串口一般命名成uart,對于名稱一般要遵循下面的命令格式。
格式:標簽:名稱@<設備地址>
其中,[標簽]和[@<設備地址>]是可選項,<名稱>是必選項。另外,這里的設備地址沒有實際意義,只是讓節點名稱更人性化,更方便閱讀。
舉例:
uart: serial@02288000
其中,uart就是這個節點標簽,也叫別名,serial@02288000就是節點名稱。?
4、reg屬性
reg屬性可以來描述地址信息。比如寄存器的地址。
reg屬性的格式如下:
reg =<address1?length1?address2 length2 address3 length3...>
舉例:
reg= <0x02200000 0x4000>;
例
reg= <x022000000x4000
? ? ? ? ? 0x02205000 0x4000>;
5、#address-cell和#size-cells屬性
#address-cell和#size-cells用來描述子節點中的reg信息中的地址和長度信息
舉例:
nodel{
????????#address-cells = <1>;
????????#size-cells = <0>;
???nodel-child {
? ? ? ? ? ?reg = <0>;????????};
};
6、model屬性
model屬性的值是一個字符串,一般用model描述一些信息。比如設備的名稱,名字等。
舉例1:
model ="wm8960-audio”;
舉例2:
model = "This is Linux board";
7、status屬性
status屬性是和設備的狀態有關系的,status的屬性值是字符串。屬性值有下面幾個狀態可選:
8、compatible屬性
compatible屬性是非常重要的一個屬性。compatible是用來和驅動進行匹配的,匹配成功以后
會執行驅動中的probe函數。
舉例:
compatible ="xunwei”, "xunwei-board";
在匹配的時候會先使用第一個值xunwei進行匹配,如果沒有就會使用第二個值xunwei-board進行匹配。
示例:
9、設備樹特殊節點
aliases
????????特殊節點aliases用來定義別名。定義別名的目的就是為了方便引用節點。當然,除了使用aliases來命名別用,也可以在對節點命名的適合添加標簽來命名別名。
舉例:
aliases{
????????mmc0?= &sdmmc0;
????????mmcl = &sdmmcl;
????????mmc2 = &sdhci;
????????serial0 ="/simple@fe000000/serial@llc500”;};
chosen
chosen節點用來uboot給內核傳遞參數。重點是bootargs參數。chosen節點必須是根節點的子節點。
舉例:
chosen{
????????bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200”;?}
device_type屬性
在某些設備樹文件中,可以看到device_type屬性,device_type屬性的值是字符串,只用于cpu節點或者memory節點進行描述。
舉例1:
memory@30000000{
????????device_type =“memory”;
????????reg =<0x30000000????0x4000000>;
?舉例2:
cpu1: cpu@1{
????????device_type = "cpu”;
????????compatible = "arm,cortex-a35″, "arm, armv8”;
????????reg = <0x0? 0x1>;};
自定義屬性
設備樹中規定的屬性有時候并不能滿足我們的需求,這時候我們可以自定義屬性。
舉例:
自定義一個管腳標號的屬性pinnum。
pinnum = <0 1 2 3 4>;?
示例:
?
四、DTS基本框架
設備樹由一系列被命名的節點(Node)和屬性(Property)組成,而節點本身可包含子節點。在設備樹中,可描述的信息包括:
·??CPU的數量和類別。
·??內存基地址和大小。
·??總線和橋。
·??外設連接。
·??中斷控制器和中斷使用情況。
·??GPIO控制器和GPIO使用情況。
·??時鐘控制器和時鐘使用情況。
基本上就是畫一棵電路板上CPU、總線、設備組成的樹,Bootloader會將這棵樹傳遞給內核,然后內核可以識別這棵樹,并根據它展開出Linux內核中的platform_device、i2c_client、spi_device等設備,而這些設備用到的內存、IRQ等資源,也被傳遞給了內核,內核會將這些資源綁定給展開的相應的設備。
//一級節點:會展開成platfor_device
//內核會把這個節點展開成平臺設備,設備名是ff720000.xyd-leds
補充:增加設備樹屬性信息,可以自動生成設備端代碼。
對平臺設備驅動模型來說,平臺設備代碼(platform_device.c)不需要實現,被設備樹中設備文件所替代了。
如何添加設備樹?
以rk3399作為例子
kernel-rockchip-nanopi4-linux-v4.4.y/arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi
需要添加的硬件資源:
xyd-leds@FF720000{
???? compatible="xyd-rk3399-leds";
???? reg=<0x0 0xFF720000 0x0 0x10000>;
???? reg-names="gpio0-reg-for-leds";
???? led-total=<1>;
????};
?
編譯:在內核源碼的頂層目錄執行以下命令:
make nanopi4-images -j8????
把生成的resource.img 和 kernel.img?燒寫到開發板上。
編寫平臺驅動模型里面的驅動代碼,修改設備樹,編譯內核kernel.img and resource.img文件燒寫到板子上.
最終結果: