diff --git a/soc/nxp/rw/Kconfig.defconfig b/soc/nxp/rw/Kconfig.defconfig index 3c4677b784c..f1890af7cf9 100644 --- a/soc/nxp/rw/Kconfig.defconfig +++ b/soc/nxp/rw/Kconfig.defconfig @@ -63,6 +63,33 @@ if PM config SYS_CLOCK_TICKS_PER_SEC default 1000 depends on $(dt_nodelabel_enabled,standby) -endif + +# Enable PM_DEVICE by default if STANDBY mode is enabled +# as we use the TURN_OFF and TURN_ON actions to recover +# from Standby mode (PM Mode 3) +config PM_DEVICE + default y + depends on "$(dt_nodelabel_enabled,standby)" + +# Enable PM_POLICY_DEVICE_CONSTRAINTS by default when doing PM_DEVICE. +# This will allow support of device power states. +config PM_POLICY_DEVICE_CONSTRAINTS + default y if PM_DEVICE + +# Enable the counter if STANDBY mode is enabled +# RTC counter is the wakeup source from STANDBY mode +config COUNTER + default y + depends on "$(dt_nodelabel_enabled,standby)" + +config MCUX_OS_TIMER_PM_POWERED_OFF + default y + +# PM code that runs from the idle loop has a large +# footprint. Hence increase the size when PM is enabled. +config IDLE_STACK_SIZE + default 640 + +endif # PM endif # SOC_SERIES_RW6XX diff --git a/soc/nxp/rw/power.c b/soc/nxp/rw/power.c index f4176b6349c..fdc24019dd7 100644 --- a/soc/nxp/rw/power.c +++ b/soc/nxp/rw/power.c @@ -1,5 +1,5 @@ /* - * Copyright 2023, NXP + * Copyright 2023-2025 NXP * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,8 @@ #include "fsl_power.h" #include +#include + LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); /* Active mode */ @@ -26,6 +28,8 @@ LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); #define NODE_ID DT_INST(0, nxp_pdcfg_power) +extern void clock_init(void); + power_sleep_config_t slp_cfg; #if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(pin0)) || DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(pin1)) @@ -56,6 +60,81 @@ static void pin1_isr(const struct device *dev) } #endif +#ifdef CONFIG_MPU + +#define MPU_NUM_REGIONS 8 + +typedef struct mpu_conf { + uint32_t CTRL; + uint32_t RNR; + uint32_t RBAR[MPU_NUM_REGIONS]; + uint32_t RLAR[MPU_NUM_REGIONS]; + uint32_t RBAR_A1; + uint32_t RLAR_A1; + uint32_t RBAR_A2; + uint32_t RLAR_A2; + uint32_t RBAR_A3; + uint32_t RLAR_A3; + uint32_t MAIR0; + uint32_t MAIR1; +} mpu_conf; +mpu_conf saved_mpu_conf; + +static void save_mpu_state(void) +{ + uint32_t index; + + /* + * MPU is divided in `MPU_NUM_REGIONS` regions. + * Here we save those regions configuration to be restored after PM3 entry + */ + for (index = 0U; index < MPU_NUM_REGIONS; index++) { + MPU->RNR = index; + saved_mpu_conf.RBAR[index] = MPU->RBAR; + saved_mpu_conf.RLAR[index] = MPU->RLAR; + } + + saved_mpu_conf.CTRL = MPU->CTRL; + saved_mpu_conf.RNR = MPU->RNR; + saved_mpu_conf.RBAR_A1 = MPU->RBAR_A1; + saved_mpu_conf.RLAR_A1 = MPU->RLAR_A1; + saved_mpu_conf.RBAR_A2 = MPU->RBAR_A2; + saved_mpu_conf.RLAR_A2 = MPU->RLAR_A2; + saved_mpu_conf.RBAR_A3 = MPU->RBAR_A3; + saved_mpu_conf.RLAR_A3 = MPU->RLAR_A3; + saved_mpu_conf.MAIR0 = MPU->MAIR0; + saved_mpu_conf.MAIR1 = MPU->MAIR1; +} + +static void restore_mpu_state(void) +{ + uint32_t index; + + for (index = 0U; index < MPU_NUM_REGIONS; index++) { + MPU->RNR = index; + MPU->RBAR = saved_mpu_conf.RBAR[index]; + MPU->RLAR = saved_mpu_conf.RLAR[index]; + } + + MPU->RNR = saved_mpu_conf.RNR; + MPU->RBAR_A1 = saved_mpu_conf.RBAR_A1; + MPU->RLAR_A1 = saved_mpu_conf.RLAR_A1; + MPU->RBAR_A2 = saved_mpu_conf.RBAR_A2; + MPU->RLAR_A2 = saved_mpu_conf.RLAR_A2; + MPU->RBAR_A3 = saved_mpu_conf.RBAR_A3; + MPU->RLAR_A3 = saved_mpu_conf.RLAR_A3; + MPU->MAIR0 = saved_mpu_conf.MAIR0; + MPU->MAIR1 = saved_mpu_conf.MAIR1; + + /* + * CTRL register must be restored last in case MPU was enabled, + * because some MPU registers can't be programmed while the MPU is enabled + */ + MPU->CTRL = saved_mpu_conf.CTRL; +} + +#endif /* CONFIG_MPU */ + /* Invoke Low Power/System Off specific Tasks */ __weak void pm_state_set(enum pm_state state, uint8_t substate_id) { @@ -92,6 +171,28 @@ __weak void pm_state_set(enum pm_state state, uint8_t substate_id) break; case PM_STATE_SUSPEND_TO_IDLE: POWER_EnterPowerMode(POWER_MODE2, &slp_cfg); + break; + case PM_STATE_STANDBY: +#ifdef CONFIG_MPU + /* Save MPU state before entering PM3 */ + save_mpu_state(); +#endif /* CONFIG_MPU */ + + POWER_EnableWakeup(DT_IRQN(DT_NODELABEL(rtc))); + if (POWER_EnterPowerMode(POWER_MODE3, &slp_cfg)) { +#ifdef CONFIG_MPU + /* Restore MPU as is lost after PM3 exit*/ + restore_mpu_state(); +#endif /* CONFIG_MPU */ + clock_init(); + + sys_clock_idle_exit(); + } + + /* Clear the RTC wakeup bits */ + POWER_ClearWakeupStatus(DT_IRQN(DT_NODELABEL(rtc))); + POWER_DisableWakeup(DT_IRQN(DT_NODELABEL(rtc))); + break; default: LOG_DBG("Unsupported power state %u", state); @@ -142,4 +243,8 @@ void nxp_rw6xx_power_init(void) IRQ_CONNECT(DT_IRQN(DT_NODELABEL(pin1)), DT_IRQ(DT_NODELABEL(pin1), priority), pin1_isr, NULL, 0); #endif + + /* Clear the RTC wakeup bits */ + POWER_ClearWakeupStatus(DT_IRQN(DT_NODELABEL(rtc))); + POWER_DisableWakeup(DT_IRQN(DT_NODELABEL(rtc))); }