-
Notifications
You must be signed in to change notification settings - Fork 716
[nrf fromlist] drivers: nrf_ironside dvfs service #2921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
|
||
/** | ||
* @} | ||
*/ | ||
|
||
/* 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
|
||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
#endif /* ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_FIRMWARE_NRF_IRONSIDE_DVFS_H_ */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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'.
Copilot uses AI. Check for mistakes.