drivers: power_domain: introduce nrfs gdpwr
Introduce the NRFS GDPWR (Global Domain Power Request) device driver and devicetree binding. Signed-off-by: Bjarki Arge Andreasen <bjarki.andreasen@nordicsemi.no>
This commit is contained in:
parent
bb92d71a6b
commit
0ec81c5fdf
@ -7,5 +7,6 @@ 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_NRFS_GDPWR power_domain_nrfs_gdpwr.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_SOC_PM_STATE power_domain_soc_state_change.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_TISCI power_domain_tisci.c)
|
||||
|
||||
@ -123,4 +123,6 @@ config SOC_POWER_DOMAIN_INIT
|
||||
|
||||
endif #POWER_DOMAIN_TISCI
|
||||
|
||||
rsource "Kconfig.nrfs_gdpwr"
|
||||
|
||||
endif
|
||||
|
||||
17
drivers/power_domain/Kconfig.nrfs_gdpwr
Normal file
17
drivers/power_domain/Kconfig.nrfs_gdpwr
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright 2025 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config POWER_DOMAIN_NRFS_GDPWR
|
||||
bool "NRFS Global Domain Power Request driver"
|
||||
depends on DT_HAS_NORDIC_NRFS_GDPWR_ENABLED
|
||||
select NRFS
|
||||
select NRFS_GDPWR_SERVICE_ENABLED
|
||||
default y
|
||||
|
||||
if POWER_DOMAIN_NRFS_GDPWR
|
||||
|
||||
config POWER_DOMAIN_NRFS_GDPWR_TIMEOUT_MS
|
||||
int "GDPWR request timeout in milliseconds"
|
||||
default 500
|
||||
|
||||
endif # POWER_DOMAIN_NRFS_GDPWR
|
||||
282
drivers/power_domain/power_domain_nrfs_gdpwr.c
Normal file
282
drivers/power_domain/power_domain_nrfs_gdpwr.c
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT nordic_nrfs_gdpwr
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <nrfs_gdpwr.h>
|
||||
#include <nrfs_backend_ipc_service.h>
|
||||
|
||||
LOG_MODULE_REGISTER(nrfs_gdpwr, CONFIG_POWER_DOMAIN_LOG_LEVEL);
|
||||
|
||||
#define MANAGER_REQUEST_TIMEOUT K_MSEC(CONFIG_POWER_DOMAIN_NRFS_GDPWR_TIMEOUT_MS)
|
||||
|
||||
static K_SEM_DEFINE(lock_sem, 1, 1);
|
||||
static K_SEM_DEFINE(req_sem, 0, 1);
|
||||
static nrfs_gdpwr_evt_type_t req_resp;
|
||||
static const struct device *const domains[] = {
|
||||
DT_INST_FOREACH_CHILD_SEP(0, DEVICE_DT_GET, (,))
|
||||
};
|
||||
|
||||
struct domain_data {
|
||||
bool off;
|
||||
bool synced;
|
||||
};
|
||||
|
||||
struct domain_config {
|
||||
gdpwr_power_domain_t domain;
|
||||
};
|
||||
|
||||
static void manager_event_handler(nrfs_gdpwr_evt_t const *evt, void *context)
|
||||
{
|
||||
ARG_UNUSED(context);
|
||||
|
||||
req_resp = evt->type;
|
||||
k_sem_give(&req_sem);
|
||||
}
|
||||
|
||||
static void manager_lock(void)
|
||||
{
|
||||
if (k_is_pre_kernel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)k_sem_take(&lock_sem, K_FOREVER);
|
||||
}
|
||||
|
||||
static void manager_unlock(void)
|
||||
{
|
||||
if (k_is_pre_kernel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
k_sem_give(&lock_sem);
|
||||
}
|
||||
|
||||
static int manager_set_domain_locked(gdpwr_power_domain_t domain, bool on)
|
||||
{
|
||||
nrfs_err_t err;
|
||||
gdpwr_request_type_t req = on ? GDPWR_POWER_REQUEST_SET : GDPWR_POWER_REQUEST_CLEAR;
|
||||
int ret;
|
||||
|
||||
err = nrfs_gdpwr_power_request(domain, req, NULL);
|
||||
if (err != NRFS_SUCCESS) {
|
||||
LOG_ERR("%s %s", "nrfs gdpwr request", "failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = k_sem_take(&req_sem, MANAGER_REQUEST_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("%s %s", "nrfs gdpwr request", "timed out");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (req_resp != NRFS_GDPWR_REQ_APPLIED) {
|
||||
LOG_ERR("%s %s", "nrfs gdpwr request", "rejected");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_set_domain(const struct device *dev, bool on)
|
||||
{
|
||||
struct domain_data *dev_data = dev->data;
|
||||
const struct domain_config *dev_config = dev->config;
|
||||
int ret;
|
||||
|
||||
manager_lock();
|
||||
|
||||
if (dev_data->synced) {
|
||||
/* NRFS GDPWR service is ready so we request domain change state */
|
||||
ret = manager_set_domain_locked(dev_config->domain, on);
|
||||
} else {
|
||||
/*
|
||||
* NRFS GDPWR service is not ready so we track what the expected
|
||||
* state of the power domain to be requested once the service
|
||||
* is ready.
|
||||
*/
|
||||
ret = 0;
|
||||
dev_data->off = !on;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
LOG_DBG("domain %s %ssynced and %s",
|
||||
dev->name,
|
||||
dev_data->synced ? "" : "un",
|
||||
on ? "on" : "off");
|
||||
}
|
||||
|
||||
manager_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int manager_sync_domain_locked(const struct device *dev)
|
||||
{
|
||||
struct domain_data *dev_data = dev->data;
|
||||
const struct domain_config *dev_config = dev->config;
|
||||
|
||||
/*
|
||||
* NRFS service is now ready. We will now synchronize the state
|
||||
* of the power domain with the expected state we tracked with
|
||||
* the struct domain_data off member. Following this, tracking
|
||||
* the power domain state is handled by device PM, thus the
|
||||
* struct domain_data off is no longer used.
|
||||
*/
|
||||
dev_data->synced = true;
|
||||
|
||||
/*
|
||||
* Power domains initialize ON so we only need to send a request
|
||||
* if the expected state of the power domain is OFF.
|
||||
*/
|
||||
if (dev_data->off) {
|
||||
return manager_set_domain_locked(dev_config->domain, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_sync_domains_locked(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ARRAY_FOR_EACH(domains, i) {
|
||||
ret = manager_sync_domain_locked(domains[i]);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int manager_init(void)
|
||||
{
|
||||
nrfs_err_t err;
|
||||
int ret;
|
||||
|
||||
err = nrfs_backend_wait_for_connection(K_FOREVER);
|
||||
if (err != NRFS_SUCCESS) {
|
||||
LOG_ERR("%s %s", "nrfs backend connection", "failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = nrfs_gdpwr_init(manager_event_handler);
|
||||
if (err != NRFS_SUCCESS) {
|
||||
LOG_ERR("%s %s", "nrfs gdpwr init", "failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
manager_lock();
|
||||
ret = manager_sync_domains_locked();
|
||||
manager_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
SYS_INIT(manager_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEVICE_DEPS) && IS_ENABLED(CONFIG_PM_DEVICE_POWER_DOMAIN)
|
||||
static void domain_pm_notify_children(const struct device *dev,
|
||||
enum pm_device_action action)
|
||||
{
|
||||
pm_device_children_action_run(dev, action, NULL);
|
||||
}
|
||||
#else
|
||||
static void domain_pm_notify_children(const struct device *dev,
|
||||
enum pm_device_action action)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
ARG_UNUSED(action);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int domain_pm_suspend(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
domain_pm_notify_children(dev, PM_DEVICE_ACTION_TURN_OFF);
|
||||
|
||||
ret = manager_set_domain(dev, false);
|
||||
if (ret) {
|
||||
domain_pm_notify_children(dev, PM_DEVICE_ACTION_TURN_ON);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int domain_pm_resume(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = manager_set_domain(dev, true);
|
||||
if (ret == 0) {
|
||||
domain_pm_notify_children(dev, PM_DEVICE_ACTION_TURN_ON);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int domain_pm_action(const struct device *dev, enum pm_device_action action)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
ret = domain_pm_suspend(dev);
|
||||
break;
|
||||
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
ret = domain_pm_resume(dev);
|
||||
break;
|
||||
|
||||
case PM_DEVICE_ACTION_TURN_OFF:
|
||||
case PM_DEVICE_ACTION_TURN_ON:
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int domain_init(const struct device *dev)
|
||||
{
|
||||
return pm_device_driver_init(dev, domain_pm_action);
|
||||
}
|
||||
|
||||
#define DOMAIN_NODE_SYMNAME(node, sym) \
|
||||
_CONCAT_4(domain, _, sym, DT_NODE_CHILD_IDX(node))
|
||||
|
||||
#define DOMAIN_NODE_TO_GDPWR_ENUM(node) \
|
||||
_CONCAT(GDPWR_GD_, DT_NODE_FULL_NAME_UPPER_TOKEN(node))
|
||||
|
||||
#define DOMAIN_DEFINE(node) \
|
||||
static struct domain_config DOMAIN_NODE_SYMNAME(node, data); \
|
||||
static const struct domain_config DOMAIN_NODE_SYMNAME(node, config) = { \
|
||||
.domain = DOMAIN_NODE_TO_GDPWR_ENUM(node), \
|
||||
}; \
|
||||
\
|
||||
PM_DEVICE_DT_DEFINE(node, domain_pm_action); \
|
||||
\
|
||||
DEVICE_DT_DEFINE( \
|
||||
node, \
|
||||
domain_init, \
|
||||
PM_DEVICE_DT_GET(node), \
|
||||
&DOMAIN_NODE_SYMNAME(node, data), \
|
||||
&DOMAIN_NODE_SYMNAME(node, config), \
|
||||
PRE_KERNEL_1, \
|
||||
0, \
|
||||
NULL \
|
||||
);
|
||||
|
||||
DT_INST_FOREACH_CHILD(0, DOMAIN_DEFINE)
|
||||
65
dts/bindings/power-domain/nordic,nrfs-gdpwr.yaml
Normal file
65
dts/bindings/power-domain/nordic,nrfs-gdpwr.yaml
Normal file
@ -0,0 +1,65 @@
|
||||
# Copyright 2025 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
Nordic NRFS Global Domain Power Request
|
||||
|
||||
The NRFS Global Domain Power Request service manages
|
||||
global power domains using NRFS.
|
||||
|
||||
Each child node represents a global power domain, mapped
|
||||
by name. The fast-active-0 child node is mapped to the
|
||||
FAST_ACTIVE_0 global power domain. The nodelabel of each
|
||||
child node is the node name prepended with "gdpwr",
|
||||
using underscores.
|
||||
|
||||
Example layout:
|
||||
|
||||
gdpwr {
|
||||
compatible = "nordic,nrfs-gdpwr";
|
||||
status = "disabled";
|
||||
|
||||
gdpwr_fast_active_0: fast-active-0 {
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
|
||||
gdpwr_fast_active_1: fast-active-1 {
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
|
||||
gdpwr_fast_main: fast-main {
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
|
||||
gdpwr_slow_active: slow-active {
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
|
||||
gdpwr_slow_main: slow-main {
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
Example usage:
|
||||
|
||||
uart120: uart@8e6000 {
|
||||
compatible = "nordic,nrf-uarte";
|
||||
reg = <0x8e6000 0x1000>;
|
||||
status = "disabled";
|
||||
power-domains = <&gdpwr_fast_active_1>;
|
||||
};
|
||||
|
||||
compatible: "nordic,nrfs-gdpwr"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
child-binding:
|
||||
description: Nordic NRFS Global Power Domain
|
||||
|
||||
properties:
|
||||
"#power-domain-cells":
|
||||
type: int
|
||||
const: 0
|
||||
|
||||
zephyr,pm-device-runtime-auto:
|
||||
type: boolean
|
||||
Loading…
Reference in New Issue
Block a user