Replace `volatile` access to pm_blocker by guarding the accesses with
`irq_disable()` ... `irq_restore()`.
`volatile` does only guarantee that no compiler optimizations are performed on
a variable access, but does not provide atomic access. E.g. on systems with
a memory bus of less than 32 bit, the access to pm_blocker cannot be done
with a single CPU instruction. Thus, resorting to disabling IRQs is the easiest
and most portable way to actually achieve atomic access.
- pm_off() should prevent other threads from getting executed after it is
called, as it should turn off the MCU completely according to its doc
- Previously, the fallback implementation would still allow other threads
to continue working
- Simply disabling IRQs and never enabling them again should make sure
the MCU behaves like it would be completely off
- pm_off() should reduce the power consumption as much as possible
- Previously, when IRQs came after the call to pm_set_lowest() in the
fallback implementation of pm_off(), `while(1) {}` got executed at full
power consumption
- Just calling `pm_set(0);` in a loop while make sure that lowest power mode
is restored if the MCU wakes up again.
- The check if the lowest power mode is available is skipped, as no code
gets executed afterwards anyway
Instead of using `weak` function definitions, this PR handles
default implementations using `PROVIDES_x` defines, allowing
for cpus/pm realted modules to use their own implementations.