3. 设备树常用属性:compatible、reg、interrupts、status、model 等核心属性详解

设备树(Device Tree)中的每个节点都通过一系列属性(Properties)来描述硬件设备的特征、资源需求和工作状态。这些属性是内核驱动与硬件信息之间的桥梁。本章将深入解析最核心的五个属性:compatible、reg、interrupts、status 和 model,并结合实战案例说明其用法。

核心要点:设备树属性本质上是“键-值”对。键是字符串,值可以是字符串、整数数组、二进制数据或字符串列表。理解每个属性的编码规则和语义,是编写正确设备树的前提。

3.1 compatible 属性:驱动匹配的“身份证”

compatible 是设备树中最重要的属性,它指定了设备与内核驱动之间的匹配关系。其值是一个字符串列表,格式通常为 "厂商,型号"。

  • 匹配顺序:内核在加载驱动时,会按照设备树中 compatible 列表的从前到后顺序,依次与驱动中 of_match_table 里的字符串进行匹配。第一个匹配成功的驱动将被绑定。
  • 命名规范:推荐使用全小写字母,厂商名与型号之间用逗号分隔,例如 "ti,am335x-uart"、"fsl,imx6q-uart"。
  • 兼容性设计:通常将最具体的型号放在最前面,通用型号放在后面。例如 "ti,am335x-uart", "ns16550",表示该设备兼容 TI 的 AM335x UART,同时也兼容标准的 16550 UART。
实战示例:以下代码展示了一个 UART 节点的 compatible 属性及其对应的内核驱动匹配表。
// 设备树节点
uart0: serial@44e09000 {
    compatible = "ti,am335x-uart", "ns16550";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
    status = "okay";
};

// 内核驱动中的匹配表 (Linux 内核源码示例)
static const struct of_device_id omap_uart_of_match[] = {
    { .compatible = "ti,am335x-uart", .data = &am335x_uart_data, },
    { .compatible = "ti,omap2-uart",  .data = &omap2_uart_data, },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, omap_uart_of_match);
compatible 属性匹配流程 设备树节点 compatible = "ti,am335x-uart", "ns16550" 匹配顺序 内核驱动 of_match_table ① "ti,am335x-uart" → 匹配成功 ② "ti,omap2-uart" → 跳过 ③ "ns16550" → 未匹配 ④ ... 驱动绑定 probe() 执行

3.2 reg 属性:描述地址空间与资源范围

reg 属性用于描述设备在系统总线上的地址空间,通常包含一个或多个 地址/长度对。其格式为 reg = <address1 length1 [address2 length2] ...>。

  • 地址单元:地址和长度的位数由父节点的 #address-cells 和 #size-cells 属性决定。通常 #address-cells = <1> 表示 32 位地址,#address-cells = <2> 表示 64 位地址。
  • 多段描述:如果一个设备有多个不连续的地址区域(如 FIFO 缓冲区、控制寄存器、数据寄存器),可以使用多个地址/长度对。
  • 资源映射:内核通过 platform_get_resource() 或 of_iomap() 等 API 从 reg 属性中获取物理地址并映射到虚拟地址空间。
