soc: nxp_rw6xx: Add support for Power Mode 3

This maps to Zephyr power state Standby. In this power
state the OS Timer cannot be used as a wakeup source as
it will be powered off. Hence the counter is enabled
and RTC is used to keep track of system ticks and wakeup
the system.

Signed-off-by: Mahesh Mahadevan <mahesh.mahadevan@nxp.com>
This commit is contained in:
Mahesh Mahadevan 2024-06-22 23:14:43 +01:00 committed by Benjamin Cabé
commit 1e492e8f91
2 changed files with 134 additions and 2 deletions

View file

@ -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

View file

@ -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 <zephyr/logging/log.h>
#include <zephyr/drivers/timer/system_timer.h>
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)));
}