Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions dts/arm/nxp/nxp_mcxw7x_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
cpu0: cpu@0 {
compatible = "arm,cortex-m33f";
reg = <0>;
cpu-power-states = <&sleep &deep_sleep>;
#address-cells = <1>;
#size-cells = <1>;

Expand All @@ -40,6 +41,19 @@
reg = <0xe000ed90 0x40>;
};
};

power-states {
sleep: sleep {
compatible = "zephyr,power-state";
power-state-name = "suspend-to-idle";
exit-latency-us = <10>;
};
deep_sleep: deep-sleep {
compatible = "zephyr,power-state";
power-state-name = "standby";
exit-latency-us = <11>;
};
};
};

soc {
Expand Down Expand Up @@ -114,6 +128,24 @@
#address-cells = <1>;
#size-cells = <1>;

cmc: system-modules@1000 {
compatible = "nxp,cmc";
reg = <0x1000 0x1000>;
interrupts = <1 0>;
};

spc: system-modules@16000 {
compatible = "nxp,spc";
reg = <0x16000 0x1000>;
interrupts = <21 0>;
};

wuu: system-modules@19000 {
compatible = "nxp,wuu";
reg = <0x19000 0x1000>;
interrupts = <22 0>;
};

scg: clock-controller@1e000 {
compatible = "nxp,scg-k4";
reg = <0x1e000 0x404>;
Expand Down Expand Up @@ -252,6 +284,7 @@
};

vbat: vbat@2b000 {
compatible = "nxp,vbat";
reg = <0x2b000 0x33c>;
interrupts = <74 0>;
};
Expand Down
12 changes: 12 additions & 0 deletions dts/bindings/interrupt-controller/nxp,wuu.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0

description: NXP Wakeup Unit (WUU)

compatible: "nxp,wuu"

include: base.yaml

properties:
reg:
required: true
12 changes: 12 additions & 0 deletions dts/bindings/power/nxp,cmc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0

description: NXP Core Mode Controller (CMC)

compatible: "nxp,cmc"

include: base.yaml

properties:
reg:
required: true
12 changes: 12 additions & 0 deletions dts/bindings/power/nxp,spc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0

description: NXP System Power Control (SPC)

compatible: "nxp,spc"

include: base.yaml

properties:
reg:
required: true
12 changes: 12 additions & 0 deletions dts/bindings/power/nxp,vbat.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0

description: NXP Smart Power Switch (VBAT)

compatible: "nxp,vbat"

include: base.yaml

properties:
reg:
required: true
8 changes: 4 additions & 4 deletions modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ set_variable_ifdef(CONFIG_UART_MCUX_IUART CONFIG_MCUX_COMPONENT_driver.iua
set_variable_ifdef(CONFIG_ADC_MCUX_12B1MSPS_SAR CONFIG_MCUX_COMPONENT_driver.adc_12b1msps_sar)
set_variable_ifdef(CONFIG_HWINFO_MCUX_MCX_CMC CONFIG_MCUX_COMPONENT_driver.mcx_cmc)
set_variable_ifdef(CONFIG_HWINFO_MCUX_SRC CONFIG_MCUX_COMPONENT_driver.src)
set_variable_ifdef(CONFIG_DT_HAS_NXP_SPC_ENABLED CONFIG_MCUX_COMPONENT_driver.spc)
set_variable_ifdef(CONFIG_DT_HAS_NXP_CMC_ENABLED CONFIG_MCUX_COMPONENT_driver.cmc)
set_variable_ifdef(CONFIG_DT_HAS_NXP_VBAT_ENABLED CONFIG_MCUX_COMPONENT_driver.vbat)
set_variable_ifdef(CONFIG_DT_HAS_NXP_WUU_ENABLED CONFIG_MCUX_COMPONENT_driver.wuu)
set_variable_ifdef(CONFIG_HWINFO_MCUX_SIM CONFIG_MCUX_COMPONENT_driver.sim)
set_variable_ifdef(CONFIG_HWINFO_MCUX_RCM CONFIG_MCUX_COMPONENT_driver.rcm)
set_variable_ifdef(CONFIG_IPM_MCUX CONFIG_MCUX_COMPONENT_driver.mailbox)
Expand Down Expand Up @@ -184,10 +188,6 @@ if(CONFIG_SOC_FAMILY_MCXN OR CONFIG_SOC_FAMILY_MCXA)
set(CONFIG_MCUX_COMPONENT_driver.mcx_spc ON)
endif()

if(CONFIG_BT_NXP AND CONFIG_SOC_SERIES_MCXW7XX OR CONFIG_IEEE802154_MCXW)
set(CONFIG_MCUX_COMPONENT_driver.spc ON)
endif()

if(((${MCUX_DEVICE} MATCHES "MIMXRT1[0-9][0-9][0-9]") AND (NOT (CONFIG_SOC_MIMXRT1166_CM4 OR CONFIG_SOC_MIMXRT1176_CM4 OR CONFIG_SOC_MIMXRT1189_CM33))) OR
((${MCUX_DEVICE} MATCHES "MIMX9596") AND CONFIG_SOC_MIMX9596_M7))
set_variable_ifdef(CONFIG_HAS_MCUX_CACHE CONFIG_MCUX_COMPONENT_driver.cache_armv7_m7)
Expand Down
1 change: 1 addition & 0 deletions soc/nxp/mcx/mcxw/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ config SOC_FAMILY_MCXW
select SOC_EARLY_INIT_HOOK
select CLOCK_CONTROL
select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE
select HAS_PM
2 changes: 2 additions & 0 deletions soc/nxp/mcx/mcxw/mcxw7xx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ zephyr_sources(soc.c)
zephyr_sources_ifdef(CONFIG_SOC_MCXW716C mcxw71_platform_init.S)
zephyr_sources_ifdef(CONFIG_SOC_MCXW727C mcxw72_platform_init.S)

zephyr_sources_ifdef(CONFIG_PM power.c)

zephyr_include_directories(./)

set(SOC_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld CACHE INTERNAL "")
Expand Down
5 changes: 5 additions & 0 deletions soc/nxp/mcx/mcxw/mcxw7xx/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ config NUM_IRQS
default 77 if SOC_MCXW727C
default 75

config MCUX_LPTMR_TIMER
default y if PM

DT_LPTMR_PATH := $(dt_nodelabel_path,lptmr0)
config SYS_CLOCK_HW_CYCLES_PER_SEC
default 96000000 if CORTEX_M_SYSTICK
default $(dt_node_int_prop_int,$(DT_LPTMR_PATH),clock-frequency) if MCUX_LPTMR_TIMER

config MCUX_FLASH_K4_API
default y
Expand Down
201 changes: 201 additions & 0 deletions soc/nxp/mcx/mcxw/mcxw7xx/power.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/pm/pm.h>
#include <fsl_cmc.h>
#include <fsl_spc.h>
#include <fsl_vbat.h>
#include <fsl_wuu.h>

#include <zephyr/logging/log.h>

LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);

#define WUU_WAKEUP_LPTMR_IDX 0
#define MCXW7_WUU_ADDR (WUU_Type *)DT_REG_ADDR(DT_INST(0, nxp_wuu))
#define MCXW7_CMC_ADDR (CMC_Type *)DT_REG_ADDR(DT_INST(0, nxp_cmc))
#define MCXW7_VBAT_ADDR (VBAT_Type *)DT_REG_ADDR(DT_INST(0, nxp_vbat))
#define MCXW7_SPC_ADDR (SPC_Type *)DT_REG_ADDR(DT_INST(0, nxp_spc))

void mcxw7xx_set_wakeup(int32_t sig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems only LPTMR is used as wakeup source, will the external pin be enabled as wake up source?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we will need to add a WUU driver for external pins.

{
WUU_SetInternalWakeUpModulesConfig(MCXW7_WUU_ADDR, sig, kWUU_InternalModuleInterrupt);
}

/*
* 1. Set power mode protection
* 2. Disable low power mode debug
* 3. Enable Flash Doze mode.
*/
static void set_cmc_configuration(void)
{
CMC_SetPowerModeProtection(MCXW7_CMC_ADDR, kCMC_AllowAllLowPowerModes);
CMC_LockPowerModeProtectionSetting(MCXW7_CMC_ADDR);
CMC_EnableDebugOperation(MCXW7_CMC_ADDR, IS_ENABLED(CONFIG_DEBUG));
CMC_ConfigFlashMode(MCXW7_CMC_ADDR, false, false, false);
}

/*
* Disable Backup SRAM regulator, FRO16K and Bandgap which
* locates in VBAT power domain for most of power modes.
*
*/
static void deinit_vbat(void)
{
VBAT_EnableBackupSRAMRegulator(MCXW7_VBAT_ADDR, false);
VBAT_EnableFRO16k(MCXW7_VBAT_ADDR, false);
while (VBAT_CheckFRO16kEnabled(MCXW7_VBAT_ADDR)) {
};
VBAT_EnableBandgap(MCXW7_VBAT_ADDR, false);
while (VBAT_CheckBandgapEnabled(MCXW7_VBAT_ADDR)) {
};
}

/* Invoke Low Power/System Off specific Tasks */
__weak void pm_state_set(enum pm_state state, uint8_t substate_id)
{
/* Set PRIMASK */
__disable_irq();
/* Set BASEPRI to 0 */
irq_unlock(0);

set_cmc_configuration();
deinit_vbat();

switch (state) {
case PM_STATE_SUSPEND_TO_IDLE:
cmc_power_domain_config_t config;

/* Set NBU into Sleep Mode */
RFMC->RF2P4GHZ_CTRL = (RFMC->RF2P4GHZ_CTRL &
(~RFMC_RF2P4GHZ_CTRL_LP_MODE_MASK)) |
RFMC_RF2P4GHZ_CTRL_LP_MODE(0x1);
RFMC->RF2P4GHZ_CTRL |= RFMC_RF2P4GHZ_CTRL_LP_ENTER_MASK;

/* Set MAIN_CORE and MAIN_WAKE power domain into sleep mode. */
config.clock_mode = kCMC_GateAllSystemClocksEnterLowPowerMode;
config.main_domain = kCMC_SleepMode;
config.wake_domain = kCMC_SleepMode;
CMC_EnterLowPowerMode(MCXW7_CMC_ADDR, &config);

break;
case PM_STATE_STANDBY:
/* Enable CORE VDD Voltage scaling. */
SPC_EnableLowPowerModeCoreVDDInternalVoltageScaling(MCXW7_SPC_ADDR, true);

/* Set NBU into Deep Sleep Mode */
RFMC->RF2P4GHZ_CTRL = (RFMC->RF2P4GHZ_CTRL & (~RFMC_RF2P4GHZ_CTRL_LP_MODE_MASK)) |
RFMC_RF2P4GHZ_CTRL_LP_MODE(0x3);
RFMC->RF2P4GHZ_CTRL |= RFMC_RF2P4GHZ_CTRL_LP_ENTER_MASK;

/* Set MAIN_CORE and MAIN_WAKE power domain into Deep Sleep Mode. */
config.clock_mode = kCMC_GateAllSystemClocksEnterLowPowerMode;
config.main_domain = kCMC_DeepSleepMode;
config.wake_domain = kCMC_DeepSleepMode;
Comment on lines +94 to +97
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this dependency needs to be expressed in DT. It's fine for now to hardcode disabling them in this state, but we need to describe the connection of these domains on being disabled by this state in DT. If you want, we can do it in a later PR since I think this platform still does not have device PM generally enabled anyways.


CMC_EnterLowPowerMode(MCXW7_CMC_ADDR, &config);

break;
default:
LOG_DBG("Unsupported power state %u", state);
break;
}
}

/* Handle SOC specific activity after Low Power Mode Exit */
__weak void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
{
ARG_UNUSED(state);
ARG_UNUSED(substate_id);

/* Clear PRIMASK */
__enable_irq();

if (SPC_CheckPowerDomainLowPowerRequest(MCXW7_SPC_ADDR, kSPC_PowerDomain0)) {
SPC_ClearPowerDomainLowPowerRequestFlag(MCXW7_SPC_ADDR, kSPC_PowerDomain0);
}
if (SPC_CheckPowerDomainLowPowerRequest(MCXW7_SPC_ADDR, kSPC_PowerDomain1)) {
SPC_ClearPowerDomainLowPowerRequestFlag(MCXW7_SPC_ADDR, kSPC_PowerDomain1);
}
if (SPC_CheckPowerDomainLowPowerRequest(MCXW7_SPC_ADDR, kSPC_PowerDomain2)) {
RFMC->RF2P4GHZ_CTRL = (RFMC->RF2P4GHZ_CTRL & (~RFMC_RF2P4GHZ_CTRL_LP_MODE_MASK));
RFMC->RF2P4GHZ_CTRL &= ~RFMC_RF2P4GHZ_CTRL_LP_ENTER_MASK;
SPC_ClearPowerDomainLowPowerRequestFlag(MCXW7_SPC_ADDR, kSPC_PowerDomain2);
}
SPC_ClearLowPowerRequest(MCXW7_SPC_ADDR);
}

/*
* In active mode, all HVDs/LVDs are disabled.
* DCDC regulated to 1.8V, Core LDO regulated to 1.1V;
* In low power modes, all HVDs/LVDs are disabled.
* Bandgap is disabled, DCDC regulated to 1.25V, Core LDO regulated to 1.05V.
*/
__weak void set_spc_configuration(void)
{
/* Disable LVDs and HVDs in Active mode. */
SPC_EnableActiveModeCoreHighVoltageDetect(MCXW7_SPC_ADDR, false);
SPC_EnableActiveModeCoreLowVoltageDetect(MCXW7_SPC_ADDR, false);
SPC_EnableActiveModeSystemHighVoltageDetect(MCXW7_SPC_ADDR, false);
SPC_EnableActiveModeSystemLowVoltageDetect(MCXW7_SPC_ADDR, false);
SPC_EnableActiveModeIOHighVoltageDetect(MCXW7_SPC_ADDR, false);
SPC_EnableActiveModeIOLowVoltageDetect(MCXW7_SPC_ADDR, false);
while (SPC_GetBusyStatusFlag(MCXW7_SPC_ADDR)) {
}

spc_active_mode_regulators_config_t active_mode_regulator;

active_mode_regulator.bandgapMode = kSPC_BandgapEnabledBufferDisabled;
active_mode_regulator.lpBuff = false;
/* DCDC regulate to 1.8V. */
active_mode_regulator.DCDCOption.DCDCVoltage = kSPC_DCDC_SafeModeVoltage;
active_mode_regulator.DCDCOption.DCDCDriveStrength = kSPC_DCDC_NormalDriveStrength;
active_mode_regulator.SysLDOOption.SysLDOVoltage = kSPC_SysLDO_NormalVoltage;
active_mode_regulator.SysLDOOption.SysLDODriveStrength = kSPC_SysLDO_NormalDriveStrength;
/* Core LDO regulate to 1.1V. */
active_mode_regulator.CoreLDOOption.CoreLDOVoltage = kSPC_CoreLDO_MidDriveVoltage;
#if defined(FSL_FEATURE_SPC_HAS_CORELDO_VDD_DS) && FSL_FEATURE_SPC_HAS_CORELDO_VDD_DS
active_mode_regulator.CoreLDOOption.CoreLDODriveStrength = kSPC_CoreLDO_NormalDriveStrength;
#endif /* FSL_FEATURE_SPC_HAS_CORELDO_VDD_DS */

SPC_SetActiveModeDCDCRegulatorConfig(MCXW7_SPC_ADDR, &active_mode_regulator.DCDCOption);

while (SPC_GetBusyStatusFlag(MCXW7_SPC_ADDR)) {
}

SPC_SetActiveModeSystemLDORegulatorConfig(MCXW7_SPC_ADDR,
&active_mode_regulator.SysLDOOption);

SPC_SetActiveModeBandgapModeConfig(MCXW7_SPC_ADDR, active_mode_regulator.bandgapMode);

SPC_SetActiveModeCoreLDORegulatorConfig(MCXW7_SPC_ADDR,
&active_mode_regulator.CoreLDOOption);

SPC_EnableActiveModeCMPBandgapBuffer(MCXW7_SPC_ADDR, active_mode_regulator.lpBuff);

spc_lowpower_mode_regulators_config_t low_power_regulator;

low_power_regulator.lpIREF = false;
low_power_regulator.bandgapMode = kSPC_BandgapDisabled;
low_power_regulator.lpBuff = false;
low_power_regulator.CoreIVS = false;
low_power_regulator.DCDCOption.DCDCVoltage = kSPC_DCDC_LowUnderVoltage;
low_power_regulator.DCDCOption.DCDCDriveStrength = kSPC_DCDC_LowDriveStrength;
low_power_regulator.SysLDOOption.SysLDODriveStrength = kSPC_SysLDO_LowDriveStrength;
low_power_regulator.CoreLDOOption.CoreLDOVoltage = kSPC_CoreLDO_MidDriveVoltage;
low_power_regulator.CoreLDOOption.CoreLDODriveStrength = kSPC_CoreLDO_LowDriveStrength;

SPC_SetLowPowerModeRegulatorsConfig(MCXW7_SPC_ADDR, &low_power_regulator);

SPC_SetLowPowerWakeUpDelay(MCXW7_SPC_ADDR, 0xFFFFU);
}

void nxp_mcxw7x_power_init(void)
{
set_spc_configuration();
/* Enable LPTMR0 as wakeup source */
NXP_ENABLE_WAKEUP_SIGNAL(WUU_WAKEUP_LPTMR_IDX);
}
4 changes: 4 additions & 0 deletions soc/nxp/mcx/mcxw/mcxw7xx/soc.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ void soc_early_init_hook(void)
/* Smart power switch initialization */
vbat_init();

if (IS_ENABLED(CONFIG_PM)) {
nxp_mcxw7x_power_init();
}

/* restore interrupt state */
irq_unlock(oldLevel);

Expand Down
10 changes: 10 additions & 0 deletions soc/nxp/mcx/mcxw/mcxw7xx/soc.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,14 @@

#define nbu_handler RF_IMU0_IRQHandler

#undef NXP_ENABLE_WAKEUP_SIGNAL
void mcxw7xx_set_wakeup(int32_t sig);
#define NXP_ENABLE_WAKEUP_SIGNAL(sig) mcxw7xx_set_wakeup(sig)

#if CONFIG_PM
void nxp_mcxw7x_power_init(void);
#else
#define nxp_mcxw7x_power_init(...) do { } while (0)
#endif

#endif /* _SOC__H_ */