360 lines
12 KiB
C
360 lines
12 KiB
C
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "ld_dvfs.h"
|
|
|
|
#include <hal/nrf_hsfll.h>
|
|
#include <hal/nrf_ramc.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(LD_DVFS_LIB, CONFIG_LOCAL_DOMAIN_DVFS_LIB_LOG_LEVEL);
|
|
|
|
#define TRANSIENT_ZBB_ABB_SLOT 0
|
|
#define CURR_TARG_ABB_SLOT 1
|
|
#define LD_ABB_CLR_ZBB 0
|
|
/* TODO: this values needs to be provided by HW team */
|
|
/* for now reset value will be used */
|
|
#define LD_ABB_CTRL4_NORMAL_OPERATION 0x10800UL
|
|
#define LD_ABB_CTRL4_TRANSITION_OPERATION 0x10800UL
|
|
|
|
/*
|
|
* wait max 500ms with 10us intervals for hsfll freq change event
|
|
*/
|
|
#define HSFLL_FREQ_CHANGE_MAX_DELAY_MS 500UL
|
|
#define HSFLL_FREQ_CHANGE_CHECK_INTERVAL_US 10
|
|
#define HSFLL_FREQ_CHANGE_CHECK_MAX_ATTEMPTS \
|
|
((HSFLL_FREQ_CHANGE_MAX_DELAY_MS) * (USEC_PER_MSEC) / (HSFLL_FREQ_CHANGE_CHECK_INTERVAL_US))
|
|
|
|
#define ABB_STATUS_CHANGE_MAX_DELAY_MS 5000UL
|
|
#define ABB_STATUS_CHANGE_CHECK_INTERVAL_US 10
|
|
#define ABB_STATUS_CHANGE_CHECK_MAX_ATTEMPTS \
|
|
((ABB_STATUS_CHANGE_MAX_DELAY_MS) * (USEC_PER_MSEC) / (ABB_STATUS_CHANGE_CHECK_INTERVAL_US))
|
|
|
|
void ld_dvfs_init(void)
|
|
{
|
|
#if defined(NRF_SECURE)
|
|
|
|
const struct dvfs_oppoint_data *opp_data = get_dvfs_oppoint_data(DVFS_FREQ_HIGH);
|
|
|
|
#if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
|
|
LOG_DBG("%s", __func__);
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_ringo);
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_lockrange);
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_pvtmoncycles);
|
|
|
|
/*For app core.*/
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_ringo);
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_lockrange);
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_pvtmoncycles);
|
|
#else
|
|
/* TODO: Change to NRFX Hal function when available. */
|
|
NRF_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT] = opp_data->abb_ringo;
|
|
NRF_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT] = opp_data->abb_lockrange;
|
|
NRF_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT] = opp_data->abb_pvtmoncycles;
|
|
|
|
NRF_APPLICATION_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT] = opp_data->abb_ringo;
|
|
NRF_APPLICATION_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT] = opp_data->abb_lockrange;
|
|
NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT] = opp_data->abb_pvtmoncycles;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void ld_dvfs_clear_zbb(void)
|
|
{
|
|
#if defined(NRF_SECURE)
|
|
#if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
|
|
LOG_DBG("%s", __func__);
|
|
LOG_DBG("REGW: NRF_ABB->CONFIG.CTRL1.MODE 0x%x, V: 0x%x",
|
|
(uint32_t)&NRF_ABB->CONFIG.CTRL1,
|
|
LD_ABB_CLR_ZBB);
|
|
#else
|
|
/* TODO: Change to NRFX Hal function when available. */
|
|
NRF_ABB->CONFIG.CTRL1 &= ~(ABB_CONFIG_CTRL1_MODE_Msk);
|
|
NRF_APPLICATION_ABB->CONFIG.CTRL1 &= ~(ABB_CONFIG_CTRL1_MODE_Msk);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
#if defined(NRF_SECURE)
|
|
|
|
#define DOWNSCALE_SAFETY_TIMEOUT (K_USEC(CONFIG_NRFS_LOCAL_DOMAIN_DOWNSCALE_SAFETY_TIMEOUT_US))
|
|
|
|
atomic_t increased_power_consumption;
|
|
|
|
/**
|
|
* @brief Secure domain needs to check if downscale is done in defined time
|
|
* window. This is needed to avoid battery drain if dvfs procedure
|
|
* takes to much time (some failure?).
|
|
*/
|
|
__weak void ld_dvfs_secure_downscale_timeout(struct k_timer *timer)
|
|
{
|
|
ARG_UNUSED(timer);
|
|
|
|
LOG_ERR("Downscale timeout expired, reset board.");
|
|
atomic_set(&increased_power_consumption, 0);
|
|
}
|
|
|
|
K_TIMER_DEFINE(dvfs_downscale_secure_timer, ld_dvfs_secure_downscale_timeout, NULL);
|
|
|
|
/**
|
|
* @brief Secure domain starts increased power consumption, needed by dvfs sequence.
|
|
* This function can be reimplemented in other module if needed.
|
|
*/
|
|
__weak void ld_dvfs_secure_start_increased_power_consumption(void)
|
|
{
|
|
LOG_DBG("Start increased power consumption for DVFS sequence and start safety timer.");
|
|
k_timer_start(&dvfs_downscale_secure_timer, DOWNSCALE_SAFETY_TIMEOUT, K_NO_WAIT);
|
|
atomic_set(&increased_power_consumption, 1);
|
|
|
|
volatile uint8_t idle_counter = 0;
|
|
|
|
while (atomic_get(&increased_power_consumption)) {
|
|
if (idle_counter < 100) {
|
|
k_yield();
|
|
idle_counter++;
|
|
} else {
|
|
idle_counter = 0;
|
|
k_usleep(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Secure domain stops increased power consumption at the end of downscale.
|
|
* This function can be reimplemented in other module if needed.
|
|
*/
|
|
__weak void ld_dvfs_secure_stop_increased_power_consumption(void)
|
|
{
|
|
LOG_DBG("Stop increased power consumption for DVFS sequence.");
|
|
k_timer_stop(&dvfs_downscale_secure_timer);
|
|
atomic_set(&increased_power_consumption, 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
void ld_dvfs_configure_abb_for_transition(enum dvfs_frequency_setting transient_opp,
|
|
enum dvfs_frequency_setting curr_targ_opp)
|
|
{
|
|
#if defined(NRF_SECURE)
|
|
const struct dvfs_oppoint_data *opp_data = get_dvfs_oppoint_data(transient_opp);
|
|
|
|
#if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
|
|
LOG_DBG("%s", __func__);
|
|
LOG_DBG("transient_opp: %d, curr_targ_opp: %d", transient_opp, curr_targ_opp);
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
|
|
TRANSIENT_ZBB_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.RINGO[TRANSIENT_ZBB_ABB_SLOT],
|
|
opp_data->abb_ringo);
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
|
|
TRANSIENT_ZBB_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.LOCKRANGE[TRANSIENT_ZBB_ABB_SLOT],
|
|
opp_data->abb_lockrange);
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
|
|
TRANSIENT_ZBB_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.PVTMONCYCLES[TRANSIENT_ZBB_ABB_SLOT],
|
|
opp_data->abb_pvtmoncycles);
|
|
|
|
/* For app core.*/
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
|
|
TRANSIENT_ZBB_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.RINGO[TRANSIENT_ZBB_ABB_SLOT],
|
|
opp_data->abb_ringo);
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
|
|
TRANSIENT_ZBB_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.LOCKRANGE[TRANSIENT_ZBB_ABB_SLOT],
|
|
opp_data->abb_lockrange);
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
|
|
TRANSIENT_ZBB_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[TRANSIENT_ZBB_ABB_SLOT],
|
|
opp_data->abb_pvtmoncycles);
|
|
#else
|
|
|
|
NRF_ABB->TRIM.RINGO[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_ringo;
|
|
NRF_ABB->TRIM.LOCKRANGE[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_lockrange;
|
|
NRF_ABB->TRIM.PVTMONCYCLES[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_pvtmoncycles;
|
|
|
|
NRF_APPLICATION_ABB->TRIM.RINGO[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_ringo;
|
|
NRF_APPLICATION_ABB->TRIM.LOCKRANGE[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_lockrange;
|
|
NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[TRANSIENT_ZBB_ABB_SLOT] = opp_data->abb_pvtmoncycles;
|
|
#endif
|
|
opp_data = get_dvfs_oppoint_data(curr_targ_opp);
|
|
|
|
#if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_ringo);
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_lockrange);
|
|
LOG_DBG("REGW: NRF_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_pvtmoncycles);
|
|
|
|
LOG_DBG("REGW: TODO: NRF_ABB->CONFIG.CTRL4 0x%x, V: 0x%lx",
|
|
(uint32_t)&NRF_ABB->CONFIG.CTRL4,
|
|
LD_ABB_CTRL4_TRANSITION_OPERATION);
|
|
|
|
/* For app core */
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.RINGO[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_ringo);
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.LOCKRANGE[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_lockrange);
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[%d] 0x%x, V: 0x%x",
|
|
CURR_TARG_ABB_SLOT,
|
|
(uint32_t)&NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT],
|
|
opp_data->abb_pvtmoncycles);
|
|
|
|
LOG_DBG("REGW: TODO: NRF_APPLICATION_ABB->CONFIG.CTRL4 0x%x, V: 0x%lx",
|
|
(uint32_t)&NRF_APPLICATION_ABB->CONFIG.CTRL4,
|
|
LD_ABB_CTRL4_TRANSITION_OPERATION);
|
|
#else
|
|
NRF_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT] = opp_data->abb_ringo;
|
|
NRF_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT] = opp_data->abb_lockrange;
|
|
NRF_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT] = opp_data->abb_pvtmoncycles;
|
|
|
|
NRF_ABB->CONFIG.CTRL4 = LD_ABB_CTRL4_TRANSITION_OPERATION;
|
|
|
|
NRF_APPLICATION_ABB->TRIM.RINGO[CURR_TARG_ABB_SLOT] = opp_data->abb_ringo;
|
|
NRF_APPLICATION_ABB->TRIM.LOCKRANGE[CURR_TARG_ABB_SLOT] = opp_data->abb_lockrange;
|
|
NRF_APPLICATION_ABB->TRIM.PVTMONCYCLES[CURR_TARG_ABB_SLOT] = opp_data->abb_pvtmoncycles;
|
|
|
|
NRF_APPLICATION_ABB->CONFIG.CTRL4 = LD_ABB_CTRL4_TRANSITION_OPERATION;
|
|
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
int32_t ld_dvfs_configure_hsfll(enum dvfs_frequency_setting oppoint)
|
|
{
|
|
nrf_hsfll_trim_t hsfll_trim = {};
|
|
|
|
if (oppoint >= DVFS_FREQ_COUNT) {
|
|
LOG_ERR("Not valid oppoint %d", oppoint);
|
|
return -EINVAL;
|
|
}
|
|
|
|
uint8_t freq_trim = get_dvfs_oppoint_data(oppoint)->new_f_trim_entry;
|
|
|
|
/* Temporary patch fixing medlow oppoint trim index */
|
|
if (oppoint == DVFS_FREQ_MEDLOW) {
|
|
freq_trim = 2;
|
|
}
|
|
|
|
#if defined(CONFIG_SOC_NRF54H20_CPUAPP) || defined(CONFIG_SOC_NRF9280_CPUAPP)
|
|
hsfll_trim.vsup = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.VSUP;
|
|
hsfll_trim.coarse = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.COARSE[freq_trim];
|
|
hsfll_trim.fine = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.FINE[freq_trim];
|
|
#else
|
|
hsfll_trim.vsup = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.VSUP;
|
|
hsfll_trim.coarse = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.COARSE[freq_trim];
|
|
hsfll_trim.fine = NRF_FICR->TRIM.SECURE.HSFLL.TRIM.FINE[freq_trim];
|
|
#endif
|
|
|
|
#if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
|
|
LOG_DBG("%s oppoint: %d", __func__, oppoint);
|
|
LOG_DBG("REGW: NRF_HSFLL->MIRROR 0x%x, V: 0x%x", (uint32_t)&NRF_HSFLL->MIRROR, 1);
|
|
LOG_DBG("REGW: NRF_HSFLL->TRIM.COARSE 0x%x, V: 0x%x",
|
|
(uint32_t)&NRF_HSFLL->TRIM.COARSE,
|
|
hsfll_trim.coarse);
|
|
LOG_DBG("REGW: NRF_HSFLL->TRIM.FINE 0x%x, V: 0x%x",
|
|
(uint32_t)&NRF_HSFLL->TRIM.FINE,
|
|
hsfll_trim.fine);
|
|
LOG_DBG("REGW: NRF_HSFLL->MIRROR 0x%x, V: 0x%x", (uint32_t)&NRF_HSFLL->MIRROR, 0);
|
|
|
|
LOG_DBG("REGW: NRF_HSFLL->CLOCKCTRL.MULT 0x%x, V: 0x%x",
|
|
(uint32_t)&NRF_HSFLL->CLOCKCTRL.MULT,
|
|
get_dvfs_oppoint_data(oppoint)->new_f_mult);
|
|
|
|
LOG_DBG("REGW: NRF_HSFLL->NRF_HSFLL_TASK_FREQ_CHANGE 0x%x, V: 0x%x",
|
|
(uint32_t)NRF_HSFLL + NRF_HSFLL_TASK_FREQ_CHANGE,
|
|
0x1);
|
|
return 0;
|
|
#else
|
|
|
|
nrf_hsfll_trim_set(NRF_HSFLL, &hsfll_trim);
|
|
nrf_barrier_w();
|
|
|
|
nrf_hsfll_clkctrl_mult_set(NRF_HSFLL, get_dvfs_oppoint_data(oppoint)->new_f_mult);
|
|
nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE);
|
|
/* Trigger hsfll task one more time, SEE PAC-4078 */
|
|
nrf_hsfll_task_trigger(NRF_HSFLL, NRF_HSFLL_TASK_FREQ_CHANGE);
|
|
|
|
bool hsfll_freq_changed = false;
|
|
|
|
NRFX_WAIT_FOR(nrf_hsfll_event_check(NRF_HSFLL, NRF_HSFLL_EVENT_FREQ_CHANGED),
|
|
HSFLL_FREQ_CHANGE_CHECK_MAX_ATTEMPTS,
|
|
HSFLL_FREQ_CHANGE_CHECK_INTERVAL_US,
|
|
hsfll_freq_changed);
|
|
|
|
if (hsfll_freq_changed) {
|
|
return 0;
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
#endif
|
|
}
|
|
|
|
void ld_dvfs_scaling_background_process(bool downscaling)
|
|
{
|
|
#if defined(NRF_SECURE)
|
|
if (NRF_DOMAIN == NRF_DOMAIN_SECURE) {
|
|
if (downscaling) {
|
|
ld_dvfs_secure_start_increased_power_consumption();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ld_dvfs_scaling_finish(bool downscaling)
|
|
{
|
|
#if defined(NRF_SECURE)
|
|
#if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_TEST)
|
|
LOG_DBG("%s", __func__);
|
|
LOG_DBG("REGW: NRF_ABB->CONFIG.CTRL4 0x%x, V: 0x%lx",
|
|
(uint32_t)&NRF_ABB->CONFIG.CTRL4,
|
|
LD_ABB_CTRL4_NORMAL_OPERATION);
|
|
LOG_DBG("REGW: NRF_APPLICATION_ABB->CONFIG.CTRL4 0x%x, V: 0x%lx",
|
|
(uint32_t)&NRF_APPLICATION_ABB->CONFIG.CTRL4,
|
|
LD_ABB_CTRL4_NORMAL_OPERATION);
|
|
#else
|
|
NRF_ABB->CONFIG.CTRL4 = LD_ABB_CTRL4_NORMAL_OPERATION;
|
|
NRF_APPLICATION_ABB->CONFIG.CTRL4 = LD_ABB_CTRL4_NORMAL_OPERATION;
|
|
#endif
|
|
|
|
if (NRF_DOMAIN == NRF_DOMAIN_SECURE) {
|
|
if (downscaling) {
|
|
ld_dvfs_secure_stop_increased_power_consumption();
|
|
}
|
|
}
|
|
#endif
|
|
}
|