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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions boards/nxp/frdm_mcxw23/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ should see the following message in the terminal:
*** Booting Zephyr OS build v4.2.0-2105-g48f2ffda26de ***
Hello World! frdm_mcxw23/mcxw236

Power Management
Copy link
Contributor

@yeaissa yeaissa Nov 27, 2025

Choose a reason for hiding this comment

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

I'm not ok with this, since it will require adding the overlay on each app that will run with PM. I expect to have everything I need once I enable CONFIG_PM.
To avoid this lets set the os_timer status to okay by default in the nxp_mcxw23x_common.dtsi file, and change the MCUX_OS_TIMER redefinition in Kconfig.defconfig to:

 config MCUX_OS_TIMER
-       default y if PM
+       default n if !PM

This will allow to run on the CORTEX_M_SYSTICK when PM is not defined and switch to OS_TIMER when PM is enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated.

================

When Power Management is enabled :kconfig:option:`CONFIG_PM`, OSTIMER is used as
OS tick timer.

Limitation: Wakeup pin can't be used as wakeup source in Standby mode.

.. include:: ../../common/board-footer.rst.inc

.. _MCXW23 SoC Website:
Expand Down
9 changes: 4 additions & 5 deletions boards/nxp/frdm_mcxw23/frdm_mcxw23_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,14 @@
pinctrl-names = "default";
};

/*
* MCXW23 FRDM board uses SYSTICK timer as the kernel timer.
* In case we need to switch to OS timer, then
* replace &systick with &os_timer
*/
&systick {
status = "okay";
};

&os_timer {
status = "okay";
};

&wwdt0 {
status = "okay";
};
Expand Down
8 changes: 8 additions & 0 deletions boards/nxp/mcxw23_evk/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ should see the following message in the terminal:
*** Booting Zephyr OS build v4.2.0-2105-g9da1d56da9e7 ***
Hello World! mcxw23_evk/mcxw236

Power Management
================

When Power Management is enabled :kconfig:option:`CONFIG_PM`, OSTIMER is used as
OS tick timer.

Limitation: Wakeup pin can't be used as wakeup source in Standby mode.

.. include:: ../../common/board-footer.rst.inc

.. _MCXW23 SoC Website:
Expand Down
9 changes: 4 additions & 5 deletions boards/nxp/mcxw23_evk/mcxw23_evk_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,14 @@
pinctrl-names = "default";
};

/*
* MCXW23 EVK board uses SYSTICK timer as the kernel timer.
* In case we need to switch to OS timer, then
* replace &systick with &os_timer
*/
&systick {
status = "okay";
};

&os_timer {
status = "okay";
};

&wwdt0 {
status = "okay";
};
Expand Down
5 changes: 5 additions & 0 deletions drivers/counter/counter_nxp_mrt.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <zephyr/irq.h>
#include <zephyr/drivers/reset.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>

#include <soc.h>

Expand Down Expand Up @@ -73,6 +74,8 @@ static int nxp_mrt_stop(const struct device *dev)
/* LOAD bit and 0 ivalue allows us to forcibly stop the timer */
base->CHANNEL[channel_id].INTVAL = MRT_CHANNEL_INTVAL_LOAD(1);

pm_policy_device_power_lock_put(dev);

return 0;
}

Expand All @@ -90,6 +93,8 @@ static int nxp_mrt_start(const struct device *dev)
data->top = config->info.max_top_value;
}

pm_policy_device_power_lock_get(dev);

/* Start with previously configured top value (if already running this has no effect) */
base->CHANNEL[channel_id].INTVAL = data->top;

Expand Down
105 changes: 104 additions & 1 deletion drivers/dma/dma_mcux_lpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,34 @@
#include <zephyr/sys/util_macro.h>
#include <zephyr/drivers/dma/dma_mcux_lpc.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>

#define DT_DRV_COMPAT nxp_lpc_dma

LOG_MODULE_REGISTER(dma_mcux_lpc, CONFIG_DMA_LOG_LEVEL);

#if CONFIG_PM_DEVICE
/*
* Data structures to backup DMA registers when the
* register content lost in low power modes.
*/
struct dma_backup_reg {
/*
* Backup the control registers.
* Don't need to backup CTRL and SRAMBASE, they
* are configured in function DMA_Init.
*/
uint32_t enableset; /* Register ENABLESET */
uint32_t intenset; /* Register INTENSET */
};

struct dma_ch_backup_reg {
/* Don't need to backup status register CTLSTAT. */
uint32_t cfg; /* Register CFG */
uint32_t xfercfg; /* Register XFERCFG */
};
#endif /* CONFIG_PM_DEVICE */

