4. 设备树地址与中断映射

设备树的核心能力之一是将硬件拓扑中的地址空间、中断连接关系以标准化的方式描述出来,使操作系统无需硬编码即可完成资源分配。本章深入解析地址编码属性 #address-cells / #size-cells 以及中断控制器与中断映射的完整机制。

核心要点:地址编码决定总线域内地址与长度的字长;中断映射通过 interrupt-parent、interrupts 和 interrupt-map 将设备中断请求路由到正确的中断控制器。

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

在设备树中,每个总线节点通过 #address-cells 和 #size-cells 定义其子节点 reg 属性的地址与长度字段所占的 32 位字(cell)数量。

属性 含义 常见值 说明
#address-cells 地址字段占用的 cell 数 1 或 2 32 位总线用 1,64 位总线用 2
#size-cells 长度字段占用的 cell 数 0 或 1 无地址范围设备用 0,有地址范围用 1
// 示例:32 位地址空间,带长度
soc {
    #address-cells = <1>;
    #size-cells    = <1>;
    uart0: serial@10000000 {
        reg = <0x10000000 0x1000>;
    };
};

// 示例:64 位地址空间,无长度(如 GPIO 控制器)
gpio0: gpio@f0000000 {
    #address-cells = <2>;
    #size-cells    = <0>;
    reg = <0x0 0xf0000000 0x0 0x1000>;
};
编码规则:子节点的 reg 属性中,地址部分占用 #address-cells 个 cell,长度部分占用 #size-cells 个 cell。若 #size-cells = 0,则 reg 只包含地址,不包含长度。
地址编码解析流程 根节点 / #address-cells = 2 #size-cells = 1 soc 总线节点 #address-cells = 1, #size-cells = 1 uart0: serial@10000000 reg = <0x10000000 0x1000> 地址: 0x10000000 长度: 0x1000

4.2 中断控制器与中断映射属性

设备树使用以下属性描述中断系统:

  • interrupt-controller:空属性,标记节点为中断控制器。
  • #interrupt-cells:指定中断描述符占用的 cell 数,通常为 1、2 或 3。
  • interrupt-parent:指定设备所连接的中断控制器 phandle。
  • interrupts:设备的中断描述符列表,格式由父中断控制器定义。
  • interrupt-map:用于复杂拓扑(如 PCIe 桥)的中断路由映射。
属性 作用对象 示例值
interrupt-controller 中断控制器节点 空属性
#interrupt-cells 中断控制器节点 <2>
interrupt-parent 设备节点 <&gic>
interrupts 设备节点 <0 33 4>
interrupt-map 桥接节点 多行映射表
// GIC 中断控制器定义
gic: interrupt-controller@f0001000 {
    compatible = "arm,gic-400";
    interrupt-controller;
    #interrupt-cells = <3>;
    reg = <0xf0001000 0x1000>;
};

// 设备连接 GIC
uart1: serial@10002000 {
    compatible = "ns16550";
    reg = <0x10002000 0x1000>;
    interrupt-parent = <&gic>;
    interrupts = <0 37 4>;  // SPI, 中断号 37, 高电平触发
};
常见错误:忘记设置 interrupt-parent 或 #interrupt-cells 不匹配会导致中断无法正确路由,内核报错 "no irq domain found"。

4.3 中断映射的完整流程

从设备产生中断到 CPU 响应的设备树映射路径:

  1. 设备节点通过 interrupt-parent 指向中断控制器。
  2. 设备节点 interrupts 属性提供中断类型、编号、触发方式。
  3. 中断控制器节点解析 interrupts 并映射到硬件中断线。
  4. 内核 irq_domain 建立软件中断号与硬件中断的映射。
  5. 驱动通过 platform_get_irq() 获取中断号并注册处理函数。
中断映射完整流程 设备节点 interrupts = <0 37 4> 中断控制器 #interrupt-cells = 3 irq_domain 软件→硬件映射 驱动层 platform_get_irq() 中断类型: 0=SPI 中断号: 37 触发: 4=高电平 中断响应:CPU → GIC → 设备 设备树描述 → 内核解析 → 驱动注册 → 中断处理

4.4 interrupt-map 与复杂拓扑

当存在中断控制器级联或 PCIe 桥等复杂拓扑时,使用 interrupt-map 和 interrupt-map-mask 实现动态路由。

// PCIe 桥中断映射示例
pcie0: pcie@f0000000 {
    #address-cells = <3>;
    #size-cells    = <2>;
    interrupt-map-mask = <0xf800 0 0 7>;
    interrupt-map = <
        0x0000 0 0 1 &gic 0 0 0 33 4
        0x0000 0 0 2 &gic 0 0 0 34 4
        0x0000 0 0 3 &gic 0 0 0 35 4
        0x0000 0 0 4 &gic 0 0 0 36 4
    >;
};
映射规则:interrupt-map 每行包含:子节点地址 + 中断描述符 → 父中断控制器 phandle + 父中断描述符。interrupt-map-mask 用于地址匹配时的掩码过滤。

4.5 实践检查清单

检查项 正确做法 常见错误
地址编码一致性 父子节点 #address-cells 匹配 子节点 reg 字段数不匹配父节点定义
中断控制器声明 必须包含 interrupt-controller 和 #interrupt-cells 缺少 #interrupt-cells 导致解析失败
中断父节点指向 设备节点显式指定 interrupt-parent 依赖隐式继承导致中断域错误
中断描述符格式 与父节点 #interrupt-cells 一致 cell 数量不匹配或触发类型值错误
interrupt-map 路由 掩码与地址对齐,phandle 有效 掩码错误导致中断无法路由
调试技巧:使用 dtc -I dtb -O dts 反编译设备树,检查 interrupts 和 interrupt-parent 是否正确;内核启动时查看 dmesg | grep irq 确认中断映射是否成功。

掌握地址编码与中断映射是设备树开发的核心技能,它为后续 overlay 动态配置中断资源打下坚实基础。