This article explains STM32 MPU interrupt topology and its management on Linux® environment.
1. Framework purpose[edit | edit source]
The Linux® kernel software layer that handles the interrupts is splitted into two parts:"
- A generic part:
- providing a common API to request and configure an interrupt line.
- creating a virtual mapping for all interrupts in order to have only one ID per interrupt.
- providing callback for irqchip registering.
- An irqchip driver part:
- handling hardware accesses and managing specific features.
For more information refer to Linux® kernel documentation in core-api/genericirq.html[1].
2. STM32 interrupt topology[edit | edit source]
As explain in Framework purpose, the irqchip driver makes the interface with the hardware to configure and manage an interrupt. On STM32 Arm® Cortex® MPUs devices, a hardware interrupt can be generated by GIC, EXTI, PWR or GPIO. Several irqchip drivers are consequently required, one per hardware block.
The next section provides topology information for each kind of interrupt source.
2.1. Overview[edit | edit source]
2.2. Component description[edit | edit source]
- procfs: provides interrupt information to the user space.
- foo_driver: device driver requesting an interrupt line.
- interrupt framework: generic part described in the Framework purpose section.
- Irqchips:
- GIC irqchip: GIC irqchip driver part. This irqchip driver is used when a GIC interrupt is directly requested by a device. This is the case for all the peripheral interrupts that are not wakeup sources. This irqchip is in charge of controlling the GIC internal peripheral (hardware). An example of GIG irqchip usage is available here.
- EXTI irqchip: EXTI irqchip driver part. This irqchip driver is used when an EXTI interrupt (EXTernal Interrupt) is requested by a device. This kind of interrupts is used to wake up the system from low-power mode. This irqchip directly controls the EXTI internal peripheral but it is also linked to the gic irqchip through the hierarchical irq domain mechanism[2]. This link is transparent for the requester.
- pwr_irq irqchip: pwr_irq irqchip driver part. This irqchip driver is used when an EXTI interrupt mapped to a wakeup pin is requested by a device. This kind of interrupts is used to wake up the system from the deepest low-power mode (more details about low-power modes are available here). This irqchip is part of the hierarchical irq domain mechanism[2]. The pwr_irq irqchip in turn controls the PWR internal peripheral and it is also linked to the gic irqchip through the same hierarchical irq domain mechanism[2]. These hierarchical links are transparent for the requester.
- optee irqchip: This irqchip driver is used when a 'wakeup' pins handled by OPTEE is requested by a device. This "optee" irqchip is created in Linux to expose those wakeup pins. In those cases, a Linux kernel device can get an interrupt mapped on a wakeup pin using an optee phandle in devicetree.
- Note:
- Relevant for STM32MP13x lines and STM32MP2 series, as in those cases, the wakeup pins are handled by OPTEE.
- See How_to_configure_PWR_Wake-up_pins for more details.
- Note:
- Pinctrl irqchip: Pinctrl irqchip driver part. This irqchip driver is used when a device wants to configure/request a GPIO as an interrupt. It directly controls the GPIO internal peripheral but it is also linked to the EXTI irqchip through the hierarchical irq domain mechanism[2]. This link is transparent for the requester.
- STM32 hardware peripherals: GIC, EXTI, PWR, GPIO
3. API description[edit | edit source]
The kernel space API is the interface for declaring and managing interrupts. The user space interface is used to monitor interrupt information or set interrupt affinity.
3.1. User space API[edit | edit source]
procfs performs the following tasks:
- It provides information about interrupts such as the virtual number, the hardware ID and the irqchip used (see chapter 1.2 Kernel data of /proc kernel documentation[3]).
root@stm32mp1:~# cat /proc/interrupts CPU0 CPU1 17: 0 0 GIC-0 37 Level rcc irq 20: 7509664 7509640 GIC-0 27 Level arch_timer 22: 0 0 GIC-0 232 Level arm-pmu 23: 0 0 GIC-0 233 Level arm-pmu 24: 0 0 GIC-0 68 Level 4000b000.audio-controller 26: 0 0 stm32-exti-h 27 Edge 4000e000.serial:wakeup 27: 8915 0 GIC-0 84 Level 40010000.serial 28: 0 0 stm32-exti-h 30 Edge 40010000.serial:wakeup 29: 654 0 GIC-0 63 Level 40012000.i2c 30: 0 0 GIC-0 64 Level 40012000.i2c 31: 0 0 stm32-exti-h 21 Edge 40012000.i2c:wakeup 33: 0 0 GIC-0 123 Level 4400b004.audio-controller, 4400b024.audio-controller ...
- It configures interrupt affinity[4], that is assigns an interrupt to a dedicated CPU.
3.2. Kernel space API[edit | edit source]
The main kernel API drivers for users are the following:
- devm_request_irq: requests an interrupt.
- devm_free_irq: frees an interrupt.
- enable_irq: enables a requested interrupt.
- disable_irq: disables a requested interrupt.
- enable_irq_wake: enables a requested interrupt that could wake up the system.
- disable_irq_wake: disables a requested interrupt that could wake up the system.
... The available routines can be found in Linux® kernel header file: include/linux/interrupt.h[5].
4. Configuration[edit | edit source]
4.1. Kernel configuration[edit | edit source]
The interrupt framework and irqchip drivers are enabled by default.
4.2. Device tree configuration[edit | edit source]
The generic way to declare an interrupt in the device tree is declared in Linux® kernel documentation in: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt [6].
However each irqchip driver has his own bindings description. The below chapters provide the link to the bindings documentation for each interrupt controller as well as a simple example of interrupt declaration.
4.2.1. GIC irqchip[edit | edit source]
- Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt [7]
- Device tree usage:
&foo_node { ... interrupts = <&intc GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "foo_name"; ... };
4.2.2. EXTI irqchip[edit | edit source]
- Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt [8]
- Device tree usage:
&foo_node { ... interrupts-extended = <&exti 26 IRQ_TYPE_EDGE_RISING>; interrupt-names = "foo_name"; ... };
4.2.3. pinctrl irqchip[edit | edit source]
- Device tree usage:
&foo_node { ... interrupts-extended = <&gpioa 14 IRQ_TYPE_EDGE_FALLING>; interrupt-names = "foo_name"; ... };
4.2.4. pwr_irq irqchip[edit | edit source]
Pwr_irq irqchip has not to be used directly. User has to use exti to invoke pwr_irq irqchip thanks to the hierarchical implementation.
4.2.5. optee irqchip[edit | edit source]
- Device tree usage:
&foo_node { ... interrupts-extended = <&optee 0>; wakeup-source; ... };
5. OP-TEE : Interrupt management[edit | edit source]
OP-TEE OS interrupt services are based on the Arm GIC driver. TF-A and U-Boot do not handle interrupts. STM32MPU uses GICv2 model.
OP-TEE OS handling of interrupt may imply CPU execution context switches between secure and non-secure worlds.
Two types of interrupts are defined from OP-TEE OS point of view:
- Native interrupt - The interrupts handled by OP-TEE OS in its secure privileged mode.
- Foreign interrupt - The interrupt to be handled by the non-secure world, that is the Linux kernel.
For Arm GICv2 mode:
- a native interrupt is signalled with a FIQ, assigned to the secure world
- a foreign interrupt is signalled with an IRQ, assigned to the non-secure world
NOTE: This description is in alignment with OP-TEE version: v3.19.0
5.1. Interrupt Framework[edit | edit source]
OP-TEE OS has the following files that implement the interrupt framework:
- plat-stm32mp*/main.c
- gic.c
- interrupt.c
Additionally, the files implement the interrupts for PMIC:
- optee_os/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pmic.c
- optee_os/core/arch/arm/plat-stm32mp1/drivers/stm32mp1_pwr_irq.c
- optee_os/core/arch/arm/plat-stm32mp2/drivers/stm32mp2_pmic.c
- optee_os/core/arch/arm/plat-stm32mp2/drivers/stm32mp25_pwr_irq.c
NOTE: pwr_irq is an extra interrupt controller used for EXTI 55 to 60. It's mapped on pwr interrupt controller.
5.2. Use cases of world context switch[edit | edit source]
OP-TEE OS executes in the secure world. World switch is done by the core’s secure monitor level/mode.
When the normal world invokes the secure world, the normal world executes a SMC instruction. The SMC exception is always trapped by the Monitor. If the related service targets the trusted OS, the Monitor will switch to OP-TEE OS world execution. When the secure world returns to the normal world, OP-TEE OS executes an SMC that is caught by the Monitor which switches back to the normal world.
When a secure interrupt is signaled by the Arm GIC, it shall reach the OP-TEE OS interrupt exception vector. If the secure world is executing, OP-TEE OS will handle interrupt straight from its exception vector. If the normal world is executing when the secure interrupt raises, the Monitor vector must handle the exception and invoke OP-TEE OS to serve the interrupt.
When a non-secure interrupt is signaled by the Arm GIC, it shall reach the normal world interrupt exception vector. If the normal world is executing, it will handle straight the exception from its exception vector. If the secure world is executed when the non-secure interrupt raises, OP-TEE OS will temporarily return back to normal world via the Monitor to let normal world serve the interrupt.
Refer documentation for details: OP-TEE Docs.[9]
5.3. Interrupt management API functions:[edit | edit source]
Interrupt management resources are declared in header file interrupt.h A single linked list is created where the interrupt handlers are stored.
- itr_enable() and itr_disable() : enable or disable an interrupt respectively.
- itr_set_ipriority(): Set the priority of an interrupt.
- itr_handle(): Handler to the interrupt, invoked by - gic_it_handle()
- itr_core_handler(): Interrupt root handler. plat-stm32mp1/main.c routes the interrupts to the interrupt handler of the GIC driver.
- itr_raise_pi(): raise the Peripheral Interrupt corresponding to the interrupt ID
- itr_raise_sgi() : raise the Software Generated Interrupt corresponding to the interrupt ID, the input argument cpu_mask represents which CPU shall receive the interrupt.
- itr_set_affinity(): let corresponding interrupt forward to the CPU interface according to the cpu_mask specified in the input argument.
- itr_add(): Register an interrupt handler function for a given interrupt ID.
- itr_free(): Unregister an interrupt handler function from a given interrupt ID.
- itr_set_pmr(): Set the GIC device Priority Mask and return its previous value.
5.3.1. Interrupt controller drivers[edit | edit source]
struct itr_chip: An interrupt controller instance, named chip, defines operation function handlers for management of the interrupt(s) it controls. An interrupt chip driver must provide operation handler functions: .add, .enable and .disable. There are other operation handler functions that are optional, as for example: .rasie_pi, .raise_sgi and .set_priority.
An interrupt chip driver registers the controller instance with API function itr_init(). It is called by the main_init_gic() API defined in the main.c present in the platform folder.
6. References[edit | edit source]
- ↑ Generic IRQ documentation
- ↑ 2.0 2.1 2.2 2.3 https://www.kernel.org/doc/Documentation/IRQ-domain.txt(master), IRQ domain documentation
- ↑ https://www.kernel.org/doc/Documentation/filesystems/proc.txt(master), User space /proc documentation
- ↑ https://www.kernel.org/doc/Documentation/IRQ-affinity.txt(master), IRQ affinity documentation
- ↑ [include/linux/interrupt.h ], Kernel interrupt API
- ↑ Generic interrupts bindings documentation, Generic interrupts bindings documentation
- ↑ https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt(master), GIC controller binding documentation
- ↑ https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt(master), Exti interrupts bindings documentation
- ↑ [1] Interrupt Handling in OP-TEE