Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rw6xx: Add Support for Standby Power Mode #84945

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
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
7 changes: 7 additions & 0 deletions boards/nxp/frdm_rw612/frdm_rw612_common.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@
&os_timer {
status = "okay";
wakeup-source;
deep-sleep-counter = <&rtc_highres>;
};

&systick {
Expand All @@ -195,6 +196,12 @@
pinctrl-names = "default";
};

/* RTC is the wakeup source for PM modes 3,4 */
&rtc_highres {
status = "okay";
wakeup-source;
};

&nbu {
status = "okay";
wakeup-source;
Expand Down
7 changes: 7 additions & 0 deletions boards/nxp/rd_rw612_bga/rd_rw612_bga.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ nxp_8080_touch_panel_i2c: &arduino_i2c {
pinctrl-names = "default";
};

/* RTC is the wakeup source for PM modes 3,4 */
&rtc_highres {
status = "okay";
wakeup-source;
};

&pmu {
reset-causes-en = <PMU_RESET_CM33_LOCKUP>,
<PMU_RESET_ITRC>,
Expand All @@ -288,6 +294,7 @@ nxp_8080_touch_panel_i2c: &arduino_i2c {
&os_timer {
status = "okay";
wakeup-source;
deep-sleep-counter = <&rtc_highres>;
};

&systick {
Expand Down
33 changes: 32 additions & 1 deletion drivers/gpio/gpio_mcux_lpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/pm/device.h>
#include <zephyr/irq.h>
#include <soc.h>
#include <fsl_common.h>
Expand Down Expand Up @@ -398,6 +399,13 @@ static int gpio_mcux_lpc_pin_interrupt_configure(const struct device *dev,
#endif
}

void gpio_mcux_lpc_trigger_cb(const struct device *dev, uint32_t pins)
{
struct gpio_mcux_lpc_data *data = dev->data;

gpio_fire_callbacks(&data->callbacks, dev, pins);
}

static int gpio_mcux_lpc_manage_cb(const struct device *port,
struct gpio_callback *callback, bool set)
{
Expand All @@ -414,6 +422,26 @@ static int gpio_mcux_lpc_init(const struct device *dev)
return 0;
}

#ifdef CONFIG_PM_DEVICE
static int gpio_mcux_lpc_pm_action(const struct device *dev, enum pm_device_action action)
{
switch (action) {
case PM_DEVICE_ACTION_RESUME:
break;
case PM_DEVICE_ACTION_SUSPEND:
break;
case PM_DEVICE_ACTION_TURN_OFF:
break;
case PM_DEVICE_ACTION_TURN_ON:
gpio_mcux_lpc_init(dev);
break;
default:
return -ENOTSUP;
}
return 0;
}
#endif /*CONFIG_PM_DEVICE*/

static DEVICE_API(gpio, gpio_mcux_lpc_driver_api) = {
.pin_configure = gpio_mcux_lpc_configure,
.port_get_raw = gpio_mcux_lpc_port_get_raw,
Expand Down Expand Up @@ -465,7 +493,10 @@ static DEVICE_API(gpio, gpio_mcux_lpc_driver_api) = {
\
static struct gpio_mcux_lpc_data gpio_mcux_lpc_data_##n; \
\
DEVICE_DT_INST_DEFINE(n, lpc_gpio_init_##n, NULL, \
PM_DEVICE_DT_INST_DEFINE(n, gpio_mcux_lpc_pm_action); \
\
DEVICE_DT_INST_DEFINE(n, lpc_gpio_init_##n, \
PM_DEVICE_DT_INST_GET(n), \
&gpio_mcux_lpc_data_##n, \
&gpio_mcux_lpc_config_##n, PRE_KERNEL_1, \
CONFIG_GPIO_INIT_PRIORITY, \
Expand Down
2 changes: 2 additions & 0 deletions drivers/power_domain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO power_domain_gpio.c)
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO_MONITOR power_domain_gpio_monitor.c)
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_INTEL_ADSP power_domain_intel_adsp.c)
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_NXP_SCU power_domain_nxp_scu.c)
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_STATE_CHANGE_DEVICEONOFF
power_domain_state_change_device_onoff.c)
9 changes: 9 additions & 0 deletions drivers/power_domain/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,13 @@ config POWER_DOMAIN_NXP_SCU_INIT_PRIORITY

endif #POWER_DOMAIN_NXP_SCU

config POWER_DOMAIN_STATE_CHANGE_DEVICEONOFF
bool "Turn on/off devices during a PM state change"
default y
depends on DT_HAS_POWER_DOMAIN_STATE_CHANGE_DEVICEONOFF_ENABLED
select DEVICE_DEPS
help
Generic power domain control to turn on/off devices when the
PM subsystem transitions in and out certain power states.

endif
116 changes: 116 additions & 0 deletions drivers/power_domain/power_domain_state_change_device_onoff.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/kernel_structs.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/pm/pm.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(power_domain_deviceonoff, CONFIG_POWER_DOMAIN_LOG_LEVEL);

/* Indicates the end of the onoff_power_states array for count purposes */
#define POWER_DOMAIN_DEVICE_ONOFF_STATE_COUNT 0xFF

struct pd_deviceonoff_config {
uint8_t *onoff_power_states;
};

struct pd_visitor_context {
const struct device *domain;
enum pm_device_action action;
};

static int pd_domain_visitor(const struct device *dev, void *context)
{
struct pd_visitor_context *visitor_context = context;

/* Only run action if the device is on the specified domain */
if (!dev->pm || (dev->pm_base->domain != visitor_context->domain)) {
return 0;
}

/* In case device is active, first suspend it before turning it off */
if ((visitor_context->action == PM_DEVICE_ACTION_TURN_OFF) &&
(dev->pm_base->state == PM_DEVICE_STATE_ACTIVE)) {
(void)pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
}
(void)pm_device_action_run(dev, visitor_context->action);
return 0;
}

static int pd_pm_action(const struct device *dev, enum pm_device_action action)
{
const struct pd_deviceonoff_config *config = dev->config;
uint8_t i = 0;
/* Get the next power state that will be used */
enum pm_state state = pm_state_next_get(_current_cpu->id)->state;
struct pd_visitor_context context = {.domain = dev};

switch (action) {
case PM_DEVICE_ACTION_RESUME:
while (config->onoff_power_states[i] != POWER_DOMAIN_DEVICE_ONOFF_STATE_COUNT) {
/* Check if we need do the turn on action for this state */
if (state == config->onoff_power_states[i]) {
/* Notify devices on the domain they are now powered */
context.action = PM_DEVICE_ACTION_TURN_ON;
(void)device_supported_foreach(dev, pd_domain_visitor, &context);
/* No need to go through the rest of the array of states */
break;
}
i++;
}
break;
case PM_DEVICE_ACTION_SUSPEND:
while (config->onoff_power_states[i] != POWER_DOMAIN_DEVICE_ONOFF_STATE_COUNT) {
/* Check if need to do the turn off action for this state */
if (state == config->onoff_power_states[i]) {
/* Notify devices on the domain that power is going down */
context.action = PM_DEVICE_ACTION_TURN_OFF;
(void)device_supported_foreach(dev, pd_domain_visitor, &context);
/* No need to go through the rest of the array of states */
break;
}
i++;
}
break;
case PM_DEVICE_ACTION_TURN_ON:
break;
case PM_DEVICE_ACTION_TURN_OFF:
break;
default:
return -ENOTSUP;
}

return 0;
}

#define DT_DRV_COMPAT power_domain_state_change_deviceonoff

#define PM_STATE_FROM_DT(i, node_id, prop_name) \
COND_CODE_1(DT_NODE_HAS_STATUS(DT_PHANDLE_BY_IDX(node_id, prop_name, i), okay), \
(PM_STATE_DT_INIT(DT_PHANDLE_BY_IDX(node_id, prop_name, i)),), ())

#define POWER_DOMAIN_DEVICE_ONOFF_STATES(inst, node_id) \
uint8_t onoff_states_##inst[] = { \
LISTIFY(DT_PROP_LEN_OR(node_id, onoff_power_states, 0), \
PM_STATE_FROM_DT, (), node_id, onoff_power_states) \
POWER_DOMAIN_DEVICE_ONOFF_STATE_COUNT \
};

#define POWER_DOMAIN_DEVICE(id) \
POWER_DOMAIN_DEVICE_ONOFF_STATES(id, DT_DRV_INST(id)) \
\
static const struct pd_deviceonoff_config pd_deviceonoff_##id##_cfg = { \
.onoff_power_states = onoff_states_##id, \
}; \
PM_DEVICE_DT_INST_DEFINE(id, pd_pm_action); \
DEVICE_DT_INST_DEFINE(id, NULL, PM_DEVICE_DT_INST_GET(id), \
NULL, &pd_deviceonoff_##id##_cfg, PRE_KERNEL_1, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);

DT_INST_FOREACH_STATUS_OKAY(POWER_DOMAIN_DEVICE)
84 changes: 81 additions & 3 deletions drivers/serial/uart_mcux_flexcomm.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/pm/policy.h>
#include <zephyr/irq.h>
#include <zephyr/pm/device.h>
#include <fsl_usart.h>
#include <soc.h>
#include <fsl_device_registers.h>
Expand Down Expand Up @@ -86,8 +88,33 @@ struct mcux_flexcomm_data {
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
struct uart_config uart_config;
#endif
#ifdef CONFIG_PM_POLICY_DEVICE_CONSTRAINTS
bool pm_policy_state_lock;
#endif
};

#ifdef CONFIG_PM_POLICY_DEVICE_CONSTRAINTS
static void mcux_flexcomm_pm_policy_state_lock_get(const struct device *dev)
{
struct mcux_flexcomm_data *data = dev->data;

if (!data->pm_policy_state_lock) {
data->pm_policy_state_lock = true;
pm_policy_device_power_lock_get(dev);
}
}

static void mcux_flexcomm_pm_policy_state_lock_put(const struct device *dev)
{
struct mcux_flexcomm_data *data = dev->data;

if (data->pm_policy_state_lock) {
data->pm_policy_state_lock = false;
pm_policy_device_power_lock_put(dev);
}
}
#endif

static int mcux_flexcomm_poll_in(const struct device *dev, unsigned char *c)
{
const struct mcux_flexcomm_config *config = dev->config;
Expand Down Expand Up @@ -179,6 +206,14 @@ static void mcux_flexcomm_irq_tx_enable(const struct device *dev)
const struct mcux_flexcomm_config *config = dev->config;
uint32_t mask = kUSART_TxLevelInterruptEnable;

/* Indicates that this device started a transaction that should
* not be interrupted by putting the SoC in states that would
* interfere with this transfer.
*/
#ifdef CONFIG_PM_POLICY_DEVICE_CONSTRAINTS
mcux_flexcomm_pm_policy_state_lock_get(dev);
#endif

USART_EnableInterrupts(config->base, mask);
}

Expand All @@ -187,6 +222,10 @@ static void mcux_flexcomm_irq_tx_disable(const struct device *dev)
const struct mcux_flexcomm_config *config = dev->config;
uint32_t mask = kUSART_TxLevelInterruptEnable;

#ifdef CONFIG_PM_POLICY_DEVICE_CONSTRAINTS
mcux_flexcomm_pm_policy_state_lock_put(dev);
#endif

USART_DisableInterrupts(config->base, mask);
}

Expand Down Expand Up @@ -467,11 +506,19 @@ static int mcux_flexcomm_uart_tx(const struct device *dev, const uint8_t *buf,
/* Enable TX DMA requests */
USART_EnableTxDMA(config->base, true);

/* Do not allow the system to suspend until the transmission has completed */
#ifdef CONFIG_PM_POLICY_DEVICE_CONSTRAINTS
mcux_flexcomm_pm_policy_state_lock_get(dev);
#endif

/* Trigger the DMA to start transfer */
ret = dma_start(config->tx_dma.dev, config->tx_dma.channel);
if (ret) {
irq_unlock(key);
return ret;
#ifdef CONFIG_PM_POLICY_DEVICE_CONSTRAINTS
mcux_flexcomm_pm_policy_state_lock_put(dev);
#endif
return ret;
}

/* Schedule a TX abort for @param timeout */
Expand Down Expand Up @@ -993,6 +1040,10 @@ static void mcux_flexcomm_isr(const struct device *dev)
data->tx_data.xfer_buf = NULL;

async_user_callback(dev, &tx_done_event);

#ifdef CONFIG_PM_POLICY_DEVICE_CONSTRAINTS
mcux_flexcomm_pm_policy_state_lock_put(dev);
#endif
}

}
Expand Down Expand Up @@ -1067,6 +1118,31 @@ static int mcux_flexcomm_init(const struct device *dev)
return 0;
}

