Skip to content

Commit debce5e

Browse files
committed
drivers: nrf_ironside dvfs service
Added handling of new IRONside DVFS service. NRFS DVFS is now not enabled by default. Signed-off-by: Łukasz Stępnicki <[email protected]>
1 parent 9075d53 commit debce5e

File tree

5 files changed

+324
-2
lines changed

5 files changed

+324
-2
lines changed

drivers/firmware/nrf_ironside/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_CALL call.c)
88
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_BOOT_REPORT boot_report.c)
99
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_CPUCONF_SERVICE cpuconf.c)
1010
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_UPDATE_SERVICE update.c)
11+
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_DVFS_SERVICE ironside_dvfs.c)

drivers/firmware/nrf_ironside/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,21 @@ config NRF_IRONSIDE_BOOT_REPORT
5050
help
5151
Support for parsing the Boot Report populated by Nordic IRONside firmware.
5252

53+
config NRF_IRONSIDE_DVFS_SERVICE
54+
bool "IRONside DVFS service"
55+
depends on SOC_NRF54H20_CPUAPP
56+
select NRF_IRONSIDE_CALL
57+
help
58+
Service used to handle DVFS operating point requests.
59+
60+
if NRF_IRONSIDE_DVFS_SERVICE
61+
62+
config NRF_IRONSIDE_DVFS_OPPOINT_CHANGE_MUTEX_TIMEOUT_MS
63+
int "IRONSside DVFS change oppoint mutex timeout"
64+
default 100
65+
help
66+
Maximum tiemout when waiting for DVFS oppoint change mutex lock.
67+
68+
endif # NRF_IRONSIDE_DVFS_SERVICE
69+
5370
endmenu
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
#include <hal/nrf_hsfll.h>
6+
#include <zephyr/kernel.h>
7+
8+
#include <zephyr/drivers/firmware/nrf_ironside/ironside_dvfs.h>
9+
#include <zephyr/drivers/firmware/nrf_ironside/call.h>
10+
11+
static enum ironside_dvfs_oppoint current_dvfs_oppoint = IRONSIDE_DVFS_OPP_HIGH;
12+
13+
struct dvfs_hsfll_data_t {
14+
uint32_t new_f_mult;
15+
uint32_t new_f_trim_entry;
16+
uint32_t max_hsfll_freq;
17+
};
18+
19+
static const struct dvfs_hsfll_data_t dvfs_hsfll_data[] = {
20+
/* ABB oppoint 0.8V */
21+
{
22+
.new_f_mult = 20,
23+
.new_f_trim_entry = 0,
24+
.max_hsfll_freq = 320000000,
25+
},
26+
/* ABB oppoint 0.6V */
27+
{
28+
.new_f_mult = 8,
29+
.new_f_trim_entry = 2,
30+
.max_hsfll_freq = 128000000,
31+
},
32+
/* ABB oppoint 0.5V */
33+
{
34+
.new_f_mult = 4,
35+
.new_f_trim_entry = 3,
36+
.max_hsfll_freq = 64000000,
37+
},
38+
};
39+
40+
BUILD_ASSERT(ARRAY_SIZE(dvfs_hsfll_data) == (IRONSIDE_DVFS_OPPOINT_COUNT),
41+
"dvfs_hsfll_data size must match number of DVFS oppoints");
42+
43+
/**
44+
* @brief Check if the requested oppoint is allowed.
45+
*
46+
* @param freq_setting The requested oppoint to check.
47+
* @return true if the oppoint is allowed, false otherwise.
48+
*/
49+
static bool ironside_dvfs_opp_allowed(enum ironside_dvfs_oppoint freq_setting)
50+
{
51+
if (freq_setting == IRONSIDE_DVFS_OPP_HIGH || freq_setting == IRONSIDE_DVFS_OPP_MEDLOW ||
52+
freq_setting == IRONSIDE_DVFS_OPP_LOW) {
53+
return true;
54+
}
55+
56+
return false;
57+
}
58+
59+
/**
60+
* @brief Check if the requested oppoint change operation is downscaling.
61+
*
62+
* @param target_freq_setting The target oppoint to check.
63+
* @return true if the current oppoint is higher than the target, false otherwise.
64+
*/
65+
static bool ironside_dvfs_is_downscaling(enum ironside_dvfs_oppoint target_freq_setting)
66+
{
67+
return current_dvfs_oppoint < target_freq_setting;
68+
}
69+
70+
/**
71+
* @brief Configure hsfll depending on selected oppoint
72+
*
73+
* @param enum oppoint target operation point
74+
* @return 0 value indicates no error.
75+
* @return -EINVAL invalid oppoint or domain.
76+
*/
77+
static int32_t ironside_dvfs_configure_hsfll(enum ironside_dvfs_oppoint oppoint)
78+
{
79+
nrf_hsfll_trim_t hsfll_trim = {};
80+
uint8_t freq_trim_idx = dvfs_hsfll_data[oppoint].new_f_trim_entry;
81+
82+
#if defined(NRF_APPLICATION)
83+
hsfll_trim.vsup = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.VSUP;
84+
hsfll_trim.coarse = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.COARSE[freq_trim_idx];
85+
hsfll_trim.fine = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.FINE[freq_trim_idx];
86+
#if NRF_HSFLL_HAS_TCOEF_TRIM
87+
hsfll_trim.tcoef = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.TCOEF;
88+
#endif
89+
#else
90+
#error "Only application core is supported for DVFS"
91+
#endif
92+
93+
nrf_hsfll_clkctrl_mult_set(NRF_HSFLL, dvfs_hsfll_data[oppoint].new_f_mult);
94+
nrf_hsfll_trim_set(NRF_HSFLL, &hsfll_trim);
95+
nrf_barrier_w();
96+
97+
nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE);
98+
/* Trigger hsfll task one more time, SEE PAC-4078 */
99+
nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE);
100+
101+
return 0;
102+
}
103+
104+
/* Function handling steps for DVFS oppoint change. */
105+
static int ironside_dvfs_prepare_to_scale(enum ironside_dvfs_oppoint freq_setting)
106+
{
107+
int err = 0;
108+
109+
if (ironside_dvfs_is_downscaling(freq_setting)) {
110+
err = ironside_dvfs_configure_hsfll(freq_setting);
111+
}
112+
113+
return err;
114+
}
115+
116+
/* Update MDK variable which is used by nrfx_coredep_delay_us (k_busy_wait). */
117+
static void ironside_dvfs_update_core_clock(enum ironside_dvfs_oppoint oppoint_freq)
118+
{
119+
extern uint32_t SystemCoreClock;
120+
121+
SystemCoreClock = dvfs_hsfll_data[oppoint_freq].max_hsfll_freq;
122+
}
123+
124+
/* Perform scaling finnish procedure. */
125+
static int ironside_dvfs_change_oppoint_complete(enum ironside_dvfs_oppoint dvfs_oppoint)
126+
{
127+
int err = 0;
128+
129+
if (!ironside_dvfs_is_downscaling(dvfs_oppoint)) {
130+
err = ironside_dvfs_configure_hsfll(dvfs_oppoint);
131+
}
132+
133+
current_dvfs_oppoint = dvfs_oppoint;
134+
ironside_dvfs_update_core_clock(dvfs_oppoint);
135+
136+
return err;
137+
}
138+
139+
/**
140+
* @brief Check if ABB analog part is locked.
141+
*
142+
* @param abb Pointer to ABB peripheral.
143+
*
144+
* @return true if ABB is locked, false otherwise.
145+
*/
146+
static inline bool sdfw_dvfs_is_abb_locked(NRF_ABB_Type *abb)
147+
{
148+
/* Check if ABB analog part is locked. */
149+
return ((abb->STATUSANA & ABB_STATUSANA_LOCKED_Msk) != 0);
150+
}
151+
152+
/**
153+
* @brief Request DVFS oppoint change from IRONside secure domain.
154+
* This function will send a request over IPC to the IRONside secure domain
155+
* This function is synchronous and will return when the request is completed.
156+
*
157+
* @param oppoint @ref enum ironside_dvfs_oppoint
158+
* @return int
159+
*/
160+
static int ironside_dvfs_req_oppoint(enum ironside_dvfs_oppoint oppoint)
161+
{
162+
int err;
163+
164+
struct ironside_call_buf *const buf = ironside_call_alloc();
165+
166+
buf->id = IRONSIDE_CALL_ID_DVFS_SERVICE_V0;
167+
buf->args[IRONSIDE_DVFS_SERVICE_OPPOINT_IDX] = oppoint;
168+
169+
ironside_call_dispatch(buf);
170+
171+
if (buf->status == IRONSIDE_CALL_STATUS_RSP_SUCCESS) {
172+
err = buf->args[IRONSIDE_DVFS_SERVICE_RETCODE_IDX];
173+
} else {
174+
err = buf->status;
175+
}
176+
177+
ironside_call_release(buf);
178+
179+
return err;
180+
}
181+
182+
int ironside_dvfs_change_oppoint(enum ironside_dvfs_oppoint dvfs_oppoint)
183+
{
184+
int status = 0;
185+
186+
if (!ironside_dvfs_opp_allowed(dvfs_oppoint)) {
187+
return -IRONSIDE_DVFS_ERROR_WRONG_OPPOINT;
188+
}
189+
190+
if (!sdfw_dvfs_is_abb_locked(NRF_ABB)) {
191+
return -IRONSIDE_DVFS_ERROR_BUSY;
192+
}
193+
194+
if (dvfs_oppoint == current_dvfs_oppoint) {
195+
return status;
196+
}
197+
198+
if (k_is_in_isr()) {
199+
return -IRONSIDE_DVFS_ERROR_ISR_NOT_ALLOWED;
200+
}
201+
202+
ironside_dvfs_prepare_to_scale(dvfs_oppoint);
203+
204+
status = ironside_dvfs_req_oppoint(dvfs_oppoint);
205+
206+
if (status != 0) {
207+
return status;
208+
}
209+
status = ironside_dvfs_change_oppoint_complete(dvfs_oppoint);
210+
211+
return status;
212+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_IRONSIDE_DVFS_H_
7+
#define ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_IRONSIDE_DVFS_H_
8+
9+
#include <stdint.h>
10+
#include <stdbool.h>
11+
#include <stddef.h>
12+
#include <errno.h>
13+
14+
enum ironside_dvfs_oppoint {
15+
IRONSIDE_DVFS_OPP_HIGH = 0,
16+
IRONSIDE_DVFS_OPP_MEDLOW = 1,
17+
IRONSIDE_DVFS_OPP_LOW = 2
18+
};
19+
20+
/**
21+
* @brief Number of DVFS oppoints supported by IRONside.
22+
*
23+
* This is the number of different DVFS oppoints that can be set on IRONside.
24+
* The oppoints are defined in the `ironside_dvfs_oppoint` enum.
25+
*/
26+
#define IRONSIDE_DVFS_OPPOINT_COUNT (3)
27+
28+
/**
29+
* @name IRONside DVFS service error codes.
30+
* @{
31+
*/
32+
33+
/** The requested DVFS oppoint is not allowed. */
34+
#define IRONSIDE_DVFS_ERROR_WRONG_OPPOINT (1)
35+
/** Waiting for mutex lock timed out, or hardware is busy. */
36+
#define IRONSIDE_DVFS_ERROR_BUSY (2)
37+
/** There is configuration error in the DVFS service. */
38+
#define IRONSIDE_DVFS_ERROR_OPPOINT_DATA (3)
39+
/** The caller does not have permission to change the DVFS oppoint. */
40+
#define IRONSIDE_DVFS_ERROR_PERMISSION (4)
41+
/** The requested DVFS oppoint is already set, no change needed. */
42+
#define IRONSIDE_DVFS_ERROR_NO_CHANGE_NEEDED (5)
43+
/** The operation timed out, possibly due to a hardware issue. */
44+
#define IRONSIDE_DVFS_ERROR_TIMEOUT (6)
45+
/** The DVFS oppoint change operation is not allowed in the ISR context. */
46+
#define IRONSIDE_DVFS_ERROR_ISR_NOT_ALLOWED (7)
47+
48+
/**
49+
* @}
50+
*/
51+
52+
/* IRONside call identifiers with implicit versions.
53+
*
54+
* With the initial "version 0", the service ABI is allowed to break until the
55+
* first production release of IRONside SE.
56+
*/
57+
#define IRONSIDE_CALL_ID_DVFS_SERVICE_V0 3
58+
59+
/* Index of the DVFS oppoint within the service buffer. */
60+
#define IRONSIDE_DVFS_SERVICE_OPPOINT_IDX (0)
61+
/* Index of the return code within the service buffer. */
62+
#define IRONSIDE_DVFS_SERVICE_RETCODE_IDX (0)
63+
64+
/**
65+
* @brief Change the current DVFS oppoint.
66+
*
67+
* This function will request a change of the current DVFS oppoint to the
68+
* specified value. It will block until the change is applied.
69+
*
70+
* @param dvfs_oppoint The new DVFS oppoint to set.
71+
* @return int 0 on success, negative error code on failure.
72+
*/
73+
int ironside_dvfs_change_oppoint(enum ironside_dvfs_oppoint dvfs_oppoint);
74+
75+
/**
76+
* @brief Check if the given oppoint is valid.
77+
*
78+
* @param dvfs_oppoint The oppoint to check.
79+
* @return true if the oppoint is valid, false otherwise.
80+
*/
81+
static inline bool ironside_dvfs_is_oppoint_valid(enum ironside_dvfs_oppoint dvfs_oppoint)
82+
{
83+
if (dvfs_oppoint != IRONSIDE_DVFS_OPP_HIGH &&
84+
dvfs_oppoint != IRONSIDE_DVFS_OPP_MEDLOW &&
85+
dvfs_oppoint != IRONSIDE_DVFS_OPP_LOW) {
86+
return false;
87+
}
88+
89+
return true;
90+
}
91+
92+
#endif /* ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_IRONSIDE_DVFS_H_ */

modules/hal_nordic/nrfs/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ if(CONFIG_NRFS)
1414

1515
zephyr_include_directories(${INC_DIR})
1616
zephyr_include_directories(${INC_DIR}/services)
17-
zephyr_include_directories(${HELPERS_DIR})
17+
zephyr_include_directories_ifdef(CONFIG_NRFS_HAS_DVFS_SERVICE ${HELPERS_DIR})
1818
zephyr_include_directories(.)
1919
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/backends)
2020
zephyr_include_directories_ifdef(CONFIG_NRFS_DVFS_LOCAL_DOMAIN ${CMAKE_CURRENT_SOURCE_DIR}/dvfs)
2121

22-
zephyr_library_sources(${HELPERS_DIR}/dvfs_oppoint.c)
22+
zephyr_library_sources_ifdef(CONFIG_NRFS_HAS_DVFS_SERVICE ${HELPERS_DIR}/dvfs_oppoint.c)
2323

2424
if(CONFIG_NRFS_LOCAL_DOMAIN)
2525
zephyr_library_sources_ifdef(CONFIG_NRFS_AUDIOPLL_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_audiopll.c)

0 commit comments

Comments
 (0)