|
| 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 | + |
| 9 | +#include <hal/nrf_hsfll.h> |
| 10 | +#include <dvfs_oppoint.h> |
| 11 | +//#include <nrfs_dvfs.h> |
| 12 | + |
| 13 | +#include <zephyr/kernel.h> |
| 14 | +#include <zephyr/logging/log.h> |
| 15 | +LOG_MODULE_REGISTER(LD_DVFS_NEW_LIB, CONFIG_LOCAL_DOMAIN_DVFS_NEW_LIB_LOG_LEVEL); |
| 16 | + |
| 17 | +/*TODO: get rid of nrfs dependency, oppoint*/ |
| 18 | +#include <sdfw/sdfw_services/dvfs_service.h> |
| 19 | + |
| 20 | +static volatile enum dvfs_frequency_setting current_freq_setting; |
| 21 | +static volatile enum dvfs_frequency_setting requested_freq_setting; |
| 22 | +static dvfs_service_handler_callback dvfs_frequency_change_applied_clb; |
| 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 | +/** |
| 33 | + * @brief Configure hsfll depending on selected oppoint |
| 34 | + * |
| 35 | + * @param enum oppoint target operation point |
| 36 | + * @return 0 value indicates no error. |
| 37 | + * @return -EINVAL invalid oppoint or domain. |
| 38 | + * @return -ETIMEDOUT frequency change took more than HSFLL_FREQ_CHANGE_MAX_DELAY_MS |
| 39 | + */ |
| 40 | +static int32_t ld_dvfs_configure_hsfll(enum dvfs_frequency_setting oppoint) |
| 41 | +{ |
| 42 | + nrf_hsfll_trim_t hsfll_trim = {}; |
| 43 | + |
| 44 | + if (oppoint >= DVFS_FREQ_COUNT) { |
| 45 | + LOG_ERR("Not valid oppoint %d", oppoint); |
| 46 | + return -EINVAL; |
| 47 | + } |
| 48 | + |
| 49 | + uint8_t freq_trim = get_dvfs_oppoint_data(oppoint)->new_f_trim_entry; |
| 50 | + |
| 51 | +#if defined(NRF_APPLICATION) |
| 52 | + hsfll_trim.vsup = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.VSUP; |
| 53 | + hsfll_trim.coarse = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.COARSE[freq_trim]; |
| 54 | + hsfll_trim.fine = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.FINE[freq_trim]; |
| 55 | +#else |
| 56 | + hsfll_trim.vsup = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.VSUP; |
| 57 | + hsfll_trim.coarse = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.COARSE[freq_trim]; |
| 58 | + hsfll_trim.fine = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.FINE[freq_trim]; |
| 59 | +#endif |
| 60 | + |
| 61 | +#if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST) |
| 62 | + LOG_DBG("%s oppoint: %d", __func__, oppoint); |
| 63 | + LOG_DBG("REGW: NRF_HSFLL->MIRROR 0x%x, V: 0x%x", (uint32_t)&NRF_HSFLL->MIRROR, 1); |
| 64 | + LOG_DBG("REGW: NRF_HSFLL->TRIM.COARSE 0x%x, V: 0x%x", |
| 65 | + (uint32_t)&NRF_HSFLL->TRIM.COARSE, |
| 66 | + hsfll_trim.coarse); |
| 67 | + LOG_DBG("REGW: NRF_HSFLL->TRIM.FINE 0x%x, V: 0x%x", |
| 68 | + (uint32_t)&NRF_HSFLL->TRIM.FINE, |
| 69 | + hsfll_trim.fine); |
| 70 | + LOG_DBG("REGW: NRF_HSFLL->MIRROR 0x%x, V: 0x%x", (uint32_t)&NRF_HSFLL->MIRROR, 0); |
| 71 | + |
| 72 | + LOG_DBG("REGW: NRF_HSFLL->CLOCKCTRL.MULT 0x%x, V: 0x%x", |
| 73 | + (uint32_t)&NRF_HSFLL->CLOCKCTRL.MULT, |
| 74 | + get_dvfs_oppoint_data(oppoint)->new_f_mult); |
| 75 | + |
| 76 | + LOG_DBG("REGW: NRF_HSFLL->NRF_HSFLL_TASK_FREQ_CHANGE 0x%x, V: 0x%x", |
| 77 | + (uint32_t)NRF_HSFLL + NRF_HSFLL_TASK_FREQ_CHANGE, |
| 78 | + 0x1); |
| 79 | + return 0; |
| 80 | +#else |
| 81 | + |
| 82 | + nrf_hsfll_trim_set(NRF_HSFLL, &hsfll_trim); |
| 83 | + nrf_barrier_w(); |
| 84 | + |
| 85 | + nrf_hsfll_clkctrl_mult_set(NRF_HSFLL, get_dvfs_oppoint_data(oppoint)->new_f_mult); |
| 86 | + nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE); |
| 87 | + /* Trigger hsfll task one more time, SEE PAC-4078 */ |
| 88 | + nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE); |
| 89 | + |
| 90 | + bool hsfll_freq_changed = false; |
| 91 | + |
| 92 | + NRFX_WAIT_FOR(nrf_hsfll_event_check(NRF_HSFLL, NRF_HSFLL_EVENT_FREQ_CHANGED), |
| 93 | + HSFLL_FREQ_CHANGE_CHECK_MAX_ATTEMPTS, |
| 94 | + HSFLL_FREQ_CHANGE_CHECK_INTERVAL_US, |
| 95 | + hsfll_freq_changed); |
| 96 | + |
| 97 | + if (hsfll_freq_changed) { |
| 98 | + return 0; |
| 99 | + } |
| 100 | + |
| 101 | + return -ETIMEDOUT; |
| 102 | +#endif |
| 103 | +} |
| 104 | + |
| 105 | + |
| 106 | +static enum dvfs_frequency_setting dvfs_service_handler_get_current_oppoint(void) |
| 107 | +{ |
| 108 | + LOG_DBG("Current LD freq setting: %d", current_freq_setting); |
| 109 | + return current_freq_setting; |
| 110 | +} |
| 111 | + |
| 112 | +static void dvfs_service_handler_error(int err) |
| 113 | +{ |
| 114 | + if (err != 0) { |
| 115 | + LOG_ERR("Failed with error: %d", err); |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +static bool dvfs_service_handler_freq_setting_allowed(enum dvfs_frequency_setting freq_setting) |
| 120 | +{ |
| 121 | + if (freq_setting == DVFS_FREQ_HIGH || freq_setting == DVFS_FREQ_MEDLOW || |
| 122 | + freq_setting == DVFS_FREQ_LOW) { |
| 123 | + return true; |
| 124 | + } |
| 125 | + |
| 126 | + return false; |
| 127 | +} |
| 128 | + |
| 129 | +/* Function to check if current operation is down-scaling */ |
| 130 | +static bool dvfs_service_handler_is_downscaling(enum dvfs_frequency_setting target_freq_setting) |
| 131 | +{ |
| 132 | + if (dvfs_service_handler_freq_setting_allowed(target_freq_setting)) { |
| 133 | + LOG_DBG("Checking if downscaling %s", |
| 134 | + (dvfs_service_handler_get_current_oppoint() < target_freq_setting) ? "YES" : |
| 135 | + "NO"); |
| 136 | + return dvfs_service_handler_get_current_oppoint() < target_freq_setting; |
| 137 | + } |
| 138 | + |
| 139 | + return false; |
| 140 | +} |
| 141 | + |
| 142 | +/* Function handling steps for scaling preparation. */ |
| 143 | +static void dvfs_service_handler_prepare_to_scale(enum dvfs_frequency_setting oppoint_freq) |
| 144 | +{ |
| 145 | + LOG_DBG("Prepare to scale, oppoint freq %d", oppoint_freq); |
| 146 | + enum dvfs_frequency_setting new_oppoint = oppoint_freq; |
| 147 | + enum dvfs_frequency_setting current_oppoint = dvfs_service_handler_get_current_oppoint(); |
| 148 | + |
| 149 | + if (new_oppoint == current_oppoint) { |
| 150 | + LOG_DBG("New oppoint is same as previous, no change"); |
| 151 | + } else { |
| 152 | + |
| 153 | + if (dvfs_service_handler_is_downscaling(new_oppoint)) { |
| 154 | + int32_t err = ld_dvfs_configure_hsfll(new_oppoint); |
| 155 | + |
| 156 | + if (err != 0) { |
| 157 | + dvfs_service_handler_error(err); |
| 158 | + } |
| 159 | + } |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +/* Function to set hsfll to highest frequency when switched to ABB. */ |
| 164 | +static int32_t dvfs_service_handler_set_initial_hsfll_config(void) |
| 165 | +{ |
| 166 | + int32_t err = ld_dvfs_configure_hsfll(DVFS_FREQ_HIGH); |
| 167 | + |
| 168 | + current_freq_setting = DVFS_FREQ_HIGH; |
| 169 | + requested_freq_setting = DVFS_FREQ_HIGH; |
| 170 | + if (err != 0) { |
| 171 | + dvfs_service_handler_error(err); |
| 172 | + } |
| 173 | + |
| 174 | + return err; |
| 175 | +} |
| 176 | + |
| 177 | +static int ld_dvfs_handler_init(void) |
| 178 | +{ |
| 179 | + LOG_DBG("LD DVFS handler init"); |
| 180 | + int32_t err = dvfs_service_handler_set_initial_hsfll_config(); |
| 181 | + |
| 182 | + return err; |
| 183 | +} |
| 184 | +SYS_INIT(ld_dvfs_handler_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); |
| 185 | + |
| 186 | +/* Update MDK variable which is used by nrfx_coredep_delay_us (k_busy_wait). */ |
| 187 | +static void dvfs_service_update_core_clock(enum dvfs_frequency_setting oppoint_freq) |
| 188 | +{ |
| 189 | + extern uint32_t SystemCoreClock; |
| 190 | + |
| 191 | + SystemCoreClock = oppoint_freq == DVFS_FREQ_HIGH ? 320000000 : |
| 192 | + oppoint_freq == DVFS_FREQ_MEDLOW ? 128000000 : 64000000; |
| 193 | +} |
| 194 | + |
| 195 | +/* Perform scaling finnish procedure. */ |
| 196 | +static void dvfs_service_handler_scaling_finish(enum dvfs_frequency_setting oppoint_freq) |
| 197 | +{ |
| 198 | + |
| 199 | + LOG_DBG("Scaling finnish oppoint freq %d", oppoint_freq); |
| 200 | + if (!dvfs_service_handler_is_downscaling(oppoint_freq)) { |
| 201 | + int32_t err = ld_dvfs_configure_hsfll(oppoint_freq); |
| 202 | + |
| 203 | + if (err != 0) { |
| 204 | + dvfs_service_handler_error(err); |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + current_freq_setting = oppoint_freq; |
| 209 | + dvfs_service_update_core_clock(oppoint_freq); |
| 210 | + LOG_DBG("Current LD freq setting: %d", current_freq_setting); |
| 211 | + if (dvfs_frequency_change_applied_clb) { |
| 212 | + dvfs_frequency_change_applied_clb(current_freq_setting); |
| 213 | + } |
| 214 | +} |
| 215 | + |
| 216 | +int32_t dvfs_service_handler_change_freq_setting(enum dvfs_frequency_setting freq_setting) |
| 217 | +{ |
| 218 | + static bool change_in_progress = false; |
| 219 | + if (change_in_progress) { |
| 220 | + LOG_ERR("Change in progress, please wait."); |
| 221 | + return -EBUSY; |
| 222 | + } |
| 223 | + |
| 224 | + if (!dvfs_service_handler_freq_setting_allowed(freq_setting)) { |
| 225 | + LOG_ERR("Requested frequency setting %d not supported.", freq_setting); |
| 226 | + return -ENXIO; |
| 227 | + } |
| 228 | + |
| 229 | + if(freq_setting == current_freq_setting) { |
| 230 | + LOG_DBG("Requested frequency setting %d is same as current.", freq_setting); |
| 231 | + return 0; |
| 232 | + } |
| 233 | + |
| 234 | + change_in_progress = true; |
| 235 | + |
| 236 | + requested_freq_setting = freq_setting; |
| 237 | + |
| 238 | + dvfs_service_handler_prepare_to_scale(requested_freq_setting); |
| 239 | + |
| 240 | + int status = ssf_dvfs_set_oppoint(requested_freq_setting); |
| 241 | + |
| 242 | + if (status != 0) { |
| 243 | + LOG_ERR("Failed to set DVFS frequency: %d", status); |
| 244 | + return status; |
| 245 | + } |
| 246 | + |
| 247 | + dvfs_service_handler_scaling_finish(requested_freq_setting); |
| 248 | + |
| 249 | + change_in_progress = false; |
| 250 | + |
| 251 | + return 0; |
| 252 | +} |
| 253 | + |
| 254 | +void dvfs_service_handler_register_freq_setting_applied_callback(dvfs_service_handler_callback clb) |
| 255 | +{ |
| 256 | + if (clb) { |
| 257 | + LOG_DBG("Registered frequency applied callback"); |
| 258 | + dvfs_frequency_change_applied_clb = clb; |
| 259 | + } else { |
| 260 | + LOG_ERR("Invalid callback function provided!"); |
| 261 | + } |
| 262 | +} |
0 commit comments