4、中断控制器基础:中断控制器(Interrupt Controller)在设备树中的角色

中断控制器(Interrupt Controller)是嵌入式系统中管理硬件中断请求的核心单元。在设备树中,中断控制器不仅是一个硬件节点,更是整个中断路由拓扑的根节点。它负责接收来自外设的中断信号,进行优先级仲裁、屏蔽、嵌套,并最终将中断向量分发给CPU。设备树通过标准化的中断绑定属性,将中断控制器、中断源和CPU核心之间的映射关系描述为可解析的树形结构。

核心概念: 设备树中的中断控制器节点必须声明 interrupt-controller 属性,并通过 #interrupt-cells 指定中断描述符的宽度。任何需要发出中断的外设节点,则通过 interrupts 或 interrupts-extended 属性引用该控制器。

4.1 中断控制器的设备树属性

一个标准的中断控制器节点在设备树中通常包含以下关键属性。这些属性共同构成了中断路由的“语法规则”。

属性名 类型 描述 示例值
compatible 字符串 中断控制器的驱动匹配标识 "arm,gic-400"
interrupt-controller 空属性 声明该节点是一个中断控制器 (无值)
#interrupt-cells u32 指定中断描述符包含的单元数 3(GIC标准)
reg 地址数组 中断控制器的MMIO寄存器基地址 <0x0 0x10400000 0x0 0x10000>
interrupts 中断数组 级联时,本控制器连接到父控制器的中断线 <0 0 4>
关键理解: #interrupt-cells 的值决定了外设节点在描述中断时需要提供多少个整数。例如,ARM GICv2 使用 3 个 cell:中断类型(0=SPI,1=PPI)、中断号、触发类型与极性。

4.2 中断映射的拓扑结构

设备树中的中断系统是一个多级树形拓扑。根中断控制器(如GIC)直接连接CPU,而其他中断控制器(如GPIO控制器、IOMUX)作为子节点或独立节点,通过 interrupt-parent 属性向上级联。这种结构允许系统支持复杂的中断路由,例如将多个外设的中断汇聚到一个父控制器上。

设备树中断控制器拓扑结构 设备树中断控制器树形拓扑 CPU 核心 GIC-400 (根中断控制器) interrupt-controller; #interrupt-cells = <3> IRQ/FIQ GPIO 控制器 interrupt-controller; #interrupt-cells = <2> IOMUX 控制器 interrupt-controller; #interrupt-cells = <1> interrupt-parent interrupt-parent UART 控制器 I2C 控制器 interrupts = <0 1> interrupts = <5> 说明:每个子中断控制器都通过 interrupt-parent 指向父控制器,外设节点则通过 interrupts 属性描述中断源。

4.3 中断描述符的编码规则

外设节点通过 interrupts 属性向中断控制器传递中断描述符。描述符的格式由父中断控制器的 #interrupt-cells 决定。以最常用的 ARM GIC 为例,其 3 个 cell 的编码规则如下:

Cell 索引 名称 取值 含义
0 中断类型 0 = SPI
1 = PPI
SPI(共享外设中断)由所有核心共享;PPI(私有外设中断)仅属于单个核心
1 中断号 SPI: 32 ~ 1019
PPI: 16 ~ 31
硬件中断线编号,相对于中断控制器
2 触发标志 位0: 1=边沿触发, 0=电平触发
位1: 1=高有效/上升沿, 0=低有效/下降沿
定义中断信号的电气特性
常见错误: 在级联场景中,如果子中断控制器没有正确设置 interrupt-parent 指向父控制器,或者 #interrupt-cells 与父控制器不匹配,内核将无法解析中断映射,导致外设中断无法触发。务必检查设备树编译时的警告信息。

4.4 中断控制器的级联与路由

在复杂SoC中,一个中断控制器可能无法覆盖所有外设的中断需求。此时,设备树支持多级中断控制器级联。子中断控制器本身也是一个“外设”,它通过自己的 interrupts 属性连接到父中断控制器的某条中断线上。当子控制器管理的某个外设产生中断时,子控制器会将该中断合并后,通过这条级联线向父控制器报告。

级联的关键属性是 interrupt-parent 和 interrupts 在子控制器节点上的配合使用:

// 父中断控制器:GIC
gic: interrupt-controller@10400000 {
    compatible = "arm,gic-400";
    interrupt-controller;
    #interrupt-cells = <3>;
    reg = <0x0 0x10400000 0x0 0x10000>;
};

// 子中断控制器:GPIO,连接到 GIC 的 SPI 中断线 64
gpio0: gpio@10500000 {
    compatible = "vendor,gpio-controller";
    interrupt-controller;
    #interrupt-cells = <2>;
    interrupt-parent = <&gic>;          // 指向父控制器
    interrupts = <0 64 4>;               // SPI, 中断号64, 高电平触发
    reg = <0x0 0x10500000 0x0 0x1000>;
};

// 外设:按键,连接到 GPIO 控制器的第 5 号中断线
button {
    compatible = "gpio-keys";
    interrupt-parent = <&gpio0>;         // 指向子控制器
    interrupts = <5 2>;                  // GPIO 中断号5, 下降沿触发
};
解析流程: 当按键按下时,GPIO 控制器检测到中断,它首先在自己的中断域中处理,然后通过预先配置的级联中断线(SPI 64)向 GIC 报告。GIC 再将此中断分发给 CPU。设备树中的 interrupt-parent 链正是这个路由路径的静态描述。

4.5 中断控制器的初始化与绑定

Linux 内核在启动过程中,会遍历设备树中的所有节点,识别出带有 interrupt-controller 属性的节点,并调用对应的驱动进行初始化。初始化顺序由设备树的层级和 interrupt-parent 依赖关系决定:父控制器必须先于子控制器初始化。

内核通过 of_irq_init() 函数完成中断控制器的注册和级联关系的建立。该函数会:

  1. 扫描设备树,收集所有中断控制器节点。
  2. 根据 interrupt-parent 构建依赖树。
  3. 按拓扑顺序依次调用各控制器的初始化回调。
  4. 为每个控制器分配 struct irq_domain,用于中断号的映射。
中断控制器初始化流程 中断控制器初始化流程 扫描设备树节点 构建依赖拓扑 按序初始化 父控制器先初始化 → 分配 irq_domain → 子控制器注册 → 建立映射 中断映射表就绪

4.6 中断控制器的角色总结

中断控制器在设备树中扮演着以下四个核心角色:

  • 中断源管理者: 定义中断描述符的语法(#interrupt-cells),统一管理所有外设的中断请求。
  • 路由拓扑节点: 通过 interrupt-parent 形成级联树,实现中断信号的层级传递。
  • 硬件抽象层: 将硬件中断号映射为内核的 IRQ 号,屏蔽不同 SoC 的寄存器差异。
  • 电源与同步控制器: 在休眠/唤醒场景中,中断控制器负责保存和恢复中断状态,确保系统正确响应唤醒事件。
学习建议: 在阅读具体 SoC 的设备树时,首先找到根中断控制器节点(通常是 GIC),然后沿着 interrupt-parent 链向下梳理,即可快速掌握整个系统的中断架构。这是调试外设中断问题的第一步。