drivers: interrupt_controller: Add SweRV PIC support
Add support for the built-in Programmable Interrupt Controller
found in the SweRV EH1 RISC-V CPU
Signed-off-by: Olof Kindgren <olof.kindgren@gmail.com>
diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt
index 96fab70..510b0c2 100644
--- a/drivers/interrupt_controller/CMakeLists.txt
+++ b/drivers/interrupt_controller/CMakeLists.txt
@@ -13,3 +13,4 @@
zephyr_sources_ifdef(CONFIG_RV32M1_INTMUX rv32m1_intmux.c)
zephyr_sources_ifdef(CONFIG_SAM0_EIC sam0_eic.c)
zephyr_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ vexriscv_litex.c)
+zephyr_sources_ifdef(CONFIG_SWERV_PIC swerv_pic.c)
diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig
index acbe1ad..4cb3fd4 100644
--- a/drivers/interrupt_controller/Kconfig
+++ b/drivers/interrupt_controller/Kconfig
@@ -98,6 +98,12 @@
Platform Level Interrupt Controller provides support
for external interrupt lines defined by the RISC-V SoC;
+config SWERV_PIC
+ bool "SweRV EH1 Programmable Interrupt Controller (PIC)"
+ default n
+ help
+ Programmable Interrupt Controller for the SweRV EH1 RISC-V CPU;
+
config VEXRISCV_LITEX_IRQ
bool "VexRiscv LiteX Interrupt controller"
depends on SOC_RISCV32_LITEX_VEXRISCV
diff --git a/drivers/interrupt_controller/swerv_pic.c b/drivers/interrupt_controller/swerv_pic.c
new file mode 100644
index 0000000..f35d023
--- /dev/null
+++ b/drivers/interrupt_controller/swerv_pic.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @brief SweRV EH1 PIC driver
+ */
+
+#include <kernel.h>
+#include <arch/cpu.h>
+#include <init.h>
+#include <sw_isr_table.h>
+
+#define SWERV_PIC_MAX_NUM CONFIG_NUM_IRQS
+#define SWERV_PIC_MAX_ID (SWERV_PIC_MAX_NUM + RISCV_MAX_GENERIC_IRQ)
+#define SWERV_PIC_MAX_PRIO 16
+
+#define SWERV_PIC_mpiccfg 0x3000
+#define SWERV_PIC_meipl(s) (0x0 + (s)*4)
+#define SWERV_PIC_meip(x) (0x1000 + (x)*4)
+#define SWERV_PIC_meie(s) (0x2000 + (s)*4)
+#define SWERV_PIC_meigwctrl(s) (0x4000 + (s)*4)
+#define SWERV_PIC_meigwclr(s) (0x5000 + (s)*4)
+
+#define SWERV_PIC_meivt "0xBC8"
+#define SWERV_PIC_meipt "0xBC9"
+#define SWERV_PIC_meicpct "0xBCA"
+#define SWERV_PIC_meicidpl "0xBCB"
+#define SWERV_PIC_meicurpl "0xBCC"
+#define SWERV_PIC_meihap "0xFC8"
+
+#define swerv_piccsr(csr) SWERV_PIC_##csr
+
+#define swerv_pic_readcsr(csr, value) \
+ volatile("csrr %0, "swerv_piccsr(csr) : "=r" (value))
+#define swerv_pic_writecsr(csr, value) \
+ volatile("csrw "swerv_piccsr(csr)", %0" :: "rK" (value))
+
+static int save_irq;
+
+static u32_t swerv_pic_read(u32_t reg)
+{
+ return *(volatile u32_t *)(DT_INST_0_SWERV_PIC_BASE_ADDRESS + reg);
+}
+
+static void swerv_pic_write(u32_t reg, u32_t val)
+{
+ *(volatile u32_t *)(DT_INST_0_SWERV_PIC_BASE_ADDRESS + reg) = val;
+}
+
+void swerv_pic_irq_enable(u32_t irq)
+{
+ u32_t key;
+
+ if ((irq >= SWERV_PIC_MAX_ID) || (irq < RISCV_MAX_GENERIC_IRQ)) {
+ return;
+ }
+
+ key = irq_lock();
+ swerv_pic_write(SWERV_PIC_meie(irq - RISCV_MAX_GENERIC_IRQ), 1);
+ irq_unlock(key);
+}
+
+void swerv_pic_irq_disable(u32_t irq)
+{
+ u32_t key;
+
+ if ((irq >= SWERV_PIC_MAX_ID) || (irq < RISCV_MAX_GENERIC_IRQ)) {
+ return;
+ }
+
+ key = irq_lock();
+ swerv_pic_write(SWERV_PIC_meie(irq - RISCV_MAX_GENERIC_IRQ), 0);
+ irq_unlock(key);
+}
+
+int swerv_pic_irq_is_enabled(u32_t irq)
+{
+ if ((irq >= SWERV_PIC_MAX_ID) || (irq < RISCV_MAX_GENERIC_IRQ)) {
+ return -1;
+ }
+
+ return swerv_pic_read(SWERV_PIC_meie(irq - RISCV_MAX_GENERIC_IRQ))
+ & 0x1;
+}
+
+void swerv_pic_set_priority(u32_t irq, u32_t priority)
+{
+ u32_t key;
+
+ if (irq <= RISCV_MAX_GENERIC_IRQ) {
+ return;
+ }
+
+ if ((irq >= SWERV_PIC_MAX_ID) || (irq < RISCV_MAX_GENERIC_IRQ)) {
+ return;
+ }
+
+ if (priority >= SWERV_PIC_MAX_PRIO) {
+ return;
+ }
+
+ key = irq_lock();
+ swerv_pic_write(SWERV_PIC_meipl(irq - RISCV_MAX_GENERIC_IRQ), priority);
+ irq_unlock(key);
+}
+
+int swerv_pic_get_irq(void)
+{
+ return save_irq;
+}
+
+static void swerv_pic_irq_handler(void *arg)
+{
+ u32_t tmp;
+ u32_t irq;
+ struct _isr_table_entry *ite;
+
+ /* trigger the capture of the interrupt source ID */
+ __asm__ swerv_pic_writecsr(meicpct, 0);
+
+ __asm__ swerv_pic_readcsr(meihap, tmp);
+ irq = (tmp >> 2) & 0xff;
+
+ save_irq = irq;
+
+ if (irq == 0U || irq >= 64) {
+ z_irq_spurious(NULL);
+ }
+ irq += RISCV_MAX_GENERIC_IRQ;
+
+ /* Call the corresponding IRQ handler in _sw_isr_table */
+ ite = (struct _isr_table_entry *)&_sw_isr_table[irq];
+ if (ite->isr)
+ ite->isr(ite->arg);
+
+ swerv_pic_write(SWERV_PIC_meigwclr(irq), 0);
+}
+
+static int swerv_pic_init(struct device *dev)
+{
+ ARG_UNUSED(dev);
+ int i;
+
+ /* Init priority order to 0, 0=lowest to 15=highest */
+ swerv_pic_write(SWERV_PIC_mpiccfg, 0);
+
+ /* Ensure that all interrupts are disabled initially */
+ for (i = 1; i < SWERV_PIC_MAX_ID; i++) {
+ swerv_pic_write(SWERV_PIC_meie(i), 0);
+ }
+
+ /* Set priority of each interrupt line to 0 initially */
+ for (i = 1; i < SWERV_PIC_MAX_ID; i++) {
+ swerv_pic_write(SWERV_PIC_meipl(i), 15);
+ }
+
+ /* Set property of each interrupt line to level-triggered/high */
+ for (i = 1; i < SWERV_PIC_MAX_ID; i++) {
+ swerv_pic_write(SWERV_PIC_meigwctrl(i), (0<<1)|(0<<0));
+ }
+
+ /* clear pending of each interrupt line */
+ for (i = 1; i < SWERV_PIC_MAX_ID; i++) {
+ swerv_pic_write(SWERV_PIC_meigwclr(i), 0);
+ }
+
+ /* No interrupts masked */
+ __asm__ swerv_pic_writecsr(meipt, 0);
+ __asm__ swerv_pic_writecsr(meicidpl, 0);
+ __asm__ swerv_pic_writecsr(meicurpl, 0);
+
+ /* Setup IRQ handler for SweRV PIC driver */
+ IRQ_CONNECT(RISCV_MACHINE_EXT_IRQ,
+ 0,
+ swerv_pic_irq_handler,
+ NULL,
+ 0);
+
+ /* Enable IRQ for SweRV PIC driver */
+ irq_enable(RISCV_MACHINE_EXT_IRQ);
+
+ return 0;
+}
+
+void arch_irq_enable(unsigned int irq)
+{
+ u32_t mie;
+
+ if (irq > RISCV_MAX_GENERIC_IRQ) {
+ swerv_pic_irq_enable(irq);
+ return;
+ }
+
+ /*
+ * CSR mie register is updated using atomic instruction csrrs
+ * (atomic read and set bits in CSR register)
+ */
+ __asm__ volatile ("csrrs %0, mie, %1\n"
+ : "=r" (mie)
+ : "r" (1 << irq));
+}
+
+void arch_irq_disable(unsigned int irq)
+{
+ u32_t mie;
+
+ if (irq > RISCV_MAX_GENERIC_IRQ) {
+ swerv_pic_irq_disable(irq);
+ return;
+ }
+
+ /*
+ * Use atomic instruction csrrc to disable device interrupt in mie CSR.
+ * (atomic read and clear bits in CSR register)
+ */
+ __asm__ volatile ("csrrc %0, mie, %1\n"
+ : "=r" (mie)
+ : "r" (1 << irq));
+};
+
+int arch_irq_is_enabled(unsigned int irq)
+{
+ u32_t mie;
+
+ if (irq > RISCV_MAX_GENERIC_IRQ)
+ return swerv_pic_irq_is_enabled(irq);
+
+ __asm__ volatile ("csrr %0, mie" : "=r" (mie));
+
+ return !!(mie & (1 << irq));
+}
+
+SYS_INIT(swerv_pic_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
diff --git a/dts/bindings/interrupt-controller/swerv,pic.yaml b/dts/bindings/interrupt-controller/swerv,pic.yaml
new file mode 100644
index 0000000..ae54afd
--- /dev/null
+++ b/dts/bindings/interrupt-controller/swerv,pic.yaml
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 2019
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+---
+title: SweRV EH1 PIC
+
+description: >
+ This binding describes the SweRV EH1 Programmable Interrupt Controller
+
+compatible: "swerv,pic"
+
+include: [interrupt-controller.yaml, base.yaml]
+
+properties:
+ reg:
+ required: true
+
+ riscv,max-priority:
+ type: int
+ description: maximum interrupt priority
+ required: true
+
+interrupt-cells:
+ - irq
+ - priority