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 的中断路径

下图展示了设备树中断绑定的完整解析流程:

设备树中断绑定协作流程 设备树中断绑定三属性协作流程 设备节点 interrupt-parent 中断控制器节点 #interrupt-cells = <2> 内核中断子系统 of_irq_parse() interrupts = <17 4> → 内核读取 2 个 u32:IRQ 号=17,触发类型=4(高电平) 硬件中断线 GPIO 引脚 17 中断控制器 GIC / GPIO 控制器 CPU 中断 IRQ 处理 interrupt-parent 指向中断控制器 → interrupts 按 #interrupt-cells 解析 → 内核完成硬件中断到软件 IRQ 的映射

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/ 遍历设备树节点。

3.8 本章小结