3、设备树中断绑定:interrupt-parent、interrupts、interrupt-cells 属性详解

好,咱们接着聊设备树里的中断绑定。说实话,这部分是很多嵌入式工程师的“噩梦”。我见过不少同事,设备树配得飞起,但一到中断就卡壳。其实没那么玄乎,说白了就是三个属性在唱戏:interrupt-parentinterruptsinterrupt-cells。今天我就带你把它们彻底捋清楚。

3.1 中断控制器与中断消费者

先搞清楚两个角色:谁产生中断?谁处理中断?

  • 中断控制器(Interrupt Controller):比如 GIC(通用中断控制器),它是负责接收各路中断信号,然后统一上报给 CPU 的硬件模块。
  • 中断消费者(Interrupt Consumer):就是那些需要中断服务的设备,比如网卡、串口、GPIO 按键等。

在设备树里,中断控制器用 interrupt-controller 属性标记,而消费者则通过 interrupt-parentinterrupts 来“绑定”到某个中断控制器上。

核心思想:设备树的中断绑定,本质上就是告诉内核——“我这个设备的中断信号,连到了哪个中断控制器的哪个引脚上”。

3.2 interrupt-parent:认准你的“顶头上司”

interrupt-parent 是一个 phandle(指针),指向设备所连接的中断控制器节点。每个需要中断服务的设备,都得明确告诉内核:我的中断归谁管。

举个例子:

// 中断控制器节点
gic: interrupt-controller@f0001000 {
    compatible = "arm,cortex-a7-gic";
    interrupt-controller;
    #interrupt-cells = <3>;
    reg = <0xf0001000 0x1000>;
};

// 使用中断的设备
uart0: serial@f0002000 {
    compatible = "ns16550a";
    reg = <0xf0002000 0x1000>;
    interrupt-parent = <&gic>;  // 指向 GIC
    interrupts = <0 33 4>;       // 具体中断描述
};

这里 interrupt-parent = <&gic> 就表示 uart0 的中断信号连到了 gic 上。嗯,这里要注意:如果设备没有显式指定 interrupt-parent,内核会尝试向上查找父节点的 interrupt-parent。但我的建议是——别偷懒,每个设备都显式写上,可读性更好,也避免踩坑。

我的经验:我曾经在一个项目里,因为某个子节点没写 interrupt-parent,结果内核默认用了父节点的中断控制器,但父节点根本没配中断,导致中断一直不触发。排查了整整一天……从那以后,我每个设备都老老实实写上 interrupt-parent。

3.3 interrupts:描述中断的“身份证”

interrupts 属性描述的是中断的具体信息。它的格式由中断控制器的 #interrupt-cells 决定。每个 cell 代表一个 32 位整数,组合起来描述中断号、触发类型等。

常见的 #interrupt-cells 取值:

#interrupt-cells 含义 示例
1 只包含中断号 interrupts = <14>;
2 中断号 + 触发类型 interrupts = <0 14>;
3 中断类型 + 中断号 + 触发类型(GIC 常用) interrupts = <0 33 4>;

以 GIC 的 3-cell 格式为例:

  • 第一个 cell:中断类型。0 表示 SPI(共享外设中断),1 表示 PPI(私有外设中断)。
  • 第二个 cell:中断号。SPI 从 32 开始编号,PPI 从 16 开始。
  • 第三个 cell:触发类型。1 表示上升沿触发,4 表示高电平触发。

所以 interrupts = <0 33 4> 的意思是:这是一个 SPI 中断,中断号 33,高电平触发。

注意:不同中断控制器的 cell 含义完全不同。比如 GPIO 控制器作为中断控制器时,它的 cell 可能表示 GPIO 引脚号和触发极性。一定要看对应控制器的 binding 文档。

3.4 interrupt-cells:定义中断描述的长度

#interrupt-cells 是中断控制器节点的属性,它告诉内核:我这个控制器发出的中断描述,需要几个 32 位整数。

