设备树语法
设备树(Device Tree)本质上就是一份写给操作系统的硬件说明书。
1. Devicetree格式
1.1 DTS文件的格式
DTS文件布局(layout):
/dts-v1/; // 表示版本
/* 设备树的内存保留区 告诉 Linux 内核:这块物理内存你不能用,留给硬件 / 固件 / 启动程序专用!*/
[memory reservations] // 格式为: /memreserve/ <address> <length>; 从物理地址 0x80000000 开始,保留长度 0x00100000(1MB),内核永远不会使用这段内存
/ {
[property definitions]
[child nodes]
};
1.2 node的格式
设备树中的基本单元,被称为“node”,其格式为:
/* label是标号,可以省略。label的作用是为了方便地引用node */
[label:] node-name[@unit-address] {
[properties definitions]
[child nodes]
};
比如:
/dts-v1/;
/ {
uart0: uart@fe001000 {
compatible="ns16550";
reg=<0xfe001000 0x100>;
};
};
可以使用下面2种方法来修改uart@fe001000这个node:
// 在根节点之外使用label引用node:
&uart0 {
status = “disabled”;
};
或在根节点之外使用全路径:
&{/uart@fe001000} {
status = “disabled”;
};
1.3 properties的格式
简单地说,properties就是“name=value”,value有多种取值方式。
Property格式1:
[label:] property-name = value;
Property格式2(没有值):
[label:] property-name;
Property取值只有3种:
arrays of cells(1个或多个32位数据, 64位数据使用2个32位数据表示),
string(字符串),
bytestring(1个或多个字节)
如:
device@12340000 {
compatible = "myvendor,mydevice"; // 字符串类型
reg = <0x12340000 0x100>; // cells数组类型
mac-address = [00 1A 2B 3C 4D 5E];// bytestring类型
interrupt-controller; // 无值属性
};
2. dts文件包含dtsi文件
设备树文件不需要我们从零写出来,内核支持了某款芯片,在内核的arch/arm/boot/dts目录下就有了能用的设备树模板,一般命名为xxxx.dtsi。“i”表示“include”,被别的文件引用的。
dts中可以包含.h头文件,也可以包含dtsi文件,在.h头文件中可以定义一些宏。
示例:
/dts-v1/;
#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"
/ {
……
};
3. 常用的属性
3.1 #address-cells、#size-cells
- cell指一个32位的数值,
- address-cells:address要用多少个32位数(cell)来表示;
- size-cells:size要用多少个32位数(cell)来表示。
它们是父节点写给子节点的 “说明书”,告诉子节点:你写地址和长度的时候,要按我定的规则来。
比如一段内存,怎么描述它的起始地址和大小?
下例中,address-cells为1,所以reg中用1个数来表示地址,即用0x80000000来表示地址;size-cells为1,所以reg中用1个数来表示大小,即用0x20000000表示大小:
合起来就是:内存从 0x80000000 开始,大小是 0x20000000(512MB)。
- 格式: 赋值为单个 32 位数字。
/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
reg = <0x80000000 0x20000000>;
};
};
3.2 compatible
- 作用:设备树中最重要的“接头暗号”,表示设备的兼容性。内核启动时,会严格根据这个字符串列表(从左到右的优先级)去寻找对应的 C 语言驱动程序。
- 格式:字符串列表。官方强烈建议采用 "manufacturer,model"("厂家名,模块名")的命名规范。
led {
compatible = “A”, “B”, “C”;// 内核中可能有A、B、C三个驱动都支持它
};
内核启动时,就会为这个LED按这样的优先顺序为它找到驱动程序:A、B、C。
compatible的值,建议取这样的形式:“manufacturer,model”,即“厂家名,模块名”。
3.3 model
- 作用: 用来向内核准确、唯一地宣告“这块板子/这个设备确切是什么型号”。因为多个不同的单板可能拥有相同的 compatible 兼容列表,内核需要依靠 model 属性来做最终的具体区分
- 格式: 一个精确的字符串。
{
compatible = "samsung,smdk2440", "samsung,mini2440";
model = "jz2440_v3";// model用来准确地定义这个硬件是什么
};
/*
这段代码定义了一个硬件平台的基本信息,告诉内核:
这个硬件平台与三星的SMDK2440和Mini2440兼容(优先匹SMDK2440)。
硬件平台的型号为“jz2440_v3”。
从compatible属性中可以知道它兼容哪些板,但是它到底是什么板?用model属性来明确。
*/
3.4 status
- 作用:dtsi文件中定义了很多设备,但是在你的板子上某些设备是没有的。这时你可以给这个设备节点添加一个status属性,设置为“disabled”:
- 格式:字符串。常用值为 "okay"(设备正常运行)或 "disabled"(设备停用/不加载驱动)。
&uart1 {
status = "disabled";
};
| value | description |
|---|---|
| okay | 设备正常运行 |
| disabled | 设备不可操作,但是后面可以恢复工作 |
| fail | 发生了严重错误,需修复 |
| fail-sss | 发生了严重错误,需修复;sss 表示错误信息 |
3.5 reg
- 作用:reg的本意是register,用来表示寄存器地址。但是在设备树里,它可以用来描述一段空间。反正对于ARM系统,寄存器和内存是统一编址的,即访寄存器时用某块地址,访问内存时用某块地址,在访问方法上没有区别。
- 格式:<address size address size ...> 的组合。reg属性的值,是一系列的“address size”,用多少个32位的数来表示address和size,由其父节点的#address-cells、#size-cells决定。
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
memory {
reg = <0x80000000 0x20000000>;
};
};
3.6 clocks 与 clock-names
- 作用:任何数字硬件都需要时钟信号才能工作。设备树负责分配时钟源。
- 格式:clocks 指向时钟提供者(通常是 CRU 节点),clock-names 给这个时钟起个别名,供 C 语言驱动中调用。
clocks = <&cru CLK_MIPI_CAMARAOUT_M2>;
clock-names = "xvclk";
3.7 xxx-gpios (GPIO 控制)
- 作用:用于控制硬件的复位(Reset)、休眠(Power down)或电源使能等状态。
- 格式:属性名通常由驱动规定(如 reset-gpios),值包含:引脚控制器、具体引脚号、有效电平(如高电平有效)。
reset-gpios = <&gpio3 RK_PB1 GPIO_ACTIVE_HIGH>;
3.8 interrupts (中断)
- 作用:当硬件有突发事件(如 USB 插入、数据接收完毕)时,主动通知 CPU 的“门铃”。
- 格式: 包含中断类型(如 GIC_SPI 共享中断)、中断号、触发方式(如电平触发/边缘触发)。
interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;
4. 常用的节点(node)
4.1 根节点
dts文件中必须有一个根节点:
/dts-v1/;
/ {
model = "SMDK24440";
compatible = "samsung,smdk2440";
#address-cells = <1>;
#size-cells = <1>;
};
根节点中必须有这些属性:
#address-cells // 在它的子节点的reg属性中, 使用多少个u32整数来描述地址(address)
#size-cells // 在它的子节点的reg属性中, 使用多少个u32整数来描述大小(size)
compatible // 定义一系列的字符串, 用来指定内核中哪个machine_desc可以支持本设备
// 即这个板子兼容哪些平台
// uImage : smdk2410 smdk2440 mini2440 ==> machine_desc
model // 咱这个板子是什么
// 比如有2款板子配置基本一致, 它们的compatible是一样的
// 那么就通过model来分辨这2款板子
4.2 CPU节点
一般不需要我们设置,在dtsi文件中都定义好了:
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
.......
}
};
4.3 memory节点
芯片厂家不可能事先确定你的板子使用多大的内存,所以memory节点需要板厂设置,比如:
memory {
reg = <0x80000000 0x20000000>;
};
4.4 chosen节点
我们可以通过设备树文件给内核传入一些参数,这要在chosen节点中设置bootargs属性:
chosen {
bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
};
5. Pinctrl (引脚复用系统)
核心属性:pinctrl-names 定义状态名(通常是 "default"),pinctrl-0 指向具体的引脚配置节点。
pinctrl-names = "default";
pinctrl-0 = <&mipim1_camera2_clk>; // 把物理针脚复用为 camera 时钟输出
6. 厂商相关内容
标准的设备树语法全世界通用,但不同的芯片原厂(如 Rockchip, Allwinner, Qualcomm)会定义自己独有的属性,用于特殊的业务逻辑匹配。
- 规则: 必须带有厂商前缀,如
rockchip,xxx。 - 出处:这些属性名和值的含义,并非凭空捏造,必须查阅原厂提供的 Binding 文档 或直接阅读 C 语言驱动源码。
devicetree rockchip,camera-module-name = "CMK-OT2016-FV1"; // 给 ISP 找对应调优文件的暗号
7. 静态枚举和动态发现
7.1 静态枚举
- 适用总线:I2C, SPI, MIPI, UART 等不可发现总线。
- 原理:CPU 无法感知引脚上挂了什么设备。
- 做法:必须在设备树中写清楚挂载了什么设备(如 ov13855),内核才能根据设备树去加载驱动。
7.2 动态发现
- 适用总线:USB, PCIe等。
- 原理:协议自带热插拔和电子名片(设备描述符)机制。设备插入时,硬件产生中断,总线读取名片上的 Class ID(如 0x0E 代表 Video),内核自动匹配对应驱动(如 uvcvideo)。
- 做法:设备树中只写控制器节点(火车站,如 dwc3 控制器和 PHY),绝对不写具体的外设节点(火车,如 U盘、键盘、摄像头)。
8. 一般模板
&csi2_dphy3 {
port@0 {
mipidphy1_in_ucam0: endpoint@0 {
};
mipidphy1_in_ucam1: endpoint@1 {
};
};
port@1 {
csidphy1_out: endpoint@0 {
};
};
};
};
8.1 第一层:大模块设备(大楼)—— &csi2_dphy3
代表了真实的、物理存在的硬件控制器实体
8.2 第二层:物理接口(大门)—— port@0 和 port@1
一个复杂的硬件往往有输入口和输出口。port 就是用来区分不同数据方向或不同物理接口的。通常行规是:port@0 作为输入(Input),port@1 作为输出(Output)。
8.3 第三层:连接端点(卸货通道/网线)—— endpoint@x
一个物理接口(大门)可能同时连接了多个外部设备。endpoint 就是大门里面具体划分的连接通道。冒号前面的 mipidphy1_in_ucam0: 只是个方便引用的标签(Label),冒号后面的 endpoint@0 才是它真正的节点名。

浙公网安备 33010602011771号