1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "ld_dvfs_handler.h"
8 #include "ld_dvfs.h"
9 
10 #include <hal/nrf_hsfll.h>
11 #include <nrfs_dvfs.h>
12 #include <nrfs_backend_ipc_service.h>
13 
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_DECLARE(LD_DVFS_LIB, CONFIG_LOCAL_DOMAIN_DVFS_LIB_LOG_LEVEL);
17 
18 static K_SEM_DEFINE(dvfs_service_sync_sem, 0, 1);
19 static K_SEM_DEFINE(dvfs_service_idle_sem, 0, 1);
20 
21 #define DVFS_SERV_HDL_INIT_DONE_BIT_POS (0)
22 #define DVFS_SERV_HDL_FREQ_CHANGE_REQ_PENDING_BIT_POS (1)
23 
24 static atomic_t dvfs_service_handler_state_bits;
25 static volatile enum dvfs_frequency_setting current_freq_setting;
26 static volatile enum dvfs_frequency_setting requested_freq_setting;
27 static dvfs_service_handler_callback dvfs_frequency_change_applied_clb;
28 
dvfs_service_handler_set_state_bit(uint32_t bit_pos)29 static void dvfs_service_handler_set_state_bit(uint32_t bit_pos)
30 {
31 	atomic_set_bit(&dvfs_service_handler_state_bits, bit_pos);
32 }
33 
dvfs_service_handler_clear_state_bit(uint32_t bit_pos)34 static void dvfs_service_handler_clear_state_bit(uint32_t bit_pos)
35 {
36 	atomic_clear_bit(&dvfs_service_handler_state_bits, bit_pos);
37 }
38 
dvfs_service_handler_get_state_bit(uint32_t bit_pos)39 static bool dvfs_service_handler_get_state_bit(uint32_t bit_pos)
40 {
41 	return atomic_test_bit(&dvfs_service_handler_state_bits, bit_pos);
42 }
43 
dvfs_service_handler_init_done(void)44 static bool dvfs_service_handler_init_done(void)
45 {
46 	return dvfs_service_handler_get_state_bit(DVFS_SERV_HDL_INIT_DONE_BIT_POS);
47 }
48 
dvfs_service_handler_freq_change_req_pending(void)49 static bool dvfs_service_handler_freq_change_req_pending(void)
50 {
51 	return dvfs_service_handler_get_state_bit(DVFS_SERV_HDL_FREQ_CHANGE_REQ_PENDING_BIT_POS);
52 }
53 
dvfs_service_handler_nrfs_error_check(nrfs_err_t err)54 static void dvfs_service_handler_nrfs_error_check(nrfs_err_t err)
55 {
56 	if (err != NRFS_SUCCESS) {
57 		LOG_ERR("Failed with nrfs error: %d", err);
58 	}
59 }
60 
dvfs_service_handler_error(int err)61 static void dvfs_service_handler_error(int err)
62 {
63 	if (err != 0) {
64 		LOG_ERR("Failed with error: %d", err);
65 	}
66 }
67 
get_next_context(void)68 static uint32_t *get_next_context(void)
69 {
70 	static uint32_t ctx;
71 
72 	ctx++;
73 	return &ctx;
74 }
75 
dvfs_service_handler_freq_setting_allowed(enum dvfs_frequency_setting freq_setting)76 static bool dvfs_service_handler_freq_setting_allowed(enum dvfs_frequency_setting freq_setting)
77 {
78 	if (freq_setting == DVFS_FREQ_HIGH || freq_setting == DVFS_FREQ_MEDLOW ||
79 	    freq_setting == DVFS_FREQ_LOW) {
80 		return true;
81 	}
82 
83 	return false;
84 }
85 
dvfs_service_handler_get_current_oppoint(void)86 static enum dvfs_frequency_setting dvfs_service_handler_get_current_oppoint(void)
87 {
88 	LOG_DBG("Current LD freq setting: %d", current_freq_setting);
89 	return current_freq_setting;
90 }
91 
dvfs_service_handler_get_requested_oppoint(void)92 static enum dvfs_frequency_setting dvfs_service_handler_get_requested_oppoint(void)
93 {
94 	LOG_DBG("Requested LD freq setting: %d", requested_freq_setting);
95 	return requested_freq_setting;
96 }
97 
98 /* Function to check if current operation is down-scaling */
dvfs_service_handler_is_downscaling(enum dvfs_frequency_setting target_freq_setting)99 static bool dvfs_service_handler_is_downscaling(enum dvfs_frequency_setting target_freq_setting)
100 {
101 	if (dvfs_service_handler_freq_setting_allowed(target_freq_setting)) {
102 		LOG_DBG("Checking if downscaling %s",
103 			(dvfs_service_handler_get_current_oppoint() < target_freq_setting) ? "YES" :
104 											     "NO");
105 		return dvfs_service_handler_get_current_oppoint() < target_freq_setting;
106 	}
107 
108 	return false;
109 }
110 
111 /* Function handling steps for scaling preparation. */
dvfs_service_handler_prepare_to_scale(enum dvfs_frequency_setting oppoint_freq)112 static void dvfs_service_handler_prepare_to_scale(enum dvfs_frequency_setting oppoint_freq)
113 {
114 	LOG_DBG("Prepare to scale, oppoint freq %d", oppoint_freq);
115 	enum dvfs_frequency_setting new_oppoint	    = oppoint_freq;
116 	enum dvfs_frequency_setting current_oppoint = dvfs_service_handler_get_current_oppoint();
117 
118 	if (new_oppoint == current_oppoint) {
119 		LOG_DBG("New oppoint is same as previous, no change");
120 	} else {
121 		ld_dvfs_configure_abb_for_transition(current_oppoint, new_oppoint);
122 
123 		if (dvfs_service_handler_is_downscaling(new_oppoint)) {
124 			int32_t err = ld_dvfs_configure_hsfll(new_oppoint);
125 
126 			if (err != 0) {
127 				dvfs_service_handler_error(err);
128 			}
129 		}
130 	}
131 }
132 
133 /* Do background job during scaling process (e.g. increased power consumption during down-scale). */
dvfs_service_handler_scaling_background_job(enum dvfs_frequency_setting oppoint_freq)134 static void dvfs_service_handler_scaling_background_job(enum dvfs_frequency_setting oppoint_freq)
135 {
136 	LOG_DBG("Perform scaling background job if needed.");
137 	if (dvfs_service_handler_is_downscaling(oppoint_freq)) {
138 		k_sem_give(&dvfs_service_idle_sem);
139 	}
140 }
141 
142 /* Update MDK variable which is used by nrfx_coredep_delay_us (k_busy_wait). */
dvfs_service_update_core_clock(enum dvfs_frequency_setting oppoint_freq)143 static void dvfs_service_update_core_clock(enum dvfs_frequency_setting oppoint_freq)
144 {
145 	extern uint32_t SystemCoreClock;
146 
147 	SystemCoreClock = oppoint_freq == DVFS_FREQ_HIGH ? 320000000 :
148 			  oppoint_freq == DVFS_FREQ_MEDLOW ? 128000000 : 64000000;
149 }
150 
151 /* Perform scaling finnish procedure. */
dvfs_service_handler_scaling_finish(enum dvfs_frequency_setting oppoint_freq)152 static void dvfs_service_handler_scaling_finish(enum dvfs_frequency_setting oppoint_freq)
153 {
154 
155 	LOG_DBG("Scaling finnish oppoint freq %d", oppoint_freq);
156 	ld_dvfs_scaling_finish(dvfs_service_handler_is_downscaling(oppoint_freq));
157 	if (!dvfs_service_handler_is_downscaling(oppoint_freq)) {
158 		int32_t err = ld_dvfs_configure_hsfll(oppoint_freq);
159 
160 		if (err != 0) {
161 			dvfs_service_handler_error(err);
162 		}
163 	}
164 	dvfs_service_handler_clear_state_bit(DVFS_SERV_HDL_FREQ_CHANGE_REQ_PENDING_BIT_POS);
165 	current_freq_setting = oppoint_freq;
166 	dvfs_service_update_core_clock(oppoint_freq);
167 	LOG_DBG("Current LD freq setting: %d", current_freq_setting);
168 	if (dvfs_frequency_change_applied_clb) {
169 		dvfs_frequency_change_applied_clb(current_freq_setting);
170 	}
171 }
172 
173 /* Function to set hsfll to highest frequency when switched to ABB. */
dvfs_service_handler_set_initial_hsfll_config(void)174 static void dvfs_service_handler_set_initial_hsfll_config(void)
175 {
176 	int32_t err = ld_dvfs_configure_hsfll(DVFS_FREQ_HIGH);
177 
178 	current_freq_setting = DVFS_FREQ_HIGH;
179 	requested_freq_setting = DVFS_FREQ_HIGH;
180 	if (err != 0) {
181 		dvfs_service_handler_error(err);
182 	}
183 }
184 
185 /* Timer to add additional delay to finish downscale procedure when domain other than secure */
186 #if !defined(NRF_SECURE)
187 #define SCALING_FINISH_DELAY_TIMEOUT_US                                                            \
188 	K_USEC(CONFIG_NRFS_LOCAL_DOMAIN_DOWNSCALE_FINISH_DELAY_TIMEOUT_US)
189 
dvfs_service_handler_scaling_finish_delay_timeout(struct k_timer * timer)190 static void dvfs_service_handler_scaling_finish_delay_timeout(struct k_timer *timer)
191 {
192 
193 	dvfs_service_handler_scaling_finish(
194 		*(enum dvfs_frequency_setting *)k_timer_user_data_get(timer));
195 }
196 
197 K_TIMER_DEFINE(dvfs_service_scaling_finish_delay_timer,
198 	       dvfs_service_handler_scaling_finish_delay_timeout, NULL);
199 #endif
200 
201 /* DVFS event handler callback function.*/
nrfs_dvfs_evt_handler(nrfs_dvfs_evt_t const * p_evt,void * context)202 static void nrfs_dvfs_evt_handler(nrfs_dvfs_evt_t const *p_evt, void *context)
203 {
204 	LOG_DBG("%s", __func__);
205 	switch (p_evt->type) {
206 	case NRFS_DVFS_EVT_INIT_PREPARATION:
207 		LOG_DBG("DVFS handler EVT_INIT_PREPARATION");
208 #if defined(NRF_SECURE)
209 		ld_dvfs_clear_zbb();
210 		dvfs_service_handler_nrfs_error_check(
211 			nrfs_dvfs_init_complete_request(get_next_context()));
212 		LOG_DBG("DVFS handler EVT_INIT_PREPARATION handled");
213 #else
214 		LOG_ERR("DVFS handler - unexpected EVT_INIT_PREPARATION");
215 #endif
216 		break;
217 	case NRFS_DVFS_EVT_INIT_DONE:
218 		LOG_DBG("DVFS handler EVT_INIT_DONE");
219 		dvfs_service_handler_set_initial_hsfll_config();
220 		dvfs_service_handler_set_state_bit(DVFS_SERV_HDL_INIT_DONE_BIT_POS);
221 		k_sem_give(&dvfs_service_sync_sem);
222 		LOG_DBG("DVFS handler EVT_INIT_DONE handled");
223 		break;
224 	case NRFS_DVFS_EVT_OPPOINT_REQ_CONFIRMED:
225 		/* Optional confirmation from sysctrl, wait for oppoint.*/
226 		dvfs_service_handler_clear_state_bit(DVFS_SERV_HDL_FREQ_CHANGE_REQ_PENDING_BIT_POS);
227 		LOG_DBG("DVFS handler EVT_OPPOINT_REQ_CONFIRMED %d", (uint32_t)p_evt->freq);
228 		if (dvfs_service_handler_get_requested_oppoint() == p_evt->freq) {
229 			dvfs_service_update_core_clock(p_evt->freq);
230 			if (dvfs_frequency_change_applied_clb) {
231 				dvfs_frequency_change_applied_clb(p_evt->freq);
232 			}
233 		}
234 		break;
235 	case NRFS_DVFS_EVT_OPPOINT_SCALING_PREPARE:
236 		/*Target oppoint will be received here.*/
237 		LOG_DBG("DVFS handler EVT_OPPOINT_SCALING_PREPARE");
238 #if !defined(NRF_SECURE)
239 		if (dvfs_service_handler_is_downscaling(p_evt->freq)) {
240 #endif
241 			dvfs_service_handler_prepare_to_scale(p_evt->freq);
242 			dvfs_service_handler_nrfs_error_check(
243 						nrfs_dvfs_ready_to_scale(get_next_context()));
244 			dvfs_service_handler_scaling_background_job(p_evt->freq);
245 			LOG_DBG("DVFS handler EVT_OPPOINT_SCALING_PREPARE handled");
246 #if !defined(NRF_SECURE)
247 			/* Additional delay for downscale to finish on secdom side */
248 			static enum dvfs_frequency_setting freq;
249 
250 			freq = p_evt->freq;
251 			k_timer_user_data_set(&dvfs_service_scaling_finish_delay_timer,
252 					      (void *)&freq);
253 			k_timer_start(&dvfs_service_scaling_finish_delay_timer,
254 				      SCALING_FINISH_DELAY_TIMEOUT_US, K_NO_WAIT);
255 		} else {
256 			LOG_ERR("DVFS handler - unexpected EVT_OPPOINT_SCALING_PREPARE");
257 		}
258 #endif
259 		break;
260 	case NRFS_DVFS_EVT_OPPOINT_SCALING_DONE:
261 		LOG_DBG("DVFS handler EVT_OPPOINT_SCALING_DONE");
262 		dvfs_service_handler_scaling_finish(p_evt->freq);
263 		LOG_DBG("DVFS handler EVT_OPPOINT_SCALING_DONE handled");
264 		break;
265 	case NRFS_DVFS_EVT_REJECT:
266 		LOG_ERR("DVFS handler - request rejected");
267 		break;
268 	default:
269 		LOG_ERR("DVFS handler - unexpected event: 0x%x", p_evt->type);
270 		break;
271 	}
272 }
273 
274 /* Task to handle dvfs init procedure. */
dvfs_service_handler_task(void * dummy0,void * dummy1,void * dummy2)275 static void dvfs_service_handler_task(void *dummy0, void *dummy1, void *dummy2)
276 {
277 	ARG_UNUSED(dummy0);
278 	ARG_UNUSED(dummy1);
279 	ARG_UNUSED(dummy2);
280 
281 	LOG_DBG("Trim ABB for default voltage.");
282 	ld_dvfs_init();
283 
284 	LOG_DBG("Waiting for backend init");
285 	/* Wait for ipc initialization */
286 	nrfs_backend_wait_for_connection(K_FOREVER);
287 
288 	nrfs_err_t status;
289 
290 	LOG_DBG("nrfs_dvfs_init");
291 	status = nrfs_dvfs_init(nrfs_dvfs_evt_handler);
292 	dvfs_service_handler_nrfs_error_check(status);
293 
294 	LOG_DBG("nrfs_dvfs_init_prepare_request");
295 	status = nrfs_dvfs_init_prepare_request(get_next_context());
296 	dvfs_service_handler_nrfs_error_check(status);
297 
298 	/* Wait for init*/
299 	k_sem_take(&dvfs_service_sync_sem, K_FOREVER);
300 
301 	LOG_DBG("DVFS init done.");
302 
303 #if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_SCALE_DOWN_AFTER_INIT)
304 	LOG_DBG("Requesting lowest frequency oppoint.");
305 	dvfs_service_handler_change_freq_setting(DVFS_FREQ_LOW);
306 #endif
307 
308 	while (1) {
309 		k_sem_take(&dvfs_service_idle_sem, K_FOREVER);
310 		/* perform background processing */
311 		ld_dvfs_scaling_background_process(true);
312 	}
313 }
314 
315 K_THREAD_DEFINE(dvfs_service_handler_task_id,
316 		CONFIG_NRFS_LOCAL_DOMAIN_DVFS_HANDLER_TASK_STACK_SIZE,
317 		dvfs_service_handler_task,
318 		NULL,
319 		NULL,
320 		NULL,
321 		CONFIG_NRFS_LOCAL_DOMAIN_DVFS_HANDLER_TASK_PRIORITY,
322 		0,
323 		0);
324 
dvfs_service_handler_change_freq_setting(enum dvfs_frequency_setting freq_setting)325 int32_t dvfs_service_handler_change_freq_setting(enum dvfs_frequency_setting freq_setting)
326 {
327 	if (!dvfs_service_handler_init_done()) {
328 		LOG_WRN("Init not done!");
329 		return -EAGAIN;
330 	}
331 
332 	if (!dvfs_service_handler_freq_setting_allowed(freq_setting)) {
333 		LOG_ERR("Requested frequency setting %d not supported.", freq_setting);
334 		return -ENXIO;
335 	}
336 
337 	if (dvfs_service_handler_freq_change_req_pending()) {
338 		LOG_DBG("Frequency change request pending.");
339 		return -EBUSY;
340 	}
341 
342 	dvfs_service_handler_set_state_bit(DVFS_SERV_HDL_FREQ_CHANGE_REQ_PENDING_BIT_POS);
343 	requested_freq_setting = freq_setting;
344 
345 	nrfs_err_t status = nrfs_dvfs_oppoint_request(freq_setting, get_next_context());
346 
347 	if (status != NRFS_SUCCESS) {
348 		dvfs_service_handler_clear_state_bit(DVFS_SERV_HDL_FREQ_CHANGE_REQ_PENDING_BIT_POS);
349 	}
350 
351 	dvfs_service_handler_nrfs_error_check(status);
352 
353 	return status;
354 }
355 
dvfs_service_handler_register_freq_setting_applied_callback(dvfs_service_handler_callback clb)356 void dvfs_service_handler_register_freq_setting_applied_callback(dvfs_service_handler_callback clb)
357 {
358 	if (clb) {
359 		LOG_DBG("Registered frequency applied callback");
360 		dvfs_frequency_change_applied_clb = clb;
361 	} else {
362 		LOG_ERR("Invalid callback function provided!");
363 	}
364 }
365