Linux设备树入门
- 0.参考资料
- 1.什么是设备树
- 2.为什么要使用设备树来替代传统的总线设备驱动模型
- [3.DTS、DTB 和 DTC](#3.DTS、DTB 和 DTC)
- [4. DTS 语法](#4. DTS 语法)
-
- [4.1 .dtsi 头文件](#4.1 .dtsi 头文件)
- [4.2 设备节点](#4.2 设备节点)
- [4.3 标准属性](#4.3 标准属性)
-
- [4.3.1compatible 属性](#4.3.1compatible 属性)
- [5. DTS 编译](#5. DTS 编译)
-
- [5.1 内核编译设备树](#5.1 内核编译设备树)
- [5.2 dtc 工具编译设备树](#5.2 dtc 工具编译设备树)
0.参考资料
1\][Linux:设备树学习篇(1)](https://cloud.tencent.com/developer/article/2163827?areaSource=102001.6&traceId=hhtPf_4sC0cKz-3kJugIT)
\[2\][初学LINUX(4):Linux的设备树](https://blog.csdn.net/Jlcczhangxu/article/details/145736681)
\[3\][Linux 设备树 DTS 与 DTSI 入门指南](https://blog.csdn.net/is0815/article/details/148365754)
\[4\][详解Linux设备树源码DTS格式](https://edu.51cto.com/article/note/4859.html)
\[5\][camera驱动开发 之 DTS](https://www.cnblogs.com/chivalrySun/p/18843294)
## 1.什么是设备树
设备树是一种数据结构,它通过特有的语法格式描述片上片外的设备信息。由BootLoader传递给kernel,kernel进行解析后形成和驱动程序关联的dev结构供驱动代码使用。
描述设备树的文件叫做 DTS(DeviceTree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、 内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备等等。
树的主干就是系统总线,IIC 控制器、GPIO 控制器、SPI 控制器等都是接到系统主线上的分支。IIC 控制器有分为 IIC1 和 IIC2 两种,其中 IIC1 上接了 FT5206 和 AT24C02这两个 IIC 设备,IIC2 上只接了 MPU6050 这个设备。DTS 文件的主要功能就是按照图所示的结构来描述板子上的设备信息。
## 2.为什么要使用设备树来替代传统的总线设备驱动模型
传统的总线设备驱动是将设备信息描述在C代码中,当需要修改驱动相关的硬件信息时,就得修改具体的代码文件,再全编译内核。整个操作繁琐且不利于代码的维护和移植。
设备树的方式将驱动和设备完全分离开。将驱动程序设计成硬件无关的类型,一切设备资源(比如memory,interrupt,clk,pinctrl)在设备树文件中定义。内核来适配驱动和设备信息。将有效的设备信息通过参数传递给驱动的probe函数,再进行具体硬件的初始化。这样当硬件出现变更时(各公司基于芯片公版单独设计PCB等情况),只需要去修改对应的设备树文件,而完全不用去更改驱动代码。驱动的通用性也会大大提供。这样多个系列芯片只需要共用同一套驱动代码,差分设备树文件就可以。
## 3.DTS、DTB 和 DTC
设备树的代码文件是dts文件和dtsi文件。
* dts 是设备树源码文件:
* dtsi文件类似include头文件,可以被dts文件包含;
* dtb是将 dts编译以后得到的二进制文件。
dts文件会被dtc(设备树编译器)编译为dtb(device tree block)的二进制文件。该文件会被烧写到内存的特定地址(由BootLoader指定,原则上随意,只要不覆盖了boot和kernel的内容就好)。再由BootLoader将地址通过参数传递给kernel。kernel根据dtb文件的特定格式解析出有效的设备信息,从而传递给驱动代码。
dtc 工具源码在 Linux 内核的 scripts/dtc 目录下:

DTC 工具依赖于 dtc.c、flattree.c、fstree.c 等文件,最终编译出 DTC 这个主机文件。如果要编译 DTS 文件的话只需要进入到 Linux 源码根目录下,然后执行如下命令:make all 或者 make dtbs。
## 4. DTS 语法
### 4.1 .dtsi 头文件
和 C 语言一样,设备树也支持头文件,设备树的头文件扩展名为.dtsi。与此同时,.dts 文件也可以引用 C 语言中的.h 文件,甚至也可以引用.dts 文件。
一般.dtsi 文件用于描述 SOC 的内部外设信息,比如 CPU 架构、主频、外设寄存器地址范围,比如 UART、IIC 等等。
### 4.2 设备节点
设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设 备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键---值对。
设备树的基本单元是节点,由根节点(/)和其子节点(name@addr)组成,子节点也可以有子节点,形成一个树状结构。
```c
/include/ "spear3xx.dtsi"
/ {
ahb {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges = <0x60000000 0x60000000 0x50000000
0xd0000000 0xd0000000 0x30000000>;
pinmux@99000000 {
compatible = "st,spear300-pinmux";
reg = <0x99000000 0x1000>;
};
clcd@60000000 {
compatible = "arm,pl110", "arm,primecell";
reg = <0x60000000 0x1000>;
interrupts = <30>;
status = "disabled";
};
fsmc: flash@94000000 {
compatible = "st,spear600-fsmc-nand";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x94000000 0x1000 /* FSMC Register */
0x80000000 0x0010 /* NAND Base DATA */
0x80020000 0x0010 /* NAND Base ADDR */
0x80010000 0x0010>; /* NAND Base CMD */
reg-names = "fsmc_regs", "nand_data", "nand_addr", "nand_cmd";
status = "disabled";
};
sdhci@70000000 {
compatible = "st,sdhci-spear";
reg = <0x70000000 0x100>;
interrupts = <1>;
status = "disabled";
};
shirq: interrupt-controller@0x50000000 {
compatible = "st,spear300-shirq";
reg = <0x50000000 0x1000>;
interrupts = <28>;
#interrupt-cells = <1>;
interrupt-controller;
};
apb {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges = <0xa0000000 0xa0000000 0x10000000
0xd0000000 0xd0000000 0x30000000>;
gpio1: gpio@a9000000 {
#gpio-cells = <2>;
compatible = "arm,pl061", "arm,primecell";
gpio-controller;
reg = <0xa9000000 0x1000>;
interrupts = <8>;
interrupt-parent = <&shirq>;
status = "disabled";
};
kbd@a0000000 {
compatible = "st,spear300-kbd";
reg = <0xa0000000 0x1000>;
interrupts = <7>;
interrupt-parent = <&shirq>;
status = "disabled";
};
};
};
};
```
上例中:"/"是根节点,每个设备树文件只有一个根节点。ahb 是子节点,在设备树中节点命名格式如下:
```c
node-name@unit-address
```
"node-name"是节点名字,为 ASCII 字符串,节点名字应该能够清晰的描述出节点的功能,比如"uart1"就表示这个节点是 UART1 外设。"unit-address"一般表示设备的地址或寄存器首地址,如果某个节点没有地址或者寄存器的话"unit-address"可以不要,比如"cpu@0"、"interrupt-controller@00a01000"。另一种格式:
```c
label: node-name@unit-address
```
引入 label 的目的就是为了方便访问节点,可以直接通过\&label 来访问这个节点,比如通过\&cpu0 就可以访问"cpu@0"这个节点,而不需要输入完整的节点名字。
### 4.3 标准属性
节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以 自定义属性。除了用户自定义属性,有很多属性是标准属性,Linux 下的很多外设驱动都会使用这些标准属性,几个常用的标准属性:
#### 4.3.1compatible 属性
compatible 属性也叫"兼容性"属性,可以理解为节点的兼容ID,它的值是一个字符串列表,内核通过它和驱动进行匹配。根节点的compatible更多表示该设备树所支持的平台类型。字符串列表用于选择设备所要使用的驱动程序,compatible 属性的值格式如下所示:
```c
"manufacturer,model"
```
manufacturer 表示厂商,model 一般是模块对应的驱动名字。
**1.model 属性**
model 属性值也是一个字符串,一般 model 属性描述设备模块信息,比如名字。
```c
/ {
model = "Q8 A33 Tablet";
compatible = "allwinner,q8-a33", "allwinner,sun8i-a33";
};
```
compatible = "allwinner,q8-a33", "allwinner,sun8i-a33";则表示当前设备树支持allwinner的q8-a33平台和sun8i-a33平台。当内核运行对应arch目录下的mach平台文件时,会匹配到这个设备树,然后进行加载。
**2.status 属性**
status 属性是和设备状态有关的,status 属性值也是字符串,字符串是设备的状态信息。

**3.address-cells属性和size-cells属性**
这两个属性的值都是无符号 32 位整形,#address-cells 和#size-cells 属性可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。#address-cells 属性值决定了子节点 reg 属性中地址信息所占用的字长(32 位),#size-cells 属性值决定了子节点 reg 属性中长度信息所占的字长(32 位)。#address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值,一般 reg 属性都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度,reg 属性的格式一为:
```c
reg =