soc: nordic: common: Add mram latency manager

Add module for managing multiple requests for MRAM latency.

Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
Krzysztof Chruściński 2024-11-13 12:30:35 +01:00 committed by Anas Nashif
commit 980e0acbf1
4 changed files with 287 additions and 0 deletions

View file

@ -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)

View file

@ -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"

View file

@ -0,0 +1,175 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#include <mram_latency.h>
#include <zephyr/kernel.h>
#include <nrfs_mram.h>
#include <nrfs_backend_ipc_service.h>
#include <zephyr/logging/log.h>
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);

View file

@ -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 <zephyr/sys/onoff.h>
#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_ */