/* * Copyright (c) 2021 Carlo Caione * * SPDX-License-Identifier: Apache-2.0 */ #include #include #define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL #include #include LOG_MODULE_REGISTER(mbox_nrfx_ipc); #define DT_DRV_COMPAT nordic_mbox_nrf_ipc struct mbox_nrf_data { mbox_callback_t cb[IPC_CONF_NUM]; void *user_data[IPC_CONF_NUM]; const struct device *dev; uint32_t enabled_mask; }; static struct mbox_nrf_data nrfx_mbox_data; static struct mbox_nrf_conf { uint32_t rx_mask; uint32_t tx_mask; } nrfx_mbox_conf = { .rx_mask = DT_INST_PROP(0, rx_mask), .tx_mask = DT_INST_PROP(0, tx_mask), }; static inline bool is_rx_channel_valid(const struct device *dev, uint32_t ch) { const struct mbox_nrf_conf *conf = dev->config; return ((ch < IPC_CONF_NUM) && (conf->rx_mask & BIT(ch))); } static inline bool is_tx_channel_valid(const struct device *dev, uint32_t ch) { const struct mbox_nrf_conf *conf = dev->config; return ((ch < IPC_CONF_NUM) && (conf->tx_mask & BIT(ch))); } static void mbox_dispatcher(uint8_t event_idx, void *p_context) { struct mbox_nrf_data *data = (struct mbox_nrf_data *) p_context; const struct device *dev = data->dev; uint32_t channel = event_idx; if (!is_rx_channel_valid(dev, channel)) { LOG_WRN("RX event on illegal channel"); } if (!(data->enabled_mask & BIT(channel))) { LOG_WRN("RX event on disabled channel"); } if (data->cb[channel] != NULL) { data->cb[channel](dev, channel, data->user_data[channel], NULL); } } static int mbox_nrf_send(const struct device *dev, uint32_t channel, const struct mbox_msg *msg) { if (msg) { LOG_WRN("Sending data not supported"); } if (!is_tx_channel_valid(dev, channel)) { return -EINVAL; } nrfx_ipc_signal(channel); return 0; } static int mbox_nrf_register_callback(const struct device *dev, uint32_t channel, mbox_callback_t cb, void *user_data) { struct mbox_nrf_data *data = dev->data; if (channel >= IPC_CONF_NUM) { return -EINVAL; } data->cb[channel] = cb; data->user_data[channel] = user_data; return 0; } static int mbox_nrf_mtu_get(const struct device *dev) { /* We only support signalling */ return 0; } static uint32_t mbox_nrf_max_channels_get(const struct device *dev) { return IPC_CONF_NUM; } static int mbox_nrf_set_enabled(const struct device *dev, uint32_t channel, bool enable) { struct mbox_nrf_data *data = dev->data; if (!is_rx_channel_valid(dev, channel)) { return -EINVAL; } if ((enable == 0 && (!(data->enabled_mask & BIT(channel)))) || (enable != 0 && (data->enabled_mask & BIT(channel)))) { return -EALREADY; } if (enable && (data->cb[channel] == NULL)) { LOG_WRN("Enabling channel without a registered callback\n"); } if (enable && data->enabled_mask == 0) { irq_enable(DT_INST_IRQN(0)); } if (enable) { data->enabled_mask |= BIT(channel); compiler_barrier(); nrfx_ipc_receive_event_enable(channel); } else { nrfx_ipc_receive_event_disable(channel); compiler_barrier(); data->enabled_mask &= ~BIT(channel); } if (data->enabled_mask == 0) { irq_disable(DT_INST_IRQN(0)); } return 0; } static void enable_dt_channels(const struct device *dev) { const struct mbox_nrf_conf *conf = dev->config; nrfx_ipc_config_t ch_config = { 0 }; if (conf->tx_mask >= BIT(IPC_CONF_NUM)) { LOG_WRN("tx_mask too big (or IPC_CONF_NUM too small)"); } if (conf->rx_mask >= BIT(IPC_CONF_NUM)) { LOG_WRN("rx_mask too big (or IPC_CONF_NUM too small)"); } /* Enable the interrupts on .set_enabled() only */ ch_config.receive_events_enabled = 0; for (size_t ch = 0; ch < IPC_CONF_NUM; ch++) { if (conf->tx_mask & BIT(ch)) { ch_config.send_task_config[ch] = BIT(ch); } if (conf->rx_mask & BIT(ch)) { ch_config.receive_event_config[ch] = BIT(ch); } } nrfx_ipc_config_load(&ch_config); } static int mbox_nrf_init(const struct device *dev) { struct mbox_nrf_data *data = dev->data; data->dev = dev; nrfx_ipc_init(0, mbox_dispatcher, (void *) data); IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), nrfx_isr, nrfx_ipc_irq_handler, 0); enable_dt_channels(dev); return 0; } static const struct mbox_driver_api mbox_nrf_driver_api = { .send = mbox_nrf_send, .register_callback = mbox_nrf_register_callback, .mtu_get = mbox_nrf_mtu_get, .max_channels_get = mbox_nrf_max_channels_get, .set_enabled = mbox_nrf_set_enabled, }; DEVICE_DT_INST_DEFINE(0, mbox_nrf_init, NULL, &nrfx_mbox_data, &nrfx_mbox_conf, POST_KERNEL, CONFIG_MBOX_INIT_PRIORITY, &mbox_nrf_driver_api);