struct dma_mcux_lpc_config {
DMA_Type *base;
uint32_t otrig_base_address;
Expand All @@ -53,6 +76,9 @@ struct channel_data {
uint8_t num_of_descriptors;
bool descriptors_queued;
bool busy;
#if CONFIG_PM_DEVICE
struct dma_ch_backup_reg backup_reg;
#endif /* CONFIG_PM_DEVICE */
};

struct dma_otrig {
Expand All @@ -67,6 +93,9 @@ struct dma_mcux_lpc_dma_data {
struct dma_otrig *otrig_array;
int8_t *channel_index;
uint8_t num_channels_used;
#if CONFIG_PM_DEVICE
struct dma_backup_reg backup_reg;
#endif /* CONFIG_PM_DEVICE */
};

struct k_spinlock configuring_otrigs;
Expand Down Expand Up @@ -108,6 +137,10 @@ static void nxp_lpc_dma_callback(dma_handle_t *handle, void *param,
ret = DMA_STATUS_COMPLETE;
}

if (!data->busy) {
pm_policy_device_power_lock_put(data->dev);
}

if (data->dma_callback) {
data->dma_callback(data->dev, data->user_data, channel, ret);
}
Expand Down Expand Up @@ -563,6 +596,7 @@ static int dma_mcux_lpc_configure(const struct device *dev, uint32_t channel,

if (data->busy) {
DMA_AbortTransfer(p_handle);
pm_policy_device_power_lock_put(dev);
}

LOG_DBG("channel is %d", p_handle->channel);
Expand Down Expand Up @@ -815,6 +849,7 @@ static int dma_mcux_lpc_start(const struct device *dev, uint32_t channel)
LOG_DBG("START TRANSFER");
LOG_DBG("DMA CTRL 0x%x", DEV_BASE(dev)->CTRL);
data->busy = true;
pm_policy_device_power_lock_get(dev);
/* In case of a restart after a stop, reinstall the DMA callback
* that was removed by the stop.
*/
Expand All @@ -836,7 +871,10 @@ static int dma_mcux_lpc_stop(const struct device *dev, uint32_t channel)
DMA_AbortTransfer(p_handle);
DMA_DisableChannel(DEV_BASE(dev), p_handle->channel);

data->busy = false;
if (data->busy) {
data->busy = false;
pm_policy_device_power_lock_put(dev);
}

/* Handle race condition where if this is called from an ISR
* and the DMA channel completion interrupt becomes pending
Expand Down Expand Up @@ -938,6 +976,69 @@ static int dma_mcux_lpc_get_attribute(const struct device *dev, uint32_t type, u
return 0;
}

#if CONFIG_PM_DEVICE
static void dma_mcux_lpc_backup_reg(const struct device *dev)
{
struct dma_mcux_lpc_dma_data *dma_data = dev->data;
const struct dma_mcux_lpc_config *config = dev->config;
struct channel_data *p_channel_data;
uint32_t virtual_channel;
DMA_Type *dma_base = DEV_BASE(dev);

dma_data->backup_reg.enableset = dma_base->COMMON[0].ENABLESET;
dma_data->backup_reg.intenset = dma_base->COMMON[0].INTENSET;

/* Only backup the used channels */
virtual_channel = 0;
for (uint32_t channel = 0; channel < config->num_of_channels; channel++) {
if (dma_data->channel_index[channel] != -1) {
p_channel_data = &dma_data->channel_data[virtual_channel];

p_channel_data->backup_reg.xfercfg = dma_base->CHANNEL[channel].XFERCFG;
p_channel_data->backup_reg.cfg = dma_base->CHANNEL[channel].CFG;
virtual_channel++;
}
}
}

static void dma_mcux_lpc_restore_reg(const struct device *dev)
{
struct dma_mcux_lpc_dma_data *dma_data = dev->data;
const struct dma_mcux_lpc_config *config = dev->config;
struct channel_data *p_channel_data;
uint32_t virtual_channel;
DMA_Type *dma_base = DEV_BASE(dev);

dma_base->COMMON[0].ENABLESET = dma_data->backup_reg.enableset;
dma_base->COMMON[0].INTENSET = dma_data->backup_reg.intenset;

/* Only backup the used channels */
virtual_channel = 0;
for (uint32_t channel = 0; channel < config->num_of_channels; channel++) {
if (dma_data->channel_index[channel] != -1) {
p_channel_data = &dma_data->channel_data[virtual_channel];

dma_base->CHANNEL[channel].XFERCFG = p_channel_data->backup_reg.xfercfg;
dma_base->CHANNEL[channel].CFG = p_channel_data->backup_reg.cfg;
virtual_channel++;
}
}
}

#else /* !CONFIG_PM_DEVICE */

static inline void dma_mcux_lpc_backup_reg(const struct device *dev)
{
ARG_UNUSED(dev);
}

static inline void dma_mcux_lpc_restore_reg(const struct device *dev)
{
ARG_UNUSED(dev);
}

#endif /* CONFIG_PM_DEVICE */

static int dma_mcux_lpc_pm_action(const struct device *dev, enum pm_device_action action)
{
switch (action) {
Expand All @@ -946,9 +1047,11 @@ static int dma_mcux_lpc_pm_action(const struct device *dev, enum pm_device_actio
case PM_DEVICE_ACTION_SUSPEND:
break;
case PM_DEVICE_ACTION_TURN_OFF:
dma_mcux_lpc_backup_reg(dev);
break;
case PM_DEVICE_ACTION_TURN_ON:
DMA_Init(DEV_BASE(dev));
dma_mcux_lpc_restore_reg(dev);
break;
default:
return -ENOTSUP;
Expand Down
16 changes: 16 additions & 0 deletions drivers/interrupt_controller/intc_nxp_pint.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,22 @@ void nxp_pint_pin_unset_callback(uint8_t pin)
pint_irq_cfg[slot].callback = NULL;
}

int nxp_pint_pin_get_slot_index(uint8_t pin)
{
int slot;

if (pin > ARRAY_SIZE(pin_pint_id)) {
return -EINVAL;
}

slot = pin_pint_id[pin];
if (slot == NO_PINT_ID) {
return -EINVAL;
}

return slot;
}

/* NXP PINT ISR handler- called with PINT slot ID */
static void nxp_pint_isr(uint8_t *slot)
{
Expand Down
1 change: 1 addition & 0 deletions drivers/timer/Kconfig.mcux_os
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ config MCUX_OS_TIMER
depends on DT_HAS_NXP_OS_TIMER_ENABLED
select TICKLESS_CAPABLE
select TIMER_HAS_64BIT_CYCLE_COUNTER
select RESET
help
This module implements a kernel device driver for the NXP OS
event timer and provides the standard "system clock driver" interfaces.
Expand Down
10 changes: 8 additions & 2 deletions drivers/timer/mcux_os_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <zephyr/spinlock.h>
#include <zephyr/drivers/counter.h>
#include <zephyr/pm/pm.h>
#include <zephyr/drivers/reset.h>
#include "fsl_ostimer.h"
#if !defined(CONFIG_SOC_FAMILY_MCXN) && !defined(CONFIG_SOC_FAMILY_MCXA)
#include "fsl_power.h"
Expand Down Expand Up @@ -179,9 +180,12 @@ static uint32_t mcux_lpc_ostick_compensate_system_timer(void)
}
slept_time_us = counter_ticks_to_us(counter_dev, slept_time_ticks);
cyc_sys_compensated += CYC_PER_US * slept_time_us;

if (IS_ENABLED(CONFIG_MCUX_OS_TIMER_PM_POWERED_OFF)) {
/* Reset the OS Timer to a known state */
RESET_PeripheralReset(kOSEVENT_TIMER_RST_SHIFT_RSTn);
const struct reset_dt_spec reset = RESET_DT_SPEC_GET(DT_DRV_INST(0));

reset_line_toggle_dt(&reset);
/* Reactivate os_timer for cases where it loses its state */
OSTIMER_Init(base);
}
Expand Down Expand Up @@ -337,7 +341,9 @@ static int sys_clock_driver_init(void)
/* On some SoC's, OS Timer cannot wakeup from low power mode in standby modes */
#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(standby)) && CONFIG_PM
counter_dev = DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(0, deep_sleep_counter));
counter_max_val = counter_get_max_top_value(counter_dev);
if (NULL != counter_dev) {
counter_max_val = counter_get_max_top_value(counter_dev);
}
#endif

#if (DT_INST_PROP(0, wakeup_source))
Expand Down
1 change: 1 addition & 0 deletions dts/arm/nxp/nxp_mcxn23x_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@
compatible = "nxp,os-timer";
reg = <0x49000 0x1000>;
interrupts = <57 0>;
resets = <&reset NXP_SYSCON_RESET(1, 1)>;
status = "disabled";
};

Expand Down
Loading