1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "ld_dvfs.h"
8
9 #include <hal/nrf_hsfll.h>
10 #include <hal/nrf_ramc.h>
11
12 #include <zephyr/kernel.h>
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(LD_DVFS_LIB, CONFIG_LOCAL_DOMAIN_DVFS_LIB_LOG_LEVEL);
15
16 #define TRANSIENT_ZBB_ABB_SLOT 0
17 #define CURR_TARG_ABB_SLOT 1
18 #define LD_ABB_CLR_ZBB 0
19 /* TODO: this values needs to be provided by HW team */
20 /* for now reset value will be used */
21 #define LD_ABB_CTRL4_NORMAL_OPERATION 0x10800UL
22 #define LD_ABB_CTRL4_TRANSITION_OPERATION 0x10800UL
23
24 /*
25 * wait max 500ms with 10us intervals for hsfll freq change event
26 */
27 #define HSFLL_FREQ_CHANGE_MAX_DELAY_MS 500UL
28 #define HSFLL_FREQ_CHANGE_CHECK_INTERVAL_US 10
29 #define HSFLL_FREQ_CHANGE_CHECK_MAX_ATTEMPTS \
30 ((HSFLL_FREQ_CHANGE_MAX_DELAY_MS) * (USEC_PER_MSEC) / (HSFLL_FREQ_CHANGE_CHECK_INTERVAL_US))
31
32 #define ABB_STATUS_CHANGE_MAX_DELAY_MS 5000UL
33 #define ABB_STATUS_CHANGE_CHECK_INTERVAL_US 10
34 #define ABB_STATUS_CHANGE_CHECK_MAX_ATTEMPTS \
35 ((ABB_STATUS_CHANGE_MAX_DELAY_MS) * (USEC_PER_MSEC) / (ABB_STATUS_CHANGE_CHECK_INTERVAL_US))
36
ld_dvfs_init(void)37 void ld_dvfs_init(void)
38 {
39 #if defined(NRF_SECURE)
40
41 const struct dvfs_oppoint_data *opp_data = get_dvfs_oppoint_data(DVFS_FREQ_HIGH);
42
43 #if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
44 LOG_DBG("%s", __func__);
45 LOG_DBG("REGW: NRF_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
46 CURR_TARG_ABB_SLOT,
47 (uint32_t)&NRF_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT],
48 opp_data->abb_ringo);
49 LOG_DBG("REGW: NRF_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
50 CURR_TARG_ABB_SLOT,
51 (uint32_t)&NRF_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT],
52 opp_data->abb_lockrange);
53 LOG_DBG("REGW: NRF_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
54 CURR_TARG_ABB_SLOT,
55 (uint32_t)&NRF_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT],
56 opp_data->abb_pvtmoncycles);
57
58 /*For app core.*/
59 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
60 CURR_TARG_ABB_SLOT,
61 (uint32_t)&NRF_APPLICATION_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT],
62 opp_data->abb_ringo);
63 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
64 CURR_TARG_ABB_SLOT,
65 (uint32_t)&NRF_APPLICATION_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT],
66 opp_data->abb_lockrange);
67 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
68 CURR_TARG_ABB_SLOT,
69 (uint32_t)&NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT],
70 opp_data->abb_pvtmoncycles);
71 #else
72 /* TODO: Change to NRFX Hal function when available. */
73 NRF_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT] = opp_data->abb_ringo;
74 NRF_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT] = opp_data->abb_lockrange;
75 NRF_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT] = opp_data->abb_pvtmoncycles;
76
77 NRF_APPLICATION_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT] = opp_data->abb_ringo;
78 NRF_APPLICATION_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT] = opp_data->abb_lockrange;
79 NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT] = opp_data->abb_pvtmoncycles;
80 #endif
81 #endif
82 }
83
ld_dvfs_clear_zbb(void)84 void ld_dvfs_clear_zbb(void)
85 {
86 #if defined(NRF_SECURE)
87 #if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
88 LOG_DBG("%s", __func__);
89 LOG_DBG("REGW: NRF_ABB->CONFIG.CTRL1.MODE 0x%x, V: 0x%x",
90 (uint32_t)&NRF_ABB->CONFIG.CTRL1,
91 LD_ABB_CLR_ZBB);
92 #else
93 /* TODO: Change to NRFX Hal function when available. */
94 NRF_ABB->CONFIG.CTRL1 &= ~(ABB_CONFIG_CTRL1_MODE_Msk);
95 NRF_APPLICATION_ABB->CONFIG.CTRL1 &= ~(ABB_CONFIG_CTRL1_MODE_Msk);
96 #endif
97 #endif
98 }
99
100 #if defined(NRF_SECURE)
101
102 #define DOWNSCALE_SAFETY_TIMEOUT (K_USEC(CONFIG_NRFS_LOCAL_DOMAIN_DOWNSCALE_SAFETY_TIMEOUT_US))
103
104 atomic_t increased_power_consumption;
105
106 /**
107 * @brief Secure domain needs to check if downscale is done in defined time
108 * window. This is needed to avoid battery drain if dvfs procedure
109 * takes to much time (some failure?).
110 */
ld_dvfs_secure_downscale_timeout(struct k_timer * timer)111 __weak void ld_dvfs_secure_downscale_timeout(struct k_timer *timer)
112 {
113 ARG_UNUSED(timer);
114
115 LOG_ERR("Downscale timeout expired, reset board.");
116 atomic_set(&increased_power_consumption, 0);
117 }
118
119 K_TIMER_DEFINE(dvfs_downscale_secure_timer, ld_dvfs_secure_downscale_timeout, NULL);
120
121 /**
122 * @brief Secure domain starts increased power consumption, needed by dvfs sequence.
123 * This function can be reimplemented in other module if needed.
124 */
ld_dvfs_secure_start_increased_power_consumption(void)125 __weak void ld_dvfs_secure_start_increased_power_consumption(void)
126 {
127 LOG_DBG("Start increased power consumption for DVFS sequence and start safety timer.");
128 k_timer_start(&dvfs_downscale_secure_timer, DOWNSCALE_SAFETY_TIMEOUT, K_NO_WAIT);
129 atomic_set(&increased_power_consumption, 1);
130
131 volatile uint8_t idle_counter = 0;
132
133 while (atomic_get(&increased_power_consumption)) {
134 if (idle_counter < 100) {
135 k_yield();
136 idle_counter++;
137 } else {
138 idle_counter = 0;
139 k_usleep(1);
140 }
141 }
142 }
143
144 /**
145 * @brief Secure domain stops increased power consumption at the end of downscale.
146 * This function can be reimplemented in other module if needed.
147 */
ld_dvfs_secure_stop_increased_power_consumption(void)148 __weak void ld_dvfs_secure_stop_increased_power_consumption(void)
149 {
150 LOG_DBG("Stop increased power consumption for DVFS sequence.");
151 k_timer_stop(&dvfs_downscale_secure_timer);
152 atomic_set(&increased_power_consumption, 0);
153 }
154
155 #endif
156
ld_dvfs_configure_abb_for_transition(enum dvfs_frequency_setting transient_opp,enum dvfs_frequency_setting curr_targ_opp)157 void ld_dvfs_configure_abb_for_transition(enum dvfs_frequency_setting transient_opp,
158 enum dvfs_frequency_setting curr_targ_opp)
159 {
160 #if defined(NRF_SECURE)
161 const struct dvfs_oppoint_data *opp_data = get_dvfs_oppoint_data(transient_opp);
162
163 #if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
164 LOG_DBG("%s", __func__);
165 LOG_DBG("transient_opp: %d, curr_targ_opp: %d", transient_opp, curr_targ_opp);
166 LOG_DBG("REGW: NRF_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
167 TRANSIENT_ZBB_ABB_SLOT,
168 (uint32_t)&NRF_ABB->TRIM.RINGO[TRANSIENT_ZBB_ABB_SLOT],
169 opp_data->abb_ringo);
170 LOG_DBG("REGW: NRF_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
171 TRANSIENT_ZBB_ABB_SLOT,
172 (uint32_t)&NRF_ABB->TRIM.LOCKRANGE[TRANSIENT_ZBB_ABB_SLOT],
173 opp_data->abb_lockrange);
174 LOG_DBG("REGW: NRF_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
175 TRANSIENT_ZBB_ABB_SLOT,
176 (uint32_t)&NRF_ABB->TRIM.PVTMONCYCLES[TRANSIENT_ZBB_ABB_SLOT],
177 opp_data->abb_pvtmoncycles);
178
179 /* For app core.*/
180 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
181 TRANSIENT_ZBB_ABB_SLOT,
182 (uint32_t)&NRF_APPLICATION_ABB->TRIM.RINGO[TRANSIENT_ZBB_ABB_SLOT],
183 opp_data->abb_ringo);
184 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
185 TRANSIENT_ZBB_ABB_SLOT,
186 (uint32_t)&NRF_APPLICATION_ABB->TRIM.LOCKRANGE[TRANSIENT_ZBB_ABB_SLOT],
187 opp_data->abb_lockrange);
188 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
189 TRANSIENT_ZBB_ABB_SLOT,
190 (uint32_t)&NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[TRANSIENT_ZBB_ABB_SLOT],
191 opp_data->abb_pvtmoncycles);
192 #else
193
194 NRF_ABB->TRIM.RINGO[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_ringo;
195 NRF_ABB->TRIM.LOCKRANGE[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_lockrange;
196 NRF_ABB->TRIM.PVTMONCYCLES[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_pvtmoncycles;
197
198 NRF_APPLICATION_ABB->TRIM.RINGO[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_ringo;
199 NRF_APPLICATION_ABB->TRIM.LOCKRANGE[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_lockrange;
200 NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_pvtmoncycles;
201 #endif
202 opp_data = get_dvfs_oppoint_data(curr_targ_opp);
203
204 #if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
205 LOG_DBG("REGW: NRF_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
206 CURR_TARG_ABB_SLOT,
207 (uint32_t)&NRF_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT],
208 opp_data->abb_ringo);
209 LOG_DBG("REGW: NRF_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
210 CURR_TARG_ABB_SLOT,
211 (uint32_t)&NRF_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT],
212 opp_data->abb_lockrange);
213 LOG_DBG("REGW: NRF_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
214 CURR_TARG_ABB_SLOT,
215 (uint32_t)&NRF_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT],
216 opp_data->abb_pvtmoncycles);
217
218 LOG_DBG("REGW: TODO: NRF_ABB->CONFIG.CTRL4 0x%x, V: 0x%lx",
219 (uint32_t)&NRF_ABB->CONFIG.CTRL4,
220 LD_ABB_CTRL4_TRANSITION_OPERATION);
221
222 /* For app core */
223 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
224 CURR_TARG_ABB_SLOT,
225 (uint32_t)&NRF_APPLICATION_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT],
226 opp_data->abb_ringo);
227 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
228 CURR_TARG_ABB_SLOT,
229 (uint32_t)&NRF_APPLICATION_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT],
230 opp_data->abb_lockrange);
231 LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
232 CURR_TARG_ABB_SLOT,
233 (uint32_t)&NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT],
234 opp_data->abb_pvtmoncycles);
235
236 LOG_DBG("REGW: TODO: NRF_APPLICATION_ABB->CONFIG.CTRL4 0x%x, V: 0x%lx",
237 (uint32_t)&NRF_APPLICATION_ABB->CONFIG.CTRL4,
238 LD_ABB_CTRL4_TRANSITION_OPERATION);
239 #else
240 NRF_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT] = opp_data->abb_ringo;
241 NRF_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT] = opp_data->abb_lockrange;
242 NRF_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT] = opp_data->abb_pvtmoncycles;
243
244 NRF_ABB->CONFIG.CTRL4 = LD_ABB_CTRL4_TRANSITION_OPERATION;
245
246 NRF_APPLICATION_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT] = opp_data->abb_ringo;
247 NRF_APPLICATION_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT] = opp_data->abb_lockrange;
248 NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT] = opp_data->abb_pvtmoncycles;
249
250 NRF_APPLICATION_ABB->CONFIG.CTRL4 = LD_ABB_CTRL4_TRANSITION_OPERATION;
251
252 #endif
253 #endif
254 }
255
ld_dvfs_configure_hsfll(enum dvfs_frequency_setting oppoint)256 int32_t ld_dvfs_configure_hsfll(enum dvfs_frequency_setting oppoint)
257 {
258 nrf_hsfll_trim_t hsfll_trim = {};
259
260 if (oppoint >= DVFS_FREQ_COUNT) {
261 LOG_ERR("Not valid oppoint %d", oppoint);
262 return -EINVAL;
263 }
264
265 uint8_t freq_trim = get_dvfs_oppoint_data(oppoint)->new_f_trim_entry;
266
267 /* Temporary patch fixing medlow oppoint trim index */
268 if (oppoint == DVFS_FREQ_MEDLOW) {
269 freq_trim = 2;
270 }
271
272 #if defined(NRF_APPLICATION)
273 hsfll_trim.vsup = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.VSUP;
274 hsfll_trim.coarse = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.COARSE[freq_trim];
275 hsfll_trim.fine = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.FINE[freq_trim];
276 #else
277 hsfll_trim.vsup = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.VSUP;
278 hsfll_trim.coarse = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.COARSE[freq_trim];
279 hsfll_trim.fine = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.FINE[freq_trim];
280 #endif
281
282 #if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
283 LOG_DBG("%s oppoint: %d", __func__, oppoint);
284 LOG_DBG("REGW: NRF_HSFLL->MIRROR 0x%x, V: 0x%x", (uint32_t)&NRF_HSFLL->MIRROR, 1);
285 LOG_DBG("REGW: NRF_HSFLL->TRIM.COARSE 0x%x, V: 0x%x",
286 (uint32_t)&NRF_HSFLL->TRIM.COARSE,
287 hsfll_trim.coarse);
288 LOG_DBG("REGW: NRF_HSFLL->TRIM.FINE 0x%x, V: 0x%x",
289 (uint32_t)&NRF_HSFLL->TRIM.FINE,
290 hsfll_trim.fine);
291 LOG_DBG("REGW: NRF_HSFLL->MIRROR 0x%x, V: 0x%x", (uint32_t)&NRF_HSFLL->MIRROR, 0);
292
293 LOG_DBG("REGW: NRF_HSFLL->CLOCKCTRL.MULT 0x%x, V: 0x%x",
294 (uint32_t)&NRF_HSFLL->CLOCKCTRL.MULT,
295 get_dvfs_oppoint_data(oppoint)->new_f_mult);
296
297 LOG_DBG("REGW: NRF_HSFLL->NRF_HSFLL_TASK_FREQ_CHANGE 0x%x, V: 0x%x",
298 (uint32_t)NRF_HSFLL + NRF_HSFLL_TASK_FREQ_CHANGE,
299 0x1);
300 return 0;
301 #else
302
303 nrf_hsfll_trim_set(NRF_HSFLL, &hsfll_trim);
304 nrf_barrier_w();
305
306 nrf_hsfll_clkctrl_mult_set(NRF_HSFLL, get_dvfs_oppoint_data(oppoint)->new_f_mult);
307 nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE);
308 /* Trigger hsfll task one more time, SEE PAC-4078 */
309 nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE);
310
311 bool hsfll_freq_changed = false;
312
313 NRFX_WAIT_FOR(nrf_hsfll_event_check(NRF_HSFLL, NRF_HSFLL_EVENT_FREQ_CHANGED),
314 HSFLL_FREQ_CHANGE_CHECK_MAX_ATTEMPTS,
315 HSFLL_FREQ_CHANGE_CHECK_INTERVAL_US,
316 hsfll_freq_changed);
317
318 if (hsfll_freq_changed) {
319 return 0;
320 }
321
322 return -ETIMEDOUT;
323 #endif
324 }
325
ld_dvfs_scaling_background_process(bool downscaling)326 void ld_dvfs_scaling_background_process(bool downscaling)
327 {
328 #if defined(NRF_SECURE)
329 if (NRF_DOMAIN == NRF_DOMAIN_SECURE) {
330 if (downscaling) {
331 ld_dvfs_secure_start_increased_power_consumption();
332 }
333 }
334 #endif
335 }
336
ld_dvfs_scaling_finish(bool downscaling)337 void ld_dvfs_scaling_finish(bool downscaling)
338 {
339 #if defined(NRF_SECURE)
340 #if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
341 LOG_DBG("%s", __func__);
342 LOG_DBG("REGW: NRF_ABB->CONFIG.CTRL4 0x%x, V: 0x%lx",
343 (uint32_t)&NRF_ABB->CONFIG.CTRL4,
344 LD_ABB_CTRL4_NORMAL_OPERATION);
345 LOG_DBG("REGW: NRF_APPLICATION_ABB->CONFIG.CTRL4 0x%x, V: 0x%lx",
346 (uint32_t)&NRF_APPLICATION_ABB->CONFIG.CTRL4,
347 LD_ABB_CTRL4_NORMAL_OPERATION);
348 #else
349 NRF_ABB->CONFIG.CTRL4 = LD_ABB_CTRL4_NORMAL_OPERATION;
350 NRF_APPLICATION_ABB->CONFIG.CTRL4 = LD_ABB_CTRL4_NORMAL_OPERATION;
351 #endif
352
353 if (NRF_DOMAIN == NRF_DOMAIN_SECURE) {
354 if (downscaling) {
355 ld_dvfs_secure_stop_increased_power_consumption();
356 }
357 }
358 #endif
359 }
360