Skip to content

Commit a077773

Browse files
lstnlkartben
authored andcommitted
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 <lukasz.stepnicki@nordicsemi.no>
1 parent 3ebae00 commit a077773

File tree

5 files changed

+296
-2
lines changed

5 files changed

+296
-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 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

drivers/firmware/nrf_ironside/dvfs.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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/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 change operation is downscaling.
45+
*
46+
* @param target_freq_setting The target oppoint to check.
47+
* @return true if the current oppoint is higher than the target, false otherwise.
48+
*/
49+
static bool ironside_dvfs_is_downscaling(enum ironside_dvfs_oppoint target_freq_setting)
50+
{
51+
return current_dvfs_oppoint < target_freq_setting;
52+
}
53+
54+
/**
55+
* @brief Configure hsfll depending on selected oppoint
56+
*
57+
* @param enum oppoint target operation point
58+
*/
59+
static void ironside_dvfs_configure_hsfll(enum ironside_dvfs_oppoint oppoint)
60+
{
61+
nrf_hsfll_trim_t hsfll_trim = {};
62+
uint8_t freq_trim_idx = dvfs_hsfll_data[oppoint].new_f_trim_entry;
63+
64+
#if defined(NRF_APPLICATION)
65+
hsfll_trim.vsup = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.VSUP;
66+
hsfll_trim.coarse = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.COARSE[freq_trim_idx];
67+
hsfll_trim.fine = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.FINE[freq_trim_idx];
68+
#if NRF_HSFLL_HAS_TCOEF_TRIM
69+
hsfll_trim.tcoef = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.TCOEF;
70+
#endif
71+
#else
72+
#error "Only application core is supported for DVFS"
73+
#endif
74+
75+
nrf_hsfll_clkctrl_mult_set(NRF_HSFLL, dvfs_hsfll_data[oppoint].new_f_mult);
76+
nrf_hsfll_trim_set(NRF_HSFLL, &hsfll_trim);
77+
nrf_barrier_w();
78+
79+
nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE);
80+
/* Trigger hsfll task one more time, SEE PAC-4078 */
81+
nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE);
82+
}
83+
84+
/* Function handling steps for DVFS oppoint change. */
85+
static void ironside_dvfs_prepare_to_scale(enum ironside_dvfs_oppoint dvfs_oppoint)
86+
{
87+
if (ironside_dvfs_is_downscaling(dvfs_oppoint)) {
88+
ironside_dvfs_configure_hsfll(dvfs_oppoint);
89+
}
90+
}
91+
92+
/* Update MDK variable which is used by nrfx_coredep_delay_us (k_busy_wait). */
93+
static void ironside_dvfs_update_core_clock(enum ironside_dvfs_oppoint dvfs_oppoint)
94+
{
95+
extern uint32_t SystemCoreClock;
96+
97+
SystemCoreClock = dvfs_hsfll_data[dvfs_oppoint].max_hsfll_freq;
98+
}
99+
100+
/* Perform scaling finnish procedure. */
101+
static void ironside_dvfs_change_oppoint_complete(enum ironside_dvfs_oppoint dvfs_oppoint)
102+
{
103+
if (!ironside_dvfs_is_downscaling(dvfs_oppoint)) {
104+
ironside_dvfs_configure_hsfll(dvfs_oppoint);
105+
}
106+
107+
current_dvfs_oppoint = dvfs_oppoint;
108+
ironside_dvfs_update_core_clock(dvfs_oppoint);
109+
}
110+
111+
/**
112+
* @brief Check if ABB analog part is locked.
113+
*
114+
* @param abb Pointer to ABB peripheral.
115+
*
116+
* @return true if ABB is locked, false otherwise.
117+
*/
118+
static inline bool ironside_dvfs_is_abb_locked(NRF_ABB_Type *abb)
119+
{
120+
/* Check if ABB analog part is locked. */
121+
return ((abb->STATUSANA & ABB_STATUSANA_LOCKED_Msk) != 0);
122+
}
123+
124+
/**
125+
* @brief Request DVFS oppoint change from IRONside secure domain.
126+
* This function will send a request over IPC to the IRONside secure domain
127+
* This function is synchronous and will return when the request is completed.
128+
*
129+
* @param oppoint @ref enum ironside_dvfs_oppoint
130+
* @return int
131+
*/
132+
static int ironside_dvfs_req_oppoint(enum ironside_dvfs_oppoint oppoint)
133+
{
134+
int err;
135+
136+
struct ironside_call_buf *const buf = ironside_call_alloc();
137+
138+
buf->id = IRONSIDE_CALL_ID_DVFS_SERVICE_V0;
139+
buf->args[IRONSIDE_DVFS_SERVICE_OPPOINT_IDX] = oppoint;
140+
141+
ironside_call_dispatch(buf);
142+
143+
if (buf->status == IRONSIDE_CALL_STATUS_RSP_SUCCESS) {
144+
err = buf->args[IRONSIDE_DVFS_SERVICE_RETCODE_IDX];
145+
} else {
146+
err = buf->status;
147+
}
148+
149+
ironside_call_release(buf);
150+
151+
return err;
152+
}
153+
154+
int ironside_dvfs_change_oppoint(enum ironside_dvfs_oppoint dvfs_oppoint)
155+
{
156+
int status = 0;
157+
158+
if (!ironside_dvfs_is_oppoint_valid(dvfs_oppoint)) {
159+
return -IRONSIDE_DVFS_ERROR_WRONG_OPPOINT;
160+
}
161+
162+
if (!ironside_dvfs_is_abb_locked(NRF_ABB)) {
163+
return -IRONSIDE_DVFS_ERROR_BUSY;
164+
}
165+
166+
if (dvfs_oppoint == current_dvfs_oppoint) {
167+
return status;
168+
}
169+
170+
if (k_is_in_isr()) {
171+
return -IRONSIDE_DVFS_ERROR_ISR_NOT_ALLOWED;
172+
}
173+
174+
ironside_dvfs_prepare_to_scale(dvfs_oppoint);
175+
176+
status = ironside_dvfs_req_oppoint(dvfs_oppoint);
177+
178+
if (status != 0) {
179+
return status;
180+
}
181+
ironside_dvfs_change_oppoint_complete(dvfs_oppoint);
182+
183+
return status;
184+
}
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_DVFS_H_
7+
#define ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_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_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)