Saturday, October 9, 2021

Interrupt controllers and interrupt sources in device trees

Refer files under devicetree bindings in kernel source for details specific to your kernel as some details may vary between kernel versions. Here, I'm referring those from linux-imx-4.14.98.

Refer the device tree specification version at

https://www.devicetree.org/specifications/ and

Documentation/devicetree/bindings/interrupt-controller/interrupts.txt

for more details.

*.dts file specifies dt specification version. e.g. /dts-v1/;

Interrupt controllers

Have interrupt-controller property

interrupt-cells property specifies the number of cells needed to encode an interrupt source

Interrupt sources

interrupt-parent property specifies the interrupt controller

interrupts property specifies generated interrupt(s), each having parent's interrupt-cells number of cells


Example from iMX8QXP 

From fsl-imx8dx.dtsi (note: most other properties removed for clarity)
 
gic: interrupt-controller@51a00000 {
    compatible = "arm,gic-v3";
    #interrupt-cells = <3>;
    interrupt-controller;
};

i2c1: i2c@5a810000 {
    compatible = "fsl,imx8qm-lpi2c", "fsl,imx7ulp-lpi2c";
    interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;
    interrupt-parent = <&gic>;
};

interrupt tree:
GIC <--- I2C1

iMX8QXP has Arm GIC-500 that is compliant with Arm GIC architecture specification version 3.0.

From 
Arm CoreLink Generic Interrupt Controller v3 and v4 Overview, Version 3.1 (or https://developer.arm.com/documentation/198123/0302/What-is-a-Generic-Interrupt-Controller-):
INTID     Type
0-15      SGIs typically used for inter-processor communication
16-31     PPIs that are private to one core
32-1019   SPIs that can be delivered to any connected core
:

From Shared Peripheral Interrupt mapping in processor reference manual:
IRQ    Interrupt    Description
253    I2C1_INT     Interrupts grouped on this line:
                    I2C#1 interrupt
                    DMA#3 interrupt#0 –I2C receive
                    DMA#3 interrupt#1 –I2C transmit
 
From Arm GICv3 dt-binding
Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
#interrupt-cells

1st cell: interrupt type (private/shared peripheral interrupts)
2nd cell: interrupt number for the interrupt type (SPI in the range [0-987], PPI in the range [0-15])
3rd cell: flags (edge/level triggered)

interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;
                       ^- 221 + 32 (=hwirq 253)

Check the interrupt in /proc/interrupts and /sys/kernel/irq/[Linux IRQ]/

Notice how hwirq has been mapped to Linux IRQ 

/proc/interrupts
           CPU0       CPU1       CPU2       CPU3       
 44:        112          0          0          0     GICv3 253 Level

Similar information can be seen under /sys/kernel/irq/[Linux IRQ]/

See Documentation/IRQ-domain.txt for details on how hwirqs are mapped to Linux IRQs.

Another example from imx8qxp-mek

gic: interrupt-controller@51a00000 {
    compatible = "arm,gic-v3";
    #interrupt-cells = <3>;
    interrupt-controller;
};

gpio1: gpio@5d090000 {
        compatible = "fsl,imx8qm-gpio", "fsl,imx35-gpio";
        interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
        interrupt-parent = <&gic>;
        gpio-controller;
        interrupt-controller;
        #interrupt-cells = <2>;
};

i2c1: i2c@5a810000 {
    compatible = "fsl,imx8qm-lpi2c", "fsl,imx7ulp-lpi2c";
    interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;
    interrupt-parent = <&gic>;

    typec_ptn5110: typec@50 {
        compatible = "usb,tcpci";
        interrupt-parent = <&gpio1>;
        interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
    };
};

interrupt tree:
GIC <--- I2C1
    <--- GPIO1 <--- PTN5110

From GPIO controller dt-binding
Documentation/devicetree/bindings/gpio/fsl-imx-gpio.txt
#interrupt-cells

1st cell: GPIO number
2nd cell: trigger type and level flags

interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
              ^- hwirq

According to Documentation/IRQ-domain.txt, each interrupt controller driver creates and registers an IRQ domain. So, in our examples above, we have two IRQ domains: GIC and GPIO1.

As mentioned in the Debugging section of Documentation/IRQ-domain.txt, you can enable CONFIG_IRQ_DOMAIN_DEBUG to see IRQ domains and interrupt mapping in the domains from
/sys/kernel/debug/irq_domain_mapping.