Skip to content

Commit

Permalink
soc: nordic: common: Add mram latency manager
Browse files Browse the repository at this point in the history
Add module for managing multiple requests for MRAM latency.

Signed-off-by: Krzysztof Chruściński <[email protected]>
  • Loading branch information
nordic-krch committed Nov 22, 2024
1 parent 2879607 commit 8960622
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 0 deletions.
1 change: 1 addition & 0 deletions soc/nordic/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,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)
20 changes: 20 additions & 0 deletions soc/nordic/common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 175 additions & 0 deletions soc/nordic/common/mram_latency.c
Original file line number Diff line number Diff line change
@@ -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 changes: 91 additions & 0 deletions soc/nordic/common/mram_latency.h
Original file line number Diff line number Diff line change
@@ -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_ */

0 comments on commit 8960622

Please sign in to comment.