#ifdef CONFIG_PM_DEVICE
static uint32_t usart_intenset;
static int mcux_flexcomm_pm_action(const struct device *dev, enum pm_device_action action)
{
const struct mcux_flexcomm_config *config = dev->config;

switch (action) {
case PM_DEVICE_ACTION_RESUME:
break;
case PM_DEVICE_ACTION_SUSPEND:
break;
case PM_DEVICE_ACTION_TURN_OFF:
usart_intenset = USART_GetEnabledInterrupts(config->base);
return 0;
case PM_DEVICE_ACTION_TURN_ON:
mcux_flexcomm_init(dev);
USART_EnableInterrupts(config->base, usart_intenset);
return 0;
default:
return -ENOTSUP;
}
return 0;
}
#endif /*CONFIG_PM_DEVICE*/

static DEVICE_API(uart, mcux_flexcomm_driver_api) = {
.poll_in = mcux_flexcomm_poll_in,
.poll_out = mcux_flexcomm_poll_out,
Expand Down Expand Up @@ -1202,9 +1278,11 @@ static const struct mcux_flexcomm_config mcux_flexcomm_##n##_config = { \
\
static const struct mcux_flexcomm_config mcux_flexcomm_##n##_config; \
\
PM_DEVICE_DT_INST_DEFINE(n, mcux_flexcomm_pm_action); \
\
DEVICE_DT_INST_DEFINE(n, \
mcux_flexcomm_init, \
NULL, \
&mcux_flexcomm_init, \
PM_DEVICE_DT_INST_GET(n), \
&mcux_flexcomm_##n##_data, \
&mcux_flexcomm_##n##_config, \
PRE_KERNEL_1, \
Expand Down
Loading
Loading