Skip to content
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
15 changes: 15 additions & 0 deletions dts/bindings/dma/nordic,nrf-mvdma.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2025, Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

description: Nordic MVDMA

compatible: "nordic,nrf-mvdma"

include: base.yaml

properties:
reg:
required: true

interrupts:
required: true
14 changes: 14 additions & 0 deletions dts/vendor/nordic/nrf54h20.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,13 @@
reg = <0x52000000 0x1000000>;
ranges = <0x0 0x52000000 0x1000000>;

cpuapp_mvdma: mvdma@3000 {
compatible = "nordic,nrf-mvdma";
reg = <0x3000 0x1000>;
status = "okay";
interrupts = <3 NRF_DEFAULT_IRQ_PRIORITY>;
};

cpuapp_hsfll: clock@d000 {
compatible = "nordic,nrf-iron-hsfll-local";
#clock-cells = <0>;
Expand Down Expand Up @@ -372,6 +379,13 @@
#size-cells = <1>;
ranges = <0x0 0x53000000 0x1000000>;

cpurad_mvdma: mvdma@3000 {
compatible = "nordic,nrf-mvdma";
reg = <0x3000 0x1000>;
status = "okay";
interrupts = <3 NRF_DEFAULT_IRQ_PRIORITY>;
};

cpurad_hsfll: clock@d000 {
compatible = "nordic,nrf-hsfll-local";
#clock-cells = <0>;
Expand Down
22 changes: 22 additions & 0 deletions include/zephyr/pm/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks);
*
* @see pm_policy_state_lock_put()
*/

void pm_policy_state_lock_get(enum pm_state state, uint8_t substate_id);

/**
Expand All @@ -125,6 +126,18 @@ void pm_policy_state_lock_get(enum pm_state state, uint8_t substate_id);
*/
void pm_policy_state_lock_put(enum pm_state state, uint8_t substate_id);

/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please split this into own PR including PM changes only, making this part of a driver PR does not give it the visibility and reviews it deserves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is there: #96909

* @brief Request to lock all power states.
*
* Requests use a reference counter.
*/
void pm_policy_state_all_lock_get(void);

/**
* @brief Release locking of all power states.
*/
void pm_policy_state_all_lock_put(void);

/**
* @brief Apply power state constraints by locking the specified states.
*
Expand Down Expand Up @@ -274,6 +287,14 @@ static inline void pm_policy_state_lock_put(enum pm_state state, uint8_t substat
ARG_UNUSED(substate_id);
}

static inline void pm_policy_state_all_lock_get(void)
{
}

static inline void pm_policy_state_all_lock_put(void)
{
}

static inline bool pm_policy_state_lock_is_active(enum pm_state state, uint8_t substate_id)
{
ARG_UNUSED(state);
Expand Down Expand Up @@ -344,6 +365,7 @@ static inline void pm_policy_device_power_lock_put(const struct device *dev)
{
ARG_UNUSED(dev);
}

#endif /* CONFIG_PM_POLICY_DEVICE_CONSTRAINTS */

#if defined(CONFIG_PM) || defined(CONFIG_PM_POLICY_LATENCY_STANDALONE) || defined(__DOXYGEN__)
Expand Down
1 change: 1 addition & 0 deletions soc/nordic/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ endif()

zephyr_library_sources_ifdef(CONFIG_NRF_SYS_EVENT nrf_sys_event.c)
zephyr_library_sources_ifdef(CONFIG_MRAM_LATENCY mram_latency.c)
zephyr_library_sources_ifdef(CONFIG_MVDMA mvdma.c)
5 changes: 5 additions & 0 deletions soc/nordic/common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ source "subsys/logging/Kconfig.template.log_config"

endif # MRAM_LATENCY

config MVDMA
depends on DT_HAS_NORDIC_NRF_MVDMA_ENABLED
default y
bool "nRF MVDMA driver"

if HAS_NORDIC_DMM

config DMM_HEAP_CHUNKS
Expand Down
206 changes: 206 additions & 0 deletions soc/nordic/common/mvdma.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <mvdma.h>
#include <zephyr/pm/policy.h>
#include <hal/nrf_cache.h>
#include <hal/nrf_mvdma.h>
#include <zephyr/cache.h>
#include <zephyr/toolchain.h>

/* To be removed when nrfx comes with those symbols. */
#define NRF_MVDMA_INT_COMPLETED0_MASK MVDMA_INTENSET_COMPLETED0_Msk
#define NRF_MVDMA_EVENT_COMPLETED0 offsetof(NRF_MVDMA_Type, EVENTS_COMPLETED[0])

#define MVDMA_DO_COUNT(node) 1 +

BUILD_ASSERT((DT_FOREACH_STATUS_OKAY(nordic_nrf_mvdma, MVDMA_DO_COUNT) 0) == 1);

#define MVDMA_NODE() DT_COMPAT_GET_ANY_STATUS_OKAY(nordic_nrf_mvdma)

static sys_slist_t list;
static atomic_t hw_err;
static struct mvdma_ctrl *curr_ctrl;
static NRF_MVDMA_Type *reg = (NRF_MVDMA_Type *)DT_REG_ADDR(MVDMA_NODE());

static uint32_t dummy_jobs[] __aligned(CONFIG_DCACHE_LINE_SIZE) = {
NRF_MVDMA_JOB_TERMINATE,
NRF_MVDMA_JOB_TERMINATE,
};

static void xfer_start(uint32_t src, uint32_t sink)
{
nrf_mvdma_event_clear(reg, NRF_MVDMA_EVENT_COMPLETED0);
nrf_mvdma_source_list_ptr_set(reg, (void *)src);
nrf_mvdma_sink_list_ptr_set(reg, (void *)sink);
nrf_mvdma_task_trigger(reg, NRF_MVDMA_TASK_START0);
}

static int xfer(struct mvdma_ctrl *ctrl, uint32_t src, uint32_t sink, bool queue)
{
int rv, key;
bool int_en = true;

key = irq_lock();
if (nrf_mvdma_activity_check(reg) || (curr_ctrl && curr_ctrl->handler)) {
if (queue) {
ctrl->source = src;
ctrl->sink = sink;
sys_slist_append(&list, &ctrl->node);
rv = 1;
} else {
irq_unlock(key);
return -EBUSY;
}
} else {
/* There might be some pending request that need to be marked as finished. */
if (curr_ctrl != NULL) {
sys_snode_t *node;
struct mvdma_ctrl *prev;

curr_ctrl->handler = (mvdma_handler_t)1;
while ((node = sys_slist_get(&list)) != NULL) {
prev = CONTAINER_OF(node, struct mvdma_ctrl, node);
prev->handler = (mvdma_handler_t)1;
}
}

curr_ctrl = ctrl;
xfer_start(src, sink);
if (ctrl->handler == NULL) {
int_en = false;
}
rv = 0;
}
irq_unlock(key);

if (int_en) {
nrf_mvdma_int_enable(reg, NRF_MVDMA_INT_COMPLETED0_MASK);
}

pm_policy_state_all_lock_get();

return rv;
}

int mvdma_xfer(struct mvdma_ctrl *ctrl, struct mvdma_jobs_desc *desc, bool queue)
{
sys_cache_data_flush_range(desc->source, desc->source_desc_size);
sys_cache_data_flush_range(desc->sink, desc->sink_desc_size);
return xfer(ctrl, (uint32_t)desc->source, (uint32_t)desc->sink, queue);
}

int mvdma_basic_xfer(struct mvdma_ctrl *ctrl, struct mvdma_basic_desc *desc, bool queue)
{
sys_cache_data_flush_range(desc, sizeof(*desc));
return xfer(ctrl, (uint32_t)&desc->source, (uint32_t)&desc->sink, queue);
}

int mvdma_xfer_check(const struct mvdma_ctrl *ctrl)
{
if (hw_err != NRF_MVDMA_ERR_NO_ERROR) {
return -EIO;
}

if (nrf_mvdma_event_check(reg, NRF_MVDMA_EVENT_COMPLETED0)) {
curr_ctrl = NULL;
} else if (ctrl->handler == NULL) {
return -EBUSY;
}

pm_policy_state_all_lock_put();

return 0;
}

enum mvdma_err mvdma_error_check(void)
{
return atomic_set(&hw_err, 0);
}

static void error_handler(void)
{
if (nrf_mvdma_event_check(reg, NRF_MVDMA_EVENT_SOURCEBUSERROR)) {
nrf_mvdma_event_clear(reg, NRF_MVDMA_EVENT_SOURCEBUSERROR);
hw_err = NRF_MVDMA_ERR_SOURCE;
}

if (nrf_mvdma_event_check(reg, NRF_MVDMA_EVENT_SINKBUSERROR)) {
nrf_mvdma_event_clear(reg, NRF_MVDMA_EVENT_SINKBUSERROR);
hw_err = NRF_MVDMA_ERR_SINK;
}
}

static void ch_handler(int status)
{
int key;
struct mvdma_ctrl *ctrl = curr_ctrl;
sys_snode_t *node;
bool int_dis = true;

key = irq_lock();
node = sys_slist_get(&list);
if (node) {
struct mvdma_ctrl *next = CONTAINER_OF(node, struct mvdma_ctrl, node);

curr_ctrl = next;
xfer_start((uint32_t)next->source, (uint32_t)next->sink);
if (next->handler || !sys_slist_is_empty(&list)) {
int_dis = false;
}
} else {
curr_ctrl = NULL;
}
if (int_dis) {
nrf_mvdma_int_disable(reg, NRF_MVDMA_INT_COMPLETED0_MASK);
}
irq_unlock(key);

if (ctrl->handler) {
pm_policy_state_all_lock_put();
ctrl->handler(ctrl->user_data, hw_err == NRF_MVDMA_ERR_NO_ERROR ? 0 : -EIO);
} else {
/* Set handler variable to non-null to indicated that transfer has finished. */
ctrl->handler = (mvdma_handler_t)1;
}
}

static void mvdma_isr(const void *arg)
{
uint32_t ints = nrf_mvdma_int_pending_get(reg);

if (ints & NRF_MVDMA_INT_COMPLETED0_MASK) {
ch_handler(0);
} else {
error_handler();
}
}

void mvdma_resume(void)
{
/* bus errors. */
nrf_mvdma_int_enable(reg, NRF_MVDMA_INT_SOURCEBUSERROR_MASK |
NRF_MVDMA_INT_SINKBUSERROR_MASK);

/* Dummy transfer to get COMPLETED event set. */
nrf_mvdma_source_list_ptr_set(reg, (void *)&dummy_jobs[0]);
nrf_mvdma_sink_list_ptr_set(reg, (void *)&dummy_jobs[1]);
nrf_mvdma_task_trigger(reg, NRF_MVDMA_TASK_START0);
}

int mvdma_init(void)
{
sys_cache_data_flush_range(dummy_jobs, sizeof(dummy_jobs));

sys_slist_init(&list);

IRQ_CONNECT(DT_IRQN(MVDMA_NODE()), DT_IRQ(MVDMA_NODE(), priority), mvdma_isr, 0, 0);
irq_enable(DT_IRQN(MVDMA_NODE()));

mvdma_resume();

return 0;
}
Loading
Loading