/* * Copyright 2022-2023 NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_s32_mru #include #include #include #include #define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL #include 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 /* 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) 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); uint8_t irq_group; }; 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; } void nxp_s32_mru_isr(const struct device *dev) { const struct nxp_s32_mru_config *config = dev->config; Mru_Ip_IrqHandler(config->hw_cfg.InstanceId, config->irq_group); } 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_BASE(n) ((RTU_MRU_Type *)DT_INST_REG_ADDR(n)) #define MRU_RX_CHANNELS(n) DT_INST_PROP_OR(n, rx_channels, 0) #define MRU_MBOX_ADDR(n, ch, mb) \ (DT_INST_REG_ADDR(n) + ((ch + 1) * MRU_CHANNEL_OFFSET) + (MRU_MBOX_SIZE * mb)) #define MRU_HW_INSTANCE_CHECK(i, n) \ ((DT_INST_REG_ADDR(n) == IP_MRU_##i##_BASE) ? i : 0) #define MRU_HW_INSTANCE(n) \ LISTIFY(__DEBRACKET RTU_MRU_INSTANCE_COUNT, MRU_HW_INSTANCE_CHECK, (|), n) #define MRU_INIT_IRQ_FUNC(n) \ static void nxp_s32_mru_##n##_init_irq(void) \ { \ IRQ_CONNECT(DT_INST_IRQN(n), \ DT_INST_IRQ(n, priority), \ nxp_s32_mru_isr, \ DEVICE_DT_INST_GET(n), \ DT_INST_IRQ(n, flags)); \ irq_enable(DT_INST_IRQN(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, \ .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_INST_IRQN(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_INST_IRQN(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_INST_GET(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 = MRU_HW_INSTANCE(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)->NOTIFY[0], \ &MRU_BASE(n)->NOTIFY[1] \ }, \ }, \ .irq_group = MRU_INT_GROUP(DT_INST_IRQN(n)), \ .config_irq = COND_CODE_0(MRU_RX_CHANNELS(n), \ (NULL), (nxp_s32_mru_##n##_init_irq)), \ }; \ \ DEVICE_DT_INST_DEFINE(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); DT_INST_FOREACH_STATUS_OKAY(MRU_INSTANCE_DEFINE)