Skip to content

Commit e3a628e

Browse files
committed
soc: mcxw: Add Power Management support
Add PM support for MCXW SoC's Signed-off-by: Mahesh Mahadevan <[email protected]>
1 parent 74629b1 commit e3a628e

File tree

6 files changed

+230
-0
lines changed

6 files changed

+230
-0
lines changed

soc/nxp/mcx/mcxw/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ config SOC_FAMILY_MCXW
1818
select SOC_EARLY_INIT_HOOK
1919
select CLOCK_CONTROL
2020
select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE
21+
select HAS_PM

soc/nxp/mcx/mcxw/mcxw7xx/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ zephyr_sources_ifdef(CONFIG_NXP_NBU
1010
../../../common/nxp_nbu.c
1111
)
1212

13+
zephyr_sources_ifdef(CONFIG_PM
14+
power.c
15+
)
16+
1317
zephyr_include_directories(./)
1418

1519
set(SOC_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld CACHE INTERNAL "")

soc/nxp/mcx/mcxw/mcxw7xx/Kconfig.defconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ config NUM_IRQS
88
default 77 if SOC_MCXW727C
99
default 75
1010

11+
config MCUX_LPTMR_TIMER
12+
default y if PM
13+
14+
DT_LPTMR_PATH := $(dt_nodelabel_path,lptmr0)
1115
config SYS_CLOCK_HW_CYCLES_PER_SEC
1216
default 96000000 if CORTEX_M_SYSTICK
17+
default $(dt_node_int_prop_int,$(DT_LPTMR_PATH),clock-frequency) if MCUX_LPTMR_TIMER
1318

1419
config MCUX_FLASH_K4_API
1520
default y

