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