3. 设备树节点与属性:标准属性详解与使用规范
设备树(Device Tree)是嵌入式 Linux 中描述硬件拓扑与资源配置的核心机制。节点(Node)与属性(Property)构成了设备树的骨架与血肉。本章聚焦于四个最基础、最通用的标准属性——compatible、reg、interrupts 和 status,深入解析其语义、编码规范与常见陷阱。
3.1 标准属性全景概览
下表列出了四个标准属性的核心作用与使用场景,帮助建立全局认知:
| 属性名 | 核心作用 | 所属节点类型 | 内核匹配机制 |
|---|---|---|---|
compatible |
指定设备兼容的驱动型号 | 所有设备节点 | of_match_table 字符串匹配 |
reg |
描述设备地址空间(寄存器/内存) | 有 MMIO 或总线地址的设备 | of_iomap / of_get_address |
interrupts |
描述设备的中断信号连接 | 产生或接收中断的设备 | of_irq_get / irq_of_parse_and_map |
status |
控制设备的启用或禁用 | 所有节点(可选) | of_device_is_available |
3.2 compatible 属性:驱动匹配的钥匙
3.2.1 语法与格式
compatible 属性是一个字符串列表(),格式为 "厂商,型号"。内核通过该属性将设备节点与对应的驱动程序绑定。
// 标准格式:厂商前缀 + 具体型号
compatible = "ti,am335x-uart", "ns16550";
// 内核匹配顺序:从左到右,越具体越靠前
// 先尝试匹配 "ti,am335x-uart",失败后回退到 "ns16550"
3.2.2 命名规范与最佳实践
| 规范项 | 正确示例 | 错误示例 | 说明 |
|---|---|---|---|
| 厂商前缀 | ti, fsl, samsung, |
texas, nxp, |
使用内核文档 vendor-prefixes.yaml 中定义的官方缩写 |
| 型号名称 | am335x-uart |
uart-am335x |
使用数据手册中的芯片型号,小写字母,连字符分隔 |
| 回退兼容 | "ti,am335x-uart", "ns16550" |
"ns16550", "ti,am335x-uart" |
最具体的型号放在最前面,通用型号作为回退 |
| 长度限制 | 不超过 31 字符 | 超长字符串 | 内核解析缓冲区有限,过长可能导致截断 |
3.2.3 内核匹配流程
下图展示了从设备树节点到驱动 probe 的完整匹配链路:
"ns16550", "ti,am335x-uart" 会导致所有 ns16550 兼容的 UART 都使用通用驱动,丢失 TI 平台的特殊功能。
3.3 reg 属性:地址空间描述
3.3.1 地址编码模型
reg 属性描述设备在总线上的地址范围,格式为 的元组列表。地址和长度的位数由父节点的 #address-cells 和 #size-cells 决定。
// 32位地址空间示例
#address-cells = <1>;
#size-cells = <1>;
uart@44e09000 {
reg = <0x44e09000 0x2000>; // 起始地址 0x44e09000,长度 0x2000
};
// 64位地址空间示例
#address-cells = <2>;
#size-cells = <2>;
memory@80000000 {
reg = <0x0 0x80000000 0x0 0x40000000>; // 起始 0x80000000,长度 0x40000000
};
3.3.2 多地址区域描述
当设备拥有多个不连续的地址区域时,reg 可以包含多个元组:
// 网卡设备:控制寄存器 + DMA 描述符区 + 数据缓冲区
ethernet@10000000 {
compatible = "vendor,eth-controller";
reg = <0x10000000 0x1000>, // 控制寄存器区
<0x10001000 0x2000>, // DMA 描述符区
<0x10003000 0x4000>; // 数据缓冲区
};
3.3.3 地址映射规则
| 场景 | #address-cells | #size-cells | reg 元组格式 | 典型值 |
|---|---|---|---|---|
| 32位内存映射 | 1 | 1 | |
<0x44e09000 0x2000> |
| 64位内存映射 | 2 | 2 | |
<0x0 0x80000000 0x0 0x40000000> |
| SPI 设备 | 1 | 0 | |
<0x0>(片选号) |
| I2C 设备 | 1 | 0 | |
<0x50>(从机地址) |
of_iomap(node, index) 映射第 index 个 reg 区域;of_get_address(node, index, &size, &flags) 获取原始地址值。务必检查返回值,避免空指针解引用。
3.4 interrupts 属性:中断连接描述
3.4.1 中断描述结构
interrupts 属性描述设备的中断输出信号连接到中断控制器(Interrupt Controller)的哪个输入。其格式由 #interrupt-cells 决定,通常为 1、2 或 3 个 cell。
// 常见 ARM GIC 中断控制器:3 cells
// cell 0: 中断类型 (0=SPI, 1=PPI)
// cell 1: 中断号
// cell 2: 触发类型 (1=上升沿, 2=下降沿, 4=高电平, 8=低电平)
interrupt-parent = <&gic>; // 指定中断控制器
interrupts = <0 29 4>; // SPI 中断号 29,高电平触发
// 简单中断控制器:1 cell
interrupts = <14>; // 中断号 14,触发类型由控制器默认
3.4.2 中断映射关系
下图展示了设备、中断控制器与 CPU 之间的中断传递路径:
3.4.3 中断类型与触发方式
| 中断类型 | GIC cell 0 值 | 说明 | 典型中断号范围 |
|---|---|---|---|
| SPI (Shared Peripheral Interrupt) | 0 | 共享外设中断,可路由到任意 CPU | 32 ~ 1019 |
| PPI (Private Peripheral Interrupt) | 1 | 私有外设中断,仅发送到指定 CPU | 16 ~ 31 |
| SGI (Software Generated Interrupt) | 2 | 软件触发中断,用于核间通信 | 0 ~ 15 |
platform_get_irq() 获取已转换的 IRQ 号。
3.5 status 属性:设备启用控制
3.5.1 标准取值与语义
status 属性控制设备节点是否被内核识别和驱动。它是一个可选的字符串属性,默认值为 "okay"(若未显式指定)。
| 取值 | 语义 | 内核行为 | 使用场景 |
|---|---|---|---|
"okay" |
设备已启用 | 正常扫描、匹配驱动、调用 probe | 默认状态,硬件正常时使用 |
"disabled" |
设备已禁用 | 跳过该节点,不创建 platform device | 硬件不存在、未焊接、或需要用户空间手动启用 |
"reserved" |
设备保留 | 类似 disabled,但暗示未来可能启用 | Bootloader 或固件保留的设备 |
"fail" |
设备故障 | 跳过,并记录错误日志 | 硬件检测到故障时由 Bootloader 设置 |
3.5.2 覆盖机制与使用规范
设备树支持通过 &{label} 语法在多个 .dtsi 文件中覆盖 status 属性,这是板级配置中常用的模式:
// 芯片级 .dtsi 文件:默认禁用所有外设
&uart1 {
status = "disabled";
};
&uart2 {
status = "disabled";
};
// 板级 .dts 文件:按需启用
&uart1 {
status = "okay";
pinctrl-0 = <&uart1_pins>;
pinctrl-names = "default";
};
3.6 综合示例:完整设备节点
以下是一个集成了所有标准属性的完整 I2C 控制器节点示例:
// i2c0 控制器节点
i2c0: i2c@44e0b000 {
compatible = "ti,am335x-i2c", "ti,omap4-i2c";
reg = <0x44e0b000 0x1000>;
interrupts = <0 70 4>; // SPI 中断号 70,高电平触发
interrupt-parent = <&gic>;
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
// 挂载在 I2C 总线上的从设备
eeprom@50 {
compatible = "atmel,24c02";
reg = <0x50>; // I2C 从机地址 0x50
status = "okay";
};
rtc@68 {
compatible = "dallas,ds1307";
reg = <0x68>;
status = "disabled"; // 该 RTC 未焊接,禁用
};
};
3.7 常见错误与调试方法
| 错误类型 | 现象 | 根因 | 解决方案 |
|---|---|---|---|
| compatible 不匹配 | 驱动 probe 未调用 | 字符串拼写错误或厂商前缀不规范 | 检查 /sys/firmware/devicetree/base/.../compatible 文件内容 |
| reg 地址越界 | of_iomap 返回 NULL | 地址超出父节点 ranges 映射范围 | 检查父节点 ranges 属性是否包含该地址段 |
| 中断号冲突 | 中断无法触发或触发异常 | 多个设备使用相同中断号且类型不兼容 | 使用 cat /proc/interrupts 查看中断分配情况 |
| status 未正确覆盖 | 设备意外禁用 | 覆盖顺序错误或语法错误 | 使用 dtc -I dtb -O dts 反编译确认最终状态 |
of_dev_dbg=1 可以打印设备树节点扫描和匹配的详细信息。使用 dtc -I fs -O dts /sys/firmware/devicetree/base 可以查看内核实际使用的设备树内容,与源文件对比定位问题。
掌握这四个标准属性是设备树开发的基础。在实际项目中,compatible 决定了驱动能否绑定,reg 决定了驱动能否访问硬件寄存器,interrupts 决定了中断能否正常工作,status 决定了设备是否被启用。每一个属性的错误配置都可能导致设备无法工作,因此必须严格遵循规范,并在开发过程中反复验证。