drivers: mbox: rework andes mbox plic to leverage intc_plic driver

Andes MBOX PLIC-SW use the same hardware as intc_plic driver. Reworked
mbox_andes_plic_sw implementation to leverage the intc_plic driver.

Signed-off-by: Jimmy Zheng <jimmyzhe@andestech.com>
This commit is contained in:
Jimmy Zheng 2024-10-07 13:57:32 +08:00 committed by Carles Cufí
parent 6caf803a41
commit 6658b9c02c
2 changed files with 91 additions and 153 deletions

View file

@ -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.

View file

@ -8,215 +8,152 @@
#define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
#include <zephyr/spinlock.h>
#include <zephyr/drivers/interrupt_controller/riscv_plic.h>
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)