arch: arm: aarch32: Fix arch_cpu_idle interrupt masking

The current AArch32 `arch_cpu_idle` implementation enables interrupt
before executing the WFI instruction, and this has the side effect of
allowing interruption and thereby calling wake-up notification
functions before the CPU enters sleep.

This commit fixes the problem described above by ensuring that
interrupt is disabled when the WFI instruction is executed and
re-enabled only after the processor wakes up.

For ARMv6-M, ARMv8-M Baseline and ARM-R, the PRIMASK (ARM-M)/
CPSR.I (ARM-R) is used to lock interrupts and therefore it is not
necessary to do anything before executing the WFI instruction.

For ARMv7-M and ARMv8-M Mainline, the BASEPRI is used to lock
interrupts and the PRIMASK is always cleared in non-interrupt context;
therefore, it is necessary to set the PRIMASK to mask interrupts,
before clearing the BASEPRI to configure wake-up interrupt priority to
the lowest.

Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
This commit is contained in:
Stephanos Ioannidis 2020-03-17 09:27:33 +09:00 committed by Ioannis Glaropoulos
commit ba0bcaf41b

View file

@ -62,19 +62,36 @@ SECTION_FUNC(TEXT, arch_cpu_idle)
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
#endif /* CONFIG_TRACING */
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) \
|| defined(CONFIG_ARMV7_R)
cpsie i
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
/* clear BASEPRI so wfi is awakened by incoming interrupts */
#if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
/*
* PRIMASK is always cleared on ARMv7-M and ARMv8-M Mainline (not used
* for interrupt locking), and configuring BASEPRI to the lowest
* priority to ensure wake-up will cause interrupts to be serviced
* before entering low power state.
*
* Set PRIMASK before configuring BASEPRI to prevent interruption
* before wake-up.
*/
cpsid i
/* Set wake-up interrupt priority to the lowest */
eors.n r0, r0
msr BASEPRI, r0
#else
#error Unknown ARM architecture
#endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */
/*
* For all the other ARM architectures that do not implement BASEPRI,
* PRIMASK is used as the interrupt locking mechanism, and it is not
* necessary to set PRIMASK here, as PRIMASK would have already been
* set by the caller as part of interrupt locking if necessary
* (i.e. if the caller sets _kernel.idle).
*/
#endif /* CONFIG_ARMV7_M_ARMV8_M_MAINLINE */
wfi
/* Clear PRIMASK to service any pending interrupt */
cpsie i
bx lr
SECTION_FUNC(TEXT, arch_cpu_atomic_idle)