zephyr/drivers/mbox/mbox_nrf_vevif_local.c
Gerard Marull-Paretas 78c40fe31b drivers: mbox: add initial driver for nRF VEVIF
Add a mailbox driver for VEVIF. The driver can be built in either
'local' or 'remote' configuration. This depends on the existence of the
'interrupts' property, which signals that the instance is managed
locally. VEVIF is, as expected, always managed by a VPR core.

Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
2024-03-05 16:50:36 +00:00

124 lines
3 KiB
C

/*
* Copyright (c) 2024 Nordic Semiconductor ASA
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nordic_nrf_vevif_local
#include <zephyr/devicetree.h>
#include <zephyr/drivers/mbox.h>
#include <hal/nrf_vpr.h>
#include <hal/nrf_vpr_csr.h>
#include <hal/nrf_vpr_csr_vevif.h>
#define VEVIF_TASKS_NUM DT_INST_PROP(0, nordic_tasks)
#define VEVIF_TASKS_MASK DT_INST_PROP(0, nordic_tasks_mask)
BUILD_ASSERT(VEVIF_TASKS_NUM <= VPR_TASKS_TRIGGER_MaxCount, "Number of tasks exceeds maximum");
BUILD_ASSERT(VEVIF_TASKS_NUM == DT_NUM_IRQS(DT_DRV_INST(0)), "# IRQs != # tasks");
/* callbacks */
struct mbox_vevif_local_cbs {
mbox_callback_t cb[VEVIF_TASKS_NUM];
void *user_data[VEVIF_TASKS_NUM];
uint32_t enabled_mask;
};
static struct mbox_vevif_local_cbs cbs;
/* IRQ list */
#define VEVIF_IRQN(idx, _) DT_INST_IRQ_BY_IDX(0, idx, irq)
static const uint8_t vevif_irqs[VEVIF_TASKS_NUM] = {
LISTIFY(DT_NUM_IRQS(DT_DRV_INST(0)), VEVIF_IRQN, (,))
};
static void vevif_local_isr(const void *parameter)
{
uint8_t id = *(uint8_t *)parameter;
nrf_vpr_csr_vevif_tasks_clear(BIT(id));
if (cbs.cb[id] != NULL) {
cbs.cb[id](DEVICE_DT_INST_GET(0), id, cbs.user_data[id], NULL);
}
}
static inline bool vevif_local_is_task_valid(uint32_t id)
{
return (id < VEVIF_TASKS_NUM) && ((VEVIF_TASKS_MASK & BIT(id)) != 0U);
}
static uint32_t vevif_local_max_channels_get(const struct device *dev)
{
ARG_UNUSED(dev);
return VEVIF_TASKS_NUM;
}
static int vevif_local_register_callback(const struct device *dev, uint32_t id, mbox_callback_t cb,
void *user_data)
{
ARG_UNUSED(dev);
if (!vevif_local_is_task_valid(id)) {
return -EINVAL;
}
cbs.cb[id] = cb;
cbs.user_data[id] = user_data;
return 0;
}
static int vevif_local_set_enabled(const struct device *dev, uint32_t id, bool enable)
{
ARG_UNUSED(dev);
if (!vevif_local_is_task_valid(id)) {
return -EINVAL;
}
if (enable) {
if ((cbs.enabled_mask & BIT(id)) != 0U) {
return -EALREADY;
}
cbs.enabled_mask |= BIT(id);
irq_enable(vevif_irqs[id]);
} else {
if ((cbs.enabled_mask & BIT(id)) == 0U) {
return -EALREADY;
}
cbs.enabled_mask &= ~BIT(id);
irq_disable(vevif_irqs[id]);
}
return 0;
}
static const struct mbox_driver_api vevif_local_driver_api = {
.max_channels_get = vevif_local_max_channels_get,
.register_callback = vevif_local_register_callback,
.set_enabled = vevif_local_set_enabled,
};
#define VEVIF_IRQ_CONNECT(idx, _) \
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, idx, irq), DT_INST_IRQ_BY_IDX(0, idx, priority), \
vevif_local_isr, &vevif_irqs[idx], 0)
static int vevif_local_init(const struct device *dev)
{
nrf_vpr_csr_rtperiph_enable_set(true);
nrf_vpr_csr_vevif_tasks_clear(NRF_VPR_TASK_TRIGGER_ALL_MASK);
LISTIFY(DT_NUM_IRQS(DT_DRV_INST(0)), VEVIF_IRQ_CONNECT, (;));
return 0;
}
DEVICE_DT_INST_DEFINE(0, vevif_local_init, NULL, NULL, NULL, POST_KERNEL, CONFIG_MBOX_INIT_PRIORITY,
&vevif_local_driver_api);