blob: 3c708b95eca5aba824c57b4af8ca5353c81f510f [file] [log] [blame]
/*
* Wrapper of the i.MX Message Unit driver into Zephyr's MBOX model.
*/
#include <zephyr/devicetree.h>
#include <zephyr/drivers/mbox.h>
#include <zephyr/sys/util_macro.h>
#include <fsl_mu.h>
#define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(nxp_mbox_imx_mu);
#define DT_DRV_COMPAT nxp_mbox_imx_mu
#define MU_MAX_CHANNELS 4
#define MU_MBOX_SIZE sizeof(uint32_t)
struct nxp_imx_mu_data {
mbox_callback_t cb[MU_MAX_CHANNELS];
void *user_data[MU_MAX_CHANNELS];
};
struct nxp_imx_mu_config {
MU_Type *base;
};
static int nxp_imx_mu_send(const struct device *dev, uint32_t channel,
const struct mbox_msg *msg)
{
uint32_t __aligned(4) data32;
const struct nxp_imx_mu_config *cfg = dev->config;
if (channel >= MU_MAX_CHANNELS) {
return -EINVAL;
}
/* Signalling mode. */
if (msg == NULL) {
return MU_TriggerInterrupts(
cfg->base, kMU_GenInt0InterruptTrigger);
}
/* Data transfer mode. */
if (msg->size != MU_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);
MU_SendMsg(cfg->base, channel, data32);
return 0;
}
static int nxp_imx_mu_register_callback(const struct device *dev, uint32_t channel,
mbox_callback_t cb, void *user_data)
{
struct nxp_imx_mu_data *data = dev->data;
if (channel >= MU_MAX_CHANNELS) {
return -EINVAL;
}
data->cb[channel] = cb;
data->user_data[channel] = user_data;
return 0;
}
static int nxp_imx_mu_mtu_get(const struct device *dev)
{
ARG_UNUSED(dev);
return MU_MBOX_SIZE;
}
static uint32_t nxp_imx_mu_max_channels_get(const struct device *dev)
{
ARG_UNUSED(dev);
return MU_MAX_CHANNELS;
}
static int nxp_imx_mu_set_enabled(const struct device *dev, uint32_t channel,
bool enable)
{
struct nxp_imx_mu_data *data = dev->data;
const struct nxp_imx_mu_config *cfg = dev->config;
if (channel >= MU_MAX_CHANNELS) {
return -EINVAL;
}
if (enable) {
if (data->cb[channel] == NULL) {
LOG_WRN("Enabling channel without a registered callback");
}
MU_EnableInterrupts(cfg->base,
kMU_GenInt0InterruptEnable | kMU_GenInt1InterruptEnable |
kMU_GenInt2InterruptEnable | kMU_GenInt3InterruptEnable |
kMU_Rx0FullInterruptEnable | kMU_Rx1FullInterruptEnable |
kMU_Rx2FullInterruptEnable | kMU_Rx3FullInterruptEnable);
} else {
MU_DisableInterrupts(cfg->base,
kMU_GenInt0InterruptEnable | kMU_GenInt1InterruptEnable |
kMU_GenInt2InterruptEnable | kMU_GenInt3InterruptEnable |
kMU_Rx0FullInterruptEnable | kMU_Rx1FullInterruptEnable |
kMU_Rx2FullInterruptEnable | kMU_Rx3FullInterruptEnable);
}
return 0;
}
static const struct mbox_driver_api nxp_imx_mu_driver_api = {
.send = nxp_imx_mu_send,
.register_callback = nxp_imx_mu_register_callback,
.mtu_get = nxp_imx_mu_mtu_get,
.max_channels_get = nxp_imx_mu_max_channels_get,
.set_enabled = nxp_imx_mu_set_enabled,
};
#define MU_INSTANCE_DEFINE(idx) \
static struct nxp_imx_mu_data nxp_imx_mu_##idx##_data; \
static struct nxp_imx_mu_config nxp_imx_mu_##idx##_config = { \
.base = (MU_Type *)DT_INST_REG_ADDR(idx), \
}; \
\
void MU_##idx##_IRQHandler(void); \
static int nxp_imx_mu_##idx##_init(const struct device *dev) \
{ \
ARG_UNUSED(dev); \
MU_Init(nxp_imx_mu_##idx##_config.base); \
IRQ_CONNECT(DT_INST_IRQN(idx), \
DT_INST_IRQ(idx, priority), \
MU_##idx##_IRQHandler, \
NULL, \
0); \
irq_enable(DT_INST_IRQN(idx)); \
return 0; \
} \
DEVICE_DT_INST_DEFINE(idx, nxp_imx_mu_##idx##_init, NULL, \
&nxp_imx_mu_##idx##_data, &nxp_imx_mu_##idx##_config, \
POST_KERNEL, CONFIG_MBOX_INIT_PRIORITY, \
&nxp_imx_mu_driver_api)
#define MU_IRQ_HANDLER(idx) \
static uint32_t mu_##idx##_received_data; \
void MU_##idx##_IRQHandler(void) \
{ \
const struct device *dev = DEVICE_DT_INST_GET(idx); \
const struct nxp_imx_mu_data *data = dev->data; \
const struct nxp_imx_mu_config *config = dev->config; \
int channel = 0; \
struct mbox_msg msg; \
struct mbox_msg *callback_msg_ptr = NULL; \
uint32_t flag = MU_GetStatusFlags(config->base); \
\
if ((flag & kMU_Rx0FullFlag) == kMU_Rx0FullFlag) { \
mu_##idx##_received_data = \
MU_ReceiveMsgNonBlocking(config->base, 0); \
msg.data = (const void *)&mu_##idx##_received_data; \
msg.size = MU_MBOX_SIZE; \
callback_msg_ptr = &msg; \
} else if ((flag & kMU_GenInt0Flag) == kMU_GenInt0Flag) { \
MU_ClearStatusFlags(config->base, kMU_GenInt0Flag); \
callback_msg_ptr = NULL; \
} \
\
if (data->cb[channel]) { \
data->cb[channel](dev, channel, \
data->user_data[channel], \
callback_msg_ptr); \
} \
}
#define MU_INST(idx) \
MU_INSTANCE_DEFINE(idx); \
MU_IRQ_HANDLER(idx);
DT_INST_FOREACH_STATUS_OKAY(MU_INST)