3. 设备树常用属性详解
设备树中的属性(property)是描述硬件设备特征、连接关系和资源需求的核心载体。每个节点通过一组键值对(key-value)向内核传递硬件信息。本章将逐一拆解嵌入式开发中最常用的 7 类属性,包括它们的语法、语义、典型取值和常见陷阱。
3.1 compatible —— 设备与驱动的匹配钥匙
compatible 是设备树中最重要的属性之一,它指定了设备与内核驱动之间的匹配名称。内核通过遍历设备树节点,将节点的 compatible 值与驱动注册时声明的 of_device_id 表进行比对,从而找到正确的驱动程序。
语法格式:
compatible = "manufacturer,model", "generic-model";
匹配规则:
- 内核优先使用列表中的第一个字符串进行精确匹配。
- 如果第一个字符串未匹配成功,则依次尝试后续字符串(回退机制)。
- 字符串格式通常为
"厂商名,器件型号",确保全局唯一性。
temp_sensor: tmp117@48 {
compatible = "ti,tmp117", "ti,tmp116";
reg = <0x48>;
};
内核先尝试匹配 ti,tmp117 驱动;若不存在,则回退匹配 ti,tmp116 驱动。这种设计提高了驱动兼容性。
3.2 reg —— 设备地址空间与片选信息
reg 属性用于描述设备在总线上的地址空间或片选(chip select)信息。它由一个或多个地址/长度对组成,每个对称为一个“region”。对于内存映射设备,reg 指定了基地址和映射长度;对于 I²C/SPI 设备,则指定从设备地址。
语法格式:
reg = <address1 length1 address2 length2 ...>;
不同总线下的含义:
| 总线类型 | address 含义 | length 含义 | 示例 |
|---|---|---|---|
| 内存映射(AMBA/AXI) | 基地址 | 映射大小(字节) | reg = <0x40000000 0x1000>; |
| I²C | 7 位从设备地址 | 通常为 0(无长度) | reg = <0x48>; |
| SPI | 片选编号 | 通常为 0 | reg = <0>; |
| PCIe | Bus/Device/Function 编码 | 地址空间大小 | reg = <0x00000800 0 0 0 0>; |
display_controller: display@1e00000 {
compatible = "vendor,display";
reg = <0x01e00000 0x100000 /* 控制寄存器块 */
0x01f00000 0x200000>; /* 帧缓冲块 */
};
内核通过 platform_get_resource(pdev, IORESOURCE_MEM, 0) 和 platform_get_resource(pdev, IORESOURCE_MEM, 1) 分别获取两个 region。
3.3 interrupts —— 中断信号描述
interrupts 属性描述设备产生的中断信号,包括中断控制器、中断号和触发类型。它通常与 interrupt-parent 配合使用,后者指定设备所连接的中断控制器节点。
语法格式:
interrupt-parent = <&gic>;
interrupts = <0 89 4>;
中断描述符字段含义(以 GICv2 为例):
| 字段位置 | 含义 | 典型值 |
|---|---|---|
| 字段 0 | 中断类型:0=SPI(共享外设中断),1=PPI(私有外设中断) | 0 |
| 字段 1 | 中断号(SPI 范围为 32~1019) | 89 |
| 字段 2 | 触发类型与极性:1=上升沿触发,2=下降沿触发,4=高电平触发,8=低电平触发 | 4 |
interrupts 可以包含多个描述符:
interrupts = <0 33 4>, <0 34 4>;
驱动通过 platform_get_irq(pdev, index) 按索引获取每个中断号。
3.4 status —— 设备启用状态控制
status 属性用于控制设备节点的启用或禁用状态。它允许在不修改硬件描述的情况下,通过设备树动态启用或禁用某个外设。这在多板型复用同一 .dtsi 文件时非常有用。
可选值:
| 值 | 含义 | 说明 |
|---|---|---|
okay |
设备启用 | 内核将正常探测并初始化该设备 |
disabled |
设备禁用 | 内核跳过该节点,不进行探测 |
reserved |
保留状态 | 通常用于特殊场景,如固件保留 |
fail |
设备故障 | 表示设备检测失败,内核不加载驱动 |
// 在 SoC 级 .dtsi 中:
&uart2 {
status = "disabled";
};
// 在板级 .dts 中:
&uart2 {
status = "okay";
pinctrl-0 = <&uart2_pins>;
};
这种模式避免了每个板型都重复定义完整节点,只需覆盖 status 和少量差异属性。
ok 而非 okay)、大小写不一致(内核要求全小写)、忘记在 .dts 中覆盖 status 导致设备未启用。调试时可检查 /sys/devices/platform/ 下是否存在对应设备目录。
3.5 clocks —— 时钟资源绑定
clocks 属性描述设备所需的时钟源,包括时钟控制器、时钟 ID 和时钟标志。它通常与 clock-names 配合使用,后者为每个时钟提供语义化名称,方便驱动通过名称获取时钟句柄。
语法格式:
clocks = <&clk_ctrl CLK_ID1>, <&clk_ctrl CLK_ID2>;
clock-names = "bus", "core";
时钟标志位(可选):
| 标志宏 | 值 | 含义 |
|---|---|---|
CLK_SET_RATE_PARENT |
1 | 允许子时钟改变父时钟频率 |
CLK_IGNORE_UNUSED |
2 | 即使未使用也不关闭该时钟 |
CLK_IS_CRITICAL |
4 | 标记为关键时钟,不可关闭 |
struct clk *bus_clk, *core_clk;
bus_clk = devm_clk_get(dev, "bus");
core_clk = devm_clk_get(dev, "core");
clk_prepare_enable(bus_clk);
clk_prepare_enable(core_clk);
使用 clock-names 使代码更具可读性,避免依赖时钟索引。
3.6 gpios —— GPIO 资源描述
gpios 属性用于描述设备使用的 GPIO 引脚,包括 GPIO 控制器、引脚编号和 GPIO 标志。与时钟类似,gpio-names 可以为每个 GPIO 提供语义化名称。
语法格式:
gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>,
<&gpio2 7 GPIO_ACTIVE_LOW>;
gpio-names = "reset", "irq";
GPIO 标志位:
| 标志宏 | 值 | 含义 |
|---|---|---|
GPIO_ACTIVE_HIGH |
0 | 高电平有效 |
GPIO_ACTIVE_LOW |
1 | 低电平有效 |
GPIO_OPEN_DRAIN |
2 | 开漏输出 |
GPIO_OPEN_SOURCE |
4 | 开源输出 |
keypad: keypad@0 {
compatible = "vendor,keypad";
gpios = <&gpio1 15 GPIO_ACTIVE_LOW>, /* 复位引脚 */
<&gpio2 7 GPIO_ACTIVE_HIGH>; /* 中断引脚 */
gpio-names = "reset", "irq";
interrupt-parent = <&gpio2>;
interrupts = <7 IRQ_TYPE_EDGE_RISING>;
};
3.7 属性综合对比与选择指南
以下表格总结了 7 类核心属性的用途、数据类型和典型场景,方便在实际开发中快速查阅:
| 属性名 | 用途 | 数据类型 | 典型场景 |
|---|---|---|---|
compatible |
驱动匹配 | 字符串列表 | 所有设备节点 |
reg |
地址空间描述 | u32/u64 数组 | 内存映射、I²C、SPI 设备 |
interrupts |
中断信号描述 | u32 数组 | 产生中断的外设 |
status |
启用/禁用控制 | 字符串 | 多板型复用场景 |
clocks |
时钟源绑定 | phandle + u32 | 需要时钟的外设 |
gpios |
GPIO 引脚描述 | phandle + u32 + flags | 使用 GPIO 的设备 |
pinctrl |
引脚复用配置 | phandle 列表 | 引脚功能选择 |
- 检查
compatible是否与驱动匹配(查看/sys/bus/platform/drivers/)。 - 检查
status是否为okay。 - 检查
reg地址是否与其他设备重叠(查看/proc/iomem)。 - 检查
interrupts中断号是否冲突(查看/proc/interrupts)。 - 检查
clocks和gpios的 phandle 是否正确指向存在的节点。
3.8 属性编写最佳实践
基于多年嵌入式开发经验,总结以下属性编写原则:
- 完整性原则: 每个设备节点至少应包含
compatible和reg(或interrupts)属性,确保内核能够识别和访问设备。 - 层次化原则: 将 SoC 级通用描述放在 .dtsi 中,板级差异(如
status、pinctrl)放在 .dts 中覆盖。 - 命名规范原则:
compatible使用"厂商,型号"格式;clock-names和gpio-names使用有意义的英文单词。 - 最小化原则: 只添加驱动实际需要的属性,避免冗余描述导致解析错误。
- 验证原则: 每次修改设备树后,使用
dtc -I dtb -O dts反编译检查,并使用dt-validate工具进行 schema 校验。
spi@ff110000 {
compatible = "vendor,spi-controller";
reg = <0xff110000 0x1000>;
interrupts = <0 56 4>;
clocks = <&clk_ctrl SPI_CLK>;
clock-names = "spi_clk";
pinctrl-0 = <&spi_pins>;
pinctrl-names = "default";
status = "okay";
adc@0 {
compatible = "ti,ads1256";
reg = <0>; /* CS0 */
spi-max-frequency = <10000000>;
gpios = <&gpio3 12 GPIO_ACTIVE_LOW>;
gpio-names = "drdy";
};
};