4. 设备树节点与属性:节点命名规范、compatible属性、reg属性、interrupts属性详解

设备树(Device Tree)是描述硬件拓扑结构的结构化数据,它通过节点(Node)和属性(Property)将外设的地址、中断、时钟、复位等资源信息传递给内核。驱动开发者必须准确理解节点命名规则以及 compatible、reg、interrupts 等核心属性的语义,才能正确完成外设内存映射与中断绑定。

4.1 节点命名规范

设备树中每个节点代表一个硬件设备或总线桥接器,其命名必须遵循以下规则:

  • 通用格式:node-name@unit-address
  • node-name:描述设备类型的通用名称,如 uart、i2c、gpio、ethernet。
  • unit-address:设备在父总线上的地址,通常为寄存器基地址或片选编号,使用小写十六进制。
  • 命名长度:建议不超过 31 个字符,避免使用下划线开头。
  • 特殊节点:根节点 /、别名节点 aliases、选择节点 chosen 为固定名称。
命名示例对比
正确命名 错误命名 说明
uart@ff010000 UART@ff010000 节点名应全小写
i2c@ff020000 i2c_controller@ff020000 避免下划线,用连字符
ethernet@ff030000 eth@ff030000 名称应体现设备类型
gpio@ff040000 gpio@0xff040000 unit-address 不带 0x 前缀
实战建议:在编写设备树时,优先参考内核文档 Documentation/devicetree/bindings/ 中对应设备的命名惯例,保持与主线内核一致,避免因命名不规范导致驱动匹配失败。
设备树节点命名结构 / (根节点) soc@0 uart@ff010000 命名规则分解: 格式:node-name@unit-address node-name = 设备类型(uart / i2c / gpio / ethernet) unit-address = 寄存器基地址(小写十六进制,无 0x 前缀) 示例:uart@ff010000 → 表示基地址为 0xff010000 的 UART 设备

4.2 compatible 属性:驱动匹配的核心

compatible 属性是设备树与驱动代码之间的“握手协议”。内核通过该属性查找对应的 platform_driver,完成设备与驱动的绑定。

  • 格式:字符串列表,推荐使用 "厂商,设备型号" 形式。
  • 匹配优先级:内核按列表顺序依次匹配,第一个匹配成功即绑定。
  • 回退机制:通常包含一个精确型号和一个通用型号,例如 "ti,am3352-uart", "ns16550"。
compatible 属性示例
uart0: uart@ff010000 {
    compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
    reg = <0x00 ff010000 0x00 0x4000>;
    interrupts = <0 58 4>;
};

上述代码中,内核优先匹配 "fsl,imx6q-uart",若未找到则尝试 "fsl,imx21-uart"。驱动端通过 of_match_table 声明支持的 compatible 列表。

compatible 值 匹配驱动 说明
"fsl,imx6q-uart" imx6q-uart.c 精确匹配 i.MX6Q 专用驱动
"fsl,imx21-uart" imx21-uart.c 回退匹配通用 i.MX 驱动
"ns16550" 8250.c 回退匹配标准 16550 驱动
常见错误:compatible 字符串中厂商名和设备名之间必须使用逗号分隔,且不能包含空格。例如 "ti, am3352-uart" 是错误的,应写为 "ti,am3352-uart"。
compatible 属性驱动匹配流程 设备树节点 compatible = "A", "B" 匹配 "A" 驱动? 绑定驱动 A 匹配 "B" 驱动? 绑定驱动 B 匹配失败,返回 -ENODEV

4.3 reg 属性:外设内存映射的基石

reg 属性描述设备在系统总线上的地址空间,包括寄存器基地址和地址范围。内核通过该属性完成 IO 内存的映射,驱动才能通过 ioremap 访问硬件寄存器。

  • 格式:reg = <address1 length1 address2 length2 ...>;
  • 地址单元:由父节点的 #address-cells 决定,通常为 1 或 2。
  • 长度单元:由父节点的 #size-cells 决定,通常为 1 或 2。
  • 多段地址:一个设备可能包含多个地址区域,如寄存器区、FIFO 区、DMA 区。