比如 GIC 用 3 个 cell,所以:

gic: interrupt-controller@f0001000 {
    #interrupt-cells = <3>;
};

而一个简单的 GPIO 中断控制器可能只需要 2 个 cell:

gpio_controller: gpio@f0003000 {
    #interrupt-cells = <2>;
    interrupt-controller;
};

为什么需要这个属性?说白了,内核解析设备树时,得知道从 interrupts 里读几个数才算完。没有 #interrupt-cells,内核就不知道中断描述有多长,解析就会出错。

3.5 实战:一个完整的中断绑定示例

咱们来看一个稍微复杂点的例子,把三个属性串起来:

/dts-v1/;

/ {
    model = "MyBoard";
    compatible = "my,board";

    // 主中断控制器:GIC
    gic: interrupt-controller@f0001000 {
        compatible = "arm,cortex-a7-gic";
        interrupt-controller;
        #interrupt-cells = <3>;
        reg = <0xf0001000 0x1000>;
    };

    // 二级中断控制器:GPIO
    gpio0: gpio@f0002000 {
        compatible = "my,gpio-controller";
        reg = <0xf0002000 0x1000>;
        gpio-controller;
        #gpio-cells = <2>;
        interrupt-controller;
        #interrupt-cells = <2>;
        interrupt-parent = <&gic>;
        interrupts = <0 50 4>;  // GPIO 控制器本身的中断连到 GIC
    };

    // 使用 GPIO 中断的设备:按键
    button {
        compatible = "my,gpio-key";
        gpios = <&gpio0 5 0>;  // GPIO 5,默认极性
        interrupt-parent = <&gpio0>;  // 按键中断连到 GPIO 控制器
        interrupts = <5 2>;           // GPIO 5,下降沿触发
    };
};

这个例子展示了中断的层级关系:

  • GIC 是顶层中断控制器,用 3-cell 格式。
  • GPIO 控制器本身也是中断消费者,它连到 GIC,用 interrupts = <0 50 4> 描述自己的中断。
  • 按键设备连到 GPIO 控制器,用 2-cell 格式描述中断。

你想想看,这种层级结构在复杂 SoC 里非常常见。我做过一个项目,中断控制器嵌套了四层,每一层的 #interrupt-cells 都不一样。当时调试的时候,我画了个树形图才理清楚。

3.6 避坑指南

最后,分享几个我踩过的坑:

  • 中断号别搞混:GIC 的 SPI 中断号从 32 开始,但有些 SoC 的 TRM 里写的是“中断号 33”,实际上对应设备树里的 33。但也有厂商从 0 开始编号,一定要看 binding 文档。
  • 触发类型别写错:电平触发和边沿触发,在 GIC 里用不同的值表示。写错了要么中断不触发,要么一直触发(电平没变)。
  • 别忘了 interrupt-controller 属性:中断控制器节点必须加上 interrupt-controller;,否则内核不认。
  • #interrupt-cells 必须和 interrupts 匹配:比如 GIC 用 3-cell,你写 interrupts = <33 4>,内核解析会错位,导致中断号完全不对。

总结一下interrupt-parent 指明方向,interrupts 描述细节,#interrupt-cells 定义格式。三个属性配合好,中断绑定就稳了。

设备树中断绑定核心逻辑 中断控制器 #interrupt-cells = <N> 设备 A interrupt-parent = <&gic> 设备 B interrupt-parent = <&gic> 设备 C interrupt-parent = <&gic> interrupts = <0 33 4> interrupts = <0 45 1> interrupts = <1 18 4> 关键点 interrupt-parent 指向中断控制器 interrupts 格式由 #interrupt-cells 决定

这张图把关系理得很清楚了。中断控制器在最上层,多个设备通过 interrupt-parent 指向它,每个设备用 interrupts 描述自己的中断细节。嗯,就是这么回事。


公众号:蓝海资料掘金营,微信deep3321