4. 设备树地址与中断映射:地址编码与中断控制器规则

设备树(Device Tree)通过标准属性描述硬件地址空间与中断系统的拓扑结构。本章深入解析 #address-cells、#size-cells 的地址编码规则,以及 interrupt-controller、interrupt-parent、interrupts 的中断映射机制,为驱动开发中的资源获取提供精确的硬件描述基础。

4.1 地址编码:address-cells 与 size-cells

设备树使用 reg 属性描述设备在总线上的地址范围。#address-cells 和 #size-cells 定义地址与长度字段的 32 位字数量,形成层次化的地址空间。

属性 作用 取值含义 典型值
#address-cells 子节点 reg 中地址字段的 32 位字个数 1:32 位地址;2:64 位地址 1(32位SoC)、2(64位SoC)
#size-cells 子节点 reg 中长度字段的 32 位字个数 0:无长度(仅地址);1:32 位长度 1(常见)、0(仅地址)
reg 设备地址与长度列表 格式:<addr1 len1 addr2 len2 ...> <0x10000000 0x1000>
关键规则:父节点的 #address-cells 和 #size-cells 决定其直接子节点 reg 的解析方式。根节点通常定义 #address-cells = <2> 和 #size-cells = <2> 以支持 64 位地址空间。

4.1.1 地址编码示例

/dts-v1/;

/ {
    #address-cells = <2>;   // 64位地址
    #size-cells = <2>;      // 64位长度

    memory@80000000 {
        device_type = "memory";
        reg = <0x0 0x80000000 0x0 0x80000000>;
        // 地址:0x80000000,长度:0x80000000(2GB)
    };

    soc {
        #address-cells = <1>;   // 32位地址
        #size-cells = <1>;      // 32位长度
        ranges = <0x0 0x0 0xfe000000 0x02000000>;
        // 子地址0映射到父地址0xfe000000,范围0x02000000

        uart@10000000 {
            compatible = "ns16550";
            reg = <0x10000000 0x1000>;
            // 地址:0x10000000,长度:0x1000(4KB)
        };
    };
};
驱动开发提示:Linux 内核通过 of_address_to_resource() 将 reg 自动转换为 struct resource,驱动无需手动解析 #address-cells 和 #size-cells。但理解编码规则对调试设备树错误至关重要。

4.2 中断控制器与中断映射

设备树通过 interrupt-controller、#interrupt-cells、interrupt-parent 和 interrupts 属性描述中断系统的拓扑结构,实现从设备中断源到中断控制器的映射。

4.2.1 中断控制器属性

属性 作用 说明
interrupt-controller 声明节点为中断控制器 空属性,存在即表示该节点可接收中断
#interrupt-cells 定义 interrupts 中每个中断描述所需的 32 位字个数 ARM GIC 通常为 3(类型、编号、标志)
interrupt-parent 指定设备连接的中断控制器 值为中断控制器的 phandle
interrupts 描述设备的中断请求线 格式由 #interrupt-cells 决定

4.2.2 中断映射规则

  1. 中断控制器声明:每个中断控制器节点必须包含 interrupt-controller 和 #interrupt-cells。
  2. 中断父级指定:设备节点通过 interrupt-parent 指向其连接的中断控制器。若未指定,则继承父节点的 interrupt-parent。
  3. 中断描述:interrupts 属性包含一个或多个中断描述,每个描述由 #interrupt-cells 个 32 位整数组成。
  4. 中断级联:中断控制器可以连接上级中断控制器,形成中断树。根中断控制器通常为 GIC(Generic Interrupt Controller)。
常见错误:中断描述中的标志位(如触发类型、极性)必须与中断控制器硬件一致。ARM GIC 中,interrupts = <0 33 4> 表示 SPI 类型、中断号 33、高电平触发。标志位错误会导致中断无法正确响应。

4.3 中断映射完整示例

