blob: cfd0640cddc005ff0160e5fb044378bc05433d9f [file] [log] [blame]
/*
* Copyright 2024 NXP
*
* SPDX-License-Identifier: Apache-2.0
*
* Wrapper of NXP Mailbox driver for Zephyr's MBOX model.
*/
#include <zephyr/devicetree.h>
#include <zephyr/drivers/mbox.h>
#include <zephyr/irq.h>
#include <zephyr/sys/util_macro.h>
#include <fsl_mailbox.h>
#define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(nxp_mbox_mailbox);
#define DT_DRV_COMPAT nxp_mbox_mailbox
#define MAILBOX_MAX_CHANNELS 4
#define MAILBOX_MBOX_SIZE 3
#if (defined(LPC55S69_cm33_core0_SERIES) || defined(LPC55S69_cm33_core1_SERIES))
#ifdef LPC55S69_cm33_core0_SERIES
#define MAILBOX_ID_THIS_CPU kMAILBOX_CM33_Core0
#define MAILBOX_ID_OTHER_CPU kMAILBOX_CM33_Core1
#else
#define MAILBOX_ID_THIS_CPU kMAILBOX_CM33_Core1
#define MAILBOX_ID_OTHER_CPU kMAILBOX_CM33_Core0
#endif
#else
#if defined(__CM4_CMSIS_VERSION)
#define MAILBOX_ID_THIS_CPU kMAILBOX_CM4
#define MAILBOX_ID_OTHER_CPU kMAILBOX_CM0Plus
#else
#define MAILBOX_ID_THIS_CPU kMAILBOX_CM0Plus
#define MAILBOX_ID_OTHER_CPU kMAILBOX_CM4
#endif
#endif
#define GENIRQ_SHIFT (28U)
#define GEN0_IRQ_TRIGGER BIT(GENIRQ_SHIFT + 3U) /*!< General interrupt 3. */
#define GEN1_IRQ_TRIGGER BIT(GENIRQ_SHIFT + 2U) /*!< General interrupt 2. */
#define GEN2_IRQ_TRIGGER BIT(GENIRQ_SHIFT + 1U) /*!< General interrupt 1. */
#define GEN3_IRQ_TRIGGER BIT(GENIRQ_SHIFT + 0U) /*!< General interrupt 0. */
#define DATA_MASK BIT_MASK(24U)
#define DATAIRQ_SHIFT (24U)
#define DATA0_IRQ_TRIGGER BIT(DATAIRQ_SHIFT + 3U) /*!< Data interrupt 3. */
#define DATA1_IRQ_TRIGGER BIT(DATAIRQ_SHIFT + 2U) /*!< Data interrupt 2. */
#define DATA2_IRQ_TRIGGER BIT(DATAIRQ_SHIFT + 1U) /*!< Data interrupt 1. */
#define DATA3_IRQ_TRIGGER BIT(DATAIRQ_SHIFT + 0U) /*!< Data interrupt 0. */
struct nxp_mailbox_data {
mbox_callback_t cb[MAILBOX_MAX_CHANNELS];
void *user_data[MAILBOX_MAX_CHANNELS];
bool channel_enable[MAILBOX_MAX_CHANNELS];
uint32_t received_data;
};
struct nxp_mailbox_config {
MAILBOX_Type *base;
};
static void mailbox_isr(const struct device *dev)
{
struct nxp_mailbox_data *data = dev->data;
const struct nxp_mailbox_config *config = dev->config;
mailbox_cpu_id_t cpu_id;
cpu_id = MAILBOX_ID_THIS_CPU;
volatile uint32_t mailbox_value = MAILBOX_GetValue(config->base, cpu_id);
uint32_t flags = mailbox_value & (~DATA_MASK);
/* Clear or the interrupt gets called intermittently */
MAILBOX_ClearValueBits(config->base, cpu_id, mailbox_value);
for (int i_channel = 0; i_channel < MAILBOX_MAX_CHANNELS; i_channel++) {
/* Continue to next channel if channel is not enabled */
if (!data->channel_enable[i_channel]) {
continue;
}
if ((flags & (DATA0_IRQ_TRIGGER >> i_channel))) {
data->received_data = mailbox_value & DATA_MASK;
struct mbox_msg msg = {(const void *)&data->received_data,
MAILBOX_MBOX_SIZE};
if (data->cb[i_channel]) {
data->cb[i_channel](dev, i_channel, data->user_data[i_channel],
&msg);
}
} else if ((flags & (GEN0_IRQ_TRIGGER >> i_channel))) {
if (data->cb[i_channel]) {
data->cb[i_channel](dev, i_channel, data->user_data[i_channel],
NULL);
}
}
}
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
* Store immediate overlapping exception return operation
* might vector to incorrect interrupt
*/
#if defined __CORTEX_M && (__CORTEX_M == 4U)
barrier_dsync_fence_full();
#endif
}
static int nxp_mailbox_send(const struct device *dev, uint32_t channel, const struct mbox_msg *msg)
{
uint32_t __aligned(4) data32;
const struct nxp_mailbox_config *cfg = dev->config;
if (channel >= MAILBOX_MAX_CHANNELS) {
return -EINVAL;
}
/* Signalling mode. */
if (msg == NULL) {
MAILBOX_SetValueBits(cfg->base, MAILBOX_ID_OTHER_CPU, GEN0_IRQ_TRIGGER >> channel);
return 0;
}
/* Data transfer mode. */
if (msg->size != MAILBOX_MBOX_SIZE) {
/* We can only send this many bytes at a time. */
return -EMSGSIZE;
}
/* memcpy to avoid issues when msg->data is not word-aligned. */
memcpy(&data32, msg->data, msg->size);
MAILBOX_SetValueBits(cfg->base, MAILBOX_ID_OTHER_CPU,
(DATA0_IRQ_TRIGGER >> channel) | (data32 & DATA_MASK));
return 0;
}
static int nxp_mailbox_register_callback(const struct device *dev, uint32_t channel,
mbox_callback_t cb, void *user_data)
{
struct nxp_mailbox_data *data = dev->data;
if (channel >= MAILBOX_MAX_CHANNELS) {
return -EINVAL;
}
data->cb[channel] = cb;
data->user_data[channel] = user_data;
return 0;
}
static int nxp_mailbox_mtu_get(const struct device *dev)
{
ARG_UNUSED(dev);
return MAILBOX_MBOX_SIZE;
}
static uint32_t nxp_mailbox_max_channels_get(const struct device *dev)
{
ARG_UNUSED(dev);
return MAILBOX_MAX_CHANNELS;
}
static int nxp_mailbox_set_enabled(const struct device *dev, uint32_t channel, bool enable)
{
struct nxp_mailbox_data *data = dev->data;
if (channel >= MAILBOX_MAX_CHANNELS) {
return -EINVAL;
}
data->channel_enable[channel] = enable;
return 0;
}
static const struct mbox_driver_api nxp_mailbox_driver_api = {
.send = nxp_mailbox_send,
.register_callback = nxp_mailbox_register_callback,
.mtu_get = nxp_mailbox_mtu_get,
.max_channels_get = nxp_mailbox_max_channels_get,
.set_enabled = nxp_mailbox_set_enabled,
};
#define MAILBOX_INSTANCE_DEFINE(idx) \
static struct nxp_mailbox_data nxp_mailbox_##idx##_data; \
const static struct nxp_mailbox_config nxp_mailbox_##idx##_config = { \
.base = (MAILBOX_Type *)DT_INST_REG_ADDR(idx), \
}; \
static int nxp_mailbox_##idx##_init(const struct device *dev) \
{ \
ARG_UNUSED(dev); \
MAILBOX_Init(nxp_mailbox_##idx##_config.base); \
IRQ_CONNECT(DT_INST_IRQN(idx), DT_INST_IRQ(idx, priority), mailbox_isr, \
DEVICE_DT_INST_GET(idx), 0); \
irq_enable(DT_INST_IRQN(idx)); \
return 0; \
} \
DEVICE_DT_INST_DEFINE(idx, nxp_mailbox_##idx##_init, NULL, &nxp_mailbox_##idx##_data, \
&nxp_mailbox_##idx##_config, POST_KERNEL, CONFIG_MBOX_INIT_PRIORITY, \
&nxp_mailbox_driver_api)
#define MAILBOX_INST(idx) MAILBOX_INSTANCE_DEFINE(idx);
DT_INST_FOREACH_STATUS_OKAY(MAILBOX_INST)