diff --git a/drivers/mbox/Kconfig.andes b/drivers/mbox/Kconfig.andes index 6412e9fb692..33f323e639c 100644 --- a/drivers/mbox/Kconfig.andes +++ b/drivers/mbox/Kconfig.andes @@ -8,7 +8,8 @@ config MBOX_ANDES_PLIC_SW bool "MBOX Andes PLIC-SW driver" default y - depends on DT_HAS_ANDESTECH_PLIC_SW_ENABLED + depends on DT_HAS_ANDESTECH_MBOX_PLIC_SW_ENABLED + depends on PLIC_SUPPORTS_SOFT_INTERRUPT + select DYNAMIC_INTERRUPTS help - Enable driver for the Andes IPM mailbox controller. - Says n if not sure. + Enable the driver for the Andes PLIC-SW based MBOX controller. diff --git a/drivers/mbox/mbox_andes_plic_sw.c b/drivers/mbox/mbox_andes_plic_sw.c index 379ba9b627d..b2972002331 100644 --- a/drivers/mbox/mbox_andes_plic_sw.c +++ b/drivers/mbox/mbox_andes_plic_sw.c @@ -8,215 +8,152 @@ #define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL #include -#include #include +#include LOG_MODULE_REGISTER(mbox_andes_plic_sw); -#define DT_DRV_COMPAT andestech_plic_sw +#define DT_DRV_COMPAT andestech_mbox_plic_sw -#define IRQ_REG(n) (n >> 5) -#define PLIC_BASE(dev) \ - ((const struct mbox_andes_conf * const)(dev)->config)->base - -#define REG_PRIORITY(dev, irq) \ - (PLIC_BASE(dev) + 0x0 + (irq << 2)) -#define REG_PENDING(dev, irq) \ - (PLIC_BASE(dev) + 0x1000 + (IRQ_REG(irq) << 2)) -#define REG_ENABLE(dev, hart, irq) \ - (PLIC_BASE(dev) + 0x2000 + (hart << 7) + IRQ_REG(irq)) -#define REG_CLAIM(dev, hart) \ - (PLIC_BASE(dev) + 0x200004 + (hart << 12)) - -#define IPI_NUM DT_INST_PROP(0, channel_max) - -static struct mbox_andes_data { - mbox_callback_t cb[IPI_NUM]; - void *user_data[IPI_NUM]; - uint32_t enabled_channel[CONFIG_MP_MAX_NUM_CPUS]; -#ifdef CONFIG_SCHED_IPI_SUPPORTED - uint32_t reg_cb_channel; - uint32_t ipi_channel; -#endif -} andes_mbox_data; - -static struct mbox_andes_conf { - uint32_t base; - uint32_t channel_max; -} andes_mbox_conf = { - .base = DT_INST_REG_ADDR(0), - .channel_max = IPI_NUM, +struct mbox_plic_data { + mbox_callback_t *cb; + void **user_data; + struct k_spinlock lock; }; -static struct k_spinlock mbox_syn; - -static void plic_sw_irq_set_pending(const struct device *dev, uint32_t irq) -{ - uint32_t pend; - k_spinlock_key_t key = k_spin_lock(&mbox_syn); - - pend = sys_read32(REG_PENDING(dev, irq)); - pend |= BIT(irq); - sys_write32(pend, REG_PENDING(dev, irq)); - - k_spin_unlock(&mbox_syn, key); -} +struct mbox_plic_conf { + uint32_t channel_max; + const uint32_t *irq_sources; +}; static inline bool is_channel_valid(const struct device *dev, uint32_t ch) { - const struct mbox_andes_conf *conf = dev->config; + const struct mbox_plic_conf *conf = dev->config; - return (ch <= conf->channel_max); + return (ch <= conf->channel_max) && conf->irq_sources[ch]; } -static int mbox_andes_send(const struct device *dev, uint32_t ch, - const struct mbox_msg *msg) +static int mbox_plic_send(const struct device *dev, uint32_t ch, const struct mbox_msg *msg) { + const struct mbox_plic_conf *conf = dev->config; + if (msg) { - LOG_WRN("Sending data not supported"); + LOG_WRN("Transfer mode is not supported"); } if (!is_channel_valid(dev, ch)) { return -EINVAL; } - /* Send IPI by triggering the pending register of PLIC SW. */ - plic_sw_irq_set_pending(dev, ch + 1); + /* Send the MBOX signal by setting the Pending bit register in the PLIC. */ + riscv_plic_irq_set_pending(conf->irq_sources[ch]); return 0; } -static int mbox_andes_register_callback(const struct device *dev, uint32_t ch, - mbox_callback_t cb, void *user_data) +static int mbox_plic_register_callback(const struct device *dev, uint32_t ch, mbox_callback_t cb, + void *user_data) { - struct mbox_andes_data *data = dev->data; - const struct mbox_andes_conf *conf = dev->config; - int ret = 0; + struct mbox_plic_data *data = dev->data; - k_spinlock_key_t key = k_spin_lock(&mbox_syn); - - if (ch > conf->channel_max) { - ret = -EINVAL; - goto out; + if (!is_channel_valid(dev, ch)) { + return -EINVAL; } -#ifdef CONFIG_SCHED_IPI_SUPPORTED - if (ch & data->ipi_channel & data->reg_cb_channel) { - ret = -EALREADY; - goto out; - } + k_spinlock_key_t key = k_spin_lock(&data->lock); - data->reg_cb_channel |= BIT(ch); -#endif data->cb[ch] = cb; data->user_data[ch] = user_data; -out: - k_spin_unlock(&mbox_syn, key); + k_spin_unlock(&data->lock, key); return 0; } -static int mbox_andes_mtu_get(const struct device *dev) +static int mbox_plic_mtu_get(const struct device *dev) { - /* We only support signalling */ - return 0; + /* MBOX PLIC only support signalling mode */ + return -ENOTSUP; } -static uint32_t mbox_andes_max_channels_get(const struct device *dev) +static uint32_t mbox_plic_max_channels_get(const struct device *dev) { - const struct mbox_andes_conf *conf = dev->config; + const struct mbox_plic_conf *conf = dev->config; return conf->channel_max; } -static int mbox_andes_set_enabled(const struct device *dev, uint32_t ch, - bool enable) +static int mbox_plic_set_enabled(const struct device *dev, uint32_t ch, bool enable) { - uint32_t en, is_enabled_ch, hartid, cpu_id, irq; - struct mbox_andes_data *data = dev->data; - int ret = 0; - - k_spinlock_key_t key = k_spin_lock(&mbox_syn); + struct mbox_plic_data *data = dev->data; + const struct mbox_plic_conf *conf = dev->config; if (!is_channel_valid(dev, ch)) { - ret = -EINVAL; - goto out; - } - - irq = ch + 1; - hartid = arch_proc_id(); - cpu_id = _current_cpu->id; - - is_enabled_ch = data->enabled_channel[cpu_id] & BIT(ch); - - if ((!enable && !is_enabled_ch) || (enable && is_enabled_ch)) { - ret = -EALREADY; - goto out; + return -EINVAL; } if (enable && !(data->cb[ch])) { LOG_WRN("Enabling channel without a registered callback\n"); } - en = sys_read32(REG_ENABLE(dev, hartid, irq)); - if (enable) { - data->enabled_channel[cpu_id] |= BIT(ch); - sys_write32(1, REG_PRIORITY(dev, irq)); - en |= BIT(irq); + riscv_plic_irq_enable(conf->irq_sources[ch]); } else { - data->enabled_channel[cpu_id] &= ~BIT(ch); - en &= ~BIT(irq); + riscv_plic_irq_disable(conf->irq_sources[ch]); } - sys_write32(en, REG_ENABLE(dev, hartid, irq)); -out: - k_spin_unlock(&mbox_syn, key); - - return ret; -} - -static void andes_plic_sw_irq_handler(const struct device *dev) -{ - struct mbox_andes_data *data = dev->data; - uint32_t irq, ch, hartid; - - hartid = arch_proc_id(); - - /* PLIC claim: Get the SW IRQ number generating the interrupt. */ - irq = sys_read32(REG_CLAIM(dev, hartid)); - ch = irq - 1; - - if (irq) { - sys_write32(irq, REG_CLAIM(dev, hartid)); - - if (data->cb[ch]) { - /* Only one MAILBOX, id is unused and set to 0 */ - data->cb[ch](dev, ch, data->user_data[ch], NULL); - } - } -} - -static int mbox_andes_init(const struct device *dev) -{ - /* Setup IRQ handler for PLIC SW driver */ - IRQ_CONNECT(RISCV_IRQ_MSOFT, 1, - andes_plic_sw_irq_handler, DEVICE_DT_INST_GET(0), 0); - -#ifndef CONFIG_SMP - irq_enable(RISCV_IRQ_MSOFT); -#endif return 0; } -static const struct mbox_driver_api mbox_andes_driver_api = { - .send = mbox_andes_send, - .register_callback = mbox_andes_register_callback, - .mtu_get = mbox_andes_mtu_get, - .max_channels_get = mbox_andes_max_channels_get, - .set_enabled = mbox_andes_set_enabled, +static const struct mbox_driver_api mbox_plic_driver_api = { + .send = mbox_plic_send, + .register_callback = mbox_plic_register_callback, + .mtu_get = mbox_plic_mtu_get, + .max_channels_get = mbox_plic_max_channels_get, + .set_enabled = mbox_plic_set_enabled, }; -DEVICE_DT_INST_DEFINE(0, mbox_andes_init, NULL, &andes_mbox_data, - &andes_mbox_conf, PRE_KERNEL_1, CONFIG_MBOX_INIT_PRIORITY, - &mbox_andes_driver_api); +#define MBOX_PLIC_ISR_FUNCTION_IDX(node, prop, idx, n) \ + static void mbox_plic_irq_handler##n##_##idx(const struct device *dev) \ + { \ + struct mbox_plic_data *data = dev->data; \ + const uint32_t irq = DT_IRQ_BY_IDX(node, idx, irq); \ + if (data->cb[irq]) { \ + data->cb[irq](dev, irq, data->user_data[irq], NULL); \ + } \ + } +#define MBOX_PLIC_ISR_FUNCTION(n) \ + DT_INST_FOREACH_PROP_ELEM_SEP_VARGS(n, interrupt_names, MBOX_PLIC_ISR_FUNCTION_IDX, (), n) +#define MBOX_PLIC_IRQ_CONNECT_IDX(node, prop, idx, n) \ + IRQ_CONNECT(DT_IRQN_BY_IDX(node, idx), 1, mbox_plic_irq_handler##n##_##idx, \ + DEVICE_DT_INST_GET(n), 0) +#define MBOX_PLIC_IRQ_CONNECT(n) \ + DT_INST_FOREACH_PROP_ELEM_SEP_VARGS(n, interrupt_names, MBOX_PLIC_IRQ_CONNECT_IDX, (;), n) +#define MBOX_PLIC_INIT_FUNCTION(n) \ + static int mbox_plic_init##n(const struct device *dev) \ + { \ + MBOX_PLIC_IRQ_CONNECT(n); \ + return 0; \ + } +#define MBOX_PLIC_IRQ_SOURCE_IDX(node, prop, idx) \ + [DT_IRQ_BY_IDX(node, idx, irq)] = DT_IRQN_BY_IDX(node, idx) +#define MBOX_PLIC_IRQ_SOURCE(n) \ + static const unsigned int irq_sources##n[] = {DT_INST_FOREACH_PROP_ELEM_SEP( \ + n, interrupt_names, MBOX_PLIC_IRQ_SOURCE_IDX, (,))}; +#define MBOX_PLIC_DEVICE_INIT(n) \ + MBOX_PLIC_ISR_FUNCTION(n) \ + MBOX_PLIC_INIT_FUNCTION(n) \ + MBOX_PLIC_IRQ_SOURCE(n) \ + static mbox_callback_t mbox_callback##n[ARRAY_SIZE(irq_sources##n)]; \ + static void *user_data##n[ARRAY_SIZE(irq_sources##n)]; \ + static struct mbox_plic_data mbox_plic_data##n = { \ + .cb = mbox_callback##n, \ + .user_data = user_data##n, \ + }; \ + static const struct mbox_plic_conf mbox_plic_conf##n = { \ + .channel_max = ARRAY_SIZE(irq_sources##n), \ + .irq_sources = irq_sources##n, \ + }; \ + DEVICE_DT_INST_DEFINE(n, &mbox_plic_init##n, NULL, &mbox_plic_data##n, &mbox_plic_conf##n, \ + PRE_KERNEL_2, CONFIG_MBOX_INIT_PRIORITY, &mbox_plic_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(MBOX_PLIC_DEVICE_INIT)