/ {
    #address-cells = <2>;
    #size-cells = <2>;

    gic: interrupt-controller@f0001000 {
        compatible = "arm,gic-400";
        reg = <0x0 0xf0001000 0x0 0x1000>,
              <0x0 0xf0002000 0x0 0x2000>;
        interrupt-controller;
        #interrupt-cells = <3>;  // 类型、编号、标志
    };

    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges = <0x0 0x0 0xfe000000 0x02000000>;
        interrupt-parent = <&gic>;  // 默认中断父级

        uart@10000000 {
            compatible = "ns16550";
            reg = <0x10000000 0x1000>;
            interrupts = <0 33 4>;
            // 类型0(SPI)、编号33、标志4(高电平触发)
        };

        gpio@10001000 {
            compatible = "brcm,bcm2835-gpio";
            reg = <0x10001000 0x1000>;
            gpio-controller;
            #gpio-cells = <2>;
            interrupt-controller;
            #interrupt-cells = <2>;
            interrupts = <0 42 4>;
            // GPIO控制器本身连接GIC,中断号42
        };
    };
};
中断映射流程:设备 uart 的 interrupts 通过 interrupt-parent 指向 GIC,GIC 根据 #interrupt-cells = <3> 解析 <0 33 4> 为 SPI 中断、编号 33、高电平触发。驱动通过 platform_get_irq() 获取中断号。

4.4 地址与中断映射的 SVG 架构图

下图展示设备树中地址编码与中断映射的完整拓扑结构,包括根节点、总线、设备、中断控制器的层次关系。

设备树地址与中断映射架构图 设备树地址与中断映射架构 根节点 / #address-cells=2, #size-cells=2 soc 总线 #address-cells=1, #size-cells=1, ranges uart@10000000 reg=<0x10000000 0x1000> interrupts=<0 33 4> gpio@10001000 reg=<0x10001000 0x1000> interrupts=<0 42 4> GIC 中断控制器 interrupt-controller, #interrupt-cells=3 CPU 核心 中断处理 虚线表示中断映射路径:设备 → GIC → CPU;实线表示地址总线层次

4.5 中断映射规则总结

规则 描述 示例
中断控制器声明 节点必须包含 interrupt-controller 和 #interrupt-cells interrupt-controller; #interrupt-cells = <3>;
中断父级继承 子节点未指定 interrupt-parent 时继承父节点 soc { interrupt-parent = <&gic>; };
中断描述格式 由 #interrupt-cells 决定,常见为 3 字段 interrupts = <0 33 4>;
中断级联 中断控制器可连接上级控制器,形成树状结构 GPIO 控制器既是中断控制器,又连接 GIC
驱动获取中断 内核 API 自动解析设备树中断映射 platform_get_irq(pdev, 0)
调试技巧:使用 dtc -I dtb -O dts 反编译设备树二进制文件,检查 interrupts 和 interrupt-parent 是否正确。内核启动日志中的 irq: no irq domain found for ... 错误通常表示中断映射缺失或 #interrupt-cells 不匹配。

4.6 地址与中断映射的驱动开发流程

驱动开发中,地址与中断映射的获取遵循标准流程,设备树解析由内核框架自动完成。

驱动开发中地址与中断映射获取流程 驱动开发中地址与中断映射获取流程 步骤1 设备树解析 步骤2 获取地址资源 步骤3 获取中断号 步骤4 注册中断处理 内核 API 与设备树属性对应关系 步骤1:设备树匹配(compatible 属性)→ of_match_table 步骤2:platform_get_resource(pdev, IORESOURCE_MEM, 0) → reg 属性 步骤3:platform_get_irq(pdev, 0) → interrupts 属性 + interrupt-parent 步骤4:request_irq(irq, handler, flags, name, dev) → 注册中断处理函数
核心结论:设备树的地址编码(#address-cells/#size-cells)和中断映射(interrupt-controller/interrupts)是驱动获取硬件资源的基础。内核框架自动完成设备树到内核资源的转换,驱动开发者只需理解属性含义,即可通过标准 API 获取地址和中断资源,无需手动解析设备树二进制数据。