reg 属性典型示例
soc {
    #address-cells = <2>;   // 地址使用 2 个 32 位单元(64 位地址)
    #size-cells    = <2>;   // 长度使用 2 个 32 位单元(64 位长度)

    uart@ff010000 {
        compatible = "fsl,imx6q-uart";
        reg = <0x00 0xff010000 0x00 0x4000>;
        // 地址:0xff010000,长度:0x4000(16KB)
    };

    dma@ff020000 {
        compatible = "fsl,imx6q-dma";
        reg = <0x00 0xff020000 0x00 0x1000   // 控制寄存器
               0x00 0xff030000 0x00 0x1000>; // 描述符区
    };
};
#address-cells #size-cells 地址表示范围 典型场景
1 1 32 位地址,32 位长度 ARM 32 位 SoC
2 2 64 位地址,64 位长度 ARM64 / RISC-V 64 位 SoC
1 0 32 位地址,无长度 GPIO 引脚编号
实战要点:驱动中通过 platform_get_resource(pdev, IORESOURCE_MEM, 0) 获取第一个 reg 段的物理地址和长度,然后调用 devm_ioremap_resource 完成内存映射。多段地址时 index 参数依次递增。
reg 属性内存映射结构 设备树节点 reg = <0x00 0xff010000 0x00 0x4000> 内核解析 reg #address-cells = 2 #size-cells = 2 物理地址空间 0xff010000 长度 0x4000 驱动调用 ioremap / devm_ioremap_resource 返回虚拟地址,驱动通过该地址访问硬件寄存器

4.4 interrupts 属性:中断绑定与触发类型

interrupts 属性描述设备的中断资源,包括中断号、中断类型和触发方式。内核通过该属性将设备的中断请求线与中断控制器关联起来。

  • 格式:interrupts = <interrupt-specifier>;
  • interrupt-specifier:由中断控制器的 #interrupt-cells 决定,通常包含中断号、触发类型和优先级。
  • 中断类型:常见值包括 IRQ_TYPE_EDGE_RISING (1)、IRQ_TYPE_EDGE_FALLING (2)、IRQ_TYPE_LEVEL_HIGH (4)、IRQ_TYPE_LEVEL_LOW (8)。
  • 中断父节点:通过 interrupt-parent 属性指定中断控制器,若不指定则继承父节点。
interrupts 属性示例
gic: interrupt-controller@ffa00000 {
    compatible = "arm,gic-400";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0x00 0xffa00000 0x00 0x10000>;
};

uart@ff010000 {
    compatible = "fsl,imx6q-uart";
    reg = <0x00 0xff010000 0x00 0x4000>;
    interrupts = <0 58 4>;
    // 0: SPI 中断类型(0=PPI, 1=SPI)
    // 58: 中断号
    // 4: 触发类型(高电平触发)
};
#interrupt-cells 中断说明符格式 典型中断控制器
1 <中断号> 简单中断控制器(如 GPIO)
2 <中断号 触发类型> ARM GIC v2
3 <中断类型 中断号 触发类型> ARM GIC v3/v4
中断触发类型对照表
宏定义 数值 触发方式 适用场景
IRQ_TYPE_EDGE_RISING 1 上升沿触发 按键、GPIO 边沿事件
IRQ_TYPE_EDGE_FALLING 2 下降沿触发 下降沿敏感设备
IRQ_TYPE_LEVEL_HIGH 4 高电平触发 UART、I2C 等标准外设
IRQ_TYPE_LEVEL_LOW 8 低电平触发 低电平有效设备
驱动获取中断资源:使用 platform_get_irq(pdev, 0) 获取第一个中断号,然后调用 devm_request_irq 注册中断处理函数。多中断时 index 参数递增。
中断路由与绑定关系 UART 外设 interrupts = <0 58 4> GIC 中断控制器 #interrupt-cells = <3> 中断号 58 SPI 类型,高电平触发 驱动调用 devm_request_irq(58, handler) 注册中断处理函数,响应 UART 中断事件

4.5 综合实战:完整设备树节点示例

以下是一个完整的 UART 设备树节点,综合运用了本节所学的所有核心属性:

/ {
    model = "Embedded Linux Board";
    compatible = "vendor,board-rev1";

    soc@0 {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges;

        gic: interrupt-controller@ffa00000 {
            compatible = "arm,gic-400";
            #interrupt-cells = <3>;
            interrupt-controller;
            reg = <0x00 0xffa00000 0x00 0x10000>;
        };

        uart0: uart@ff010000 {
            compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
            reg = <0x00 0xff010000 0x00 0x4000>;
            interrupts = <0 58 4>;
            clocks = <&clk 20>;
            clock-names = "uart";
            status = "okay";
        };
    };
};
属性总结表
属性 作用 驱动获取方式 常见错误
compatible 驱动匹配标识 of_match_table 厂商名与设备名间缺少逗号
reg 物理地址与长度 platform_get_resource address/size-cells 不匹配
interrupts 中断号与触发类型 platform_get_irq 中断类型值与硬件不匹配
interrupt-parent 指定中断控制器 自动继承 未指定导致中断无法路由
实战建议:在移植驱动时,先通过 cat /proc/device-tree/<设备路径>/compatible 确认设备树中的 compatible 值,再与驱动中的 of_match_table 对比,确保完全一致。使用 devicetree.org 的规范命名可提高代码的可移植性。