Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/firmware/nrf_ironside/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_CALL call.c)
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_BOOT_REPORT boot_report.c)
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_CPUCONF_SERVICE cpuconf.c)
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_UPDATE_SERVICE update.c)
zephyr_library_sources_ifdef(CONFIG_NRF_IRONSIDE_DVFS_SERVICE dvfs.c)
17 changes: 17 additions & 0 deletions drivers/firmware/nrf_ironside/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,21 @@ config NRF_IRONSIDE_BOOT_REPORT
help
Support for parsing the Boot Report populated by Nordic IRONside firmware.

config NRF_IRONSIDE_DVFS_SERVICE
bool "IRONside DVFS service"
depends on SOC_NRF54H20_CPUAPP
select NRF_IRONSIDE_CALL
help
Service used to handle DVFS operating point requests.

if NRF_IRONSIDE_DVFS_SERVICE

config NRF_IRONSIDE_DVFS_OPPOINT_CHANGE_MUTEX_TIMEOUT_MS
int "IRONSside DVFS change oppoint mutex timeout"
default 100
help
Maximum tiemout when waiting for DVFS oppoint change mutex lock.
Copy link

Copilot AI Jun 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word 'tiemout' appears to be a misspelling. It should be corrected to 'timeout'.

Suggested change
Maximum tiemout when waiting for DVFS oppoint change mutex lock.
Maximum timeout when waiting for DVFS oppoint change mutex lock.

Copilot uses AI. Check for mistakes.


endif # NRF_IRONSIDE_DVFS_SERVICE

endmenu
184 changes: 184 additions & 0 deletions drivers/firmware/nrf_ironside/dvfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <hal/nrf_hsfll.h>
#include <zephyr/kernel.h>

#include <zephyr/drivers/firmware/nrf_ironside/dvfs.h>
#include <zephyr/drivers/firmware/nrf_ironside/call.h>

static enum ironside_dvfs_oppoint current_dvfs_oppoint = IRONSIDE_DVFS_OPP_HIGH;

struct dvfs_hsfll_data_t {
uint32_t new_f_mult;
uint32_t new_f_trim_entry;
uint32_t max_hsfll_freq;
};

static const struct dvfs_hsfll_data_t dvfs_hsfll_data[] = {
/* ABB oppoint 0.8V */
{
.new_f_mult = 20,
.new_f_trim_entry = 0,
.max_hsfll_freq = 320000000,
},
/* ABB oppoint 0.6V */
{
.new_f_mult = 8,
.new_f_trim_entry = 2,
.max_hsfll_freq = 128000000,
},
/* ABB oppoint 0.5V */
{
.new_f_mult = 4,
.new_f_trim_entry = 3,
.max_hsfll_freq = 64000000,
},
};

BUILD_ASSERT(ARRAY_SIZE(dvfs_hsfll_data) == (IRONSIDE_DVFS_OPPOINT_COUNT),
"dvfs_hsfll_data size must match number of DVFS oppoints");

/**
* @brief Check if the requested oppoint change operation is downscaling.
*
* @param target_freq_setting The target oppoint to check.
* @return true if the current oppoint is higher than the target, false otherwise.
*/
static bool ironside_dvfs_is_downscaling(enum ironside_dvfs_oppoint target_freq_setting)
{
return current_dvfs_oppoint < target_freq_setting;
}

/**
* @brief Configure hsfll depending on selected oppoint
*
* @param enum oppoint target operation point
*/
static void ironside_dvfs_configure_hsfll(enum ironside_dvfs_oppoint oppoint)
{
nrf_hsfll_trim_t hsfll_trim = {};
uint8_t freq_trim_idx = dvfs_hsfll_data[oppoint].new_f_trim_entry;

#if defined(NRF_APPLICATION)
hsfll_trim.vsup = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.VSUP;
hsfll_trim.coarse = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.COARSE[freq_trim_idx];
hsfll_trim.fine = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.FINE[freq_trim_idx];
#if NRF_HSFLL_HAS_TCOEF_TRIM
hsfll_trim.tcoef = NRF_FICR->TRIM.APPLICATION.HSFLL.TRIM.TCOEF;
#endif
#else
#error "Only application core is supported for DVFS"
#endif

nrf_hsfll_clkctrl_mult_set(NRF_HSFLL, dvfs_hsfll_data[oppoint].new_f_mult);
nrf_hsfll_trim_set(NRF_HSFLL, &hsfll_trim);
nrf_barrier_w();

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);
}

/* Function handling steps for DVFS oppoint change. */
static void ironside_dvfs_prepare_to_scale(enum ironside_dvfs_oppoint dvfs_oppoint)
{
if (ironside_dvfs_is_downscaling(dvfs_oppoint)) {
ironside_dvfs_configure_hsfll(dvfs_oppoint);
}
}

