什么是设备树
在Linux3.x之前的内核源码中,存在大量对板级细节信息描述的代码。这些代码充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目录。为了解决这个问题而引入设备树。
官方对设备树的描述是,一种描述硬件资源的数据结构。它通过bootloader将硬件资源传给内核,使得内核和硬件资源描述相对独立。
设备树的主要优势:对于同一SOC的不同主板,只需更换设备树文件.dtb即可实现不同主板的无差异支持,而无需更换内核文件。
设备树组成
设备树包含DTC(device tree compiler),DTS(device tree source和DTB(device tree blob)。
DTS: dts文件是对Device Tree的描述,放置在内核的/arch/arm/boot/dts目录。一个*.dts文件对应一个ARM的machine。dts文件描述了一个板子的硬件资源。以前写在mach-xxx文件中的内容被转成了dts文件。
DTC: DTC为编译工具,它可以将.dts文件编译成.dtb文件。
DTB: DTC编译*.dts生成的二进制文件(.dtb),bootloader在引导内核时,会预先读取.dtb到内存,进而由内核解析。
设备树中还有一种文件 ------- dtsi文件,由于一个SOC可能有多个不同的电路板,而每个电路板拥有一个 .dts。这些dts势必会存在许多共同部分,为了减少代码的冗余,设备树将这些共同部分提炼保存在.dtsi文件中,供不同的dts共同使用。
语法规则
一、设备树语法
(1)设备树节点语法
[label:] node-name[@unit-address] { [properties definitions]; [child nodes];};
解释:
label: 可选项,节点别名
node-name: 节点名
unit-address: 设备地址
properties definitions:属性定义
child nodes:子节点
(2) 属性定义语法
[label:] property-name = value;[label:] property-name;
属性分为有属性值和无属性值
属性值有三种取值:
arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示),用尖括号表示(< >)
string(字符串), 用双引号表示(" ")
bytestring(1个或多个字节),用方括号表示([ ])
二、特殊属性
(1)根节点
(2) /memory
所有设备树文件的必需节点,它定义了系统物理内存的 layout
device_type = "memory";reg // 用来指定内存的地址、大小
(3) /chosen
传递runtime parameter
bootargs // 内核command line参数, 跟u-boot中设置的bootargs作用一样
(4) /cpus
/cpus节点下有1个或多个cpu子节点, cpu子节点中用reg属性用来标明自己是哪一个cpu
(5) aliases
用来定义别名
aliases { ethernet0 = &gmac; i2c0 = &i2c0; i2c1 = &i2c1; i2c2 = &i2c2;
mshc0 = &emmc; mshc1 = &sdmmc; mshc2 = &sdio0; mshc3 = &sdio1;
serial0 = &uart0; serial1 = &uart1; spi0 = &spi0; spi1 = &spi1; };
三、引用其他节点
(1) phandle属性引用
节点中的phandle属性, 它的取值必须是唯一的(不要跟其他的phandle值一样)
pic@10000000 {
phandle = <1>;
interrupt-controller;
};
another-device-node { interrupt-parent = <1>; // 使用phandle值为1来引用上述节点};
(2)使用别名(本质还是phandle)
PIC: pic@10000000 { interrupt-controller;};
another-device-node { interrupt-parent = <&PIC>; // 使用label来引用上述节点
四、包含dtsi文件
#include "rk3288-firefly.dtsi"
五、使用宏定义
六、属性重写
//rk3288.dtsihdmi: hdmi@ff980000 { compatible = "rockchip,rk3288-dw-hdmi";
reg = <0x0 0xff980000 0x0 0x20000>; reg-io-width = <4>; #sound-dai-cells = <0>;
rockchip,grf = <&grf>; interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru PCLK_HDMI_CTRL>, <&cru SCLK_HDMI_HDCP>, <&cru SCLK_HDMI_CEC>;
clock-names = "iahb", "isfr", "cec"; power-domains = <&power RK3288_PD_VIO>;
status = "disabled";};
//rk3288-firefly.dtsi&hdmi { ddc-i2c-bus = <&i2c5>; status = "okay";};
上面的rk3288-firefly.dtsi包含rk3288.dtsi, 然后使用&hdmi引用rk3288.dtsi中的hdmi节点。并在原有的基础上添加ddc-i2c-bus属性,然后将status属性进行重写覆盖。这就体现了dtsi文件的价值,dtsi定义公共的部分,板级差异再进行添加或重写。