soc/nxp/mcx/mcxw/mcxw7xx/power.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright 2025 NXP
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include <zephyr/kernel.h>
7+
#include <zephyr/pm/pm.h>
8+
#include "fsl_cmc.h"
9+
#include "fsl_spc.h"
10+
#include "fsl_vbat.h"
11+
12+
#include <zephyr/logging/log.h>
13+
14+
LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
15+
16+
/*
17+
* 1. Set power mode protection
18+
* 2. Disable low power mode debug
19+
* 3. Enable Flash Doze mode.
20+
*/
21+
static void set_cmc_configuration(void)
22+
{
23+
CMC_SetPowerModeProtection(CMC0, kCMC_AllowAllLowPowerModes);
24+
CMC_LockPowerModeProtectionSetting(CMC0);
25+
#ifdef CONFIG_DEBUG
26+
CMC_EnableDebugOperation(CMC0, true);
27+
#else
28+
CMC_EnableDebugOperation(CMC0, false);
29+
#endif
30+
CMC_ConfigFlashMode(CMC0, false, false, false);
31+
}
32+
33+
/*
34+
* Disable Backup SRAM regulator, FRO16K and Bandgap which
35+
* locates in VBAT power domain for most of power modes.
36+
*
37+
*/
38+
static void deinit_vbat(void)
39+
{
40+
VBAT_EnableBackupSRAMRegulator(VBAT0, false);
41+
VBAT_EnableFRO16k(VBAT0, false);
42+
while (VBAT_CheckFRO16kEnabled(VBAT0)) {
43+
};
44+
VBAT_EnableBandgap(VBAT0, false);
45+
while (VBAT_CheckBandgapEnabled(VBAT0)) {
46+
};
47+
}
48+
49+
/* Invoke Low Power/System Off specific Tasks */
50+
__weak void pm_state_set(enum pm_state state, uint8_t substate_id)
51+
{
52+
/* Set PRIMASK */
53+
__disable_irq();
54+
/* Set BASEPRI to 0 */
55+
irq_unlock(0);
56+
57+
if (state == PM_STATE_RUNTIME_IDLE) {
58+
k_cpu_idle();
59+
return;
60+
}
61+
62+
set_cmc_configuration();
63+
deinit_vbat();
64+
65+
switch (state) {
66+
case PM_STATE_SUSPEND_TO_IDLE:
67+
cmc_power_domain_config_t config;
68+
69+
if (substate_id == 0) {
70+
/* Set NBU into Sleep Mode */
71+
RFMC->RF2P4GHZ_CTRL = (RFMC->RF2P4GHZ_CTRL &
72+
(~RFMC_RF2P4GHZ_CTRL_LP_MODE_MASK)) |
73+
RFMC_RF2P4GHZ_CTRL_LP_MODE(0x1);
74+
RFMC->RF2P4GHZ_CTRL |= RFMC_RF2P4GHZ_CTRL_LP_ENTER_MASK;
75+
76+
/* Set MAIN_CORE and MAIN_WAKE power domain into sleep mode. */
77+
config.clock_mode = kCMC_GateAllSystemClocksEnterLowPowerMode;
78+
config.main_domain = kCMC_SleepMode;
79+
config.wake_domain = kCMC_SleepMode;
80+
CMC_EnterLowPowerMode(CMC0, &config);
81+
} else if (substate_id == 1) {
82+
} else {
83+
/* Nothing to do */
84+
}
85+
break;
86+
case PM_STATE_STANDBY:
87+
/* Enable CORE VDD Voltage scaling. */
88+
SPC_EnableLowPowerModeCoreVDDInternalVoltageScaling(SPC0, true);
89+
90+
/* Set NBU into Deep Sleep Mode */
91+
RFMC->RF2P4GHZ_CTRL = (RFMC->RF2P4GHZ_CTRL & (~RFMC_RF2P4GHZ_CTRL_LP_MODE_MASK)) |
92+
RFMC_RF2P4GHZ_CTRL_LP_MODE(0x3);
93+
RFMC->RF2P4GHZ_CTRL |= RFMC_RF2P4GHZ_CTRL_LP_ENTER_MASK;
94+
95+
/* Set MAIN_CORE and MAIN_WAKE power domain into Deep Sleep Mode. */
96+
config.clock_mode = kCMC_GateAllSystemClocksEnterLowPowerMode;
97+
config.main_domain = kCMC_DeepSleepMode;
98+
config.wake_domain = kCMC_DeepSleepMode;
99+
100+
CMC_EnterLowPowerMode(CMC0, &config);
101+
102+
break;
103+
default:
104+
LOG_DBG("Unsupported power state %u", state);
105+
break;
106+
}
107+
}
108+
109+
/* Handle SOC specific activity after Low Power Mode Exit */
110+
__weak void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
111+
{
112+
ARG_UNUSED(state);
113+
ARG_UNUSED(substate_id);
114+
115+
/* Clear PRIMASK */
116+
__enable_irq();
117+
118+
if (state == PM_STATE_RUNTIME_IDLE) {
119+
return;
120+
}
121+
122+
if (SPC_CheckPowerDomainLowPowerRequest(SPC0, kSPC_PowerDomain0)) {
123+
SPC_ClearPowerDomainLowPowerRequestFlag(SPC0, kSPC_PowerDomain0);
124+
}
125+
if (SPC_CheckPowerDomainLowPowerRequest(SPC0, kSPC_PowerDomain1)) {
126+
SPC_ClearPowerDomainLowPowerRequestFlag(SPC0, kSPC_PowerDomain1);
127+
}
128+
if (SPC_CheckPowerDomainLowPowerRequest(SPC0, kSPC_PowerDomain2)) {
129+
RFMC->RF2P4GHZ_CTRL = (RFMC->RF2P4GHZ_CTRL & (~RFMC_RF2P4GHZ_CTRL_LP_MODE_MASK));
130+
RFMC->RF2P4GHZ_CTRL &= ~RFMC_RF2P4GHZ_CTRL_LP_ENTER_MASK;
131+
SPC_ClearPowerDomainLowPowerRequestFlag(SPC0, kSPC_PowerDomain2);
132+
}
133+
SPC_ClearLowPowerRequest(SPC0);
134+
}
135+
136+
void nxp_mcxw7x_power_init(void)
137+
{
138+
/* Enable LPTMR0 as wakeup source */
139+
NXP_ENABLE_WAKEUP_SIGNAL(0);
140+
}

soc/nxp/mcx/mcxw/mcxw7xx/soc.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <fsl_ccm32k.h>
1616
#include <fsl_common.h>
1717
#include <fsl_clock.h>
18+
#include "fsl_spc.h"
1819

1920
extern uint32_t SystemCoreClock;
2021
extern void nxp_nbu_init(void);
@@ -221,6 +222,75 @@ static void vbat_init(void)
221222
base->STATUSA |= VBAT_STATUSA_POR_DET_MASK;
222223
};
223224

