设备树的起源
设备树(Device Tree)是一种描述硬件资源的数据结构,它由 uboot 传递给 Linux 内核,被内核解析,内核根据设备树中的硬件描述信息加载利用相应驱动资源。在引入设备树之前,Linux 内核中充斥着大量的用于描述板级硬件信息的文件,拥有不同硬件资源的板卡 ,都有其对应的板级文件或者冗余文件,随着 Linux 内核支持的板卡越来越多,板级文件也越来越多。Linus Torvalds 在 2011 年 3 月 17 日的 ARMLinux 邮件列表宣称“this whole ARM thing is a fucking pain in the ass”,因此,ARM Linux 社区作出了回应,引入了设备树 Device Tree,这样一来许多硬件的细节可以直接透过设备树传递给 Linux 内核,而不再需要在内核中进行大量的冗余编码来适配不同的板卡。
设备树 设备树 组成和结构及 及 dts 、dtb 、dtsi
设备树 Device Tree 由一系列被命名的节点(node)和属性(property)组成,而节点本身可包含子节点。所谓属性,其实就是成对出现的 name 和 value。在设备树中,可描述的信息包括:
- CPU 的数量和类别
- 内存基地址和大小
- 总线和桥
- 外设连接
- 中断控制器和中断使用情况
- GPIO 控制器和 GPIO 使用情况
- Clock 控制器和 Clock 使用情况
这种以树状节点的方式描述一个设备的各种硬件信息:CPU、GPIO、时钟、中断、内存等,形成类似文本文件,很好的解决了这些问题。这种描述设备的方法更像编程中的结构的定义方法。对比设备树形成之前的描述方法(文件),相当于c语言中的配置文件,个人认为,如果这个设备树也类似于现在xml文件的一种表达方法,都可以这么了解。
通常由.dts 文件以文本方式对系统设备树进行描述,经过 Device Tree Compiler(dtc)将 dts 文件转换成二进制文件 binary device tree blob(dtb),.dtb 文件可由 Linux 内核解析,有了 device tree 就可以在不改动 Linux 内核的情况下,对不同的平台实现无差异的支持,硬件有变动时不需要重新编译内核或驱动程序,只需更换相应的 dts 文件即可。
基于同样的软件分层设计的思想,由于一个SoC可能对应多个machine,如果每个machine的设备树都写成一个完全独立的.dts文件,那么势必相当一些.dts文件有重复的部分,为了解决这个问题,Linux设备树目录把一个SoC公用的部分或者多个machine共同的部分提炼为相应的.dtsi文件。这样每个.dts就只有自己差异的部分,公有的部分只需要"include"相应的.dtsi文件, 这样就是整个设备树的管理更加有序。
设备组织架构
设备树用树状结构描述设备信息,它有以下几种特性
- 每个设备树文件都有一个根节点,每个设备都是一个节点。
- 节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。
- 每个设备的属性都用一组key-value对(键值对)来描述。
- 每个属性的描述用
;结束
下图是一个设备树文件的基本架构示例:

简单概括一下有这几个部分:
1. 节点
节点的命名方式一般为 node-name@unit-address:
其中 node-name 为节点名称,unit-address 为节点地址。例如上示例图中 node@0 的 node 为节点名称; 0 就是节点的地址,地址主要是为了区分其他的节点以保证节点的唯一性,节点地址是非必须的,可以省略。
在一个节点下面可包含一系列的子节点,例如上示例图的 note@0 下面又可包含其自己的子节点
child-node@0 和 child-node@1。
下面是几种常见的节点:
1)根节点:设备树文件中都包含一个根节点,使用“/”表示。
2)CPU 节点

3)memory 节点
该节点是设置内存起始地址及其大小。

