From 980e0acbf1c9e9377a5c85d40b2749dc28db04a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Wed, 13 Nov 2024 12:30:35 +0100 Subject: [PATCH] soc: nordic: common: Add mram latency manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add module for managing multiple requests for MRAM latency. Signed-off-by: Krzysztof Chruściński --- soc/nordic/common/CMakeLists.txt | 1 + soc/nordic/common/Kconfig | 20 ++++ soc/nordic/common/mram_latency.c | 175 +++++++++++++++++++++++++++++++ soc/nordic/common/mram_latency.h | 91 ++++++++++++++++ 4 files changed, 287 insertions(+) create mode 100644 soc/nordic/common/mram_latency.c create mode 100644 soc/nordic/common/mram_latency.h diff --git a/soc/nordic/common/CMakeLists.txt b/soc/nordic/common/CMakeLists.txt index 4808319557c..8c00b768b90 100644 --- a/soc/nordic/common/CMakeLists.txt +++ b/soc/nordic/common/CMakeLists.txt @@ -34,3 +34,4 @@ if(CONFIG_TFM_PARTITION_PLATFORM) endif() zephyr_library_sources_ifdef(CONFIG_NRF_SYS_EVENT nrf_sys_event.c) +zephyr_library_sources_ifdef(CONFIG_MRAM_LATENCY mram_latency.c) diff --git a/soc/nordic/common/Kconfig b/soc/nordic/common/Kconfig index ef32e7403c1..6ed44bdd8cb 100644 --- a/soc/nordic/common/Kconfig +++ b/soc/nordic/common/Kconfig @@ -8,4 +8,24 @@ config NRF_SYS_EVENT bool "nRF system event support" select NRFX_POWER if !NRF_PLATFORM_HALTIUM +config MRAM_LATENCY + bool "MRAM latency manager" + depends on NRFS_HAS_MRAM_SERVICE + select ONOFF + select NRFS_MRAM_SERVICE_ENABLED + +if MRAM_LATENCY + +config MRAM_LATENCY_SYNC_TIMEOUT + int "Timeout in synchronous request" + default 1000 + help + Timeout is given in milliseconds. + +module = MRAM_LATENCY +module-str = mram_latency +source "subsys/logging/Kconfig.template.log_config" + +endif # MRAM_LATENCY + rsource "vpr/Kconfig" diff --git a/soc/nordic/common/mram_latency.c b/soc/nordic/common/mram_latency.c new file mode 100644 index 00000000000..0bfc6d4bfe2 --- /dev/null +++ b/soc/nordic/common/mram_latency.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(mram_latency, CONFIG_MRAM_LATENCY_LOG_LEVEL); + +enum mram_latency_state { + MRAM_LATENCY_OFF = 0, + MRAM_LATENCY_OFF_PENDING, + MRAM_LATENCY_ON, +}; + +static struct k_work work; +static bool no_latency; +static enum mram_latency_state state; + +static onoff_notify_fn onoff_notify; +struct onoff_manager mram_latency_mgr; + +struct sync_latency_req { + struct onoff_client cli; + struct k_sem sem; + int res; +}; + +static void latency_change_req(bool latency_not_allowed) +{ + nrfs_err_t err; + + if (latency_not_allowed) { + err = nrfs_mram_set_latency(MRAM_LATENCY_NOT_ALLOWED, NULL); + if (err != NRFS_SUCCESS) { + onoff_notify(&mram_latency_mgr, -EIO); + } + } else { + /* There is no event for that setting so we can notify onoff manager + * immediately. + */ + err = nrfs_mram_set_latency(MRAM_LATENCY_ALLOWED, NULL); + onoff_notify(&mram_latency_mgr, err != NRFS_SUCCESS ? -EIO : 0); + } +} + +static void latency_change(bool latency_not_allowed) +{ + LOG_DBG("Request: latency %s allowed", latency_not_allowed ? "not " : ""); + if (state == MRAM_LATENCY_OFF) { + state = MRAM_LATENCY_OFF_PENDING; + } else if (k_is_in_isr()) { + /* nrfs cannot be called from interrupt context so defer to the work + * queue context and execute from there. + */ + no_latency = latency_not_allowed; + k_work_submit(&work); + } else { + latency_change_req(latency_not_allowed); + } +} + +static void no_latency_start(struct onoff_manager *mgr, onoff_notify_fn notify) +{ + onoff_notify = notify; + latency_change(true); +} + +static void no_latency_stop(struct onoff_manager *mgr, onoff_notify_fn notify) +{ + latency_change(false); +} + +static void evt_handler(nrfs_mram_latency_evt_t const *p_evt, void *context) +{ + int res = p_evt->type == NRFS_MRAM_LATENCY_REQ_APPLIED ? 0 : -EIO; + + LOG_DBG("Latency not allowed - applied"); + onoff_notify(&mram_latency_mgr, res); +} + +static void work_handler(struct k_work *work) +{ + latency_change_req(no_latency); +} + +int mram_no_latency_cancel_or_release(struct onoff_client *cli) +{ + return onoff_cancel_or_release(&mram_latency_mgr, cli); +} + +int mram_no_latency_request(struct onoff_client *cli) +{ + return onoff_request(&mram_latency_mgr, cli); +} + +static void sync_req_cb(struct onoff_manager *mgr, struct onoff_client *cli, uint32_t state, + int res) +{ + struct sync_latency_req *req = CONTAINER_OF(cli, struct sync_latency_req, cli); + + req->res = res; + k_sem_give(&req->sem); +} + +int mram_no_latency_sync_request(void) +{ + struct sync_latency_req req; + int rv; + + if (k_is_in_isr() || (state != MRAM_LATENCY_ON)) { + return -ENOTSUP; + } + + k_sem_init(&req.sem, 0, 1); + sys_notify_init_callback(&req.cli.notify, sync_req_cb); + rv = onoff_request(&mram_latency_mgr, &req.cli); + if (rv < 0) { + return rv; + } + + rv = k_sem_take(&req.sem, K_MSEC(CONFIG_MRAM_LATENCY_SYNC_TIMEOUT)); + if (rv < 0) { + return rv; + } + + return req.res; +} + +int mram_no_latency_sync_release(void) +{ + return onoff_release(&mram_latency_mgr) >= 0 ? 0 : -EIO; +} + +/* First initialize onoff manager to be able to accept requests. */ +static int init_manager(void) +{ + static const struct onoff_transitions transitions = + ONOFF_TRANSITIONS_INITIALIZER(no_latency_start, no_latency_stop, NULL); + + return onoff_manager_init(&mram_latency_mgr, &transitions); +} + +/* When kernel and IPC is running initialize nrfs. Optionally, execute pending request. */ +static int init_nrfs(void) +{ + nrfs_err_t err; + int rv; + + err = nrfs_backend_wait_for_connection(K_FOREVER); + if (err != NRFS_SUCCESS) { + return -EIO; + } + + err = nrfs_mram_init(evt_handler); + if (err != NRFS_SUCCESS) { + return -EIO; + } + + k_work_init(&work, work_handler); + + if (state == MRAM_LATENCY_OFF_PENDING) { + latency_change(true); + } + + state = MRAM_LATENCY_ON; + + return rv; +} + +SYS_INIT(init_manager, PRE_KERNEL_1, 0); +SYS_INIT(init_nrfs, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); diff --git a/soc/nordic/common/mram_latency.h b/soc/nordic/common/mram_latency.h new file mode 100644 index 00000000000..dc24566a905 --- /dev/null +++ b/soc/nordic/common/mram_latency.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * nRF SoC specific public APIs for MRAM latency management + * @brief Experimental. It will be replaced by the PM latency policy API in the future. + */ + +#ifndef SOC_NORDIC_COMMON_MRAM_LATENCY_H_ +#define SOC_NORDIC_COMMON_MRAM_LATENCY_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @internal For test purposes only. */ +extern struct onoff_manager mram_latency_mgr; + +/** @brief Request MRAM operations without latency. + * + * The return value indicates the success or failure of an attempt to initiate + * an operation to request the MRAM low latency. If initiation of the + * operation succeeds, the result of the request operation is provided through + * the configured client notification method, possibly before this call returns. + * + * @param cli pointer to client state providing instructions on synchronous + * expectations and how to notify the client when the request + * completes. Behavior is undefined if client passes a pointer + * object associated with an incomplete service operation. + * + * @retval non-negative the observed state of the on-off service associated + * with the MRAM latency service. + * @retval -EIO if MRAM latency service returned error. + * @retval -EINVAL if the parameters are invalid. + * @retval -EAGAIN if the reference count would overflow. + */ +int mram_no_latency_request(struct onoff_client *cli); + +/** @brief Request MRAM operations without latency. + * + * Request is synchronous and blocks until it is completed. It can be called only + * from the thread context and cannot be called in the pre kernel stage. + * + * @retval 0 on successful request. + * @retval -EIO if MRAM latency service returned error. + * @retval -EAGAIN if request was not completed on time. + */ +int mram_no_latency_sync_request(void); + +/** + * @brief Safely cancel a request for MRAM operations without latency. + * + * It may be that a client has issued a reservation request but needs to + * shut down before the request has completed. This function attempts to + * cancel the request and issues a release if cancellation fails because + * the request was completed. This synchronously ensures that ownership + * data reverts to the client so is available for a future request. + * + * @param cli a pointer to the same client state that was provided + * when the operation to be cancelled was issued. + * + * @retval ONOFF_STATE_TO_ON if the cancellation occurred before the transition + * completed. + * @retval ONOFF_STATE_ON if the cancellation occurred after the transition + * completed. + * @retval -EINVAL if the parameters are invalid. + * @retval -EIO if MRAM latency service returned error. + * @retval negative other errors produced by onoff_release(). + */ +int mram_no_latency_cancel_or_release(struct onoff_client *cli); + +/** + * @brief Release a request for MRAM operations without latency. + * + * It should match with a completed @ref mram_no_latency_sync_request call. + * + * @retval 0 on successful request. + * @retval -EIO if MRAM latency service returned error. + */ +int mram_no_latency_sync_release(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SOC_NORDIC_COMMON_MRAM_LATENCY_H_ */