3. 设备树中断绑定规范
设备树(Device Tree)通过三个核心属性将硬件中断控制器与设备驱动连接起来:interrupt-parent、interrupts 和 interrupt-cells。理解这三个属性的协作关系,是配置 GPIO 与 IRQ 绑定的基础。本节将从规范定义、属性语法、绑定流程三个维度进行深度解析。
3.1 中断绑定三要素概览
在设备树中,一个完整的中断绑定由以下三部分构成:
| 属性 | 作用 | 所在节点 | 是否必需 |
|---|---|---|---|
interrupt-parent |
指定设备所连接的中断控制器 | 设备节点(或继承自父节点) | 是(若未继承) |
interrupts |
描述设备的中断请求线编号与触发类型 | 设备节点 | 是 |
interrupt-cells |
定义中断控制器描述每条中断所需的单元数 | 中断控制器节点 | 是 |
关键理解:
interrupt-cells 是中断控制器的“协议声明”,interrupts 是设备按照该协议填写的中断描述,interrupt-parent 则是将两者关联的“指针”。
3.2 interrupt-parent:中断父级连接
interrupt-parent 是一个 phandle 类型的属性,指向设备所连接的中断控制器节点。其语法如下:
device_node {
interrupt-parent = <&gpio_controller>;
interrupts = <17 IRQ_TYPE_EDGE_RISING>;
};
如果设备节点没有显式定义 interrupt-parent,则继承其父节点的该属性。根节点通常不定义 interrupt-parent,因此每个设备必须确保在祖先链中能找到有效的 interrupt-parent。
常见错误: 多个设备共享同一个
interrupt-parent 时,若其中一个设备节点错误地覆盖了该属性,会导致其他设备的中断路由失效。建议在 SoC 级总线上统一设置 interrupt-parent,子设备仅填写 interrupts。
3.3 interrupts:中断描述数组
interrupts 属性是一个 u32 数组,其长度和含义由 interrupt-cells 决定。以最常见的 interrupt-cells = <2> 为例:
interrupts = <IRQ_NUMBER IRQ_FLAGS>;
- IRQ_NUMBER:中断号,从 0 开始,对应中断控制器的硬件中断线索引。
- IRQ_FLAGS:中断触发类型标志,常用值如下:
| 标志值 | 宏定义 | 触发类型 |
|---|---|---|
| 0 | IRQ_TYPE_NONE | 未指定 |
| 1 | IRQ_TYPE_EDGE_RISING | 上升沿触发 |
| 2 | IRQ_TYPE_EDGE_FALLING | 下降沿触发 |
| 3 | IRQ_TYPE_EDGE_BOTH | 双边沿触发 |
| 4 | IRQ_TYPE_LEVEL_HIGH | 高电平触发 |
| 8 | IRQ_TYPE_LEVEL_LOW | 低电平触发 |
实战提示: 对于 GPIO 按键,通常使用
IRQ_TYPE_EDGE_BOTH(值 3)来捕获按下和释放两个动作。对于 GPIO 中断唤醒,推荐使用 IRQ_TYPE_LEVEL_LOW(值 8)以保证低功耗模式下的可靠唤醒。
3.4 interrupt-cells:中断控制器的协议声明
interrupt-cells 定义在中断控制器节点中,告诉内核解析 interrupts 数组时需要读取多少个 u32 单元。常见配置如下:
| interrupt-cells 值 | 典型场景 | interrupts 格式示例 |
|---|---|---|
| 1 | 简单中断控制器(如老式 PIC) | interrupts = <5>; |
| 2 | 通用中断控制器(如 GIC、GPIO 控制器) | interrupts = <17 4>; |
| 3 | 复杂中断控制器(如带 SPI/PPI 的 GICv2) | interrupts = <0 30 4>; |
对于 GPIO 控制器,interrupt-cells 通常为 2:第一个单元是 GPIO 引脚编号,第二个单元是中断触发标志。例如:
gpio_controller: gpio@ff0a0000 {
compatible = "vendor,gpio-controller";
reg = <0xff0a0000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
3.5 三属性协作流程:从设备到 CPU 的中断路径
下图展示了设备树中断绑定的完整解析流程:
3.6 实战案例:GPIO 按键中断绑定
以下是一个完整的 GPIO 按键中断绑定示例,展示三个属性的实际配合:
// 中断控制器节点(GPIO 控制器)
gpio0: gpio@ff0a0000 {
compatible = "vendor,gpio-controller";
reg = <0xff0a0000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <0 30 4>; // 连接到 GIC,SPI 30,高电平
interrupt-parent = <&gic>;
};
// 设备节点(按键)
key_enter {
compatible = "gpio-keys";
label = "enter";
gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpio0>; // 指向 GPIO 控制器
interrupts = <17 IRQ_TYPE_EDGE_BOTH>; // GPIO 引脚 17,双边沿触发
};
解析: 按键节点通过
interrupt-parent 连接到 gpio0 控制器。gpio0 声明 #interrupt-cells = <2>,因此内核将 interrupts = <17 IRQ_TYPE_EDGE_BOTH> 解析为:中断线 17,触发类型 3(双边沿)。最终,GPIO 控制器将硬件中断映射到 GIC,再路由到 CPU。
3.7 常见绑定错误与排查
| 错误现象 | 可能原因 | 排查方法 |
|---|---|---|
| 中断无法触发 | interrupt-parent 指向错误或缺失 |
检查设备节点及其父节点是否定义了 interrupt-parent |
| 中断号偏移 | interrupts 中的编号与硬件不匹配 |
核对 GPIO 引脚编号与中断控制器硬件线号 |
| 触发类型错误 | IRQ_FLAGS 与硬件实际触发方式不一致 |
使用示波器或逻辑分析仪确认信号波形 |
| 内核解析失败 | #interrupt-cells 与 interrupts 长度不匹配 |
查看 dmesg 中 "of_irq_parse" 相关错误信息 |
调试命令: 使用
cat /proc/interrupts 查看已注册的中断;使用 dtc -I dtb -O dts 反编译设备树,检查绑定是否正确;使用 ls /sys/firmware/devicetree/base/ 遍历设备树节点。