diff --git a/doc/services/pm/system.rst b/doc/services/pm/system.rst index 989fcdb4a9150..ccbe7ccca8f3d 100644 --- a/doc/services/pm/system.rst +++ b/doc/services/pm/system.rst @@ -93,3 +93,16 @@ applications to configure the policy manager to block system from transitioning into certain power states. This can be used by devices when executing tasks in background to prevent the system from going to a specific state where it would lose context. + +Some power states above S2RAM may also affect peripheral context; this can lead +to a peripheral losing configuration when certain SoCs enters these specific +low-power states. + +These exception states can be described in a peripheral's devicetree node with +the ``reinit-power-states`` property: + +.. code-block:: devicetree + + device-1 { + reinit-power-states = <&stop2>; + }; diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c index 9243b10d7e73c..15064c52290b1 100644 --- a/drivers/serial/uart_stm32.c +++ b/drivers/serial/uart_stm32.c @@ -1955,6 +1955,29 @@ static int uart_stm32_registers_configure(const struct device *dev) return 0; } +#ifdef CONFIG_PM +/** + * @brief Reinitialization of UART context + * + * This function reenables clocks, and reconfigures the peripheral registers, + * which is required upon exiting certain low-power modes on select SoCs. + * + * @param direction PM state transition direction + * @param ctx UART device struct + */ +static void uart_stm32_reinit(uint8_t direction, void *ctx) +{ + ARG_UNUSED(direction); + const struct device *dev = ctx; + + if (uart_stm32_clocks_enable(dev)) { + return; + } + + uart_stm32_registers_configure(dev); +} +#endif /* CONFIG_PM */ + /** * @brief Initialize UART channel * @@ -2007,6 +2030,15 @@ static int uart_stm32_init(const struct device *dev) } #endif /* CONFIG_PM */ +#ifdef CONFIG_PM + if (config->reinit_states_size > 0) { + for (uint8_t i = 0; i < config->reinit_states_size; i++) { + pm_notifier_register(config->notifier, config->reinit_states[i].state, + config->reinit_states[i].substate_id); + } + } +#endif /* CONFIG_PM */ + #ifdef CONFIG_UART_ASYNC_API return uart_stm32_async_init(dev); #else @@ -2162,9 +2194,20 @@ static void uart_stm32_irq_config_func_##index(const struct device *dev) \ .wakeup_line = COND_CODE_1(DT_INST_NODE_HAS_PROP(index, wakeup_line), \ (DT_INST_PROP(index, wakeup_line)), \ (STM32_EXTI_LINE_NONE)), + +#define STM32_UART_REINIT_STATES_INIT(index) \ + static const struct pm_state_info reinit_states_##index[] \ + = PM_STATE_INFO_LIST_FROM_DT_REINIT(DT_DRV_INST(index)) + +#define STM32_UART_REINIT_CFG_INIT(index) \ + .notifier = &PM_NOTIFIER(DT_INST_DEP_ORD(index)), \ + .reinit_states = reinit_states_##index, \ + .reinit_states_size = ARRAY_SIZE(reinit_states_##index), #else #define STM32_UART_PM_WAKEUP(index) /* Not used */ -#endif +#define STM32_UART_REINIT_STATES_INIT(index) +#define STM32_UART_REINIT_CFG_INIT(index) +#endif /* CONFIG_PM */ /* Ensure DTS doesn't present an incompatible parity configuration. * Mark/space parity isn't supported on the STM32 family. @@ -2273,10 +2316,16 @@ BUILD_ASSERT( \ #endif #define STM32_UART_INIT(index) \ + \ STM32_UART_IRQ_HANDLER_DECL(index) \ \ PINCTRL_DT_INST_DEFINE(index); \ \ +STM32_UART_REINIT_STATES_INIT(index); \ + \ +PM_NOTIFIER_DEFINE(DT_INST_DEP_ORD(index), PM_STATE_EXIT, \ + uart_stm32_reinit, DEVICE_DT_INST_GET(index)); \ + \ static const struct stm32_pclken pclken_##index[] = \ STM32_DT_INST_CLOCKS(index);\ \ @@ -2309,6 +2358,7 @@ static const struct uart_stm32_config uart_stm32_cfg_##index = { \ .de_deassert_time = DT_INST_PROP(index, de_deassert_time), \ .de_invert = DT_INST_PROP(index, de_invert), \ STM32_UART_IRQ_HANDLER_FUNC(index) \ + STM32_UART_REINIT_CFG_INIT(index) \ STM32_UART_PM_WAKEUP(index) \ }; \ \ diff --git a/drivers/serial/uart_stm32.h b/drivers/serial/uart_stm32.h index ed8e8584cd66d..4dafb5165e6c9 100644 --- a/drivers/serial/uart_stm32.h +++ b/drivers/serial/uart_stm32.h @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -59,6 +60,9 @@ struct uart_stm32_config { /* Device defined as wake-up source */ bool wakeup_source; uint32_t wakeup_line; + struct pm_notifier *notifier; + const struct pm_state_info *reinit_states; + size_t reinit_states_size; #endif /* CONFIG_PM */ }; diff --git a/drivers/spi/spi_ll_stm32.c b/drivers/spi/spi_ll_stm32.c index 948b7641eaf61..a6d8814dd2a87 100644 --- a/drivers/spi/spi_ll_stm32.c +++ b/drivers/spi/spi_ll_stm32.c @@ -13,7 +13,6 @@ LOG_MODULE_REGISTER(spi_ll_stm32); #include #include #include -#include #include #include #include @@ -983,10 +982,8 @@ static inline bool spi_stm32_is_subghzspi(const struct device *dev) #endif } -static int spi_stm32_init(const struct device *dev) +static int spi_stm32_clock_configure(const struct spi_stm32_config *cfg) { - struct spi_stm32_data *data __attribute__((unused)) = dev->data; - const struct spi_stm32_config *cfg = dev->config; int err; if (!device_is_ready(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE))) { @@ -1011,6 +1008,41 @@ static int spi_stm32_init(const struct device *dev) } } + return 0; +} + +#ifdef CONFIG_PM +/** + * @brief Reinitialization of SPI context + * + * This function reenables clocks, which is required upon exiting certain + * low-power modes on select SoCs. + * + * @param dev SPI device struct + * + * @return 0 + */ +static void spi_stm32_reinit(uint8_t direction, void *ctx) +{ + ARG_UNUSED(direction); + const struct device *dev = ctx; + const struct spi_stm32_config *cfg = dev->config; + + spi_stm32_clock_configure(cfg); +} +#endif /* CONFIG_PM */ + +static int spi_stm32_init(const struct device *dev) +{ + struct spi_stm32_data *data __attribute__((unused)) = dev->data; + const struct spi_stm32_config *cfg = dev->config; + int err; + + err = spi_stm32_clock_configure(cfg); + if (err < 0) { + return err; + } + if (!spi_stm32_is_subghzspi(dev)) { /* Configure dt provided device signals when available */ err = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); @@ -1048,6 +1080,15 @@ static int spi_stm32_init(const struct device *dev) spi_context_unlock_unconditionally(&data->ctx); +#ifdef CONFIG_PM + if (cfg->reinit_states_size > 0) { + for (size_t i = 0; i < cfg->reinit_states_size; i++) { + pm_notifier_register(cfg->notifier, cfg->reinit_states[i].state, + cfg->reinit_states[i].substate_id); + } + } +#endif /* CONFIG_PM */ + return 0; } @@ -1119,13 +1160,30 @@ static void spi_stm32_irq_config_func_##id(const struct device *dev) \ #define STM32_SPI_USE_SUBGHZSPI_NSS_CONFIG(id) #endif +#ifdef CONFIG_PM +#define STM32_SPI_REINIT_STATE_INIT(id) \ + static const struct pm_state_info reinit_states_##id[] \ + = PM_STATE_INFO_LIST_FROM_DT_REINIT(DT_DRV_INST(id)) +#define STM32_SPI_REINIT_CFG_INIT(id) \ + .notifier = &PM_NOTIFIER(DT_INST_DEP_ORD(id)), \ + .reinit_states = reinit_states_##id, \ + .reinit_states_size = ARRAY_SIZE(reinit_states_##id), +#else +#define STM32_SPI_REINIT_STATE_INIT(id) +#define STM32_SPI_REINIT_CFG_INIT(id) +#endif /* CONFIG_PM */ #define STM32_SPI_INIT(id) \ STM32_SPI_IRQ_HANDLER_DECL(id); \ \ PINCTRL_DT_INST_DEFINE(id); \ \ +STM32_SPI_REINIT_STATE_INIT(id); \ + \ +PM_NOTIFIER_DEFINE(DT_INST_DEP_ORD(id), PM_STATE_EXIT, \ + spi_stm32_reinit, DEVICE_DT_INST_GET(id)); \ + \ static const struct stm32_pclken pclken_##id[] = \ STM32_DT_INST_CLOCKS(id);\ \ @@ -1136,6 +1194,7 @@ static const struct spi_stm32_config spi_stm32_cfg_##id = { \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \ STM32_SPI_IRQ_HANDLER_FUNC(id) \ STM32_SPI_USE_SUBGHZSPI_NSS_CONFIG(id) \ + STM32_SPI_REINIT_CFG_INIT(id) \ }; \ \ static struct spi_stm32_data spi_stm32_dev_data_##id = { \ diff --git a/drivers/spi/spi_ll_stm32.h b/drivers/spi/spi_ll_stm32.h index 949b8882dd7d0..6311b820ab118 100644 --- a/drivers/spi/spi_ll_stm32.h +++ b/drivers/spi/spi_ll_stm32.h @@ -7,6 +7,9 @@ #ifndef ZEPHYR_DRIVERS_SPI_SPI_LL_STM32_H_ #define ZEPHYR_DRIVERS_SPI_SPI_LL_STM32_H_ +#include +#include + #include "spi_context.h" typedef void (*irq_config_func_t)(const struct device *port); @@ -30,6 +33,11 @@ struct spi_stm32_config { #endif size_t pclk_len; const struct stm32_pclken *pclken; +#ifdef CONFIG_PM + struct pm_notifier *notifier; + const struct pm_state_info *reinit_states; + size_t reinit_states_size; +#endif }; #ifdef CONFIG_SPI_STM32_DMA diff --git a/dts/arm/st/wl/stm32wl.dtsi b/dts/arm/st/wl/stm32wl.dtsi index 26da70106c104..f60dc01c97e8f 100644 --- a/dts/arm/st/wl/stm32wl.dtsi +++ b/dts/arm/st/wl/stm32wl.dtsi @@ -229,6 +229,7 @@ clocks = <&rcc STM32_CLOCK_BUS_APB2 0x00004000>; resets = <&rctl STM32_RESET(APB2, 14U)>; interrupts = <36 0>; + reinit-power-states = <&stop2>; status = "disabled"; }; @@ -238,6 +239,7 @@ clocks = <&rcc STM32_CLOCK_BUS_APB1 0x00020000>; resets = <&rctl STM32_RESET(APB1L, 17U)>; interrupts = <37 0>; + reinit-power-states = <&stop2>; status = "disabled"; }; @@ -293,6 +295,7 @@ reg = <0x40013000 0x400>; interrupts = <34 5>; clocks = <&rcc STM32_CLOCK_BUS_APB2 0x00001000>; + reinit-power-states = <&stop2>; status = "disabled"; }; @@ -303,6 +306,7 @@ reg = <0x40003800 0x400>; interrupts = <35 5>; clocks = <&rcc STM32_CLOCK_BUS_APB1 0x00004000>; + reinit-power-states = <&stop2>; status = "disabled"; }; @@ -313,6 +317,7 @@ reg = <0x58010000 0x400>; interrupts = <44 5>; clocks = <&rcc STM32_CLOCK_BUS_APB3 0x00000001>; + reinit-power-states = <&stop2>; status = "disabled"; use-subghzspi-nss; diff --git a/dts/bindings/base/pm.yaml b/dts/bindings/base/pm.yaml index 0776170d8f205..9b9129f6ba423 100644 --- a/dts/bindings/base/pm.yaml +++ b/dts/bindings/base/pm.yaml @@ -30,3 +30,12 @@ properties: description: | Automatically configure the device for runtime power management after the init function runs. + + reinit-power-states: + type: phandles + description: | + Specifies special power states where a peripheral requires + reinitialization due to configuration loss. + + While states at or beyond S2RAM are naturally assumed to need + reinitialization, this property highlights the exceptions to that rule. diff --git a/include/zephyr/pm/pm.h b/include/zephyr/pm/pm.h index 03062eefd12ed..b3c7e25a67a9b 100644 --- a/include/zephyr/pm/pm.h +++ b/include/zephyr/pm/pm.h @@ -53,18 +53,87 @@ extern "C" { */ struct pm_notifier { sys_snode_t _node; + + /** + * Bit field indicating whether the callback should trigger on power state entry, exit, or + * both. + * + * @note See PM_STATE_ENTRY and PM_STATE_EXIT + */ + uint8_t direction; + /** - * Application defined function for doing any target specific operations - * for power state entry. + * Application or driver defined function for doing any target specific operations during + * power state transition. + * + * @param direction Bit field indicator specifying whether the callback was triggered during + * state entry or exit + * @param ctx Optional callback context, e.g. driver instance, application data, etc. */ - void (*state_entry)(enum pm_state state); + void (*callback)(uint8_t direction, void *ctx); + /** - * Application defined function for doing any target specific operations - * for power state exit. + * Optional callback context */ - void (*state_exit)(enum pm_state state); + const void *ctx; }; +/** @cond INTERNAL_HIDDEN */ + +/** + * @brief Helper macro for building a pm_notifiers list name. + * + * @param node_id zephyr,power-state node identifer + */ +#define Z_PM_NOTIFIERS_DT_NAME(node_id) UTIL_CAT(pm_notifiers_, DT_NODE_CHILD_IDX(node_id)) + +/** + * @brief Generate a pm_notifiers list for the passed power-states child node + * identifier. + * + * @param node_id zephyr,power-state node identifier + */ +#define Z_PM_NOTIFIERS_CREATE_DT_STATES(node_id) \ + static sys_slist_t Z_PM_NOTIFIERS_DT_NAME(node_id) = \ + SYS_SLIST_STATIC_INIT(&Z_PM_NOTIFIERS_DT_NAME(node_id)) + +/** + * @brief Provides a pm_notifiers list pointer for a power-states child node + * identifier. + * + * @param node_id zephyr,power-state node identifier + */ +#define Z_PM_NOTIFIERS_DT_STATES(node_id) &Z_PM_NOTIFIERS_DT_NAME(node_id) + +/** @endcond */ + +#ifdef CONFIG_PM +/** + * @brief Generic helper for defining a pm_notifier struct. + * + * @param id Unique alphanumeric identifier, specific to usage context + * (avoid using only numbers here to prevent collisions) + * @param state_direction Transition direction: state entry, exit, or both + * @param callback_fn callback function + */ +#define PM_NOTIFIER_DEFINE(id, state_direction, callback_fn, context) \ + static struct pm_notifier UTIL_CAT(pm_notifier_, id) = { \ + .direction = state_direction, \ + .callback = callback_fn, \ + .ctx = context, \ + } + +/** + * @brief Retrieve the name of a pm_notifier struct instance. + * + * @param id Unique alphanumeric identifier, specific to usage context + */ +#define PM_NOTIFIER(id) UTIL_CAT(pm_notifier_, id) +#else +#define PM_NOTIFIER_DEFINE(id, state_direction, callback_fn, context) +#define PM_NOTIFIER(id) NULL +#endif + #if defined(CONFIG_PM) || defined(__DOXYGEN__) /** * @brief Force usage of given power state. @@ -87,8 +156,13 @@ bool pm_state_force(uint8_t cpu, const struct pm_state_info *info); * list. * * @param notifier pm_notifier object to be registered. + * @param state PM state for which the notifier should be registered + * @param substate_id PM substate for which the notifier should be registered. + * + * @return 0 if the notifier was successfully registered, a negative value + * otherwise. */ -void pm_notifier_register(struct pm_notifier *notifier); +int pm_notifier_register(struct pm_notifier *notifier, enum pm_state state, uint8_t substate_id); /** * @brief Unregister a power management notifier @@ -97,11 +171,13 @@ void pm_notifier_register(struct pm_notifier *notifier); * list. After that this object callbacks will not be called. * * @param notifier pm_notifier object to be unregistered. + * @param state PM state for which the notifier should be registered + * @param substate_id PM substate for which the notifier should be registered. * * @return 0 if the notifier was successfully removed, a negative value * otherwise. */ -int pm_notifier_unregister(struct pm_notifier *notifier); +int pm_notifier_unregister(struct pm_notifier *notifier, enum pm_state state, uint8_t substate_id); /** * @brief Gets the next power state that will be used. @@ -155,14 +231,22 @@ void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id); #else /* CONFIG_PM */ -static inline void pm_notifier_register(struct pm_notifier *notifier) +static inline int pm_notifier_register(struct pm_notifier *notifier, + enum pm_state state, uint8_t substate_id) { ARG_UNUSED(notifier); + ARG_UNUSED(state); + ARG_UNUSED(substate_id); + + return -ENOSYS; } -static inline int pm_notifier_unregister(struct pm_notifier *notifier) +static inline int pm_notifier_unregister(struct pm_notifier *notifier, + enum pm_state state, uint8_t substate_id) { ARG_UNUSED(notifier); + ARG_UNUSED(state); + ARG_UNUSED(substate_id); return -ENOSYS; } diff --git a/include/zephyr/pm/state.h b/include/zephyr/pm/state.h index 1d21ffd997a91..a6d0bf12cc407 100644 --- a/include/zephyr/pm/state.h +++ b/include/zephyr/pm/state.h @@ -156,6 +156,16 @@ struct pm_state_info { uint32_t exit_latency_us; }; +/** + * @brief PM state transition direction: state entry bit field value + */ +#define PM_STATE_ENTRY BIT(0) + +/** + * @brief PM state transition direction: state exit bit field value + */ +#define PM_STATE_EXIT BIT(1) + /** @cond INTERNAL_HIDDEN */ /** @@ -182,6 +192,20 @@ struct pm_state_info { COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i), okay), \ (PM_STATE_INFO_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)),), ()) +/** + * @brief Helper macro to initialize an entry of a struct pm_state_info array + * when using UTIL_LISTIFY in PM_STATE_INFO_LIST_FROM_DT_REINIT. + * + * @note Only enabled states are initialized. + * + * @param i UTIL_LISTIFY entry index. + * @param node_id A node identifier with compatible zephyr,power-state + */ +#define Z_PM_STATE_INFO_FROM_DT_REINIT(i, node_id) \ + COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, reinit_power_states, i), okay), \ + (PM_STATE_INFO_DT_INIT(DT_PHANDLE_BY_IDX(node_id, reinit_power_states, i)),), \ + ()) + /** * @brief Helper macro to initialize an entry of a struct pm_state array when * using UTIL_LISTIFY in PM_STATE_LIST_FROM_DT_CPU. @@ -195,6 +219,18 @@ struct pm_state_info { COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i), okay), \ (PM_STATE_DT_INIT(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, i)),), ()) +/** + * @brief Call a function for an enabled power-state phandle list item + * + * @param node_id zephyr,power-state node identifier + * @param prop Property holding power-state phandle(s) + * @param idx Index within the phandle list + * @param fn Function taking a power-state node_id + */ +#define Z_PM_STATE_DT_PHANDLE_GEN(node_id, prop, idx, fn) \ + COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, idx), okay), \ + (fn(DT_PHANDLE_BY_IDX(node_id, cpu_power_states, idx))), ()) + /** @endcond */ /** @@ -281,6 +317,60 @@ struct pm_state_info { Z_PM_STATE_INFO_FROM_DT_CPU, (), node_id) \ } +/** + * @brief Initialize an array of struct pm_state_info with information from all + * the states present and enabled in the given device node identifier. + * + * Example devicetree fragment: + * + * @code{.dts} + soc { + * ... + * cpu0: cpu@0 { + * device_type = "cpu"; + * ... + * cpu-power-states = <&state0 &state1>; + * }; + + * i2c1: i2c@50000000 { + * ... + * reinit-power-states = <&state1>; + * }; + * + * power-states { + * state0: state0 { + * compatible = "zephyr,power-state"; + * power-state-name = "suspend-to-idle"; + * min-residency-us = <10000>; + * exit-latency-us = <100>; + * }; + * + * state1: state1 { + * compatible = "zephyr,power-state"; + * power-state-name = "suspend-to-ram"; + * min-residency-us = <50000>; + * exit-latency-us = <500>; + * }; + * }; + * }; + + * @endcode + * + * Example usage: + * + * @code{.c} + * const struct pm_state_info states[] = + * PM_STATE_INFO_LIST_FROM_DT_REINIT(DT_NODELABEL(i2c1)); + * @endcode + * + * @param node_id A device node identifier. + */ +#define PM_STATE_INFO_LIST_FROM_DT_REINIT(node_id) \ + { \ + LISTIFY(DT_PROP_LEN_OR(node_id, reinit_power_states, 0), \ + Z_PM_STATE_INFO_FROM_DT_REINIT, (), node_id) \ + } + /** * @brief Initialize an array of struct pm_state with information from all the * states present and enabled in the given CPU node identifier. @@ -339,6 +429,27 @@ struct pm_state_info { */ uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states); +/** + * Retrieve the index of a CPU's DT cpu-power-states array element. + * + * @param cpu CPU index. + * @param state pm_state state. + * @param substate_id pm_state substate ID. + * + * @return Index of the PM state, or -1. + */ +uint8_t pm_state_cpu_get_index(uint8_t cpu, enum pm_state state, uint8_t substate_id); + +/** + * Retrieve the index of a DT power-states array element. + * + * @param state pm_state state. + * @param substate_id pm_state substate ID. + * + * @return Index of the PM state, or -1. + */ +uint8_t pm_state_get_index(enum pm_state state, uint8_t substate_id); + /** * @} */ diff --git a/samples/boards/mec15xxevb_assy6853/power_management/src/power_mgmt.c b/samples/boards/mec15xxevb_assy6853/power_management/src/power_mgmt.c index df898394b7f3a..886c913dbedd5 100644 --- a/samples/boards/mec15xxevb_assy6853/power_management/src/power_mgmt.c +++ b/samples/boards/mec15xxevb_assy6853/power_management/src/power_mgmt.c @@ -90,44 +90,43 @@ static void pm_latency_check(void) } /* Hooks to count entry/exit */ -static void notify_pm_state_entry(enum pm_state state) +static void notify_pm_state(uint8_t direction, void *ctx) { - if (!checks_enabled) { - return; - } + ARG_UNUSED(ctx); - switch (state) { - case PM_STATE_SUSPEND_TO_IDLE: - GPIO_CTRL_REGS->CTRL_0012 = 0x240ul; - pm_counters[0].entry_cnt++; - break; - case PM_STATE_SUSPEND_TO_RAM: - GPIO_CTRL_REGS->CTRL_0013 = 0x240ul; - pm_counters[1].entry_cnt++; - pm_latency_check(); - break; - default: - break; - } -} + const struct pm_state_info *pm_state = pm_state_next_get(0u); -static void notify_pm_state_exit(enum pm_state state) -{ if (!checks_enabled) { return; } - switch (state) { - case PM_STATE_SUSPEND_TO_IDLE: - GPIO_CTRL_REGS->CTRL_0012 = 0x10240ul; - pm_counters[0].exit_cnt++; - break; - case PM_STATE_SUSPEND_TO_RAM: - GPIO_CTRL_REGS->CTRL_0013 = 0x10240ul; - pm_counters[1].exit_cnt++; - break; - default: - break; + if (direction & PM_STATE_ENTRY) { + pm_counters[pm_state->state].entry_cnt++; + + switch (pm_state->state) { + case PM_STATE_SUSPEND_TO_IDLE: + GPIO_CTRL_REGS->CTRL_0012 = 0x240ul; + break; + case PM_STATE_SUSPEND_TO_RAM: + GPIO_CTRL_REGS->CTRL_0013 = 0x240ul; + pm_latency_check(); + break; + default: + break; + } + } else { + pm_counters[pm_state->state].exit_cnt++; + + switch (pm_state->state) { + case PM_STATE_SUSPEND_TO_IDLE: + GPIO_CTRL_REGS->CTRL_0012 = 0x10240ul; + break; + case PM_STATE_SUSPEND_TO_RAM: + GPIO_CTRL_REGS->CTRL_0013 = 0x10240ul; + break; + default: + break; + } } } @@ -256,21 +255,24 @@ static void resume_all_tasks(void) k_thread_resume(&thread_b_id); } -static struct pm_notifier notifier = { - .state_entry = notify_pm_state_entry, - .state_exit = notify_pm_state_exit, -}; +PM_NOTIFIER_DEFINE(test_light_sleep, (PM_STATE_ENTRY|PM_STATE_EXIT), notify_pm_state, NULL); +PM_NOTIFIER_DEFINE(test_deep_sleep, (PM_STATE_ENTRY|PM_STATE_EXIT), notify_pm_state, NULL); int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles) { uint8_t iterations = cycles; + const struct pm_state_info *light_sleep = &residency_info[0]; + const struct pm_state_info *deep_sleep = &residency_info[residency_info_len - 1]; /* Ensure we can enter deep sleep when stopping threads * No UART output should occur when threads are suspended * Test to verify Zephyr RTOS issue #20033 * https://github.com/zephyrproject-rtos/zephyr/issues/20033 */ - pm_notifier_register(¬ifier); + pm_notifier_register(&PM_NOTIFIER(test_light_sleep), light_sleep->state, + light_sleep->substate_id); + pm_notifier_register(&PM_NOTIFIER(test_deep_sleep), deep_sleep->state, + deep_sleep->substate_id); create_tasks(); LOG_WRN("PM multi-thread test started for cycles: %d, logging: %d", @@ -283,7 +285,7 @@ int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles) LOG_INF("Suspend..."); suspend_all_tasks(); LOG_INF("About to enter light sleep"); - k_msleep((residency_info[0].min_residency_us / 1000U) + + k_msleep((light_sleep->min_residency_us / 1000U) + LT_EXTRA_SLP_TIME); k_busy_wait(100); @@ -304,7 +306,7 @@ int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles) /* GPIO toggle to measure latency for deep sleep */ pm_trigger_marker(); k_msleep( - (residency_info[residency_info_len - 1].min_residency_us / + (deep_sleep->min_residency_us / 1000U) + DP_EXTRA_SLP_TIME); k_busy_wait(100); @@ -324,7 +326,10 @@ int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles) LOG_INF("PM multi-thread completed"); pm_check_counters(cycles); pm_reset_counters(); - pm_notifier_unregister(¬ifier); + pm_notifier_unregister(&PM_NOTIFIER(test_light_sleep), light_sleep->state, + light_sleep->substate_id); + pm_notifier_unregister(&PM_NOTIFIER(test_deep_sleep), deep_sleep->state, + deep_sleep->substate_id); return 0; } @@ -332,17 +337,22 @@ int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles) int test_pwr_mgmt_singlethread(bool use_logging, uint8_t cycles) { uint8_t iterations = cycles; + const struct pm_state_info *light_sleep = &residency_info[0]; + const struct pm_state_info *deep_sleep = &residency_info[residency_info_len - 1]; LOG_WRN("PM single-thread test started for cycles: %d, logging: %d", cycles, use_logging); - pm_notifier_register(¬ifier); + pm_notifier_register(&PM_NOTIFIER(test_light_sleep), light_sleep->state, + light_sleep->substate_id); + pm_notifier_register(&PM_NOTIFIER(test_deep_sleep), deep_sleep->state, + deep_sleep->substate_id); checks_enabled = true; while (iterations-- > 0) { /* Trigger Light Sleep 1 state. 48MHz PLL stays on */ LOG_INF("About to enter light sleep"); - k_msleep((residency_info[0].min_residency_us / 1000U) + + k_msleep((light_sleep->min_residency_us / 1000U) + LT_EXTRA_SLP_TIME); k_busy_wait(100); @@ -358,7 +368,7 @@ int test_pwr_mgmt_singlethread(bool use_logging, uint8_t cycles) /* GPIO toggle to measure latency */ pm_trigger_marker(); k_msleep( - (residency_info[residency_info_len - 1].min_residency_us / + (deep_sleep->min_residency_us / 1000U) + DP_EXTRA_SLP_TIME); k_busy_wait(100); @@ -374,7 +384,10 @@ int test_pwr_mgmt_singlethread(bool use_logging, uint8_t cycles) LOG_INF("PM single-thread completed"); pm_check_counters(cycles); pm_reset_counters(); - pm_notifier_unregister(¬ifier); + pm_notifier_unregister(&PM_NOTIFIER(test_light_sleep), light_sleep->state, + light_sleep->substate_id); + pm_notifier_unregister(&PM_NOTIFIER(test_deep_sleep), deep_sleep->state, + deep_sleep->substate_id); return 0; } diff --git a/subsys/pm/pm.c b/subsys/pm/pm.c index ae6e8505d891b..031276136a054 100644 --- a/subsys/pm/pm.c +++ b/subsys/pm/pm.c @@ -27,7 +27,27 @@ LOG_MODULE_REGISTER(pm, CONFIG_PM_LOG_LEVEL); (COND_CODE_1(CONFIG_SMP, (arch_curr_cpu()->id), (_current_cpu->id))) static ATOMIC_DEFINE(z_post_ops_required, CONFIG_MP_MAX_NUM_CPUS); -static sys_slist_t pm_notifiers = SYS_SLIST_STATIC_INIT(&pm_notifiers); + +/* Active state notifiers list */ +static sys_slist_t pm_notifiers_active = SYS_SLIST_STATIC_INIT(&pm_notifiers_active); + +#if CONFIG_MP_NUM_CPUS > 1 && DT_NODE_EXISTS(DT_PATH(cpus, power_states)) +/* Generate a notifier list for each DT power-state */ +DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus, power_states), Z_PM_NOTIFIERS_CREATE_DT_STATES, (;)); + +static sys_slist_t *pm_notifiers_array[] = { + DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus, power_states), Z_PM_NOTIFIERS_DT_STATES, (,)) +}; +#elif DT_NODE_HAS_PROP(DT_PATH(cpus, cpu_0), cpu_power_states) +/* Generate a notifier list for each DT cpu-power-state */ +DT_FOREACH_PROP_ELEM_SEP_VARGS(DT_PATH(cpus, cpu_0), cpu_power_states, Z_PM_STATE_DT_PHANDLE_GEN, + (;), Z_PM_NOTIFIERS_CREATE_DT_STATES); + +static sys_slist_t *pm_notifiers_array[] = { + DT_FOREACH_PROP_ELEM_SEP_VARGS(DT_PATH(cpus, cpu_0), cpu_power_states, + Z_PM_STATE_DT_PHANDLE_GEN, (,), Z_PM_NOTIFIERS_DT_STATES) +}; +#endif /* * Properly initialize cpu power states. Do not make assumptions that @@ -111,22 +131,28 @@ static void pm_resume_devices(void) * Function called to notify when the system is entering / exiting a * power state */ -static inline void pm_state_notify(bool entering_state) +static void pm_state_notify(uint8_t direction) { struct pm_notifier *notifier; + static sys_slist_t *notifiers; k_spinlock_key_t pm_notifier_key; - void (*callback)(enum pm_state state); - pm_notifier_key = k_spin_lock(&pm_notifier_lock); - SYS_SLIST_FOR_EACH_CONTAINER(&pm_notifiers, notifier, _node) { - if (entering_state) { - callback = notifier->state_entry; - } else { - callback = notifier->state_exit; + if (z_cpus_pm_state[CURRENT_CPU].state == PM_STATE_ACTIVE) { + notifiers = &pm_notifiers_active; + } else { + uint8_t state_idx = pm_state_get_index(z_cpus_pm_state[CURRENT_CPU].state, + z_cpus_pm_state[CURRENT_CPU].substate_id); + if (state_idx < 0) { + LOG_ERR("Sub/state passed didn't match any enabled system states"); + return; } + notifiers = pm_notifiers_array[state_idx]; + } - if (callback) { - callback(z_cpus_pm_state[_current_cpu->id].state); + pm_notifier_key = k_spin_lock(&pm_notifier_lock); + SYS_SLIST_FOR_EACH_CONTAINER(notifiers, notifier, _node) { + if ((direction & notifier->direction) && notifier->callback) { + notifier->callback(direction, (void *)notifier->ctx); } } k_spin_unlock(&pm_notifier_lock, pm_notifier_key); @@ -149,10 +175,13 @@ void pm_system_resume(void) * and it may schedule another thread. */ if (atomic_test_and_clear_bit(z_post_ops_required, id)) { + /* Exiting low-power state */ + pm_state_notify(PM_STATE_EXIT); pm_state_exit_post_ops(z_cpus_pm_state[id].state, z_cpus_pm_state[id].substate_id); - pm_state_notify(false); z_cpus_pm_state[id] = (struct pm_state_info){PM_STATE_ACTIVE, 0, 0}; + /* Entering active state */ + pm_state_notify(PM_STATE_ENTRY); } } @@ -177,6 +206,9 @@ bool pm_system_suspend(int32_t ticks) SYS_PORT_TRACING_FUNC_ENTER(pm, system_suspend, ticks); + /* Exiting active state */ + pm_state_notify(PM_STATE_EXIT); + key = k_spin_lock(&pm_forced_state_lock); if (z_cpus_pm_forced_state[id].state != PM_STATE_ACTIVE) { z_cpus_pm_state[id] = z_cpus_pm_forced_state[id]; @@ -234,8 +266,8 @@ bool pm_system_suspend(int32_t ticks) */ k_sched_lock(); pm_stats_start(); - /* Enter power state */ - pm_state_notify(true); + /* Entering low-power state */ + pm_state_notify(PM_STATE_ENTRY); atomic_set_bit(z_post_ops_required, id); pm_state_set(z_cpus_pm_state[id].state, z_cpus_pm_state[id].substate_id); pm_stats_stop(); @@ -255,21 +287,50 @@ bool pm_system_suspend(int32_t ticks) return true; } -void pm_notifier_register(struct pm_notifier *notifier) +int pm_notifier_register(struct pm_notifier *notifier, enum pm_state state, uint8_t substate_id) { - k_spinlock_key_t pm_notifier_key = k_spin_lock(&pm_notifier_lock); + static sys_slist_t *notifiers; + k_spinlock_key_t pm_notifier_key; - sys_slist_append(&pm_notifiers, ¬ifier->_node); + if (state == PM_STATE_ACTIVE) { + notifiers = &pm_notifiers_active; + } else { + uint8_t state_idx = pm_state_get_index(state, substate_id); + + if (state_idx < 0) { + LOG_ERR("Sub/state passed didn't match any enabled system states"); + return -EINVAL; + } + notifiers = pm_notifiers_array[state_idx]; + } + + pm_notifier_key = k_spin_lock(&pm_notifier_lock); + sys_slist_append(notifiers, ¬ifier->_node); k_spin_unlock(&pm_notifier_lock, pm_notifier_key); + + return 0; } -int pm_notifier_unregister(struct pm_notifier *notifier) +int pm_notifier_unregister(struct pm_notifier *notifier, enum pm_state state, uint8_t substate_id) { int ret = -EINVAL; + static sys_slist_t *notifiers; k_spinlock_key_t pm_notifier_key; + if (state == PM_STATE_ACTIVE) { + notifiers = &pm_notifiers_active; + } else { + uint8_t state_idx = pm_state_get_index(state, substate_id); + + if (state_idx < 0) { + LOG_ERR("Sub/state passed didn't match any enabled system states"); + return -EINVAL; + } + notifiers = pm_notifiers_array[state_idx]; + } + pm_notifier_key = k_spin_lock(&pm_notifier_lock); - if (sys_slist_find_and_remove(&pm_notifiers, &(notifier->_node))) { + if (sys_slist_find_and_remove(notifiers, &(notifier->_node))) { ret = 0; } k_spin_unlock(&pm_notifier_lock, pm_notifier_key); diff --git a/subsys/pm/state.c b/subsys/pm/state.c index 297d28a37680b..41c2b18dc0991 100644 --- a/subsys/pm/state.c +++ b/subsys/pm/state.c @@ -40,12 +40,20 @@ BUILD_ASSERT(DT_NODE_EXISTS(DT_PATH(cpus)), DT_FOREACH_CHILD(DT_PATH(cpus), CHECK_POWER_STATES_CONSISTENCY) #define DEFINE_CPU_STATES(n) \ - static const struct pm_state_info pmstates_##n[] \ + static const struct pm_state_info pmstates_cpu##n[] \ = PM_STATE_INFO_LIST_FROM_DT_CPU(n); -#define CPU_STATE_REF(n) pmstates_##n +#define CPU_STATE_REF(n) pmstates_cpu##n DT_FOREACH_CHILD(DT_PATH(cpus), DEFINE_CPU_STATES); +#if CONFIG_MP_NUM_CPUS > 1 && DT_NODE_EXISTS(DT_PATH(cpus, power_states)) +/** General power-states list on SMP platforms, as individual per-CPU states may vary */ +static const struct pm_state_info pmstates_all[] = { + DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus, power_states), + PM_STATE_INFO_DT_INIT, (,)), +}; +#endif + /** CPU power states information for each CPU */ static const struct pm_state_info *cpus_states[] = { DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), CPU_STATE_REF, (,)) @@ -66,3 +74,34 @@ uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states) return states_per_cpu[cpu]; } + +inline uint8_t pm_state_cpu_get_index(uint8_t cpu, enum pm_state state, uint8_t substate_id) +{ + for (uint8_t i = 0; i < states_per_cpu[cpu]; i++) { + if (cpus_states[cpu][i].state == state && + cpus_states[cpu][i].substate_id == substate_id) { + return i; + } + } + + return -1; +} + +#if CONFIG_MP_NUM_CPUS > 1 +inline uint8_t pm_state_get_index(enum pm_state state, uint8_t substate_id) +{ + for (uint8_t i = 0; i < ARRAY_SIZE(pmstates_all); i++) { + if (pmstates_all[i].state == state && + pmstates_all[i].substate_id == substate_id) { + return i; + } + } + + return -1; +} +#else +inline uint8_t pm_state_get_index(enum pm_state state, uint8_t substate_id) +{ + return pm_state_cpu_get_index(0U, state, substate_id); +} +#endif diff --git a/tests/subsys/pm/device_wakeup_api/boards/native_posix.overlay b/tests/subsys/pm/device_wakeup_api/boards/native_posix.overlay index 97fa7c3c71aa0..ad6508d69eb57 100644 --- a/tests/subsys/pm/device_wakeup_api/boards/native_posix.overlay +++ b/tests/subsys/pm/device_wakeup_api/boards/native_posix.overlay @@ -9,3 +9,18 @@ gpio-controller; wakeup-source; }; + +/ { + cpus { + power-states { + s2ram: s2ram { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-ram"; + }; + }; + }; +}; + +&cpu0 { + cpu-power-states = <&s2ram>; +}; diff --git a/tests/subsys/pm/power_mgmt/boards/native_posix.overlay b/tests/subsys/pm/power_mgmt/boards/native_posix.overlay index 8cd648a06e3d4..32192868ef6cb 100644 --- a/tests/subsys/pm/power_mgmt/boards/native_posix.overlay +++ b/tests/subsys/pm/power_mgmt/boards/native_posix.overlay @@ -5,6 +5,15 @@ */ / { + cpus { + power-states { + suspend: suspend { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-idle"; + }; + }; + }; + device_a: device_a { compatible = "test-device-pm"; }; @@ -21,3 +30,7 @@ compatible = "test-device-pm"; }; }; + +&cpu0 { + cpu-power-states = <&suspend>; +}; diff --git a/tests/subsys/pm/power_mgmt/src/main.c b/tests/subsys/pm/power_mgmt/src/main.c index 7931386c4ec26..fec1830a8e927 100644 --- a/tests/subsys/pm/power_mgmt/src/main.c +++ b/tests/subsys/pm/power_mgmt/src/main.c @@ -180,8 +180,7 @@ void pm_state_set(enum pm_state state, uint8_t substate_id) return; } - - /* at this point, notify_pm_state_entry() implemented in + /* at this point, notify_pm_state() implemented in * this file has been called and set_pm should have been set */ zassert_true(set_pm == true, @@ -245,46 +244,42 @@ const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks) } /* implement in application, called by idle thread */ -static void notify_pm_state_entry(enum pm_state state) +static void notify_pm_state(uint8_t direction, void *ctx) { enum pm_device_state device_power_state; - - /* enter suspend */ - zassert_true(notify_app_entry == true, - "Notification to enter suspend was not sent to the App"); - zassert_true(z_is_idle_thread_object(_current)); - zassert_equal(state, PM_STATE_SUSPEND_TO_IDLE); - - pm_device_state_get(device_dummy, &device_power_state); - if (testing_device_runtime) { - /* If device runtime is enable, the device should still be - * active - */ - zassert_true(device_power_state == PM_DEVICE_STATE_ACTIVE); + const struct pm_state_info *pm_state = pm_state_next_get(0u); + + if (direction & PM_STATE_ENTRY) { + /* enter suspend */ + zassert_true(notify_app_entry == true, + "Notification to enter suspend was not sent to the App"); + zassert_true(z_is_idle_thread_object(_current)); + zassert_equal(pm_state->state, PM_STATE_SUSPEND_TO_IDLE); + + pm_device_state_get(device_dummy, &device_power_state); + if (testing_device_runtime) { + /* If device runtime is enable, the device should still be + * active + */ + zassert_true(device_power_state == PM_DEVICE_STATE_ACTIVE); + } else { + /* at this point, devices should not be active */ + zassert_false(device_power_state == PM_DEVICE_STATE_ACTIVE); + } + set_pm = true; + notify_app_exit = true; } else { - /* at this point, devices should not be active */ - zassert_false(device_power_state == PM_DEVICE_STATE_ACTIVE); + /* leave suspend */ + zassert_true(notify_app_exit == true, + "Notification to leave suspend was not sent to the App"); + zassert_true(z_is_idle_thread_object(_current)); + zassert_equal(pm_state->state, PM_STATE_SUSPEND_TO_IDLE); + + /* at this point, devices are active again*/ + pm_device_state_get(device_dummy, &device_power_state); + zassert_equal(device_power_state, PM_DEVICE_STATE_ACTIVE); + leave_idle = true; } - set_pm = true; - notify_app_exit = true; -} - -/* implement in application, called by idle thread */ -static void notify_pm_state_exit(enum pm_state state) -{ - enum pm_device_state device_power_state; - - /* leave suspend */ - zassert_true(notify_app_exit == true, - "Notification to leave suspend was not sent to the App"); - zassert_true(z_is_idle_thread_object(_current)); - zassert_equal(state, PM_STATE_SUSPEND_TO_IDLE); - - /* at this point, devices are active again*/ - pm_device_state_get(device_dummy, &device_power_state); - zassert_equal(device_power_state, PM_DEVICE_STATE_ACTIVE); - leave_idle = true; - } /* @@ -309,10 +304,7 @@ ZTEST(power_management_1cpu, test_power_idle) zassert_true(idle_entered, "Never entered idle thread"); } -static struct pm_notifier notifier = { - .state_entry = notify_pm_state_entry, - .state_exit = notify_pm_state_exit, -}; +PM_NOTIFIER_DEFINE(test, (PM_STATE_ENTRY|PM_STATE_EXIT), notify_pm_state, NULL); /* * @brief test power state transition @@ -321,9 +313,9 @@ static struct pm_notifier notifier = { * - The system support control of power state ordering between * subsystems and devices * - The application can control system power state transitions in idle thread - * through pm_notify_pm_state_entry and pm_notify_pm_state_exit + * through pm_notify_pm_state * - * @see pm_notify_pm_state_entry(), pm_notify_pm_state_exit() + * @see pm_notify_pm_state() * * @ingroup power_tests */ @@ -331,7 +323,7 @@ ZTEST(power_management_1cpu, test_power_state_trans) { int ret; - pm_notifier_register(¬ifier); + pm_notifier_register(&PM_NOTIFIER(test), PM_STATE_SUSPEND_TO_IDLE, 0); enter_low_power = true; ret = pm_device_runtime_disable(device_dummy); @@ -344,7 +336,7 @@ ZTEST(power_management_1cpu, test_power_state_trans) ret = pm_device_runtime_enable(device_dummy); zassert_true(ret == 0, "Failed to enable device runtime PM"); - pm_notifier_unregister(¬ifier); + pm_notifier_unregister(&PM_NOTIFIER(test), PM_STATE_SUSPEND_TO_IDLE, 0); } /* @@ -366,7 +358,7 @@ ZTEST(power_management_1cpu, test_power_state_notification) int ret; enum pm_device_state device_power_state; - pm_notifier_register(¬ifier); + pm_notifier_register(&PM_NOTIFIER(test), PM_STATE_SUSPEND_TO_IDLE, 0); enter_low_power = true; ret = api->open(device_dummy); @@ -385,7 +377,7 @@ ZTEST(power_management_1cpu, test_power_state_notification) api->close(device_dummy); pm_device_state_get(device_dummy, &device_power_state); zassert_equal(device_power_state, PM_DEVICE_STATE_SUSPENDED); - pm_notifier_unregister(¬ifier); + pm_notifier_unregister(&PM_NOTIFIER(test), PM_STATE_SUSPEND_TO_IDLE, 0); testing_device_runtime = false; } @@ -446,7 +438,7 @@ ZTEST(power_management_1cpu, test_device_state_lock) void power_management_1cpu_teardown(void *data) { - pm_notifier_unregister(¬ifier); + pm_notifier_unregister(&PM_NOTIFIER(test), PM_STATE_SUSPEND_TO_IDLE, 0); } static void *power_management_1cpu_setup(void) diff --git a/tests/subsys/pm/power_mgmt_multicore/boards/qemu_x86_64.overlay b/tests/subsys/pm/power_mgmt_multicore/boards/qemu_x86_64.overlay new file mode 100644 index 0000000000000..82c35253e54a8 --- /dev/null +++ b/tests/subsys/pm/power_mgmt_multicore/boards/qemu_x86_64.overlay @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Kenneth J. Miller + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + cpus { + cpu@0 { + cpu-power-states = <&idle &s2idle &stdby>; + }; + + power-states { + idle: idle { + compatible = "zephyr,power-state"; + power-state-name = "runtime-idle"; + }; + + s2idle: s2idle { + compatible = "zephyr,power-state"; + power-state-name = "suspend-to-idle"; + }; + + stdby: stdby { + compatible = "zephyr,power-state"; + power-state-name = "standby"; + }; + }; + }; +}; diff --git a/tests/subsys/pm/power_mgmt_soc/src/power_mgmt.c b/tests/subsys/pm/power_mgmt_soc/src/power_mgmt.c index 3d7f232fbd51b..d2a6473b1511d 100644 --- a/tests/subsys/pm/power_mgmt_soc/src/power_mgmt.c +++ b/tests/subsys/pm/power_mgmt_soc/src/power_mgmt.c @@ -71,13 +71,21 @@ static void pm_latency_check(void) secs, msecs); } -static void notify_pm_state_entry(enum pm_state state) +static void notify_pm_state(uint8_t direction, void *ctx) { + ARG_UNUSED(ctx); + + const struct pm_state_info *pm_state = pm_state_next_get(0u); + if (!checks_enabled) { return; } - pm_counters[(int)state].entry_cnt++; + if (direction & PM_STATE_ENTRY) { + pm_counters[pm_state->state].entry_cnt++; + } else { + pm_counters[pm_state->state].exit_cnt++; + } if (measure_entry_latency) { pm_latency_check(); @@ -89,19 +97,7 @@ static void notify_pm_state_entry(enum pm_state state) } } -static void notify_pm_state_exit(enum pm_state state) -{ - if (!checks_enabled) { - return; - } - - pm_counters[(int)state].exit_cnt++; -} - -static struct pm_notifier notifier = { - .state_entry = notify_pm_state_entry, - .state_exit = notify_pm_state_exit, -}; +PM_NOTIFIER_DEFINE(test, (PM_STATE_ENTRY|PM_STATE_EXIT), notify_pm_state, NULL); static void pm_check_counters(uint8_t cycles) { @@ -224,8 +220,10 @@ static void resume_all_tasks(void) int test_pwr_mgmt_multithread(uint8_t cycles) { uint8_t iterations = cycles; + const struct pm_state_info *sleep_light = &residency_info[0]; + const struct pm_state_info *sleep_deep = &residency_info[residency_info_len - 1]; - pm_notifier_register(¬ifier); + pm_notifier_register(&PM_NOTIFIER(test), sleep_light->state, sleep_light->substate_id); create_tasks(); LOG_INF("PM multi-thread test started for cycles: %d", cycles); @@ -239,8 +237,7 @@ int test_pwr_mgmt_multithread(uint8_t cycles) LOG_INF("About to enter light sleep"); measure_entry_latency = true; pm_trigger_marker(); - k_usleep(residency_info[0].min_residency_us + - LT_EXTRA_SLP_TIME_US); + k_usleep(sleep_light->min_residency_us + LT_EXTRA_SLP_TIME_US); LOG_INF("Wake from Light Sleep"); pm_exit_marker(); @@ -257,9 +254,7 @@ int test_pwr_mgmt_multithread(uint8_t cycles) measure_entry_latency = true; pm_trigger_marker(); - k_usleep( - residency_info[residency_info_len - 1].min_residency_us + - DP_EXTRA_SLP_TIME_US); + k_usleep(sleep_deep->min_residency_us + DP_EXTRA_SLP_TIME_US); LOG_INF("Wake from Deep Sleep"); pm_exit_marker(); @@ -268,7 +263,7 @@ int test_pwr_mgmt_multithread(uint8_t cycles) } destroy_tasks(); - pm_notifier_unregister(¬ifier); + pm_notifier_unregister(&PM_NOTIFIER(test), sleep_light->state, sleep_light->substate_id); LOG_INF("PM multi-thread completed"); pm_check_counters(cycles); @@ -280,10 +275,12 @@ int test_pwr_mgmt_multithread(uint8_t cycles) int test_pwr_mgmt_singlethread(uint8_t cycles) { uint8_t iterations = cycles; + const struct pm_state_info *sleep_light = &residency_info[0]; + const struct pm_state_info *sleep_deep = &residency_info[residency_info_len - 1]; LOG_INF("PM single-thread test started for cycles: %d", cycles); - pm_notifier_register(¬ifier); + pm_notifier_register(&PM_NOTIFIER(test), sleep_light->state, sleep_light->substate_id); checks_enabled = true; while (iterations-- > 0) { @@ -291,8 +288,7 @@ int test_pwr_mgmt_singlethread(uint8_t cycles) LOG_INF("About to enter light sleep"); measure_entry_latency = true; pm_trigger_marker(); - k_usleep(residency_info[0].min_residency_us + - LT_EXTRA_SLP_TIME_US); + k_usleep(sleep_light->min_residency_us + LT_EXTRA_SLP_TIME_US); LOG_INF("Wake from Light Sleep"); pm_exit_marker(); @@ -303,14 +299,12 @@ int test_pwr_mgmt_singlethread(uint8_t cycles) LOG_INF("About to enter deep Sleep"); measure_entry_latency = true; pm_trigger_marker(); - k_usleep( - residency_info[residency_info_len - 1].min_residency_us + - DP_EXTRA_SLP_TIME_US); + k_usleep(sleep_deep->min_residency_us + DP_EXTRA_SLP_TIME_US); LOG_INF("Wake from Deep Sleep"); pm_exit_marker(); } - pm_notifier_unregister(¬ifier); + pm_notifier_unregister(&PM_NOTIFIER(test), sleep_light->state, sleep_light->substate_id); LOG_INF("PM single-thread completed"); pm_check_counters(cycles); pm_reset_counters();