2. 属性
每个节点都有不同属性,属性包括属性名称和属性值,属性值可以为空或任意的字节流属性,一般属
性值有三类:
string 字符串型,使用双引号“”包含;
byte 数组型:使用方括号[]包含;
u32 型:使用尖括号<>包含。
如示例图中节点
node@0 的第一个属性名称是 a-string-property,属性值为字符串“string”;
node@0 的第三个属性名称为 a-byte-data-property,其属性值为 byte 数组:[0x01 0x02 0x03 0x04],
node1(node@1)节点的 a-cell-property 属性值为 u32 型<1 2 3 4>。
常见的属性:
1) compatible:
compatible 是兼容的意思,即该设备可被一个或者多个驱动匹配。例如在
arch/arm/boot/dts/imx6ull-14x14-evk.dts 文件中的 pxp_v4l2 节点:
此节点的 Compatible 的值为“"fsl,imx6ul-pxp-v4l2", "fsl,imx6sx-pxp-v4l2", "fsl,imx6sl-pxp-v4l2",内核在解析设备树之后,会首先根据“fsl,imx6ul-pxp-v4l2”查找匹配驱动模块,其中“fsl”是厂商名称, “mx6ul-pxp-v4l2”是驱动模块的名称。如果没有匹配到此驱动模块,就会再次使用” fsl,imx6sx-pxp-v4l2”进行查找匹配,依次类推。
2) model
model 属性用于表示设备型号信息。如下图中 model = "Freescale i.MX6 ULL 14x14 EVK Board",表示该设备为 freescale 的 i.MX6ULL EVK 板。

再如下图 sound 节点,设备型号信息为 wm8960-audio:

3) #address-cells、#size-cells、reg
reg 属性用来表示节点地址资源的,属性值一般是寄存器的起始地址及地址之后连续的空间长度。
#address-cells、#size-cells 分别表示子节点中 reg 的地址以及地址之后连续的空间长度用几个 32bit数据表示。


如上图,#address-cells=<1>,表示其子节点 ocrams 中 reg 的地址是用一个 32bit 数据表示,上图中的 0x00900000。#size-cells=<1>,说明其子节点 ocrams 中 reg 地址之后的空间长度也是用一个 32bit 数据表示,上图中的 0x4000。
4) Status
Status 属性用来表示节点的状态,即相关硬件的状态,用字符串表示。'okay'表示硬件正常工作,
“disabled”表示硬件当前不可用,“fail”表示因为出错不可用,“fail-sss”表示因为某种原因出错不可
用,sss 表示具体的出错原因。实际中,基本只用'okay'和'disabled'。 
3. 标签
图中的 node1: node@1 节点,其中 node1 就是本节点的一个标签,标签不是必须的,定义标签主要为了方便在其他地方对该节点进行引用的时候,不必写该节点的全路径,直接使用标签即可。如在node@0 的子节点 child-node@0 的第三个属性就引用了 node1(&node1)。
4.引用
当我们找一个节点的时候,我们必须书写完整的节点路径,这样当一个节点嵌套比较深的时候就不是很方便,所以,设备树允许我们用下面的形式为节点标注引用(起别名),借以省去冗长的路径。这样就可以实现类似函数调用的效果。编译设备树的时候,相同的节点的不同属性信息都会被合并,相同节点的相同的属性会被重写,使用引用可以避免移植者四处找节点,直接在板级.dts增改即可。

下面的例子中就是直接引用了dtsi中的一个节点,并向其中添加/修改新的属性信息

5.key
在设备树中,键值对是描述属性的方式,比如,Linux驱动中可以通过设备节点中的"compatible"这个属性查找设备节点。
Linux设备树语法中定义了一些具有规范意义的属性,包括:compatible, address, interrupt等,这些信息能够在内核初始化找到节点的时候,自动解析生成相应的设备信息。此外,还有一些Linux内核定义好的,一类设备通用的有默认意义的属性,这些属性一般不能被内核自动解析生成相应的设备信息,但是内核已经编写的相应的解析提取函数,常见的有 "mac_addr","gpio","clock","power"。"regulator" 等等。
设备树 Makefile 修改
在 arch/arm/boot/dts/目录下会有很多.dts 文件被编译成.dtb 文件,通过配置 Makefile 文件可以对这些dts 文件进行选择性编译,打开 arch/arm/boot/dts/Makefile:
以OKMX6ULL-S2为例:

400 行,有一个相关平台配置的变量,我们使用的是 i.MX6ULL 平台,所以,编译器会编译生成 400行命令下.dtb 文件,这些.dtb 都应有其对应的.dts 文件。如果我们因为板子硬件改动或硬件外设配置不同而新增加了 dts,则需要在这个 Makefile 的这个位置添加上对应的.dtb 文件名,参与编译。
