You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
4.5 KiB
203 lines
4.5 KiB
/* |
|
* Copyright (c) 2021 Carlo Caione <ccaione@baylibre.com> |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/drivers/mbox.h> |
|
#include <nrfx_ipc.h> |
|
|
|
#define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL |
|
#include <zephyr/logging/log.h> |
|
#include <zephyr/irq.h> |
|
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 DEVICE_API(mbox, 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);
|
|
|