manifest: Adding nRF Services library

Adding nRF Services library to the hal-nordic repo

Signed-off-by: Rafal Dyla <rafal.dyla@nordicsemi.no>
This commit is contained in:
Rafal Dyla 2024-03-14 19:42:21 +01:00 committed by Fabio Baltieri
commit 13aa26eac2
18 changed files with 1368 additions and 2 deletions

View file

@ -72,7 +72,7 @@
shared_ram20_region: memory@2f88f000 { shared_ram20_region: memory@2f88f000 {
compatible = "nordic,owned-memory"; compatible = "nordic,owned-memory";
reg = <0x2f88f000 DT_SIZE_K(4)>; reg = <0x2f88f000 DT_SIZE_K(4)>;
status = "disabled"; status = "okay";
perm-read; perm-read;
perm-write; perm-write;
#address-cells = <1>; #address-cells = <1>;

View file

@ -38,6 +38,7 @@
sw1 = &button1; sw1 = &button1;
sw2 = &button2; sw2 = &button2;
sw3 = &button3; sw3 = &button3;
ipc-to-cpusys = &cpuapp_cpusys_ipc;
}; };
buttons { buttons {
@ -117,6 +118,10 @@
status = "okay"; status = "okay";
}; };
&cpusys_vevif {
status = "okay";
};
&cpusec_cpuapp_ipc { &cpusec_cpuapp_ipc {
mbox-names = "tx", "rx"; mbox-names = "tx", "rx";
tx-region = <&cpuapp_cpusec_ipc_shm>; tx-region = <&cpuapp_cpusec_ipc_shm>;
@ -133,6 +138,7 @@ ipc0: &cpuapp_cpurad_ipc {
}; };
&cpuapp_cpusys_ipc { &cpuapp_cpusys_ipc {
status = "okay";
mbox-names = "rx", "tx"; mbox-names = "rx", "tx";
tx-region = <&cpuapp_cpusys_ipc_shm>; tx-region = <&cpuapp_cpusys_ipc_shm>;
rx-region = <&cpusys_cpuapp_ipc_shm>; rx-region = <&cpusys_cpuapp_ipc_shm>;

View file

@ -29,6 +29,9 @@
zephyr,bt-hci-ipc = &ipc0; zephyr,bt-hci-ipc = &ipc0;
nordic,802154-spinel-ipc = &ipc0; nordic,802154-spinel-ipc = &ipc0;
}; };
aliases {
ipc-to-cpusys = &cpurad_cpusys_ipc;
};
}; };
&shared_ram3x_region { &shared_ram3x_region {
@ -51,6 +54,10 @@
status = "okay"; status = "okay";
}; };
&cpusys_vevif {
status = "okay";
};
&cpusec_cpurad_ipc { &cpusec_cpurad_ipc {
mbox-names = "tx", "rx"; mbox-names = "tx", "rx";
tx-region = <&cpurad_cpusec_ipc_shm>; tx-region = <&cpurad_cpusec_ipc_shm>;
@ -67,6 +74,7 @@ ipc0: &cpuapp_cpurad_ipc {
}; };
&cpurad_cpusys_ipc { &cpurad_cpusys_ipc {
status = "okay";
mbox-names = "rx", "tx"; mbox-names = "rx", "tx";
tx-region = <&cpurad_cpusys_ipc_shm>; tx-region = <&cpurad_cpusys_ipc_shm>;
rx-region = <&cpusys_cpurad_ipc_shm>; rx-region = <&cpusys_cpurad_ipc_shm>;

View file

@ -6,12 +6,13 @@ if (CONFIG_NRF_802154_RADIO_DRIVER OR CONFIG_NRF_802154_SERIALIZATION)
endif (CONFIG_NRF_802154_RADIO_DRIVER OR CONFIG_NRF_802154_SERIALIZATION) endif (CONFIG_NRF_802154_RADIO_DRIVER OR CONFIG_NRF_802154_SERIALIZATION)
add_subdirectory_ifdef(CONFIG_HAS_NRFX nrfx) add_subdirectory_ifdef(CONFIG_HAS_NRFX nrfx)
add_subdirectory_ifdef(CONFIG_HAS_NRFS nrfs)
if(CONFIG_NRF_REGTOOL_GENERATE_UICR) if(CONFIG_NRF_REGTOOL_GENERATE_UICR)
list(APPEND nrf_regtool_components GENERATE:UICR) list(APPEND nrf_regtool_components GENERATE:UICR)
endif() endif()
if(DEFINED nrf_regtool_components) if(DEFINED nrf_regtool_components)
find_package(nrf-regtool 5.1.0 find_package(nrf-regtool 5.2.0
COMPONENTS ${nrf_regtool_components} COMPONENTS ${nrf_regtool_components}
PATHS ${CMAKE_CURRENT_LIST_DIR}/nrf-regtool PATHS ${CMAKE_CURRENT_LIST_DIR}/nrf-regtool
NO_CMAKE_PATH NO_CMAKE_PATH

View file

@ -235,5 +235,6 @@ endif # NRF_802154_RADIO_DRIVER || NRF_802154_SERIALIZATION
endmenu # HAS_NORDIC_DRIVERS endmenu # HAS_NORDIC_DRIVERS
rsource "nrfs/Kconfig"
rsource "nrfx/Kconfig" rsource "nrfx/Kconfig"
rsource "Kconfig.nrf_regtool" rsource "Kconfig.nrf_regtool"

View file

@ -0,0 +1,41 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
if(CONFIG_NRFS)
zephyr_library()
if(NOT DEFINED NRFS_DIR)
set(NRFS_DIR ${ZEPHYR_CURRENT_MODULE_DIR}/nrfs CACHE PATH "nrfs directory")
endif()
set(INC_DIR ${NRFS_DIR}/include)
set(SRC_DIR ${NRFS_DIR}/src)
set(HELPERS_DIR ${NRFS_DIR}/helpers)
zephyr_include_directories(${INC_DIR})
zephyr_include_directories(${INC_DIR}/services)
zephyr_include_directories(${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)
if(CONFIG_NRFS_LOCAL_DOMAIN)
zephyr_library_sources_ifdef(CONFIG_NRFS_CLOCK_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_clock.c)
zephyr_library_sources_ifdef(CONFIG_NRFS_DIAG_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_diag.c)
zephyr_library_sources_ifdef(CONFIG_NRFS_DVFS_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_dvfs.c)
zephyr_library_sources_ifdef(CONFIG_NRFS_MRAM_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_mram.c)
zephyr_library_sources_ifdef(CONFIG_NRFS_PMIC_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_pmic.c)
zephyr_library_sources_ifdef(CONFIG_NRFS_RESET_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_reset.c)
zephyr_library_sources_ifdef(CONFIG_NRFS_TEMP_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_temp.c)
zephyr_library_sources_ifdef(CONFIG_NRFS_VBUS_DETECTOR_SERVICE_ENABLED ${SRC_DIR}/services/nrfs_usb.c)
zephyr_library_sources(${SRC_DIR}/internal/nrfs_dispatcher.c)
add_subdirectory_ifdef(CONFIG_NRFS_DVFS_LOCAL_DOMAIN dvfs)
if(CONFIG_NRFS_DIAG_SERVICE_ENABLED)
message(WARNING "This service is for Nordic Semiconductor INTERNAL purposes ONLY. Use it with caution due to risk of hardware damage!")
endif()
endif()
zephyr_library_sources_ifdef(CONFIG_NRFS_LOCAL_DOMAIN backends/nrfs_backend_ipc_service.c)
endif()

View file

@ -0,0 +1,116 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config HAS_NRFS
bool
menu "nRF Services"
depends on HAS_NRFS
config NRFS_HAS_CLOCK_SERVICE
bool
config NRFS_HAS_CONST_LATENCY_SERVICE
bool
config NRFS_HAS_DIAG_SERVICE
bool
config NRFS_HAS_DVFS_SERVICE
bool
config NRFS_HAS_MRAM_SERVICE
bool
config NRFS_HAS_PMIC_SERVICE
bool
config NRFS_HAS_RESET_SERVICE
bool
config NRFS_HAS_TEMP_SERVICE
bool
config NRFS_HAS_VBUS_DETECTOR_SERVICE
bool
config NRFS
bool "nRF Services Support"
select NRFS_LOCAL_DOMAIN if (SOC_NRF54H20_CPUAPP || SOC_NRF54H20_CPURAD)
depends on HAS_NRFS
help
This option enables the nRF Services library.
if NRFS
config NRFS_LOCAL_DOMAIN
bool "nRF Services Local Domain Support"
depends on $(dt_alias_enabled,ipc-to-cpusys)
select IPC_SERVICE
select MBOX
select EVENTS
select REBOOT
help
This option enables the nRF Services Local Domain libraries.
config NRFS_DVFS_LOCAL_DOMAIN
bool "Local domain that supports DVFS"
depends on NRFS_LOCAL_DOMAIN
depends on NRFS_DVFS_SERVICE_ENABLED
default y if NRFS_DVFS_SERVICE_ENABLED
menu "Enabled Services"
module = NRFS
module-str = nRF-Services
source "subsys/logging/Kconfig.template.log_config"
config NRFS_RESET_SERVICE_ENABLED
bool "Reset service"
depends on NRFS_HAS_RESET_SERVICE
config NRFS_MRAM_SERVICE_ENABLED
bool "MRAM latency service"
depends on NRFS_HAS_MRAM_SERVICE
config NRFS_TEMP_SERVICE_ENABLED
bool "Temperature service"
depends on NRFS_HAS_TEMP_SERVICE
default y
config NRFS_VBUS_DETECTOR_SERVICE_ENABLED
bool "VBUS detector for the USB peripheral"
depends on NRFS_HAS_VBUS_DETECTOR_SERVICE
default y
config NRFS_CONST_LATENCY_SERVICE_ENABLED
bool "DPPI constant latency service"
depends on NRFS_HAS_CONST_LATENCY_SERVICE
default y
config NRFS_PMIC_SERVICE_ENABLED
bool "PMIC service"
depends on NRFS_HAS_PMIC_SERVICE
config NRFS_DVFS_SERVICE_ENABLED
bool "DVFS service"
depends on NRFS_HAS_DVFS_SERVICE
default y if SOC_NRF54H20_CPUAPP
config NRFS_DIAG_SERVICE_ENABLED
bool "System Diagnostics service (only for development purposes)"
depends on NRFS_HAS_DIAG_SERVICE
config NRFS_CLOCK_SERVICE_ENABLED
bool "Clock service"
depends on NRFS_HAS_CLOCK_SERVICE
default y
endmenu
rsource "backends/Kconfig"
if NRFS_DVFS_LOCAL_DOMAIN
rsource "dvfs/Kconfig"
endif # NRFS_DVFS_LOCAL_DOMAIN
endif # NRFS
endmenu

View file

@ -0,0 +1,28 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
menu "NRFS backend settings"
module = NRFS_BACKEND
module-str = NRFS backend
source "subsys/logging/Kconfig.template.log_config"
config NRFS_BACKEND_IPC_SERVICE_INIT_PRIO
int "Initialization priority for NRFS IPC backend"
default 51
help
This should be higher than priority of other drivers/subsystems
used by NRFS backend. For example MBOX_INIT_PRIORITY which is 50.
config NRFS_MAX_BACKEND_PACKET_SIZE
int "Maximum IPC data packet size in bytes"
range 8 128
default 32
config NRFS_BACKEND_TX_MSG_QUEUE_SIZE
int "Size of TX buffer message queue size"
range 1 16
default 8
endmenu

View file

@ -0,0 +1,226 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "nrfs_backend_ipc_service.h"
#include <internal/nrfs_backend.h>
#include <internal/nrfs_dispatcher.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/sys_clock.h>
#include <zephyr/logging/log.h>
#include <zephyr/init.h>
LOG_MODULE_REGISTER(NRFS_BACKEND, CONFIG_NRFS_BACKEND_LOG_LEVEL);
#define MAX_PACKET_DATA_SIZE (CONFIG_NRFS_MAX_BACKEND_PACKET_SIZE)
K_MSGQ_DEFINE(ipc_transmit_msgq, sizeof(struct ipc_data_packet),
CONFIG_NRFS_BACKEND_TX_MSG_QUEUE_SIZE, 4);
static struct k_work backend_send_work;
static struct ipc_data_packet rx_data;
static struct ipc_data_packet tx_data;
static void ipc_sysctrl_ept_bound(void *priv);
static void ipc_sysctrl_ept_recv(const void *data, size_t size, void *priv);
static K_EVENT_DEFINE(ipc_connected_event);
#define IPC_INIT_DONE_EVENT (0x01)
struct ipc_channel_config {
const struct device *ipc_instance;
struct ipc_ept_cfg *endpoint_config;
struct ipc_ept ipc_ept;
atomic_t status;
bool enabled;
};
static struct ipc_ept_cfg ipc_sysctrl_ept_cfg = {
.name = "ipc_to_sysctrl",
.cb = {
.bound = ipc_sysctrl_ept_bound,
.received = ipc_sysctrl_ept_recv,
},
};
static struct ipc_channel_config ipc_cpusys_channel_config = {
.ipc_instance = DEVICE_DT_GET(DT_ALIAS(ipc_to_cpusys)),
.endpoint_config = &ipc_sysctrl_ept_cfg,
.status = ATOMIC_INIT(NOT_CONNECTED),
.enabled = true
};
/**
* @brief nrfs backend error handler
*
* @param error_id The id of an error to handle.
* @param error additional error code if needed, if not needed use 0.
* @param fatal true if fatal error and needs special handling
*/
__weak void nrfs_backend_error_handler(enum nrfs_backend_error error_id, int error, bool fatal)
{
switch (error_id) {
case NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG:
LOG_ERR("Received data is too long. Config error.");
break;
case NRFS_ERROR_NO_DATA_RECEIVED:
LOG_ERR("No data in received message!");
break;
case NRFS_ERROR_IPC_OPEN_INSTANCE:
LOG_ERR("IPC open instance failure with error: %d", error);
break;
case NRFS_ERROR_IPC_REGISTER_ENDPOINT:
LOG_ERR("IPC register endpoint failure with error: %d", error);
break;
default:
LOG_ERR("Undefined error id: %d, error cause: %d", error_id, error);
break;
}
if (fatal) {
nrfs_backend_fatal_error_handler(error_id);
}
}
static void ipc_sysctrl_ept_bound(void *priv)
{
LOG_INF("Bound to sysctrl.");
k_event_post(&ipc_connected_event, IPC_INIT_DONE_EVENT);
atomic_set(&ipc_cpusys_channel_config.status, CONNECTED);
}
static void ipc_sysctrl_ept_recv(const void *data, size_t size, void *priv)
{
__ASSERT(size <= MAX_PACKET_DATA_SIZE, "Received data is too long. Config error.");
if (size <= MAX_PACKET_DATA_SIZE) {
rx_data.channel_id = IPC_CPUSYS_CHANNEL_ID;
rx_data.size = size;
if (data) {
memcpy(rx_data.data, (uint8_t *)data, size);
nrfs_dispatcher_notify(&rx_data.data, rx_data.size);
} else {
nrfs_backend_error_handler(NRFS_ERROR_NO_DATA_RECEIVED, 0, false);
}
} else {
nrfs_backend_error_handler(NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG, 0, true);
}
}
static void nrfs_backend_send_work(struct k_work *item)
{
static struct ipc_data_packet data_to_send;
LOG_DBG("Sending data from workqueue");
while (k_msgq_get(&ipc_transmit_msgq, &data_to_send, K_NO_WAIT) == 0) {
ipc_service_send(&ipc_cpusys_channel_config.ipc_ept, &tx_data.data, tx_data.size);
}
}
/**
* @brief Initialize ipc channel
*
* @return -EINVAL when instance configuration is invalid.
* @return -EIO when no backend is registered.
* @return -EALREADY when the instance is already opened (or being opened).
* @return -EBUSY when the instance is busy.
* @return 0 on success
*/
static int ipc_channel_init(void)
{
struct ipc_channel_config *ch_cfg;
int ret = 0;
k_work_init(&backend_send_work, nrfs_backend_send_work);
ch_cfg = &ipc_cpusys_channel_config;
ret = ipc_service_open_instance(ch_cfg->ipc_instance);
if ((ret < 0) && (ret != -EALREADY)) {
nrfs_backend_error_handler(NRFS_ERROR_IPC_OPEN_INSTANCE, ret, false);
return ret;
}
LOG_INF("ipc_service_open_instance() done.");
ret = ipc_service_register_endpoint(ch_cfg->ipc_instance,
&ch_cfg->ipc_ept,
ch_cfg->endpoint_config);
if (ret < 0) {
nrfs_backend_error_handler(NRFS_ERROR_IPC_REGISTER_ENDPOINT, ret, false);
return ret;
}
LOG_INF("ipc_service_register_endpoint() done.");
return ret;
}
nrfs_err_t nrfs_backend_send(void *message, size_t size)
{
return nrfs_backend_send_ex(message, size, K_NO_WAIT, false);
}
nrfs_err_t nrfs_backend_send_ex(void *message, size_t size, k_timeout_t timeout, bool high_prio)
{
if (atomic_get(&ipc_cpusys_channel_config.status) != CONNECTED) {
LOG_WRN("Backend not yet connected to sysctrl");
return NRFS_ERR_INVALID_STATE;
}
if (size <= MAX_PACKET_DATA_SIZE) {
int err;
tx_data.channel_id = IPC_CPUSYS_CHANNEL_ID;
tx_data.size = size;
memcpy(tx_data.data, (uint8_t *)message, size);
err = k_msgq_put(&ipc_transmit_msgq, &tx_data, timeout);
if (err) {
return NRFS_ERR_IPC;
}
err = k_work_submit(&backend_send_work);
return err >= 0 ? 0 : NRFS_ERR_IPC;
}
LOG_ERR("Trying to send %d bytes where max is %d.", size, MAX_PACKET_DATA_SIZE);
return NRFS_ERR_IPC;
}
bool nrfs_backend_connected(void)
{
return atomic_get(&ipc_cpusys_channel_config.status) == CONNECTED;
}
int nrfs_backend_wait_for_connection(k_timeout_t timeout)
{
uint32_t events;
if (nrfs_backend_connected()) {
return 0;
}
events = k_event_wait(&ipc_connected_event, IPC_INIT_DONE_EVENT, false, timeout);
return (events == IPC_INIT_DONE_EVENT ? 0 : (-EAGAIN));
}
__weak void nrfs_backend_fatal_error_handler(enum nrfs_backend_error error_id)
{
LOG_ERR("Fatal error: %d rebooting...", error_id);
sys_reboot(SYS_REBOOT_WARM);
}
SYS_INIT(ipc_channel_init, POST_KERNEL, CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO);

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef NRFS_BACKEND_IPC_SERVICE_H
#define NRFS_BACKEND_IPC_SERVICE_H
#include <stdint.h>
#include <nrfs_common.h>
#include <zephyr/ipc/ipc_service.h>
#include <zephyr/kernel.h>
#ifdef __cplusplus
extern "C" {
#endif
struct __packed ipc_data_packet {
uint16_t channel_id;
uint16_t size;
uint8_t data[CONFIG_NRFS_MAX_BACKEND_PACKET_SIZE];
};
enum ipc_channel_status {
NOT_CONNECTED = 0,
CONNECTED = 1
};
enum nrfs_backend_error {
NRFS_ERROR_EPT_RECEIVE_QUEUE_ERROR = 0,
NRFS_ERROR_EPT_RECEIVE_DATA_TOO_LONG,
NRFS_ERROR_IPC_CHANNEL_INIT,
NRFS_ERROR_SEND_DATA,
NRFS_ERROR_NO_DATA_RECEIVED,
NRFS_ERROR_IPC_OPEN_INSTANCE,
NRFS_ERROR_IPC_REGISTER_ENDPOINT,
NRFS_ERROR_BACKEND_NOT_CONNECTED,
NRFS_ERROR_COUNT
};
#define IPC_CPUSYS_CHANNEL_ID 0x5C
/**
* @brief function to check if backend is connected to sysctrl
*
* @return true Backend connected.
* @return false Backend not connected.
*/
bool nrfs_backend_connected(void);
/**
* @brief this function will block until connection or timeout expires
*
* @param timeout
*
* @return 0 Connection done.
* @return -EAGAIN Waiting period timed out.
*/
int nrfs_backend_wait_for_connection(k_timeout_t timeout);
/**
* @brief Extended function for sending a message over the chosen transport backend.
*
* This function is used by services to send requests to the System Controller.
*
* @param[in] message Pointer to the message payload.
* @param[in] size Message payload size.
* @param[in] timeout Non-negative waiting period to add the message,
* or one of the special values K_NO_WAIT and K_FOREVER.
* @param[in] high_prio True if message should be sent with higher priority.
*
* @retval NRFS_SUCCESS Message sent successfully.
* @retval NRFS_ERR_IPC Backend returned error during message sending.
*/
nrfs_err_t nrfs_backend_send_ex(void *message, size_t size, k_timeout_t timeout, bool high_prio);
/**
* @brief Fatal error handler for unrecoverable errors
*
* This is weak function so it can be overridden if needed.
* Error is considered fatal when there is no option to send message to sysctrl
* even after retry. Communication with sysctrl is crucial for system to work properly.
*
* @param error_id parameter to identify error.
*/
void nrfs_backend_fatal_error_handler(enum nrfs_backend_error error_id);
#ifdef __cplusplus
}
#endif
#endif /* NRFS_BACKEND_IPC_SERVICE_H */

View file

@ -0,0 +1,5 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
zephyr_library_sources_ifdef(CONFIG_NRFS_DVFS_LOCAL_DOMAIN ld_dvfs.c
ld_dvfs_handler.c)

View file

@ -0,0 +1,40 @@
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
menu "Local domain DVFS library"
module = LOCAL_DOMAIN_DVFS_LIB
module-str = Local domain DVFS library
source "subsys/logging/Kconfig.template.log_config"
config NRFS_LOCAL_DOMAIN_DVFS_TEST
bool "Local domain DVFS test"
help
Disable hw registers interaction for testing.
config NRFS_LOCAL_DOMAIN_DVFS_SCALE_DOWN_AFTER_INIT
bool "Local domain scale down after init"
help
Request lowest oppoint after DVFS initialization.
config NRFS_LOCAL_DOMAIN_DOWNSCALE_SAFETY_TIMEOUT_US
int "Voltage downscale procedure safety timeout in us"
range 1 10000000
default 1000000 if (NRFS_LOCAL_DOMAIN_DVFS_TEST || LOG)
default 1500
config NRFS_LOCAL_DOMAIN_DVFS_HANDLER_TASK_STACK_SIZE
int "Stack size used for DVFS handling task"
range 256 2048
default 1024 if LOG
default 512
config NRFS_LOCAL_DOMAIN_DVFS_HANDLER_TASK_PRIORITY
int "Priority of DVFS handling task"
range -16 NUM_PREEMPT_PRIORITIES
default 0
endmenu

View file

@ -0,0 +1,354 @@
/*
* 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_INF("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_INF("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;
#ifdef CONFIG_SOC_NRF54H20_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
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef LD_DVFS_H
#define LD_DVFS_H
#include <dvfs_oppoint.h>
#include <nrfs_common.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Function for initializing the Dynamic Voltage and Frequency Scaling service
* from LD perspective..
*
*/
void ld_dvfs_init(void);
/**
* @brief Function for clearing the zero bias
*
*/
void ld_dvfs_clear_zbb(void);
/**
* @brief Configure ABB registers to transition process.
*
* @param transient_opp current operation point
* @param curr_targ_opp target operation point
*/
void ld_dvfs_configure_abb_for_transition(enum dvfs_frequency_setting transient_opp,
enum dvfs_frequency_setting curr_targ_opp);
/**
* @brief Configure hsfll depending on selected oppoint
*
* @param enum oppoint target operation point
* @return 0 value indicates no error.
* @return -EINVAL invalid oppoint or domain.
* @return -ETIMEDOUT frequency change took more than HSFLL_FREQ_CHANGE_MAX_DELAY_MS
*/
int32_t ld_dvfs_configure_hsfll(enum dvfs_frequency_setting oppoint);
/**
* @brief Background process during scaling.
*
* @param downscaling indicates if down-scaling is running
*/
void ld_dvfs_scaling_background_process(bool downscaling);
/**
* @brief Last step for local domain in downscale procedure
*
* @param downscaling indicates if down-scaling is running
*/
void ld_dvfs_scaling_finish(bool downscaling);
#ifdef __cplusplus
}
#endif
#endif /* LD_DVFS_H */

View file

@ -0,0 +1,292 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ld_dvfs_handler.h"
#include "ld_dvfs.h"
#include <hal/nrf_hsfll.h>
#include <nrfs_dvfs.h>
#include <nrfs_backend_ipc_service.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(LD_DVFS_LIB, CONFIG_LOCAL_DOMAIN_DVFS_LIB_LOG_LEVEL);
static K_SEM_DEFINE(dvfs_service_sync_sem, 0, 1);
static K_SEM_DEFINE(dvfs_service_idle_sem, 0, 1);
#define DVFS_SERV_HDL_INIT_DONE_BIT_POS (0)
#define DVFS_SERV_HDL_FREQ_CHANGE_IN_PROGRESS_BIT_POS (1)
static atomic_t dvfs_service_handler_state_bits;
static enum dvfs_frequency_setting current_freq_setting;
static void dvfs_service_handler_set_state_bit(uint32_t bit_pos)
{
atomic_set_bit(&dvfs_service_handler_state_bits, bit_pos);
}
static void dvfs_service_handler_clear_state_bit(uint32_t bit_pos)
{
atomic_clear_bit(&dvfs_service_handler_state_bits, bit_pos);
}
static bool dvfs_service_handler_get_state_bit(uint32_t bit_pos)
{
return atomic_test_bit(&dvfs_service_handler_state_bits, bit_pos);
}
static bool dvfs_service_handler_init_done(void)
{
return dvfs_service_handler_get_state_bit(DVFS_SERV_HDL_INIT_DONE_BIT_POS);
}
static bool dvfs_service_handler_freq_change_in_progress(void)
{
return dvfs_service_handler_get_state_bit(DVFS_SERV_HDL_FREQ_CHANGE_IN_PROGRESS_BIT_POS);
}
static void dvfs_service_handler_nrfs_error_check(nrfs_err_t err)
{
if (err != NRFS_SUCCESS) {
LOG_ERR("Failed with nrfs error: %d", err);
}
}
static void dvfs_service_handler_error(int err)
{
if (err != 0) {
LOG_ERR("Failed with error: %d", err);
}
}
static uint32_t *get_next_context(void)
{
static uint32_t ctx;
ctx++;
return &ctx;
}
static bool dvfs_service_handler_freq_setting_allowed(enum dvfs_frequency_setting freq_setting)
{
if (freq_setting == DVFS_FREQ_HIGH || freq_setting == DVFS_FREQ_MEDLOW ||
freq_setting == DVFS_FREQ_LOW) {
return true;
}
return false;
}
static enum dvfs_frequency_setting dvfs_service_handler_get_current_oppoint(void)
{
LOG_INF("Current LD freq setting: %d", current_freq_setting);
return current_freq_setting;
}
/* Function to check if current operation is down-scaling */
static bool dvfs_service_handler_is_downscaling(enum dvfs_frequency_setting target_freq_setting)
{
if (dvfs_service_handler_freq_setting_allowed(target_freq_setting)) {
LOG_DBG("Checking if downscaling %s",
(dvfs_service_handler_get_current_oppoint() < target_freq_setting) ? "YES" :
"NO");
return dvfs_service_handler_get_current_oppoint() < target_freq_setting;
}
return false;
}
/* Function handling steps for scaling preparation. */
static void dvfs_service_handler_prepare_to_scale(enum dvfs_frequency_setting oppoint_freq)
{
LOG_INF("Prepare to scale, oppoint freq %d", oppoint_freq);
enum dvfs_frequency_setting new_oppoint = oppoint_freq;
enum dvfs_frequency_setting current_oppoint = dvfs_service_handler_get_current_oppoint();
if (new_oppoint == current_oppoint) {
LOG_INF("New oppoint is same as previous, no change");
} else {
ld_dvfs_configure_abb_for_transition(current_oppoint, new_oppoint);
if (dvfs_service_handler_is_downscaling(new_oppoint)) {
int32_t err = ld_dvfs_configure_hsfll(new_oppoint);
if (err != 0) {
dvfs_service_handler_error(err);
}
}
}
}
/* Do background job during scaling process (e.g. increased power consumption during down-scale). */
static void dvfs_service_handler_scaling_background_job(enum dvfs_frequency_setting oppoint_freq)
{
LOG_INF("Perform scaling background job if needed.");
if (dvfs_service_handler_is_downscaling(oppoint_freq)) {
k_sem_give(&dvfs_service_idle_sem);
}
}
/* Perform scaling finnish procedure. */
static void dvfs_service_handler_scaling_finish(enum dvfs_frequency_setting oppoint_freq)
{
LOG_INF("Scaling finnish oppoint freq %d", oppoint_freq);
ld_dvfs_scaling_finish(dvfs_service_handler_is_downscaling(oppoint_freq));
if (!dvfs_service_handler_is_downscaling(oppoint_freq)) {
int32_t err = ld_dvfs_configure_hsfll(oppoint_freq);
if (err != 0) {
dvfs_service_handler_error(err);
}
}
current_freq_setting = oppoint_freq;
}
/* Function to set hsfll to highest frequency when switched to ABB. */
static void dvfs_service_handler_set_initial_hsfll_config(void)
{
int32_t err = ld_dvfs_configure_hsfll(DVFS_FREQ_HIGH);
current_freq_setting = DVFS_FREQ_HIGH;
if (err != 0) {
dvfs_service_handler_error(err);
}
}
/* DVFS event handler callback function.*/
static void nrfs_dvfs_evt_handler(nrfs_dvfs_evt_t const *p_evt, void *context)
{
LOG_INF("%s", __func__);
switch (p_evt->type) {
case NRFS_DVFS_EVT_INIT_PREPARATION:
LOG_INF("DVFS handler EVT_INIT_PREPARATION");
#if defined(NRF_SECURE)
ld_dvfs_clear_zbb();
dvfs_service_handler_nrfs_error_check(
nrfs_dvfs_init_complete_request(get_next_context()));
LOG_INF("DVFS handler EVT_INIT_PREPARATION handled");
#else
LOG_ERR("DVFS handler - unexpected EVT_INIT_PREPARATION");
#endif
break;
case NRFS_DVFS_EVT_INIT_DONE:
LOG_INF("DVFS handler EVT_INIT_DONE");
dvfs_service_handler_set_initial_hsfll_config();
dvfs_service_handler_set_state_bit(DVFS_SERV_HDL_INIT_DONE_BIT_POS);
k_sem_give(&dvfs_service_sync_sem);
LOG_INF("DVFS handler EVT_INIT_DONE handled");
break;
case NRFS_DVFS_EVT_OPPOINT_REQ_CONFIRMED:
/* Optional confirmation from sysctrl, wait for oppoint.*/
LOG_INF("DVFS handler EVT_OPPOINT_REQ_CONFIRMED");
break;
case NRFS_DVFS_EVT_OPPOINT_SCALING_PREPARE:
/*Target oppoint will be received here.*/
LOG_INF("DVFS handler EVT_OPPOINT_SCALING_PREPARE");
#if !defined(NRF_SECURE)
if (dvfs_service_handler_is_downscaling(p_evt->freq)) {
#endif
dvfs_service_handler_prepare_to_scale(p_evt->freq);
dvfs_service_handler_nrfs_error_check(
nrfs_dvfs_ready_to_scale(get_next_context()));
dvfs_service_handler_scaling_background_job(p_evt->freq);
LOG_INF("DVFS handler EVT_OPPOINT_SCALING_PREPARE handled");
#if !defined(NRF_SECURE)
current_freq_setting = p_evt->freq;
} else {
LOG_ERR("DVFS handler - unexpected EVT_OPPOINT_SCALING_PREPARE");
}
#endif
break;
case NRFS_DVFS_EVT_OPPOINT_SCALING_DONE:
LOG_INF("DVFS handler EVT_OPPOINT_SCALING_DONE");
dvfs_service_handler_clear_state_bit(DVFS_SERV_HDL_FREQ_CHANGE_IN_PROGRESS_BIT_POS);
dvfs_service_handler_scaling_finish(p_evt->freq);
LOG_INF("DVFS handler EVT_OPPOINT_SCALING_DONE handled");
break;
case NRFS_DVFS_EVT_REJECT:
LOG_ERR("DVFS handler - request rejected");
break;
default:
LOG_ERR("DVFS handler - unexpected event: 0x%x", p_evt->type);
break;
}
}
/* Task to handle dvfs init procedure. */
static void dvfs_service_handler_task(void *dummy0, void *dummy1, void *dummy2)
{
ARG_UNUSED(dummy0);
ARG_UNUSED(dummy1);
ARG_UNUSED(dummy2);
LOG_INF("Trim ABB for default voltage.");
ld_dvfs_init();
LOG_INF("Waiting for backend init");
/* Wait for ipc initialization */
nrfs_backend_wait_for_connection(K_FOREVER);
nrfs_err_t status;
LOG_INF("nrfs_dvfs_init");
status = nrfs_dvfs_init(nrfs_dvfs_evt_handler);
dvfs_service_handler_nrfs_error_check(status);
LOG_INF("nrfs_dvfs_init_prepare_request");
status = nrfs_dvfs_init_prepare_request(get_next_context());
dvfs_service_handler_nrfs_error_check(status);
/* Wait for init*/
k_sem_take(&dvfs_service_sync_sem, K_FOREVER);
LOG_INF("DVFS init done.");
#if defined(CONFIG_NRFS_LOCAL_DOMAIN_DVFS_SCALE_DOWN_AFTER_INIT)
LOG_INF("Requesting lowest frequency oppoint.");
dvfs_service_handler_change_freq_setting(DVFS_FREQ_LOW);
#endif
while (1) {
k_sem_take(&dvfs_service_idle_sem, K_FOREVER);
/* perform background processing */
ld_dvfs_scaling_background_process(true);
}
}
K_THREAD_DEFINE(dvfs_service_handler_task_id,
CONFIG_NRFS_LOCAL_DOMAIN_DVFS_HANDLER_TASK_STACK_SIZE,
dvfs_service_handler_task,
NULL,
NULL,
NULL,
CONFIG_NRFS_LOCAL_DOMAIN_DVFS_HANDLER_TASK_PRIORITY,
0,
0);
int32_t dvfs_service_handler_change_freq_setting(enum dvfs_frequency_setting freq_setting)
{
if (!dvfs_service_handler_init_done()) {
LOG_INF("Init not done!");
return -EAGAIN;
}
if (dvfs_service_handler_freq_change_in_progress()) {
LOG_INF("Frequency change in progress.");
return -EBUSY;
}
if (!dvfs_service_handler_freq_setting_allowed(freq_setting)) {
return -ENXIO;
}
nrfs_err_t status = nrfs_dvfs_oppoint_request(freq_setting, get_next_context());
dvfs_service_handler_nrfs_error_check(status);
return status;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef LD_DVFS_HANDLER_H
#define LD_DVFS_HANDLER_H
#include <dvfs_oppoint.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Function to request LD frequency change.
*
* @param frequency requested frequency setting from enum dvfs_frequency_setting.
* @return EBUSY Frequency change in progress.
* @return EAGAIN DVFS init in progress.
* @return ENXIO Not supported frequency settings.
* @return NRFS_SUCCESS Request sent successfully.
* @return NRFS_ERR_INVALID_STATE Service is uninitialized.
* @return NRFS_ERR_IPC Backend returned error during request sending.
*/
int32_t dvfs_service_handler_change_freq_setting(enum dvfs_frequency_setting freq_setting);
#ifdef __cplusplus
}
#endif
#endif /* LD_DVFS_HANDLER_H */

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef NRFS_CONFIG_H
#define NRFS_CONFIG_H
#ifdef CONFIG_NRFS_TEMP_SERVICE_ENABLED
#define NRFS_TEMP_SERVICE_ENABLED
#endif
#ifdef CONFIG_NRFS_MRAM_SERVICE_ENABLED
#define NRFS_MRAM_SERVICE_ENABLED
#endif
#ifdef CONFIG_NRFS_RESET_SERVICE_ENABLED
#define NRFS_RESET_SERVICE_ENABLED
#endif
#ifdef CONFIG_NRFS_VBUS_DETECTOR_SERVICE_ENABLED
#define NRFS_VBUS_DETECTOR_SERVICE_ENABLED
#endif
#ifdef CONFIG_NRFS_PMIC_SERVICE_ENABLED
#define NRFS_PMIC_SERVICE_ENABLED
#endif
#ifdef CONFIG_NRFS_DVFS_SERVICE_ENABLED
#define NRFS_DVFS_SERVICE_ENABLED
#endif
#ifdef CONFIG_NRFS_DIAG_SERVICE_ENABLED
#define NRFS_DIAG_SERVICE_ENABLED
#endif
#ifdef CONFIG_NRFS_CLOCK_SERVICE_ENABLED
#define NRFS_CLOCK_SERVICE_ENABLED
#endif
#ifdef CONFIG_SOC_POSIX
#define NRFS_UNIT_TESTS_ENABLED
#endif
#endif /* NRFS_CONFIG_H */

View file

@ -4,6 +4,7 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
config SOC_SERIES_NRF54HX config SOC_SERIES_NRF54HX
select HAS_NRFS
select HAS_NRFX select HAS_NRFX
select HAS_NORDIC_DRIVERS select HAS_NORDIC_DRIVERS
@ -18,6 +19,11 @@ config SOC_NRF54H20_CPUAPP
select CPU_HAS_FPU select CPU_HAS_FPU
select CPU_HAS_CUSTOM_FIXED_SOC_MPU_REGIONS select CPU_HAS_CUSTOM_FIXED_SOC_MPU_REGIONS
select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE
select NRFS_HAS_CLOCK_SERVICE
select NRFS_HAS_DVFS_SERVICE
select NRFS_HAS_MRAM_SERVICE
select NRFS_HAS_TEMP_SERVICE
select NRFS_HAS_VBUS_DETECTOR_SERVICE
config SOC_NRF54H20_CPURAD config SOC_NRF54H20_CPURAD
select ARM select ARM
@ -30,6 +36,9 @@ config SOC_NRF54H20_CPURAD
select CPU_HAS_FPU select CPU_HAS_FPU
select CPU_HAS_CUSTOM_FIXED_SOC_MPU_REGIONS select CPU_HAS_CUSTOM_FIXED_SOC_MPU_REGIONS
select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE select HAS_SEGGER_RTT if ZEPHYR_SEGGER_MODULE
select NRFS_HAS_CLOCK_SERVICE
select NRFS_HAS_MRAM_SERVICE
select NRFS_HAS_TEMP_SERVICE
config SOC_NRF54H20_CPUPPR config SOC_NRF54H20_CPUPPR
depends on RISCV_CORE_NORDIC_VPR depends on RISCV_CORE_NORDIC_VPR