/* Update MDK variable which is used by nrfx_coredep_delay_us (k_busy_wait). */
static void ironside_dvfs_update_core_clock(enum ironside_dvfs_oppoint dvfs_oppoint)
{
extern uint32_t SystemCoreClock;

SystemCoreClock = dvfs_hsfll_data[dvfs_oppoint].max_hsfll_freq;
}

/* Perform scaling finnish procedure. */
static void ironside_dvfs_change_oppoint_complete(enum ironside_dvfs_oppoint dvfs_oppoint)
{
if (!ironside_dvfs_is_downscaling(dvfs_oppoint)) {
ironside_dvfs_configure_hsfll(dvfs_oppoint);
}

current_dvfs_oppoint = dvfs_oppoint;
ironside_dvfs_update_core_clock(dvfs_oppoint);
}

/**
* @brief Check if ABB analog part is locked.
*
* @param abb Pointer to ABB peripheral.
*
* @return true if ABB is locked, false otherwise.
*/
static inline bool ironside_dvfs_is_abb_locked(NRF_ABB_Type *abb)
{
/* Check if ABB analog part is locked. */
return ((abb->STATUSANA & ABB_STATUSANA_LOCKED_Msk) != 0);
}

/**
* @brief Request DVFS oppoint change from IRONside secure domain.
* This function will send a request over IPC to the IRONside secure domain
* This function is synchronous and will return when the request is completed.
*
* @param oppoint @ref enum ironside_dvfs_oppoint
* @return int
*/
static int ironside_dvfs_req_oppoint(enum ironside_dvfs_oppoint oppoint)
{
int err;

struct ironside_call_buf *const buf = ironside_call_alloc();

buf->id = IRONSIDE_CALL_ID_DVFS_SERVICE_V0;
buf->args[IRONSIDE_DVFS_SERVICE_OPPOINT_IDX] = oppoint;

ironside_call_dispatch(buf);

if (buf->status == IRONSIDE_CALL_STATUS_RSP_SUCCESS) {
err = buf->args[IRONSIDE_DVFS_SERVICE_RETCODE_IDX];
} else {
err = buf->status;
}

ironside_call_release(buf);

return err;
}

int ironside_dvfs_change_oppoint(enum ironside_dvfs_oppoint dvfs_oppoint)
{
int status = 0;

if (!ironside_dvfs_is_oppoint_valid(dvfs_oppoint)) {
return -IRONSIDE_DVFS_ERROR_WRONG_OPPOINT;
}

if (!ironside_dvfs_is_abb_locked(NRF_ABB)) {
return -IRONSIDE_DVFS_ERROR_BUSY;
}

if (dvfs_oppoint == current_dvfs_oppoint) {
return status;
}

if (k_is_in_isr()) {
return -IRONSIDE_DVFS_ERROR_ISR_NOT_ALLOWED;
}

ironside_dvfs_prepare_to_scale(dvfs_oppoint);

status = ironside_dvfs_req_oppoint(dvfs_oppoint);

if (status != 0) {
return status;
}
ironside_dvfs_change_oppoint_complete(dvfs_oppoint);

return status;
}
92 changes: 92 additions & 0 deletions include/zephyr/drivers/firmware/nrf_ironside/dvfs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_DVFS_H_
#define ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_DVFS_H_

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <errno.h>

enum ironside_dvfs_oppoint {
IRONSIDE_DVFS_OPP_HIGH = 0,
IRONSIDE_DVFS_OPP_MEDLOW = 1,
IRONSIDE_DVFS_OPP_LOW = 2
};

/**
* @brief Number of DVFS oppoints supported by IRONside.
*
* This is the number of different DVFS oppoints that can be set on IRONside.
* The oppoints are defined in the `ironside_dvfs_oppoint` enum.
*/
#define IRONSIDE_DVFS_OPPOINT_COUNT (3)

/**
* @name IRONside DVFS service error codes.
* @{
*/

/** The requested DVFS oppoint is not allowed. */
#define IRONSIDE_DVFS_ERROR_WRONG_OPPOINT (1)
/** Waiting for mutex lock timed out, or hardware is busy. */
#define IRONSIDE_DVFS_ERROR_BUSY (2)
/** There is configuration error in the DVFS service. */
#define IRONSIDE_DVFS_ERROR_OPPOINT_DATA (3)
/** The caller does not have permission to change the DVFS oppoint. */
#define IRONSIDE_DVFS_ERROR_PERMISSION (4)
/** The requested DVFS oppoint is already set, no change needed. */
#define IRONSIDE_DVFS_ERROR_NO_CHANGE_NEEDED (5)
/** The operation timed out, possibly due to a hardware issue. */
#define IRONSIDE_DVFS_ERROR_TIMEOUT (6)
/** The DVFS oppoint change operation is not allowed in the ISR context. */
#define IRONSIDE_DVFS_ERROR_ISR_NOT_ALLOWED (7)