225+
#if CONFIG_PM
226+
/*
227+
* In active mode, all HVDs/LVDs are disabled.
228+
* DCDC regulated to 1.8V, Core LDO regulated to 1.1V;
229+
* In low power modes, all HVDs/LVDs are disabled.
230+
* Bandgap is disabled, DCDC regulated to 1.25V, Core LDO regulated to 1.05V.
231+
*/
232+
233+
__weak void set_spc_configuration(void)
234+
{
235+
/* Disable LVDs and HVDs in Active mode. */
236+
SPC_EnableActiveModeCoreHighVoltageDetect(SPC0, false);
237+
SPC_EnableActiveModeCoreLowVoltageDetect(SPC0, false);
238+
SPC_EnableActiveModeSystemHighVoltageDetect(SPC0, false);
239+
SPC_EnableActiveModeSystemLowVoltageDetect(SPC0, false);
240+
SPC_EnableActiveModeIOHighVoltageDetect(SPC0, false);
241+
SPC_EnableActiveModeIOLowVoltageDetect(SPC0, false);
242+
243+
while (SPC_GetBusyStatusFlag(SPC0)) {
244+
}
245+
246+
spc_active_mode_regulators_config_t active_mode_regulator;
247+
248+
active_mode_regulator.bandgapMode = kSPC_BandgapEnabledBufferDisabled;
249+
active_mode_regulator.lpBuff = false;
250+
/* DCDC regulate to 1.8V. */
251+
active_mode_regulator.DCDCOption.DCDCVoltage = kSPC_DCDC_SafeModeVoltage;
252+
active_mode_regulator.DCDCOption.DCDCDriveStrength = kSPC_DCDC_NormalDriveStrength;
253+
active_mode_regulator.SysLDOOption.SysLDOVoltage = kSPC_SysLDO_NormalVoltage;
254+
active_mode_regulator.SysLDOOption.SysLDODriveStrength = kSPC_SysLDO_NormalDriveStrength;
255+
/* Core LDO regulate to 1.1V. */
256+
active_mode_regulator.CoreLDOOption.CoreLDOVoltage = kSPC_CoreLDO_MidDriveVoltage;
257+
#if defined(FSL_FEATURE_SPC_HAS_CORELDO_VDD_DS) && FSL_FEATURE_SPC_HAS_CORELDO_VDD_DS
258+
active_mode_regulator.CoreLDOOption.CoreLDODriveStrength = kSPC_CoreLDO_NormalDriveStrength;
259+
#endif /* FSL_FEATURE_SPC_HAS_CORELDO_VDD_DS */
260+
261+
SPC_SetActiveModeDCDCRegulatorConfig(SPC0, &active_mode_regulator.DCDCOption);
262+
263+
while (SPC_GetBusyStatusFlag(SPC0)) {
264+
}
265+
266+
SPC_SetActiveModeSystemLDORegulatorConfig(SPC0, &active_mode_regulator.SysLDOOption);
267+
268+
SPC_SetActiveModeBandgapModeConfig(SPC0, active_mode_regulator.bandgapMode);
269+
270+
SPC_SetActiveModeCoreLDORegulatorConfig(SPC0, &active_mode_regulator.CoreLDOOption);
271+
272+
SPC_EnableActiveModeCMPBandgapBuffer(SPC0, active_mode_regulator.lpBuff);
273+
274+
spc_lowpower_mode_regulators_config_t low_power_regulator;
275+
276+
low_power_regulator.lpIREF = false;
277+
low_power_regulator.bandgapMode = kSPC_BandgapDisabled;
278+
low_power_regulator.lpBuff = false;
279+
low_power_regulator.CoreIVS = false;
280+
low_power_regulator.DCDCOption.DCDCVoltage = kSPC_DCDC_LowUnderVoltage;
281+
low_power_regulator.DCDCOption.DCDCDriveStrength = kSPC_DCDC_LowDriveStrength;
282+
low_power_regulator.SysLDOOption.SysLDODriveStrength = kSPC_SysLDO_LowDriveStrength;
283+
low_power_regulator.CoreLDOOption.CoreLDOVoltage = kSPC_CoreLDO_MidDriveVoltage;
284+
low_power_regulator.CoreLDOOption.CoreLDODriveStrength = kSPC_CoreLDO_LowDriveStrength;
285+
286+
SPC_SetLowPowerModeRegulatorsConfig(SPC0, &low_power_regulator);
287+
288+
SPC_SetLowPowerWakeUpDelay(SPC0, 0xFFFFU);
289+
}
290+
#endif
291+
292+
extern void nxp_mcxw7x_power_init(void);
293+
224294
void soc_early_init_hook(void)
225295
{
226296
unsigned int oldLevel; /* old interrupt lock level */
@@ -234,6 +304,11 @@ void soc_early_init_hook(void)
234304
/* Smart power switch initialization */
235305
vbat_init();
236306

307+
#if CONFIG_PM
308+
nxp_mcxw7x_power_init();
309+
set_spc_configuration();
310+
#endif
311+
237312
/* restore interrupt state */
238313
irq_unlock(oldLevel);
239314

soc/nxp/mcx/mcxw/mcxw7xx/soc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@
88

99
#include <fsl_port.h>
1010
#include <soc_common.h>
11+
#include <fsl_wuu.h>
1112

1213
#define PORT_MUX_GPIO kPORT_MuxAsGpio
1314

1415
#define nbu_handler RF_IMU0_IRQHandler
1516

17+
#undef NXP_ENABLE_WAKEUP_SIGNAL
18+
#define NXP_ENABLE_WAKEUP_SIGNAL(sig) WUU_SetInternalWakeUpModulesConfig(WUU0,\
19+
sig, kWUU_InternalModuleInterrupt);
20+
1621
#endif /* _SOC__H_ */

0 commit comments

Comments
 (0)