linux设备树是用于描述硬件及部分启动指令的文件,由bootloader传递给内核(U-boot需要在config文件中加入”#define CONFIG_OF_LIBFDT“),
内核分析此文件而对硬件使用不同的参数。
比如两块开发板仅仅是内存容量不一样,那么就只需要修改设备树中对内存容量的描述即可,
而不需要重新编译内核。
与设备树相关的文件有如下几种:
DTS(device tree source)
.dts文件,就是ASCII字符串形式的文本文件,直接由开发人员修改。
对于ARM架构而言,这些文件位于:arch/arm/boot/dts 目录下。
DTSI(device tree source include)
.dtsi文件,用于被.dts文件所包含。并且.dtsi文件也可以包含.dtsi文件。与c/c++ 包含头文件一个道理。
此文件包含了很多设备下所共有的许多配置。
在.dts文件下,使用”#include “file.dtsi”” 或”/include/ “file.dtsi”“来包含。
DTB(device tree blob)
通过工具提前将DTS文件编译为.dtb二进制文件,bootloader传递此文件给内核,这样内核的解析速度才快。
DTC(device tree compiler)
将.dts文件编译为.dtb的工具,其源码位于scripts/dtc目录下,此工具根据用户设定的SOC来确定编译哪种目标板。
Binding
对于设备树中的节点及属性具体描述文档,位于Document/devicetree/bindings,、
需要仔细阅读!需要仔细阅读!需要仔细阅读!
设备树的结构是由节点和节点中的属性构成的,这就如同c/c++中结构体名称和元素名称及元素值的关系一样。
#include "skelston.dtsi" /*包含*/ /{ node{ a-string-property = "string"; }; }; /* "/" 代表根节点,根节点是设备树的起始节点,一个设备文件中必须且仅有一个根节点,"node"是一个子节点名, "a-string-property"是一个属性名,"string"是属性值。 */
节点命名可以有如下几种方式:
1、以”设备名”为节点名
比如设备DM9000,对应的节点路径为:/dm9000
/{
...
dm9000{
...
};
...
};
2、以”设备名@I/O地址”为节点名:
比如设备DM9000位于I/O地址为0x8000 0000,对应的节点路径为:/dm9000@80000000
注意:这种方式下,若两个节点具有相同的设备名和不同的I/O地址,也同样代表两个不同的设备!
/{ ... dm9000@80000000{ ... }; ... };
如同linux中可以为命令命名别名(alias)一样,节点也可以先命名别名再引用。
注意:在节点引用的同时,其内容等同于被引用过来了。
/{ ... dm9000:dm9000@80000000{ ... }; ... &dm9000{ }; }; /* &dm9000就等价于 dm9000@80000000 */
若同一设备树中有相同的节点名及地址时,节点的内容会合并。
若节点内容中出现了相同的属性,则新属性值替换掉老的属性值。
比如在petalinux中可以修改顶层的设备树文件,将新的设置信息与老的合并或替换。而不用关心以前默认的设定值。
属性的内容比较灵活,可以为:
1、空(比如:an-empty-proerty;)
2、字符串(比如:a-string-property = “A string”;)
3、字符串数组(比如:a-string-list-property = “first string”,”second string”;)
4、整数(比如:a-cell-property = <1234>;)
5、16进制数(比如:a-byte-data-property = [0x01 0x02 0x03 0x04];)
有关属性名及内容更为具体的意义和使用方式,需要仔细反复阅读Documentation/devicetree/目录下的相关文件!
下面列出一些常用的属性:
1、compatible(兼容)
compatible的内容为字符串数组,其形式为:
其意义代表设备”厂商和哪个具体的型号”或者”具体设备名称和兼容设备”,linux内核中的解析代码,通过节点此属性来确定这个具体的驱动匹配,
比如在zynq-7000.dtsi中根节点属性:
compatible = "xlnx,zynq-7000"
在zynq-zed.dts中qspi结点中flash子节点的属性:
/{ ... &qspi{ ... flash@0{ compatible = "n25q128a11"; ... } }; ... };
2、reg(设备地址)
reg的内容一般为16进制数,其形式为:
,address代表此设备相对于父节点的偏移地址
length代表设备可以操作的地址范围,也就是地址大小
reg中的address和length数量的多少,是由#address-cells和#size-cells两个属性来决定子节点的reg内容。
比如:
在zynq-70000.dtsi文件中 cpus节点定义#address-cells = 1,#size-cells=0,代表子节点的reg属性中地址1位,大小位为空。
/{ ... cpus{ #address-cells = <1>; #size-cells = <0>; cpu@0{ .... reg = <0>; .... }; cpu@1{ .... reg = <1>; .... }; }; };
在ambs节点中定义#address-cells = 1,#size-cells = 1,代表子节点reg属性中地址和大小都为1位。
/{ amba:amba{ .... #address-cells = <1>; #size-cells = <1>; .... adc:adc@f8007100{ .... reg = <0xf8007100 0x20>; .... }; gpio0:gpio@e000a000{ ... reg = <0xe000a000 0x1000>; ... }; }; };
3、interrupt-controller(中断控制器)
此属性为空,通过此属性以表明此设备具有中断相关功能。
4、#interrupt-cells(表明interrupts属性中地址大小)
与#size-cells相似
5、interrupt-parent(指明此控制器所依附的中断控制器)
若子节点中没有指明此属性,则从父节点中继承。
比如zynq-70000.dtsi中的gpio控制器节点:
/{ .... gpio0:gpio@e00a000{ compatible = "xlnx,zynq-gpio-1.0"; #gpio-cells = <2>; #interrupt-cells = <2>; clocks = <&clkc 42>; gpio-controller; interrupt-controller; interrupt-parent = <&intc>; interrupts = <0 20 4>; reg = <0xe00a000 0x1000>; }; .... };