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:
parent
15d8fd2ad3
commit
1e492e8f91
2 changed files with 134 additions and 2 deletions
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue