Skip to content

Commit a6b0bad

Browse files
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>
1 parent b3ccee4 commit a6b0bad

File tree

5 files changed

+367
-0
lines changed

5 files changed

+367
-0
lines changed

drivers/power_domain/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO power_domain_gpio.c)
77
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_GPIO_MONITOR power_domain_gpio_monitor.c)
88
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_INTEL_ADSP power_domain_intel_adsp.c)
99
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_NXP_SCU power_domain_nxp_scu.c)
10+
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_NRFS_GDPWR power_domain_nrfs_gdpwr.c)
1011
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_SOC_PM_STATE power_domain_soc_state_change.c)
1112
zephyr_library_sources_ifdef(CONFIG_POWER_DOMAIN_TISCI power_domain_tisci.c)

drivers/power_domain/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,6 @@ config SOC_POWER_DOMAIN_INIT
123123

124124
endif #POWER_DOMAIN_TISCI
125125

126+
rsource "Kconfig.nrfs_gdpwr"
127+
126128
endif
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config POWER_DOMAIN_NRFS_GDPWR
5+
bool "NRFS Global Domain Power Request driver"
6+
depends on DT_HAS_NORDIC_NRFS_GDPWR_ENABLED
7+
select NRFS
8+
select NRFS_GDPWR_SERVICE_ENABLED
9+
default y
10+
11+
if POWER_DOMAIN_NRFS_GDPWR
12+
13+
config POWER_DOMAIN_NRFS_GDPWR_TIMEOUT_MS
14+
int "GDPWR request timeout in milliseconds"
15+
default 500
16+
17+
endif # POWER_DOMAIN_NRFS_GDPWR
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT nordic_nrfs_gdpwr
8+
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/device.h>
11+
#include <zephyr/pm/device.h>
12+
#include <zephyr/logging/log.h>
13+
14+
#include <nrfs_gdpwr.h>
15+
#include <nrfs_backend_ipc_service.h>
16+
17+
LOG_MODULE_REGISTER(nrfs_gdpwr, CONFIG_POWER_DOMAIN_LOG_LEVEL);
18+
19+
#define MANAGER_REQUEST_TIMEOUT K_MSEC(CONFIG_POWER_DOMAIN_NRFS_GDPWR_TIMEOUT_MS)
20+
21+
static K_SEM_DEFINE(lock_sem, 1, 1);
22+
static K_SEM_DEFINE(req_sem, 0, 1);
23+
static nrfs_gdpwr_evt_type_t req_resp;
24+
static const struct device *const domains[] = {
25+
DT_INST_FOREACH_CHILD_SEP(0, DEVICE_DT_GET, (,))
26+
};
27+
28+
struct domain_data {
29+
bool off;
30+
bool synced;
31+
};
32+
33+
struct domain_config {
34+
gdpwr_power_domain_t domain;
35+
};
36+
37+
static void manager_event_handler(nrfs_gdpwr_evt_t const *evt, void *context)
38+
{
39+
ARG_UNUSED(context);
40+
41+
req_resp = evt->type;
42+
k_sem_give(&req_sem);
43+
}
44+
45+
static void manager_lock(void)
46+
{
47+
if (k_is_pre_kernel()) {
48+
return;
49+
}
50+
51+
(void)k_sem_take(&lock_sem, K_FOREVER);
52+
}
53+
54+
static void manager_unlock(void)
55+
{
56+
if (k_is_pre_kernel()) {
57+
return;
58+
}
59+
60+
k_sem_give(&lock_sem);
61+
}
62+
63+
static int manager_set_domain_locked(gdpwr_power_domain_t domain, bool on)
64+
{
65+
nrfs_err_t err;
66+
gdpwr_request_type_t req = on ? GDPWR_POWER_REQUEST_SET : GDPWR_POWER_REQUEST_CLEAR;
67+
int ret;
68+
69+
err = nrfs_gdpwr_power_request(domain, req, NULL);
70+
if (err != NRFS_SUCCESS) {
71+
LOG_ERR("%s %s", "nrfs gdpwr request", "failed");
72+
return -EIO;
73+
}
74+
75+
ret = k_sem_take(&req_sem, MANAGER_REQUEST_TIMEOUT);
76+
if (ret < 0) {
77+
LOG_ERR("%s %s", "nrfs gdpwr request", "timed out");
78+
return -ETIMEDOUT;
79+
}
80+
81+
if (req_resp != NRFS_GDPWR_REQ_APPLIED) {
82+
LOG_ERR("%s %s", "nrfs gdpwr request", "rejected");
83+
return -EIO;
84+
}
85+
86+
return 0;
87+
}
88+
89+
static int manager_set_domain(const struct device *dev, bool on)
90+
{
91+
struct domain_data *dev_data = dev->data;
92+
const struct domain_config *dev_config = dev->config;
93+
int ret;
94+
95+
manager_lock();
96+
97+
if (dev_data->synced) {
98+
/* NRFS GDPWR service is ready so we request domain change state */
99+
ret = manager_set_domain_locked(dev_config->domain, on);
100+
} else {
101+
/*
102+
* NRFS GDPWR service is not ready so we track what the expected
103+
* state of the power domain to be requested once the service
104+
* is ready.
105+
*/
106+
ret = 0;
107+
dev_data->off = !on;
108+
}
109+
110+
if (ret == 0) {
111+
LOG_DBG("domain %s %ssynced and %s",
112+
dev->name,
113+
dev_data->synced ? "" : "un",
114+
on ? "on" : "off");
115+
}
116+
117+
manager_unlock();
118+
return ret;
119+
}
120+
121+
static int manager_sync_domain_locked(const struct device *dev)
122+
{
123+
struct domain_data *dev_data = dev->data;
124+
const struct domain_config *dev_config = dev->config;
125+
126+
/*
127+
* NRFS service is now ready. We will now synchronize the state
128+
* of the power domain with the expected state we tracked with
129+
* the struct domain_data off member. Following this, tracking
130+
* the power domain state is handled by device PM, thus the
131+
* struct domain_data off is no longer used.
132+
*/
133+
dev_data->synced = true;
134+
135+
/*
136+
* Power domains initialize ON so we only need to send a request
137+
* if the expected state of the power domain is OFF.
138+
*/
139+
if (dev_data->off) {
140+
return manager_set_domain_locked(dev_config->domain, false);
141+
}
142+
143+
return 0;
144+
}
145+
146+
static int manager_sync_domains_locked(void)
147+
{
148+
int ret;
149+
150+
ARRAY_FOR_EACH(domains, i) {
151+
ret = manager_sync_domain_locked(domains[i]);
152+
if (ret) {
153+
break;
154+
}
155+
}
156+
157+
return ret;
158+
}
159+
160+
static int manager_init(void)
161+
{
162+
nrfs_err_t err;
163+
int ret;
164+
165+
err = nrfs_backend_wait_for_connection(K_FOREVER);
166+
if (err != NRFS_SUCCESS) {
167+
LOG_ERR("%s %s", "nrfs backend connection", "failed");
168+
return -EIO;
169+
}
170+
171+
err = nrfs_gdpwr_init(manager_event_handler);
172+
if (err != NRFS_SUCCESS) {
173+
LOG_ERR("%s %s", "nrfs gdpwr init", "failed");
174+
return -EIO;
175+
}
176+
177+
manager_lock();
178+
ret = manager_sync_domains_locked();
179+
manager_unlock();
180+
return ret;
181+
}
182+
183+
SYS_INIT(manager_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
184+
185+
#if IS_ENABLED(CONFIG_DEVICE_DEPS) && IS_ENABLED(CONFIG_PM_DEVICE_POWER_DOMAIN)
186+
static void domain_pm_notify_children(const struct device *dev,
187+
enum pm_device_action action)
188+
{
189+
pm_device_children_action_run(dev, action, NULL);
190+
}
191+
#else
192+
static void domain_pm_notify_children(const struct device *dev,
193+
enum pm_device_action action)
194+
{
195+
ARG_UNUSED(dev);
196+
ARG_UNUSED(action);
197+
}
198+
#endif
199+
200+
static int domain_pm_suspend(const struct device *dev)
201+
{
202+
int ret;
203+
204+
domain_pm_notify_children(dev, PM_DEVICE_ACTION_TURN_OFF);
205+
206+
ret = manager_set_domain(dev, false);
207+
if (ret) {
208+
domain_pm_notify_children(dev, PM_DEVICE_ACTION_TURN_ON);
209+
}
210+
211+
return ret;
212+
}
213+
214+
static int domain_pm_resume(const struct device *dev)
215+
{
216+
int ret;
217+
218+
ret = manager_set_domain(dev, true);
219+
if (ret == 0) {
220+
domain_pm_notify_children(dev, PM_DEVICE_ACTION_TURN_ON);
221+
}
222+
223+
return ret;
224+
}
225+
226+
static int domain_pm_action(const struct device *dev, enum pm_device_action action)
227+
{
228+
int ret;
229+
230+
switch (action) {
231+
case PM_DEVICE_ACTION_SUSPEND:
232+
ret = domain_pm_suspend(dev);
233+
break;
234+
235+
case PM_DEVICE_ACTION_RESUME:
236+
ret = domain_pm_resume(dev);
237+
break;
238+
239+
case PM_DEVICE_ACTION_TURN_OFF:
240+
case PM_DEVICE_ACTION_TURN_ON:
241+
ret = -ENOTSUP;
242+
break;
243+
244+
default:
245+
ret = -EINVAL;
246+
break;
247+
}
248+
249+
return ret;
250+
}
251+
252+
static int domain_init(const struct device *dev)
253+
{
254+
return pm_device_driver_init(dev, domain_pm_action);
255+
}
256+
257+
#define DOMAIN_NODE_SYMNAME(node, sym) \
258+
_CONCAT_4(domain, _, sym, DT_NODE_CHILD_IDX(node))
259+
260+
#define DOMAIN_NODE_TO_GDPWR_ENUM(node) \
261+
_CONCAT(GDPWR_GD_, DT_NODE_FULL_NAME_UPPER_TOKEN(node))
262+
263+
#define DOMAIN_DEFINE(node) \
264+
static struct domain_config DOMAIN_NODE_SYMNAME(node, data); \
265+
static const struct domain_config DOMAIN_NODE_SYMNAME(node, config) = { \
266+
.domain = DOMAIN_NODE_TO_GDPWR_ENUM(node), \
267+
}; \
268+
\
269+
PM_DEVICE_DT_DEFINE(node, domain_pm_action); \
270+
\
271+
DEVICE_DT_DEFINE( \
272+
node, \
273+
domain_init, \
274+
PM_DEVICE_DT_GET(node), \
275+
&DOMAIN_NODE_SYMNAME(node, data), \
276+
&DOMAIN_NODE_SYMNAME(node, config), \
277+
PRE_KERNEL_1, \
278+
0, \
279+
NULL \
280+
);
281+
282+
DT_INST_FOREACH_CHILD(0, DOMAIN_DEFINE)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright 2025 Nordic Semiconductor ASA
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: |
5+
Nordic NRFS Global Domain Power Request
6+
7+
The NRFS Global Domain Power Request service manages
8+
global power domains using NRFS.
9+
10+
Each child node represents a global power domain, mapped
11+
by name. The fast-active-0 child node is mapped to the
12+
FAST_ACTIVE_0 global power domain. The nodelabel of each
13+
child node is the node name prepended with "gdpwr",
14+
using underscores.
15+
16+
Example layout:
17+
18+
gdpwr {
19+
compatible = "nordic,nrfs-gdpwr";
20+
status = "disabled";
21+
22+
gdpwr_fast_active_0: fast-active-0 {
23+
#power-domain-cells = <0>;
24+
};
25+
26+
gdpwr_fast_active_1: fast-active-1 {
27+
#power-domain-cells = <0>;
28+
};
29+
30+
gdpwr_fast_main: fast-main {
31+
#power-domain-cells = <0>;
32+
};
33+
34+
gdpwr_slow_active: slow-active {
35+
#power-domain-cells = <0>;
36+
};
37+
38+
gdpwr_slow_main: slow-main {
39+
#power-domain-cells = <0>;
40+
};
41+
};
42+
43+
Example usage:
44+
45+
uart120: uart@8e6000 {
46+
compatible = "nordic,nrf-uarte";
47+
reg = <0x8e6000 0x1000>;
48+
status = "disabled";
49+
power-domains = <&gdpwr_fast_active_1>;
50+
};
51+
52+
compatible: "nordic,nrfs-gdpwr"
53+
54+
include: base.yaml
55+
56+
child-binding:
57+
description: Nordic NRFS Global Power Domain
58+
59+
properties:
60+
"#power-domain-cells":
61+
type: int
62+
const: 0
63+
64+
zephyr,pm-device-runtime-auto:
65+
type: boolean

0 commit comments

Comments
 (0)