常见错误:忘记在父节点中正确设置 #address-cells 和 #size-cells,或者子节点的 reg 属性中地址/长度数量与父节点定义不匹配,会导致内核解析失败,驱动无法获取正确的资源。
// 父节点定义地址单元
soc {
    compatible = "simple-bus";
    #address-cells = <1>;   // 32位地址
    #size-cells = <1>;      // 32位长度

    // 子节点使用 reg 描述两段地址空间
    ethernet: ethernet@4a100000 {
        compatible = "ti,cpsw";
        reg = <0x4a100000 0x800   // 控制寄存器基址+长度
               0x4a100800 0x100>; // 数据寄存器基址+长度
        interrupts = <40 41>;
        status = "okay";
    };
};
reg 属性地址空间映射 父节点 soc ( #address-cells = 1, #size-cells = 1 ) reg 第一段 地址: 0x4A100000 长度: 0x800 reg 第二段 地址: 0x4A100800 长度: 0x100 内核通过 of_iomap(0) 和 of_iomap(1) 分别映射两段物理地址

3.3 interrupts 属性:连接中断控制器

interrupts 属性用于描述设备产生的中断信号,其格式依赖于父节点(中断控制器)的 #interrupt-cells 属性。通常包含中断号和触发类型。

  • 中断号:在 ARM GIC(通用中断控制器)中,通常使用 SPI(共享外设中断)或 PPI(私有外设中断)编号。
  • 触发类型:常见值包括 IRQ_TYPE_EDGE_RISING (上升沿触发)、IRQ_TYPE_LEVEL_HIGH (高电平触发) 等。
  • 多中断:如果一个设备有多个中断源(如 TX 中断、RX 中断),可以在 interrupts 属性中列出多个中断描述。
  • 中断父节点:通过 interrupt-parent 属性指定该设备连接的中断控制器节点。如果未指定,则继承父节点的中断控制器。
实战示例:以下代码展示了一个 GPIO 按键的中断配置,使用上升沿触发。
// 中断控制器节点 (GIC)
gic: interrupt-controller@48241000 {
    compatible = "arm,cortex-a15-gic";
    #interrupt-cells = <3>;  // 格式: <中断类型 中断号 触发类型>
    interrupt-controller;
    reg = <0x48241000 0x1000>;
};

// 使用中断的设备节点
key: gpio-keys {
    compatible = "gpio-keys";
    #address-cells = <1>;
    #size-cells = <0>;
    interrupt-parent = <&gpio1>;  // 连接到 GPIO 控制器

    button@0 {
        label = "User Button";
        linux,code = <KEY_ENTER>;
        interrupts = <3 IRQ_TYPE_EDGE_RISING>;  // GPIO1_3 上升沿中断
        status = "okay";
    };
};
interrupts 属性与中断控制器连接 设备节点 interrupts = <3 1> interrupt-parent = <&gpio1> 中断请求 中断控制器 #interrupt-cells = <3> interrupt-controller; 类型: GIC / GPIO 路由 CPU 内核 中断处理

3.4 status 属性:控制设备启用状态

status 属性用于指示设备的启用或禁用状态。它允许在不修改硬件描述的情况下,通过设备树来启用或禁用某个外设。

状态值 含义 说明
"okay" 设备已启用 内核将正常探测并初始化该设备
"disabled" 设备已禁用 内核将跳过该设备,不进行探测
"reserved" 设备保留 通常用于固件保留的设备,操作系统不应使用
"fail" 设备检测失败 表示设备在硬件上存在但检测失败,通常由 Bootloader 设置
最佳实践:在 SoC 的 dtsi 文件中,通常将外设的 status 默认设置为 "disabled",然后在具体的板级 dts 文件中,根据实际硬件情况将需要启用的外设设置为 "okay"。这样可以避免未使用的外设占用资源。
// SoC 级 dtsi 文件 (默认禁用)
i2c0: i2c@44e0b000 {
    compatible = "ti,am335x-i2c";
    reg = <0x44e0b000 0x1000>;
    interrupts = <70>;
    status = "disabled";  // 默认禁用
};

// 板级 dts 文件 (启用并配置)
&i2c0 {
    status = "okay";      // 在板级文件中启用
    clock-frequency = <400000>;
    pinctrl-names = "default";
    pinctrl-0 = <&i2c0_pins>;
};

3.5 model 属性:描述设备型号

model 属性是一个字符串,用于描述设备或板卡的具体型号。它通常出现在根节点或设备节点中,提供人类可读的硬件标识信息。

  • 根节点使用:在根节点中,model 属性描述整个开发板的型号,例如 "TI AM335x BeagleBone Black"。
  • 设备节点使用:在具体设备节点中,model 属性可以描述该设备的精确型号,例如 "Samsung S5K5BAFX 5MP Camera"。
  • 与 compatible 的区别:compatible 用于驱动匹配,是内核编程接口;model 主要用于信息展示和调试,不参与驱动匹配逻辑。
// 根节点中的 model 属性
/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
    #address-cells = <1>;
    #size-cells = <1>;

    // 设备节点中的 model 属性
    lcd: display@0 {
        compatible = "ilitek,ili9341";
        model = "ILI9341 2.8 inch TFT LCD";
        reg = <0>;
        status = "okay";
    };
};
model 属性与 compatible 属性对比 model 属性 用途:描述设备型号(人类可读) 示例:model = "BeagleBone Black" 不参与驱动匹配 对比 compatible 属性 用途:驱动匹配(内核接口) 示例:compatible = "ti,am335x-bone" 参与驱动 probe 匹配

3.6 属性综合实战:构建一个完整的 I2C 设备节点

下面通过一个完整的 I2C 温度传感器节点,综合运用本章所学的五个核心属性。

// I2C 控制器节点
i2c2: i2c@4819c000 {
    compatible = "ti,omap4-i2c";
    reg = <0x4819c000 0x1000>;
    interrupts = <30>;
    #address-cells = <1>;
    #size-cells = <0>;
    status = "okay";
    clock-frequency = <100000>;

    // 挂载在 I2C 总线上的温度传感器
    temp_sensor: temperature-sensor@48 {
        compatible = "ti,tmp102";          // 驱动匹配
        reg = <0x48>;                      // I2C 从设备地址
        interrupts = <5 IRQ_TYPE_EDGE_FALLING>; // 中断配置
        model = "TI TMP102 Digital Temperature Sensor"; // 型号描述
        status = "okay";                   // 启用设备
    };
};
实战要点:
  • compatible:使用 "ti,tmp102" 与内核中的 TMP102 驱动匹配。
  • reg:在 I2C 子节点中,reg 表示从设备地址 0x48,而不是内存地址。
  • interrupts:使用 5 号中断,下降沿触发。
  • status:显式设置为 "okay",确保设备被探测。
  • model:提供人类可读的型号信息,便于调试。

3.7 属性使用避坑指南

属性 常见错误 正确做法
compatible 字符串拼写错误,或顺序颠倒 严格遵循 "厂商,型号" 格式,最具体在前
reg 地址/长度数量与父节点 cells 不匹配 检查父节点 #address-cells 和 #size-cells
interrupts 中断号超出范围,或触发类型错误 查阅芯片手册,确认中断号和 GIC 类型
status 忘记设置,或拼写为 "ok"、"disable" 使用标准值 "okay" 或 "disabled"
model 与 compatible 混淆,或包含特殊字符 仅用于描述,使用简洁明了的字符串
重要提醒:设备树属性是大小写敏感的。"okay" 不能写成 "OK" 或 "Ok";"ti,am335x-uart" 不能写成 "TI,AM335X-UART"。错误的拼写会导致驱动匹配失败或设备无法正常工作。