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:
parent
c7ce8614b4
commit
980e0acbf1
4 changed files with 287 additions and 0 deletions
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
175
soc/nordic/common/mram_latency.c
Normal file
175
soc/nordic/common/mram_latency.c
Normal 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);
|
91
soc/nordic/common/mram_latency.h
Normal file
91
soc/nordic/common/mram_latency.h
Normal 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_ */
|
Loading…
Add table
Add a link
Reference in a new issue