drivers: power_domain: Add a driver to trigger TURN_ON/TURN_OFF actions

This driver triggers the TURN_ON and TURN_OFF actions for certain
power states. These power states are specified via device tree.

Signed-off-by: Mahesh Mahadevan <mahesh.mahadevan@nxp.com>
This commit is contained in:
Mahesh Mahadevan 2024-10-17 11:44:05 -05:00 committed by Benjamin Cabé
commit b579a90413
4 changed files with 150 additions and 0 deletions

View file

@ -7,3 +7,4 @@ 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_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_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_NXP_SCU power_domain_nxp_scu.c)
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_SOC_PM_STATE power_domain_soc_state_change.c)

View file

@ -90,4 +90,13 @@ config POWER_DOMAIN_NXP_SCU_INIT_PRIORITY
endif #POWER_DOMAIN_NXP_SCU endif #POWER_DOMAIN_NXP_SCU
config POWER_DOMAIN_SOC_PM_STATE
bool "SoC PM state power domain"
default y
depends on DT_HAS_POWER_DOMAIN_SOC_STATE_CHANGE_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 endif

View file

@ -0,0 +1,117 @@
/*
* Copyright 2024-25 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_soc_state_change, CONFIG_POWER_DOMAIN_LOG_LEVEL);
/* Indicates the end of the onoff_power_states array */
#define POWER_DOMAIN_DEVICE_ONOFF_STATE_MARKER 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:
LOG_DBG("%s: resuming", dev->name);
while (config->onoff_power_states[i] != POWER_DOMAIN_DEVICE_ONOFF_STATE_MARKER) {
/* 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:
LOG_DBG("%s: suspending", dev->name);
while (config->onoff_power_states[i] != POWER_DOMAIN_DEVICE_ONOFF_STATE_MARKER) {
/* 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_soc_state_change
#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(id, node_id) \
uint8_t onoff_states_##id[] = { \
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_MARKER \
};
#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)

View file

@ -0,0 +1,23 @@
# Copyright 2024-25 NXP
# SPDX-License-Identifier: Apache-2.0
description: |
This power domain will Turn On and Off devices when transitioning
in and out a specified Power State. Power States that trigger this
action is specified via the onoff-power-states property.
compatible: "power-domain-soc-state-change"
include: power-domain.yaml
properties:
onoff-power-states:
type: phandles
description: |
List of power management states.
The registered devices will be turned off before the PM subsystem
enters these PM states. The devices will be turned on after the
PM subsystem exits these PM states.
"#power-domain-cells":
const: 0