|  | /* | 
|  | * Copyright 2022 NXP | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <zephyr/drivers/mbox.h> | 
|  | #include <zephyr/irq.h> | 
|  | #include <zephyr/sys/util_macro.h> | 
|  | #include <Mru_Ip.h> | 
|  |  | 
|  | #define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(nxp_s32_mru); | 
|  |  | 
|  | #define MRU_MAX_INT_GROUPS	2 | 
|  | #define MRU_MAX_CHANNELS	12 | 
|  | #define MRU_MAX_MBOX_PER_CHAN	1 | 
|  | #define MRU_MBOX_SIZE		4 | 
|  | #define MRU_CHANNEL_OFFSET	0x1000 | 
|  |  | 
|  | #define MRU_NODE(n)		DT_NODELABEL(mru##n) | 
|  | #define MRU_BASE(n)		((RTU_MRU_Type *)DT_REG_ADDR(MRU_NODE(n))) | 
|  | #define MRU_RX_CHANNELS(n)	DT_PROP_OR(MRU_NODE(n), rx_channels, 0) | 
|  | #define MRU_MBOX_ADDR(n, ch, mb)	\ | 
|  | (DT_REG_ADDR(MRU_NODE(n)) + ((ch + 1) * MRU_CHANNEL_OFFSET) + (MRU_MBOX_SIZE * mb)) | 
|  |  | 
|  | /* Utility macros to convert from GIC index to interrupt group index */ | 
|  | #define _MRU_IRQ_17		MRU_IP_INT_GROUP_0 | 
|  | #define _MRU_IRQ_18		MRU_IP_INT_GROUP_1 | 
|  | #define MRU_INT_GROUP(irq)	_CONCAT(_MRU_IRQ_, irq) | 
|  |  | 
|  | #define _CONCAT7(...)		DT_CAT7(__VA_ARGS__) | 
|  | #define MRU_ISR_FUNC(n)							\ | 
|  | _CONCAT7(Mru_Ip_RTU, CONFIG_NXP_S32_RTU_INDEX, _MRU, n, _Int,	\ | 
|  | MRU_INT_GROUP(DT_IRQN(MRU_NODE(n))), _IRQHandler) | 
|  |  | 
|  | struct nxp_s32_mru_data { | 
|  | mbox_callback_t cb[MRU_MAX_CHANNELS]; | 
|  | void *user_data[MRU_MAX_CHANNELS]; | 
|  | }; | 
|  |  | 
|  | struct nxp_s32_mru_config { | 
|  | RTU_MRU_Type *base; | 
|  | Mru_Ip_ConfigType hw_cfg; | 
|  | void (*config_irq)(void); | 
|  | }; | 
|  |  | 
|  | static inline bool is_rx_channel_valid(const struct device *dev, uint32_t ch) | 
|  | { | 
|  | const struct nxp_s32_mru_config *cfg = dev->config; | 
|  |  | 
|  | return ((ch < MRU_MAX_CHANNELS) && (ch < cfg->hw_cfg.NumChannel)); | 
|  | } | 
|  |  | 
|  | /* Get a channel's mailbox address, no boundaries validation */ | 
|  | static inline uintptr_t get_mbox_addr(const struct device *dev, uint32_t channel, | 
|  | uint32_t mbox) | 
|  | { | 
|  | const struct nxp_s32_mru_config *cfg = dev->config; | 
|  |  | 
|  | return ((uintptr_t)cfg->base + (channel + 1) * MRU_CHANNEL_OFFSET | 
|  | + mbox * MRU_MBOX_SIZE); | 
|  | } | 
|  |  | 
|  | static int nxp_s32_mru_send(const struct device *dev, uint32_t channel, | 
|  | const struct mbox_msg *msg) | 
|  | { | 
|  | const struct nxp_s32_mru_config *cfg = dev->config; | 
|  | uint32_t *tx_mbox_addr[MRU_MAX_MBOX_PER_CHAN]; | 
|  | Mru_Ip_TransmitChannelType tx_cfg; | 
|  | Mru_Ip_StatusType status; | 
|  |  | 
|  | if (channel >= MRU_MAX_CHANNELS) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (msg == NULL) { | 
|  | return -EINVAL; | 
|  | } else if (msg->size > (MRU_MBOX_SIZE * MRU_MAX_MBOX_PER_CHAN)) { | 
|  | return -EMSGSIZE; | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < MRU_MAX_MBOX_PER_CHAN; i++) { | 
|  | tx_mbox_addr[i] = (uint32_t *)get_mbox_addr(dev, channel, i); | 
|  | } | 
|  |  | 
|  | tx_cfg.NumTxMB = MRU_MAX_MBOX_PER_CHAN, | 
|  | tx_cfg.LastTxMBIndex = MRU_MAX_MBOX_PER_CHAN - 1, | 
|  | tx_cfg.MBAddList = (volatile uint32 * const *)tx_mbox_addr, | 
|  | tx_cfg.ChMBSTATAdd = &cfg->base->CHXCONFIG[channel].CH_MBSTAT, | 
|  |  | 
|  | status = Mru_Ip_Transmit(&tx_cfg, (const uint32_t *)msg->data); | 
|  |  | 
|  | return (status == MRU_IP_STATUS_SUCCESS ? 0 : -EBUSY); | 
|  | } | 
|  |  | 
|  | static int nxp_s32_mru_register_callback(const struct device *dev, uint32_t channel, | 
|  | mbox_callback_t cb, void *user_data) | 
|  | { | 
|  | struct nxp_s32_mru_data *data = dev->data; | 
|  |  | 
|  | if (!is_rx_channel_valid(dev, channel)) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | data->cb[channel] = cb; | 
|  | data->user_data[channel] = user_data; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int nxp_s32_mru_mtu_get(const struct device *dev) | 
|  | { | 
|  | return (MRU_MBOX_SIZE * MRU_MAX_MBOX_PER_CHAN); | 
|  | } | 
|  |  | 
|  | static uint32_t nxp_s32_mru_max_channels_get(const struct device *dev) | 
|  | { | 
|  | return MRU_MAX_CHANNELS; | 
|  | } | 
|  |  | 
|  | static int nxp_s32_mru_set_enabled(const struct device *dev, uint32_t channel, | 
|  | bool enable) | 
|  | { | 
|  | struct nxp_s32_mru_data *data = dev->data; | 
|  | const struct nxp_s32_mru_config *cfg = dev->config; | 
|  |  | 
|  | const Mru_Ip_ChannelCfgType *ch_cfg = cfg->hw_cfg.ChannelCfg; | 
|  |  | 
|  | if (!is_rx_channel_valid(dev, channel)) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (enable && (data->cb[channel] == NULL)) { | 
|  | LOG_WRN("Enabling channel without a registered callback\n"); | 
|  | } | 
|  |  | 
|  | if (enable) { | 
|  | /* | 
|  | * Make the channel's registers writable and then once again after | 
|  | * enabling interrupts and mailboxes so remote can transmit | 
|  | */ | 
|  | *ch_cfg[channel].ChCFG0Add = RTU_MRU_CH_CFG0_CHE(1); | 
|  | *ch_cfg[channel].ChCFG0Add = RTU_MRU_CH_CFG0_IE(1) | 
|  | | RTU_MRU_CH_CFG0_MBE0(1) | 
|  | | RTU_MRU_CH_CFG0_CHE(1); | 
|  | } else { | 
|  | /* | 
|  | * Disable interrupts and mailboxes on this channel, making | 
|  | * the channel's registers not writable afterwards | 
|  | */ | 
|  | *ch_cfg[channel].ChCFG0Add = RTU_MRU_CH_CFG0_IE(0) | 
|  | | RTU_MRU_CH_CFG0_MBE0(0); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int nxp_s32_mru_init(const struct device *dev) | 
|  | { | 
|  | const struct nxp_s32_mru_config *cfg = dev->config; | 
|  |  | 
|  | if (cfg->hw_cfg.NumChannel == 0) { | 
|  | /* Nothing to do if no Rx channels are configured */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* All configured Rx channels will be disabled after this call */ | 
|  | Mru_Ip_Init(&cfg->hw_cfg); | 
|  |  | 
|  | /* | 
|  | * Configure and enable interrupt group, but channel's interrupt are | 
|  | * disabled until calling .set_enabled() | 
|  | */ | 
|  | cfg->config_irq(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct mbox_driver_api nxp_s32_mru_driver_api = { | 
|  | .send = nxp_s32_mru_send, | 
|  | .register_callback = nxp_s32_mru_register_callback, | 
|  | .mtu_get = nxp_s32_mru_mtu_get, | 
|  | .max_channels_get = nxp_s32_mru_max_channels_get, | 
|  | .set_enabled = nxp_s32_mru_set_enabled, | 
|  | }; | 
|  |  | 
|  | #define MRU_ISR_FUNC_DECLARE(n)		extern void MRU_ISR_FUNC(n)(void) | 
|  |  | 
|  | #define MRU_INIT_IRQ_FUNC(n)					\ | 
|  | MRU_ISR_FUNC_DECLARE(n);				\ | 
|  | static void nxp_s32_mru_##n##_init_irq(void)		\ | 
|  | {							\ | 
|  | IRQ_CONNECT(DT_IRQN(MRU_NODE(n)),		\ | 
|  | DT_IRQ(MRU_NODE(n), priority),	\ | 
|  | MRU_ISR_FUNC(n),			\ | 
|  | NULL,				\ | 
|  | DT_IRQ(MRU_NODE(n), flags));	\ | 
|  | irq_enable(DT_IRQN(MRU_NODE(n)));		\ | 
|  | } | 
|  |  | 
|  | #define MRU_CH_RX_CFG(i, n)								\ | 
|  | static volatile const uint32_t * const						\ | 
|  | nxp_s32_mru_##n##_ch_##i##_rx_mbox_addr[MRU_MAX_MBOX_PER_CHAN] = {		\ | 
|  | (uint32_t *const)MRU_MBOX_ADDR(n, i, 0),				\ | 
|  | };										\ | 
|  | static uint32_t nxp_s32_mru_##n##_ch_##i##_buf[MRU_MAX_MBOX_PER_CHAN];		\ | 
|  | static const Mru_Ip_ReceiveChannelType nxp_s32_mru_##n##_ch_##i##_rx_cfg = {	\ | 
|  | .ChannelId = i,								\ | 
|  | .InstanceId = n,							\ | 
|  | .ChannelIndex = i,							\ | 
|  | .NumRxMB = MRU_MAX_MBOX_PER_CHAN,					\ | 
|  | .MBAddList = nxp_s32_mru_##n##_ch_##i##_rx_mbox_addr,			\ | 
|  | .RxBuffer = nxp_s32_mru_##n##_ch_##i##_buf,				\ | 
|  | .ReceiveNotification = nxp_s32_mru_##n##_cb				\ | 
|  | } | 
|  |  | 
|  | #define MRU_CH_RX_LINK_CFG_MBOX(i, n, chan, intgroup)					\ | 
|  | {										\ | 
|  | [intgroup] = { &nxp_s32_mru_##n##_ch_##chan##_rx_cfg }			\ | 
|  | } | 
|  |  | 
|  | #define MRU_CH_RX_LINK_CFG(i, n)							\ | 
|  | static const Mru_Ip_MBLinkReceiveChannelType					\ | 
|  | nxp_s32_mru_##n##_ch_##i##_rx_link_cfg[MRU_MAX_MBOX_PER_CHAN][MRU_MAX_INT_GROUPS] = {\ | 
|  | MRU_CH_RX_LINK_CFG_MBOX(0, n, i, MRU_INT_GROUP(DT_IRQN(MRU_NODE(n))))	\ | 
|  | } | 
|  |  | 
|  | #define MRU_CH_CFG(i, n)								\ | 
|  | {										\ | 
|  | .ChCFG0Add = &MRU_BASE(n)->CHXCONFIG[i].CH_CFG0,			\ | 
|  | .ChCFG0 = RTU_MRU_CH_CFG0_IE(0) | RTU_MRU_CH_CFG0_MBE0(0),		\ | 
|  | .ChCFG1Add = &MRU_BASE(n)->CHXCONFIG[i].CH_CFG1,			\ | 
|  | .ChCFG1 = RTU_MRU_CH_CFG1_MBIC0(MRU_INT_GROUP(DT_IRQN(MRU_NODE(n)))),	\ | 
|  | .ChMBSTATAdd = &MRU_BASE(n)->CHXCONFIG[i].CH_MBSTAT,			\ | 
|  | .NumMailbox = MRU_MAX_MBOX_PER_CHAN,					\ | 
|  | .MBLinkReceiveChCfg = nxp_s32_mru_##n##_ch_##i##_rx_link_cfg		\ | 
|  | } | 
|  |  | 
|  | /* Callback wrapper to adapt MRU's baremetal driver callback to Zephyr's mbox driver callback */ | 
|  | #define MRU_CALLBACK_WRAPPER_FUNC(n)								\ | 
|  | void nxp_s32_mru_##n##_cb(uint8_t channel, const uint32_t *buf, uint8_t mbox_count)	\ | 
|  | {											\ | 
|  | const struct device *dev = DEVICE_DT_GET(MRU_NODE(n));				\ | 
|  | struct nxp_s32_mru_data *data = dev->data;					\ | 
|  | \ | 
|  | if (is_rx_channel_valid(dev, channel)) {					\ | 
|  | if (data->cb[channel] != NULL) {					\ | 
|  | struct mbox_msg msg = {						\ | 
|  | .data = (const void *)buf,				\ | 
|  | .size = mbox_count * MRU_MBOX_SIZE			\ | 
|  | };								\ | 
|  | data->cb[channel](dev, channel, data->user_data[channel], &msg);\ | 
|  | }									\ | 
|  | }										\ | 
|  | } | 
|  |  | 
|  | #define MRU_CH_RX_DEFINITIONS(n)						\ | 
|  | MRU_CALLBACK_WRAPPER_FUNC(n)						\ | 
|  | MRU_INIT_IRQ_FUNC(n)							\ | 
|  | LISTIFY(MRU_RX_CHANNELS(n), MRU_CH_RX_CFG, (;), n);			\ | 
|  | LISTIFY(MRU_RX_CHANNELS(n), MRU_CH_RX_LINK_CFG, (;), n);		\ | 
|  | static const Mru_Ip_ChannelCfgType nxp_s32_mru_##n##_ch_cfg[] = {	\ | 
|  | LISTIFY(MRU_RX_CHANNELS(n), MRU_CH_CFG, (,), n)			\ | 
|  | } | 
|  |  | 
|  | #define MRU_INSTANCE_DEFINE(n)								\ | 
|  | COND_CODE_0(MRU_RX_CHANNELS(n), (EMPTY), (MRU_CH_RX_DEFINITIONS(n)));		\ | 
|  | static struct nxp_s32_mru_data nxp_s32_mru_##n##_data;				\ | 
|  | static struct nxp_s32_mru_config nxp_s32_mru_##n##_config = {			\ | 
|  | .base = MRU_BASE(n),							\ | 
|  | .hw_cfg = {								\ | 
|  | .InstanceId = n,						\ | 
|  | .StateIndex = n,						\ | 
|  | .NumChannel = MRU_RX_CHANNELS(n),				\ | 
|  | .ChannelCfg = COND_CODE_0(MRU_RX_CHANNELS(n),			\ | 
|  | (NULL), (nxp_s32_mru_##n##_ch_cfg)),	\ | 
|  | .NOTIFYAdd = {							\ | 
|  | &MRU_BASE(n)->NOTIFY0,					\ | 
|  | &MRU_BASE(n)->NOTIFY1					\ | 
|  | },								\ | 
|  | },									\ | 
|  | .config_irq = COND_CODE_0(MRU_RX_CHANNELS(n),				\ | 
|  | (NULL), (nxp_s32_mru_##n##_init_irq)),	\ | 
|  | };										\ | 
|  | \ | 
|  | DEVICE_DT_DEFINE(MRU_NODE(n), nxp_s32_mru_init, NULL,				\ | 
|  | &nxp_s32_mru_##n##_data, &nxp_s32_mru_##n##_config,		\ | 
|  | POST_KERNEL, CONFIG_MBOX_INIT_PRIORITY,				\ | 
|  | &nxp_s32_mru_driver_api) | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(MRU_NODE(0), okay) | 
|  | MRU_INSTANCE_DEFINE(0); | 
|  | #endif | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(MRU_NODE(1), okay) | 
|  | MRU_INSTANCE_DEFINE(1); | 
|  | #endif | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(MRU_NODE(2), okay) | 
|  | MRU_INSTANCE_DEFINE(2); | 
|  | #endif | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(MRU_NODE(3), okay) | 
|  | MRU_INSTANCE_DEFINE(3); | 
|  | #endif | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(MRU_NODE(4), okay) | 
|  | MRU_INSTANCE_DEFINE(4); | 
|  | #endif | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(MRU_NODE(5), okay) | 
|  | MRU_INSTANCE_DEFINE(5); | 
|  | #endif | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(MRU_NODE(6), okay) | 
|  | MRU_INSTANCE_DEFINE(6); | 
|  | #endif | 
|  |  | 
|  | #if DT_NODE_HAS_STATUS(MRU_NODE(7), okay) | 
|  | MRU_INSTANCE_DEFINE(7); | 
|  | #endif |