intc: Add support for GRLIB IRQMP
This adds support for the GRLIB IRQMP interrupt controller commonly used
in LEON3/4/5 systems.
The driver supports the 15 SPARC interrupts and 16 extended interrupts.
Signed-off-by: Martin Åberg <martin.aberg@gaisler.com>
diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt
index 2b31302..e8bc013 100644
--- a/drivers/interrupt_controller/CMakeLists.txt
+++ b/drivers/interrupt_controller/CMakeLists.txt
@@ -17,3 +17,4 @@
zephyr_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ intc_vexriscv_litex.c)
zephyr_sources_ifdef(CONFIG_SWERV_PIC intc_swerv_pic.c)
zephyr_sources_ifdef(CONFIG_NPCX_MIWU intc_miwu.c)
+zephyr_sources_ifdef(CONFIG_LEON_IRQMP intc_irqmp.c)
diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig
index ec3084c..6ab2084 100644
--- a/drivers/interrupt_controller/Kconfig
+++ b/drivers/interrupt_controller/Kconfig
@@ -39,6 +39,13 @@
help
IRQ implementation for LiteX VexRiscv
+config LEON_IRQMP
+ bool "GRLIB IRQMP interrupt controller"
+ default y
+ depends on SOC_SPARC_LEON
+ help
+ GRLIB IRQMP and IRQAMP
+
source "drivers/interrupt_controller/Kconfig.multilevel"
source "drivers/interrupt_controller/Kconfig.loapic"
diff --git a/drivers/interrupt_controller/intc_irqmp.c b/drivers/interrupt_controller/intc_irqmp.c
new file mode 100644
index 0000000..30faa4e
--- /dev/null
+++ b/drivers/interrupt_controller/intc_irqmp.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2019-2020 Cobham Gaisler AB
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/*
+ * This is a driver for the GRLIB IRQMP interrupt controller common in LEON
+ * systems.
+ *
+ * Interrupt level 1..15 are SPARC interrupts. Interrupt level 16..31, if
+ * implemented in the interrupt controller, are IRQMP "extended interrupts".
+ *
+ * For more information about IRQMP, see the GRLIB IP Core User's Manual.
+ */
+
+#define DT_DRV_COMPAT gaisler_irqmp
+
+#include <kernel.h>
+#include <init.h>
+
+/*
+ * Register description for IRQMP and IRQAMP interrupt controllers
+ * IRQMP - Multiprocessor Interrupt Controller
+ * IRQ(A)MP - Multiprocessor Interrupt Controller with extended ASMP support
+ */
+#define IRQMP_NCPU_MAX 16
+struct irqmp_regs {
+ uint32_t ilevel; /* 0x00 */
+ uint32_t ipend; /* 0x04 */
+ uint32_t iforce0; /* 0x08 */
+ uint32_t iclear; /* 0x0c */
+ uint32_t mpstat; /* 0x10 */
+ uint32_t brdlst; /* 0x14 */
+ uint32_t errstat; /* 0x18 */
+ uint32_t wdogctrl; /* 0x1c */
+ uint32_t asmpctrl; /* 0x20 */
+ uint32_t icselr[2]; /* 0x24 */
+ uint32_t reserved2c; /* 0x2c */
+ uint32_t reserved30; /* 0x30 */
+ uint32_t reserved34; /* 0x34 */
+ uint32_t reserved38; /* 0x38 */
+ uint32_t reserved3c; /* 0x3c */
+ uint32_t pimask[IRQMP_NCPU_MAX]; /* 0x40 */
+ uint32_t piforce[IRQMP_NCPU_MAX]; /* 0x80 */
+ uint32_t pextack[IRQMP_NCPU_MAX]; /* 0xc0 */
+};
+
+#define IRQMP_PEXTACK_EID (0x1f << 0)
+
+static volatile struct irqmp_regs *get_irqmp_regs(void)
+{
+ return (struct irqmp_regs *) DT_INST_REG_ADDR(0);
+}
+
+static int get_irqmp_eirq(void)
+{
+ return DT_INST_PROP(0, eirq);
+}
+
+void arch_irq_enable(unsigned int source)
+{
+ volatile struct irqmp_regs *regs = get_irqmp_regs();
+ volatile uint32_t *pimask = ®s->pimask[0];
+ const uint32_t setbit = (1U << source);
+ unsigned int key;
+
+ key = arch_irq_lock();
+ *pimask |= setbit;
+ arch_irq_unlock(key);
+}
+
+void arch_irq_disable(unsigned int source)
+{
+ volatile struct irqmp_regs *regs = get_irqmp_regs();
+ volatile uint32_t *pimask = ®s->pimask[0];
+ const uint32_t keepbits = ~(1U << source);
+ unsigned int key;
+
+ key = arch_irq_lock();
+ *pimask &= keepbits;
+ arch_irq_unlock(key);
+}
+
+int arch_irq_is_enabled(unsigned int source)
+{
+ volatile struct irqmp_regs *regs = get_irqmp_regs();
+ volatile uint32_t *pimask = ®s->pimask[0];
+
+ return !!(*pimask & (1U << source));
+}
+
+int z_sparc_int_get_source(int irl)
+{
+ volatile struct irqmp_regs *regs = get_irqmp_regs();
+ const int eirq = get_irqmp_eirq();
+ int source;
+
+ if ((eirq != 0) && (irl == eirq)) {
+ source = regs->pextack[0] & IRQMP_PEXTACK_EID;
+ if (source == 0) {
+ source = irl;
+ }
+ } else {
+ source = irl;
+ }
+
+ return source;
+}
+
+static int irqmp_init(const struct device *dev)
+{
+ ARG_UNUSED(dev);
+ volatile struct irqmp_regs *regs = get_irqmp_regs();
+
+ regs->ilevel = 0;
+ regs->ipend = 0;
+ regs->iforce0 = 0;
+ regs->pimask[0] = 0;
+ regs->piforce[0] = 0xfffe0000;
+
+ return 0;
+}
+
+SYS_INIT(irqmp_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
diff --git a/dts/bindings/interrupt-controller/gaisler,irqmp.yaml b/dts/bindings/interrupt-controller/gaisler,irqmp.yaml
new file mode 100644
index 0000000..270431e
--- /dev/null
+++ b/dts/bindings/interrupt-controller/gaisler,irqmp.yaml
@@ -0,0 +1,23 @@
+# Copyright (c) 2020 Cobham Gaisler AB
+# SPDX-License-Identifier: Apache-2.0
+
+description: GRLIB Multiprocessor Interrupt Controller
+
+compatible: "gaisler,irqmp"
+
+include: [interrupt-controller.yaml, base.yaml]
+
+properties:
+ reg:
+ required: true
+
+ eirq:
+ type: int
+ default: 0
+ description: Extended interrupt number (or 0 for none)
+
+ "#interrupt-cells":
+ const: 1
+
+interrupt-cells:
+ - irq