2. 设备树基础回顾:核心语法与关键属性
设备树(Device Tree,DT)是嵌入式 Linux 驱动开发中描述硬件拓扑的标准机制。它使用树形结构将 CPU、内存、外设、中断控制器等硬件资源以节点和属性的形式呈现,供内核在启动时解析并生成平台设备。理解设备树的语法与核心属性,是排查 reg 地址映射错误的前提。
2.1 设备树语法概览
设备树源文件(.dts)采用人类可读的文本格式,经过 dtc 编译为二进制 .dtb 后由 Bootloader 传递给内核。其基本结构如下:
/dts-v1/;
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
chosen {
stdout-path = &uart0;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 256 MB */
};
leds {
compatible = "gpio-leds";
led0 {
label = "beaglebone:green:usr0";
gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
};
};
};
关键点:设备树以根节点
/ 开始,每个节点用 node-name@unit-address 标识。unit-address 通常与 reg 属性的首地址一致,用于区分同名节点。
2.2 节点与属性
节点是设备树的骨架,属性是节点的具体描述。下表列出最常用的属性及其用途:
| 属性名 | 类型 | 用途说明 | 示例 |
|---|---|---|---|
compatible |
字符串列表 | 匹配驱动,格式为 "厂商,型号" |
"ti,am335x-uart" |
reg |
u32 数组 | 描述地址空间:<起始地址 长度> |
<0x44E09000 0x2000> |
interrupts |
u32 数组 | 描述中断号与触发类型 | <0 56 4> |
interrupt-parent |
phandle | 指定中断控制器节点 | &intc |
clocks |
phandle 列表 | 引用时钟源 | <&clk_uart1> |
status |
字符串 | 设备状态:"okay" / "disabled" |
"okay" |
2.3 compatible 属性:驱动匹配的钥匙
compatible 是驱动与设备绑定的核心桥梁。内核在解析设备树时,会遍历节点的 compatible 列表,与驱动中 of_match_table 声明的字符串逐一比对。匹配顺序遵循“最具体优先”原则:
- 格式规范:
"厂商,设备型号",如"fsl,imx6q-uart"。 - 回退机制:可列出多个值,内核按顺序匹配。例如
"ti,am335x-uart", "ns16550"表示优先使用 TI 专用驱动,否则回退到通用 16550 驱动。 - 常见错误:拼写错误、厂商前缀未注册、大小写不一致,导致驱动无法加载。
调试技巧:在驱动入口打印
of_match_device 的返回值,确认匹配是否成功。若返回 NULL,则检查 compatible 字符串是否完全一致。
2.4 reg 属性:地址映射的基石
reg 属性描述设备在总线上的地址范围,格式为 <address1 length1 address2 length2 ...>。对于内存映射外设,内核通过 of_iomap() 或 platform_get_resource() 读取 reg 并建立虚拟地址映射。
/* 设备树片段:UART1 控制器 */
uart1: serial@44E09000 {
compatible = "ti,am335x-uart", "ns16550";
reg = <0x44E09000 0x2000>; /* 起始物理地址 0x44E09000,长度 8KB */
interrupts = <72>;
clock-frequency = <48000000>;
status = "okay";
};
reg 的解析依赖 #address-cells 和 #size-cells 属性:
| 属性 | 含义 | 默认值 | 示例 |
|---|---|---|---|
#address-cells |
地址字段占用的 u32 个数 | 2 | 1 表示 32 位地址 |
#size-cells |
长度字段占用的 u32 个数 | 1 | 1 表示 32 位长度 |
常见陷阱:若父节点
#address-cells = <2>,则 reg 中的地址必须写两个 u32,如 <0x0 0x44E09000 0x0 0x2000>。遗漏会导致地址解析错位,映射到错误的物理地址。
2.5 interrupts 属性:中断路由描述
interrupts 属性描述设备的中断请求线。其格式由中断控制器节点的 #interrupt-cells 决定。以 ARM GIC(通用中断控制器)为例:
/* GIC 中断控制器 */
intc: interrupt-controller@48200000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>; /* 中断类型、中断号、触发标志 */
interrupt-controller;
reg = <0x48200000 0x1000>;
};
/* 使用示例:GPIO 按键中断 */
key {
compatible = "gpio-keys";
interrupt-parent = <&intc>;
interrupts = <0 30 4>; /* SPI 中断,中断号 30,高电平触发 */
};
中断号与触发类型的常见组合:
| 触发标志值 | 含义 |
|---|---|
| 0 | 未指定 |
| 1 | 上升沿触发 |
| 2 | 下降沿触发 |
| 4 | 高电平触发 |
| 8 | 低电平触发 |
2.6 设备树结构总览
下图展示了一个典型嵌入式系统的设备树层级关系,包含根节点、内存节点、中断控制器、外设节点及其属性:
2.7 核心概念总结
- 节点:树形结构的基本单元,通过
node-name@unit-address唯一标识。 - 属性:描述节点特征,
compatible用于驱动匹配,reg用于地址映射,interrupts用于中断路由。 - 地址单元:
#address-cells和#size-cells决定reg的解析方式,错误配置会导致地址错位。 - 中断父节点:
interrupt-parent指定中断控制器,#interrupt-cells决定中断描述格式。
学习建议:在调试前,先使用
dtc -I dtb -O dts -o dump.dts your.dtb 反编译设备树,检查 reg 和 interrupts 的数值是否与硬件手册一致。这是排查地址映射错误的第一步。