中断树模型:在设备树中,存在逻辑中断树,该逻辑中断树表示平台硬件中的中断的层次结构和路由。虽然通常称为中断树,其实它的结构是有向无环图。
在设备树中,使用interrupt-parent属性表示中断源与中断控制器的物理连线。代表中断产生设备的节点都包含interrupt-parent属性,该值用于接收中断的设备。

中断域的概念:在中断初始化过程中,Linux内核会从特定整数区间中选取一个整数作为一个中断源interrupt source的编号,并且使每个中断源都有一个独一无二且不重复的编号。如果系统仅存有一个中断控制器,那么能简单地给每个引脚对应的中断源分配一个编号。不过,如前所述,在有些情况下系统可能配置了多个中断控制器,所以中断源的编号方式将会有点复杂。因此内核需要一种机制去管理硬件中断编号hwirq与Linux中断编号irq之间的映射,以便使多个中断控制器之间不会出现重复相同的irq。正因如此,内核抽象出了中断域概念,由它实现处理中断号的分配和管理。

例如,定义了一个soc的设备树文件。
04.中断和中断映射 - 图1
根据interrupt-parent属性可以整理出该设备树中的中断树,如下
04.中断和中断映射 - 图2
从这个例子中大致可以了解interrupt-parent属性的作用,就是明确的指出设备树中跟中断相关的各个设备的关系。

产生中断的设备属性

interrupts属性

值的类型:prop-encoded-array
描述:设备的interrupts属性定义了设备产生的中断
例子:在开放的PIC兼容中断域中,中断说明符的常见定义由两个单元组成。第一个单元定义了中断数和中断等级。下面定义的中断说明符,中断数是0xA,中断等级为8。

  1. interrupts = <0xA 8>;

interrupt-parent属性

值的类型:phandle
描述:由于节点在中断树中的层级和在设备树中的层级可能不匹配。所以设定了这个属性用来显式的指定中断源。

interrupts-extended属性

值的类型: phandle prop-encodeed-array
描述:该属性描述了该设备能够产生的中断。当存在多个中断控制器时,使用interrupts-extended代替interrupts描述中断。
例子:下面的代码,描述了两个中断输入分别接在两个不同的中断控制器上,pic是一个#interrupt-cells属性为2的中断控制器,gic是一个#interrupt-cells属性为1的中断控制器。

  1. interrupts-extended = <&pic 0xA 8>, <&gic 0xda>;

中断控制器的属性

#interrupt-cells属性

值的类型: <u32>
描述:该属性定义了一个中断说明符(中断域)所需要的单元数。

interrupt-controller属性

值的类型: 空值
描述:该属性将包含该属性的节点定义为中断控制器。

中断链接属性

每个中断链接节点都要拥有#interrupt-cells属性。

interrupt-map属性

值的类型:prop-encode-array
该属性将一个中断域与一组父中断域桥接,并指定子域中的中断说明符如何映射到其各自的父域。
每一行有5项,格式如下:

  • child unit address: 子节点的单元地址
  • child interrupt specifier: 子节点的中断说明符
  • interrupt-parent: 该值指向子节点将被映射到的中断父域
  • parent unit address: 中断父域的地址
  • parent interrupt specifier: 父域中的中断描述符

    interrupt-map-mask属性

    值的类型:prop-encoded-array
    该属性指定了设备树中的链接节点

    #interrupt-cells属性

    值的类型: <u32>
    该属性定义了一个中断说明符(中断域)所需要的单元数。

    中断映射的例子

    1. soc {
    2. compatible = "simple-bus";
    3. #address-cells = <1>;
    4. #size-cells = <1>;
    5. open-pic {
    6. clock-frequency = <0>;
    7. interrupt-controller;
    8. #address-cells = <0>;
    9. #interrupt-cells = <2>;
    10. };
    11. pci {
    12. #interrupt-cells = <1>;
    13. #size-cells = <2>;
    14. #address-cells = <3>;
    15. interrupt-map-mask = <0xf800 0 0 7>;
    16. interrupt-map = <
    17. /* IDSEL 0x11 - PCI slot 1 */
    18. 0x8800 0 0 1 &open-pic 2 1 /* INTA */
    19. 0x8800 0 0 2 &open-pic 3 1 /* INTB */
    20. 0x8800 0 0 3 &open-pic 4 1 /* INTC */
    21. 0x8800 0 0 4 &open-pic 1 1 /* INTD */
    22. /* IDSEL 0x12 - PCI slot 2 */
    23. 0x9000 0 0 1 &open-pic 3 1 /* INTA */
    24. 0x9000 0 0 2 &open-pic 4 1 /* INTB */
    25. 0x9000 0 0 3 &open-pic 1 1 /* INTC */
    26. 0x9000 0 0 4 &open-pic 2 1 /* INTD */
    27. >;
    28. };
    29. };

    中断映射表的每一行都包含5个部分,第一行的组成如下:

  • child unit address:0x8800 0 0 (pci的#address-cells属性是3)

  • child interrupt specifier: 1 (pci的#interrupt-cells属性是1)
  • interrupt parent: &open-pic
  • parent unit address:是空的(open-picaddress-cells为0)
  • parent interrupt specifier: 2 1(open-pic#interrupt-cells属性是2)

在这个例子中,interrupt-map-mask属性的值为<0xf800 0 0 7>
上面的设备树文件主要描述的就是一个具有两个pci插槽的soc,两个pci直接连在soc上,总线号为0x0pci的设备号,一个是0x11,另一个是0x12。要从open-pic找到function 0x3,IDSEL 0x12(slot2)的父级中断域的说明符(子中断域说明符为2),需要执行以下步骤:

  • 先得到设备A的子单元地址和中断说明符,步骤是这样的,总线号左移16位(0x0<<16),设备号左移11位(0x12<<11),函数号左移8位(0x3<<8),得到的结果`<0x9300 0 0 2>`
  • 把上面中得到的结果与interrupt-map-mask进行与操作,得到结果<0x9000 0 0 2>
  • 根据中断映射表得到父级中断说明符为<4 1>