4. 中断控制器属性:interrupt-controller, #interrupt-cells, interrupt-parent 属性解析
在设备树中,中断系统的描述是连接外设与中断控制器(Interrupt Controller)的关键桥梁。本章将深入解析三个核心中断属性:interrupt-controller、#interrupt-cells 和 interrupt-parent。理解这些属性是正确配置中断映射、编写设备树中断节点的基础。
interrupt-controller 标记生产者身份,#interrupt-cells 定义中断描述格式,interrupt-parent 指定消费者连接的生产者。
4.1 interrupt-controller 属性:标记中断控制器节点
interrupt-controller 是一个布尔属性(无值),用于在设备树节点中声明该节点是一个中断控制器。只有被标记的节点才能接收和处理来自其他设备的中断请求。
- 作用: 标识节点为中断资源的管理者,是中断路由的起点。
- 使用位置: 通常出现在 SoC 内部的中断控制器节点(如 GIC、INTC、NVIC)或 GPIO 控制器节点(当 GPIO 可作为中断源时)。
- 语法: 在节点内直接写入
interrupt-controller;即可。
// 示例:ARM Generic Interrupt Controller (GIC) 节点
gic: interrupt-controller@f0001000 {
compatible = "arm,cortex-a15-gic";
reg = <0x0 0xf0001000 0x0 0x1000>,
<0x0 0xf0002000 0x0 0x2000>;
interrupt-controller; // 声明本节点是中断控制器
#interrupt-cells = <3>; // 每个中断描述需要3个cell
#address-cells = <0>;
#size-cells = <0>;
};
interrupt-parent 引用,但自身未声明 interrupt-controller,内核在解析中断时会报错或忽略该中断映射。务必确保中断控制器节点包含此属性。
4.2 #interrupt-cells 属性:定义中断描述格式
#interrupt-cells 是一个整型属性,用于指定该中断控制器在描述一个中断源时需要多少个 32 位单元(cell)。每个 cell 通常包含中断类型、触发方式、优先级等信息,具体含义由中断控制器的绑定文档定义。
| 中断控制器类型 | #interrupt-cells 值 | cell 含义 |
|---|---|---|
| ARM GIC (v2/v3) | 3 | cell[0]: 中断类型 (0=SPI, 1=PPI);cell[1]: 中断号;cell[2]: 触发标志 (如 IRQ_TYPE_EDGE_RISING) |
| RISC-V PLIC | 1 | cell[0]: 中断源编号 (1~N) |
| GPIO 控制器 (作为中断控制器) | 2 | cell[0]: GPIO 引脚号;cell[1]: 触发标志 (低电平、高电平、上升沿等) |
| Xilinx INTC | 2 | cell[0]: 中断号 (0~31);cell[1]: 触发类型 (边沿/电平) |
// 示例:GPIO 控制器作为中断控制器
gpio0: gpio@ff000000 {
compatible = "snps,dw-apb-gpio";
reg = <0xff000000 0x1000>;
#gpio-cells = <2>;
gpio-controller;
interrupt-controller; // 该GPIO控制器也能处理中断
#interrupt-cells = <2>; // 中断描述:引脚号 + 触发标志
interrupts = <0 32 4>; // 该GPIO控制器本身连接到GIC的中断
};
#interrupt-cells 的值决定了外设节点在 interrupts 属性中需要提供多少个 cell。例如,若父中断控制器的 #interrupt-cells = <3>,则外设的 interrupts = <0 42 4>; 表示 SPI 类型、中断号 42、高电平触发。
4.3 interrupt-parent 属性:指定中断连接关系
interrupt-parent 是一个 phandle 属性,用于显式指定当前设备节点连接到哪个中断控制器。它解决了中断路由的“寻路”问题,尤其是在存在多个中断控制器或中断层级嵌套时。
- 默认行为: 如果设备节点没有显式定义
interrupt-parent,内核会沿着设备树向上查找父节点,直到找到一个包含interrupt-controller属性的节点作为默认中断控制器。 - 显式覆盖: 当设备需要连接到非父节点的中断控制器时(如连接到 GPIO 控制器或专用中断控制器),必须使用
interrupt-parent指定。 - 语法:
interrupt-parent = <&gic>;或interrupt-parent = <&gpio0>;
// 示例:外设连接到不同的中断控制器
// 1. 连接到 GIC(默认通过父节点继承)
uart0: serial@10000000 {
compatible = "ns16550a";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>; // 隐式使用父节点的 interrupt-parent
};
// 2. 连接到 GPIO 控制器(显式指定)
button: gpio-keys {
compatible = "gpio-keys";
interrupt-parent = <&gpio0>; // 显式指定中断控制器为 gpio0
interrupts = <5 2>; // GPIO0 的引脚5,下降沿触发
label = "User Button";
};
4.4 中断属性协同工作流程
以下 SVG 图展示了设备树中中断属性的完整解析流程,从外设触发中断到内核处理。
4.5 多级中断控制器与 interrupt-parent 嵌套
在复杂 SoC 中,可能存在多级中断控制器。例如,一个 GPIO 控制器本身连接到 GIC,而外设又连接到 GPIO 控制器。此时,interrupt-parent 的嵌套关系至关重要。
// 多级中断控制器示例
gic: interrupt-controller@f0001000 {
interrupt-controller;
#interrupt-cells = <3>;
};
gpio0: gpio@ff000000 {
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gic>; // GPIO控制器连接到GIC
interrupts = <0 32 4>; // GPIO控制器本身的中断
};
// 外设连接到GPIO控制器
keypad: keypad@0 {
interrupt-parent = <&gpio0>; // 显式指定父中断为GPIO控制器
interrupts = <3 1>; // GPIO0的引脚3,上升沿触发
};
keypad 的中断时,首先通过 interrupt-parent 找到 gpio0,读取其 #interrupt-cells = <2>,将 interrupts = <3 1> 解析为 GPIO 引脚 3、上升沿触发。然后 gpio0 再通过自身的 interrupt-parent 将中断路由到 gic,最终由 GIC 上报给 CPU。
4.6 属性对比与常见陷阱
| 属性 | 类型 | 所属节点 | 作用 | 是否必需 |
|---|---|---|---|---|
interrupt-controller |
布尔 | 中断控制器节点 | 声明本节点是中断控制器 | 是(中断控制器节点) |
#interrupt-cells |
整型 | 中断控制器节点 | 定义中断描述所需的 cell 数量 | 是(中断控制器节点) |
interrupt-parent |
phandle | 任何需要中断的设备节点 | 指定该设备连接的中断控制器 | 否(默认向上查找) |
- 中断号冲突: 不同中断控制器可能使用相同的中断号,但含义不同。务必通过
interrupt-parent明确指定。 - 遗漏 interrupt-controller: 在 GPIO 控制器节点中,如果忘记添加
interrupt-controller,即使有#interrupt-cells,外设也无法通过该节点申请中断。 - cell 数量不匹配: 外设的
interrupts属性提供的 cell 数量必须严格等于父中断控制器的#interrupt-cells值,否则解析会失败。 - 循环依赖: 避免中断控制器之间形成循环的
interrupt-parent引用,这会导致内核死锁或解析失败。
4.7 实战:编写一个完整的中断控制器节点
以下是一个基于 RISC-V PLIC(Platform-Level Interrupt Controller)的完整设备树节点示例,展示了三个属性的协同使用。
// RISC-V PLIC 中断控制器节点
plic: interrupt-controller@c000000 {
compatible = "riscv,plic0";
reg = <0x0 0xc000000 0x0 0x4000000>;
interrupts-extended = <&cpu0_intc 11>,
<&cpu1_intc 11>;
interrupt-controller; // 标记为中断控制器
#interrupt-cells = <1>; // 每个中断用1个cell描述(中断源编号)
riscv,ndev = <31>; // 最大支持31个外部中断源
};
// 外设连接到 PLIC
uart0: serial@10000000 {
compatible = "ns16550a";
reg = <0x0 0x10000000 0x0 0x1000>;
interrupt-parent = <&plic>; // 连接到 PLIC
interrupts = <10>; // 中断源编号10
clock-frequency = <3686400>;
};
cat /proc/interrupts 查看中断映射是否正确。如果设备的中断号显示为 0 或缺失,通常意味着 interrupt-parent 或 #interrupt-cells 配置有误。使用 dtc -I dtb -O dts 反编译设备树二进制文件,检查属性值是否符合预期。
通过本章的学习,你应该能够准确理解并配置设备树中的中断控制器属性。这三个属性共同构成了中断系统的“骨架”,为外设与中断控制器之间的通信提供了清晰的描述规范。在实际开发中,务必参考对应中断控制器的绑定文档,确保 cell 含义与硬件一致。