Check notice on line 47 in include/zephyr/drivers/firmware/nrf_ironside/dvfs.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

include/zephyr/drivers/firmware/nrf_ironside/dvfs.h:47 -#define IRONSIDE_DVFS_ERROR_WRONG_OPPOINT (1) +#define IRONSIDE_DVFS_ERROR_WRONG_OPPOINT (1) /** Waiting for mutex lock timed out, or hardware is busy. */ -#define IRONSIDE_DVFS_ERROR_BUSY (2) +#define IRONSIDE_DVFS_ERROR_BUSY (2) /** There is configuration error in the DVFS service. */ -#define IRONSIDE_DVFS_ERROR_OPPOINT_DATA (3) +#define IRONSIDE_DVFS_ERROR_OPPOINT_DATA (3) /** The caller does not have permission to change the DVFS oppoint. */ -#define IRONSIDE_DVFS_ERROR_PERMISSION (4) +#define IRONSIDE_DVFS_ERROR_PERMISSION (4) /** The requested DVFS oppoint is already set, no change needed. */ #define IRONSIDE_DVFS_ERROR_NO_CHANGE_NEEDED (5) /** The operation timed out, possibly due to a hardware issue. */ -#define IRONSIDE_DVFS_ERROR_TIMEOUT (6) +#define IRONSIDE_DVFS_ERROR_TIMEOUT (6) /** The DVFS oppoint change operation is not allowed in the ISR context. */ -#define IRONSIDE_DVFS_ERROR_ISR_NOT_ALLOWED (7) +#define IRONSIDE_DVFS_ERROR_ISR_NOT_ALLOWED (7)
/**
* @}
*/

/* IRONside call identifiers with implicit versions.
*
* With the initial "version 0", the service ABI is allowed to break until the
* first production release of IRONside SE.
*/
#define IRONSIDE_CALL_ID_DVFS_SERVICE_V0 3

/* Index of the DVFS oppoint within the service buffer. */
#define IRONSIDE_DVFS_SERVICE_OPPOINT_IDX (0)
/* Index of the return code within the service buffer. */
#define IRONSIDE_DVFS_SERVICE_RETCODE_IDX (0)

/**
* @brief Change the current DVFS oppoint.
*
* This function will request a change of the current DVFS oppoint to the
* specified value. It will block until the change is applied.
*
* @param dvfs_oppoint The new DVFS oppoint to set.
* @return int 0 on success, negative error code on failure.
*/
int ironside_dvfs_change_oppoint(enum ironside_dvfs_oppoint dvfs_oppoint);

/**
* @brief Check if the given oppoint is valid.
*
* @param dvfs_oppoint The oppoint to check.
* @return true if the oppoint is valid, false otherwise.
*/
static inline bool ironside_dvfs_is_oppoint_valid(enum ironside_dvfs_oppoint dvfs_oppoint)
{
if (dvfs_oppoint != IRONSIDE_DVFS_OPP_HIGH &&
dvfs_oppoint != IRONSIDE_DVFS_OPP_MEDLOW &&
dvfs_oppoint != IRONSIDE_DVFS_OPP_LOW) {

Check notice on line 85 in include/zephyr/drivers/firmware/nrf_ironside/dvfs.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

include/zephyr/drivers/firmware/nrf_ironside/dvfs.h:85 - if (dvfs_oppoint != IRONSIDE_DVFS_OPP_HIGH && - dvfs_oppoint != IRONSIDE_DVFS_OPP_MEDLOW && + if (dvfs_oppoint != IRONSIDE_DVFS_OPP_HIGH && dvfs_oppoint != IRONSIDE_DVFS_OPP_MEDLOW &&
return false;
}

return true;
}

#endif /* ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_DVFS_H_ */
4 changes: 2 additions & 2 deletions modules/hal_nordic/nrfs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ if(CONFIG_NRFS)

zephyr_include_directories(${INC_DIR})
zephyr_include_directories(${INC_DIR}/services)
zephyr_include_directories(${HELPERS_DIR})
zephyr_include_directories_ifdef(CONFIG_NRFS_HAS_DVFS_SERVICE ${HELPERS_DIR})
zephyr_include_directories(.)
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/backends)
zephyr_include_directories_ifdef(CONFIG_NRFS_DVFS_LOCAL_DOMAIN ${CMAKE_CURRENT_SOURCE_DIR}/dvfs)

zephyr_library_sources(${HELPERS_DIR}/dvfs_oppoint.c)
zephyr_library_sources_ifdef(CONFIG_NRFS_HAS_DVFS_SERVICE ${HELPERS_DIR}/dvfs_oppoint.c)

if(CONFIG_NRFS_LOCAL_DOMAIN)
zephyr_library_sources_ifdef(CONFIG_NRFS_AUDIOPLL_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_audiopll.c)
Expand Down
Loading