pm: Optimize pre-wakeup in suspend procedure

When core is suspended and power mode has non zero exit_latency_us
a system timeout will be rescheduled to a point in time that is
earlier by exit_latency_us than request. It is to accommodate for
lengthy resuming procedure which would cause requested timeout to
be significantly late. However, setting additional wake up point
has cost, it is one more redundant core wake up and that impacts
performance and power consumption.

Add Kconfig option to chose what conversion method is used. It has
the biggest impact on small exit_latency_us where conversion may
result in 0 ticks (no pre-wake up) or 1 tick (wake up).

Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
Krzysztof Chruściński 2025-04-03 14:50:27 +02:00 committed by Benjamin Cabé
parent ddedff7155
commit 226c6ae17c
2 changed files with 29 additions and 6 deletions

View File

@ -53,6 +53,26 @@ config PM_NEED_ALL_DEVICES_IDLE
When this option is enabled, check that no devices are busy before
entering into system low power mode.
choice PM_PREWAKEUP_CONV_MODE
prompt "Pre-wakeup microseconds to ticks conversion method"
default PM_PREWAKEUP_CONV_MODE_NEAR
help
Exit latency for each power state is given in microseconds. It is converted
to ticks and system clock is set to wake up the core earlier before scheduled
wake up. This option specifies rounding that is used when converting exit
latency from microseconds to system ticks.
config PM_PREWAKEUP_CONV_MODE_NEAR
bool "Nearest"
config PM_PREWAKEUP_CONV_MODE_CEIL
bool "Round up"
config PM_PREWAKEUP_CONV_MODE_FLOOR
bool "Round down"
endchoice
endif # PM
config PM_DEVICE

View File

@ -26,6 +26,11 @@ LOG_MODULE_REGISTER(pm, CONFIG_PM_LOG_LEVEL);
static ATOMIC_DEFINE(z_post_ops_required, CONFIG_MP_MAX_NUM_CPUS);
static sys_slist_t pm_notifiers = SYS_SLIST_STATIC_INIT(&pm_notifiers);
/* Convert exit-latency-us to ticks using specified method. */
#define EXIT_LATENCY_US_TO_TICKS(us) \
IS_ENABLED(CONFIG_PM_PREWAKEUP_CONV_MODE_NEAR) ? k_us_to_ticks_near32(us) : \
IS_ENABLED(CONFIG_PM_PREWAKEUP_CONV_MODE_CEIL) ? k_us_to_ticks_ceil32(us) : \
k_us_to_ticks_floor32(us)
/*
* Properly initialize cpu power states. Do not make assumptions that
* ACTIVE_STATE is 0
@ -145,6 +150,7 @@ bool pm_system_suspend(int32_t kernel_ticks)
uint8_t id = CPU_ID;
k_spinlock_key_t key;
int32_t ticks, events_ticks;
uint32_t exit_latency_ticks;
SYS_PORT_TRACING_FUNC_ENTER(pm, system_suspend, kernel_ticks);
@ -194,16 +200,13 @@ bool pm_system_suspend(int32_t kernel_ticks)
}
#endif
if ((z_cpus_pm_state[id].exit_latency_us != 0) &&
(ticks != K_TICKS_FOREVER)) {
exit_latency_ticks = EXIT_LATENCY_US_TO_TICKS(z_cpus_pm_state[id].exit_latency_us);
if ((exit_latency_ticks > 0) && (ticks != K_TICKS_FOREVER)) {
/*
* We need to set the timer to interrupt a little bit early to
* accommodate the time required by the CPU to fully wake up.
*/
sys_clock_set_timeout(ticks -
k_us_to_ticks_ceil32(
z_cpus_pm_state[id].exit_latency_us),
true);
sys_clock_set_timeout(ticks - exit_latency_ticks, true);
}
/*