Some clarification on Power Management using NCS/Zephyr, please, for present
and future use:
1. There is System PM and Device PM.
2. System PM is associated with the processor. When the main thread (and all
other threads) end (yield or sleep) and the idle thread runs, the processor
core is stopped. If CONFIG_PM is enabled, before stopping, the processor
core is configured for low-power state, which is apparently "SystemOn Idle";
that is, either PM_STATE_RUNTIME_IDLE or PM_STATE_SUSPEND_TO_IDLE.
3. By default (or if CONFIG_PM_POLICY_DEFAULT is enabled), no other attention
to managing the system power savings is required.
If CONFIG_PM_POLICY_CUSTOM is enabled, the API can/must be used to handle
power savings under direct program control.
4. The nRF52840 (and other nRF52, and maybe other nRF5) can turn off all/part
of RAM for further power savings. There's no mention of this in the Zephyr
docs, but if CONFIG_APP_RETENTION is enabled, nrfx library resources can
be used to do this. I'm guessing ordinarily RAM is left alone and all
the static, stack, and heap items are there available for when the
system wakes up again.
3. Device PM is associated with the devices or on-chip peripherals.
Unless otherwise stated, the devices get turned off when the CPU
does (i.e., Device PM == System PM). I'm guessing Device PM does
not occur and the peripherals are not altered (PM_STATE_RUNTIME_IDLE)
unless CONFIG_PM_DEVICE is enabled, in which case the peripherals
can be shut down or powered off during System PM
(PM_STATE_SUSPEND_TO_IDLE). Or will be shut down in the absence of
any other config options.
4. The recommendation for Device PM is CONFIG_PM_DEVICE_RUNTIME.
After activating a peripheral (via is_device_ready(), it is prepped
for Device PM by calling pm_device_runtime_enable() (which also shuts it
down). Thereafter, the application code must call pm_device_runtime_get()
to wake it up when it is required and then call pm_device_runtime_put()
to shut down a device when it is done being used.
Otherwise, all the peripherals wake up when the system does, even if
they're not going to be used.
5. If Runtime Device PM is enabled, a peripheral can be shut down and remove
its contribution from the power demand even while the rest of the system
is running.
6. I've seen external sample code (from a DevZone query) in which all the
unused peripherals are "disabled" in a DeviceTree overlay. Since none of
the Zephyr samples do this, I'm guessing that unless a peripheral is
activated (via is_device_ready()), it never gets powered, or Device PM
makes sure it never gets powered.
7. Apparently, Zephyr runs (gets its "ticks") from a RTC peripheral, which is
driven either by a 32KHz crystal or a 32KHz waveform synthesized from the
main clock. I'm guessing the RTC wakes up the CPU to run any timers that
are configured, and to perform the Bluetooth radio activities on schedule.
So one of the three RTCs never gets shut down (which one?).
None of the timers are involved with the kernel, so if timer activity is
required across system sleep/low-power intervals, either no Device PM
can be used or Runtime Device PM must intentionally exclude such timers
from being shut down. I'm guessing a timer interrupt will wake up
the system just like a "tick", and there will be a system wake-up latency.
8. In the nRF52840 datasheet, peripherals don't appear to have shutdown or
power-off controls but they do have enable/disable registers. I'm guessing
that Device PM turns on/off these bits and then the PMU turns off the power
to the disabled peripheral.
8. There are a CONFIG_PM_CPU_OPS and a CONFIG_PM_DEVICE_POWER_DOMAIN and a
CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE and a series of CONFIG_PM_MCUX_ config
options, but none of the nrf or Zephyr samples use them, so they must not
be critical. Or maybe not even wired up in NCS.
9. I see some main() functions in the samples end in a busy loop and some
that do not. I've got (pre-PM) code now that after setting up the
Bluetooth structure and start a timer just ends and lets everything
just work. I'm guessing that the main() function exits to the idle
thread. Or would it be better to end in a forever-sleep?