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 属性向上级联。这种结构允许系统支持复杂的中断路由,例如将多个外设的中断汇聚到一个父控制器上。
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, 下降沿触发
};
interrupt-parent 链正是这个路由路径的静态描述。
4.5 中断控制器的初始化与绑定
Linux 内核在启动过程中,会遍历设备树中的所有节点,识别出带有 interrupt-controller 属性的节点,并调用对应的驱动进行初始化。初始化顺序由设备树的层级和 interrupt-parent 依赖关系决定:父控制器必须先于子控制器初始化。
内核通过 of_irq_init() 函数完成中断控制器的注册和级联关系的建立。该函数会:
- 扫描设备树,收集所有中断控制器节点。
- 根据
interrupt-parent构建依赖树。 - 按拓扑顺序依次调用各控制器的初始化回调。
- 为每个控制器分配
struct irq_domain,用于中断号的映射。
4.6 中断控制器的角色总结
中断控制器在设备树中扮演着以下四个核心角色:
- 中断源管理者: 定义中断描述符的语法(
#interrupt-cells),统一管理所有外设的中断请求。 - 路由拓扑节点: 通过
interrupt-parent形成级联树,实现中断信号的层级传递。 - 硬件抽象层: 将硬件中断号映射为内核的 IRQ 号,屏蔽不同 SoC 的寄存器差异。
- 电源与同步控制器: 在休眠/唤醒场景中,中断控制器负责保存和恢复中断状态,确保系统正确响应唤醒事件。
interrupt-parent 链向下梳理,即可快速掌握整个系统的中断架构。这是调试外设中断问题的第一步。