Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nrf fromtree] soc: nordic: common: Add MRAM latency manager #2271

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions soc/nordic/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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_ */
10 changes: 10 additions & 0 deletions tests/boards/nrf/mram_latency/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(mram_latency)

FILE(GLOB app_sources src/*.c)

target_sources(app PRIVATE ${app_sources})
2 changes: 2 additions & 0 deletions tests/boards/nrf/mram_latency/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_ZTEST=y
CONFIG_MRAM_LATENCY=y
Loading