[PATCH v2 08/13] lib: utils/irqchip: Add IMSIC library
Anup Patel
apatel at ventanamicro.com
Wed Feb 9 07:04:35 PST 2022
We add simple IMSIC library which is independent of hardware description
format (FDT or ACPI). This IMSIC library can be used by custom OpenSBI
platform support to setup IMSIC for external interrupts.
Signed-off-by: Anup Patel <anup.patel at wdc.com>
Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
include/sbi_utils/irqchip/imsic.h | 50 ++++++
lib/utils/irqchip/imsic.c | 287 ++++++++++++++++++++++++++++++
lib/utils/irqchip/objects.mk | 1 +
3 files changed, 338 insertions(+)
create mode 100644 include/sbi_utils/irqchip/imsic.h
create mode 100644 lib/utils/irqchip/imsic.c
diff --git a/include/sbi_utils/irqchip/imsic.h b/include/sbi_utils/irqchip/imsic.h
new file mode 100644
index 0000000..cffcb5a
--- /dev/null
+++ b/include/sbi_utils/irqchip/imsic.h
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (c) 2022 Ventana Micro Systems Inc.
+ *
+ * Authors:
+ * Anup Patel <anup.patel at wdc.com>
+ */
+
+#ifndef __IRQCHIP_IMSIC_H__
+#define __IRQCHIP_IMSIC_H__
+
+#include <sbi/sbi_types.h>
+
+#define IMSIC_MMIO_PAGE_SHIFT 12
+#define IMSIC_MMIO_PAGE_SZ (1UL << IMSIC_MMIO_PAGE_SHIFT)
+
+#define IMSIC_MAX_REGS 16
+
+struct imsic_regs {
+ unsigned long addr;
+ unsigned long size;
+};
+
+struct imsic_data {
+ bool targets_mmode;
+ u32 guest_index_bits;
+ u32 hart_index_bits;
+ u32 group_index_bits;
+ u32 group_index_shift;
+ unsigned long num_ids;
+ struct imsic_regs regs[IMSIC_MAX_REGS];
+};
+
+int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file);
+
+struct imsic_data *imsic_get_data(u32 hartid);
+
+int imsic_get_target_file(u32 hartid);
+
+void imsic_local_irqchip_init(void);
+
+int imsic_warm_irqchip_init(void);
+
+int imsic_data_check(struct imsic_data *imsic);
+
+int imsic_cold_irqchip_init(struct imsic_data *imsic);
+
+#endif
diff --git a/lib/utils/irqchip/imsic.c b/lib/utils/irqchip/imsic.c
new file mode 100644
index 0000000..123d01f
--- /dev/null
+++ b/lib/utils/irqchip/imsic.c
@@ -0,0 +1,287 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Western Digital Corporation or its affiliates.
+ * Copyright (c) 2022 Ventana Micro Systems Inc.
+ *
+ * Authors:
+ * Anup Patel <anup.patel at wdc.com>
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_io.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_hartmask.h>
+#include <sbi/sbi_ipi.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_trap.h>
+#include <sbi_utils/irqchip/imsic.h>
+
+#define IMSIC_MMIO_PAGE_LE 0x00
+#define IMSIC_MMIO_PAGE_BE 0x04
+
+#define IMSIC_MIN_ID 63
+#define IMSIC_MAX_ID 2047
+
+#define IMSIC_EIDELIVERY 0x70
+
+#define IMSIC_EITHRESHOLD 0x72
+
+#define IMSIC_TOPEI 0x76
+#define IMSIC_TOPEI_ID_SHIFT 16
+#define IMSIC_TOPEI_ID_MASK 0x7ff
+#define IMSIC_TOPEI_PRIO_MASK 0x7ff
+
+#define IMSIC_EIP0 0x80
+
+#define IMSIC_EIP63 0xbf
+
+#define IMSIC_EIE0 0xc0
+
+#define IMSIC_EIE63 0xff
+
+#define IMSIC_DISABLE_EIDELIVERY 0
+#define IMSIC_ENABLE_EIDELIVERY 1
+#define IMSIC_DISABLE_EITHRESHOLD 1
+#define IMSIC_ENABLE_EITHRESHOLD 0
+
+#define IMSIC_IPI_ID 1
+
+#define imsic_csr_write(__c, __v) \
+do { \
+ csr_write(CSR_MISELECT, __c); \
+ csr_write(CSR_MIREG, __v); \
+} while (0)
+
+#define imsic_csr_read(__c) \
+({ \
+ unsigned long __v; \
+ csr_write(CSR_MISELECT, __c); \
+ __v = csr_read(CSR_MIREG); \
+ __v; \
+})
+
+static struct imsic_data *imsic_hartid2data[SBI_HARTMASK_MAX_BITS];
+static int imsic_hartid2file[SBI_HARTMASK_MAX_BITS];
+
+int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file)
+{
+ if (!imsic || !imsic->targets_mmode ||
+ (SBI_HARTMASK_MAX_BITS <= hartid))
+ return SBI_EINVAL;
+
+ imsic_hartid2data[hartid] = imsic;
+ imsic_hartid2file[hartid] = file;
+ return 0;
+}
+
+struct imsic_data *imsic_get_data(u32 hartid)
+{
+ if (SBI_HARTMASK_MAX_BITS <= hartid)
+ return NULL;
+ return imsic_hartid2data[hartid];
+}
+
+int imsic_get_target_file(u32 hartid)
+{
+ if ((SBI_HARTMASK_MAX_BITS <= hartid) ||
+ !imsic_hartid2data[hartid])
+ return SBI_ENOENT;
+ return imsic_hartid2file[hartid];
+}
+
+static int imsic_external_irqfn(struct sbi_trap_regs *regs)
+{
+ ulong mirq;
+
+ while ((mirq = csr_swap(CSR_MTOPEI, 0))) {
+ mirq = (mirq >> IMSIC_TOPEI_ID_SHIFT);
+
+ switch (mirq) {
+ case IMSIC_IPI_ID:
+ sbi_ipi_process();
+ break;
+ default:
+ sbi_printf("%s: unhandled IRQ%d\n",
+ __func__, (u32)mirq);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void imsic_ipi_send(u32 target_hart)
+{
+ unsigned long reloff;
+ struct imsic_regs *regs;
+ struct imsic_data *data = imsic_hartid2data[target_hart];
+ int file = imsic_hartid2file[target_hart];
+
+ if (!data || !data->targets_mmode)
+ return;
+
+ regs = &data->regs[0];
+ reloff = file * (1UL << data->guest_index_bits) * IMSIC_MMIO_PAGE_SZ;
+ while (regs->size && (regs->size <= reloff)) {
+ reloff -= regs->size;
+ regs++;
+ }
+
+ if (regs->size && (reloff < regs->size))
+ writel(IMSIC_IPI_ID,
+ (void *)(regs->addr + reloff + IMSIC_MMIO_PAGE_LE));
+}
+
+static struct sbi_ipi_device imsic_ipi_device = {
+ .name = "aia-imsic",
+ .ipi_send = imsic_ipi_send
+};
+
+void imsic_local_irqchip_init(void)
+{
+ /*
+ * This function is expected to be called from:
+ * 1) nascent_init() platform callback which is called
+ * very early on each HART in boot-up path and and
+ * HSM resume path.
+ * 2) irqchip_init() platform callback which is called
+ * in boot-up path.
+ */
+
+ /* Setup threshold to allow all enabled interrupts */
+ imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
+
+ /* Enable interrupt delivery */
+ imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
+
+ /* Enable IPI */
+ csr_write(CSR_MSETEIENUM, IMSIC_IPI_ID);
+}
+
+int imsic_warm_irqchip_init(void)
+{
+ unsigned long i;
+ struct imsic_data *imsic = imsic_hartid2data[current_hartid()];
+
+ /* Sanity checks */
+ if (!imsic || !imsic->targets_mmode)
+ return SBI_EINVAL;
+
+ /* Disable all interrupts */
+ for (i = 1; i <= imsic->num_ids; i++)
+ csr_write(CSR_MCLREIENUM, i);
+
+ /* Clear IPI */
+ csr_write(CSR_MCLREIPNUM, IMSIC_IPI_ID);
+
+ /* Local IMSIC initialization */
+ imsic_local_irqchip_init();
+
+ return 0;
+}
+
+int imsic_data_check(struct imsic_data *imsic)
+{
+ u32 i, tmp;
+ unsigned long base_addr, addr, mask;
+
+ /* Sanity checks */
+ if (!imsic ||
+ (imsic->num_ids < IMSIC_MIN_ID) ||
+ (IMSIC_MAX_ID < imsic->num_ids))
+ return SBI_EINVAL;
+
+ tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT;
+ if (tmp < imsic->guest_index_bits)
+ return SBI_EINVAL;
+
+ tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
+ imsic->guest_index_bits;
+ if (tmp < imsic->hart_index_bits)
+ return SBI_EINVAL;
+
+ tmp = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
+ imsic->guest_index_bits - imsic->hart_index_bits;
+ if (tmp < imsic->group_index_bits)
+ return SBI_EINVAL;
+
+ tmp = IMSIC_MMIO_PAGE_SHIFT + imsic->guest_index_bits +
+ imsic->hart_index_bits;
+ if (imsic->group_index_shift < tmp)
+ return SBI_EINVAL;
+ tmp = imsic->group_index_bits + imsic->group_index_shift - 1;
+ if (tmp >= BITS_PER_LONG)
+ return SBI_EINVAL;
+
+ /*
+ * Number of interrupt identities should be 1 less than
+ * multiple of 63
+ */
+ if ((imsic->num_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID)
+ return SBI_EINVAL;
+
+ /* We should have at least one regset */
+ if (!imsic->regs[0].size)
+ return SBI_EINVAL;
+
+ /* Match patter of each regset */
+ base_addr = imsic->regs[0].addr;
+ base_addr &= ~((1UL << (imsic->guest_index_bits +
+ imsic->hart_index_bits +
+ IMSIC_MMIO_PAGE_SHIFT)) - 1);
+ base_addr &= ~(((1UL << imsic->group_index_bits) - 1) <<
+ imsic->group_index_shift);
+ for (i = 0; i < IMSIC_MAX_REGS && imsic->regs[i].size; i++) {
+ mask = (1UL << imsic->guest_index_bits) * IMSIC_MMIO_PAGE_SZ;
+ mask -= 1UL;
+ if (imsic->regs[i].size & mask)
+ return SBI_EINVAL;
+
+ addr = imsic->regs[i].addr;
+ addr &= ~((1UL << (imsic->guest_index_bits +
+ imsic->hart_index_bits +
+ IMSIC_MMIO_PAGE_SHIFT)) - 1);
+ addr &= ~(((1UL << imsic->group_index_bits) - 1) <<
+ imsic->group_index_shift);
+ if (base_addr != addr)
+ return SBI_EINVAL;
+ }
+
+ return 0;
+}
+
+int imsic_cold_irqchip_init(struct imsic_data *imsic)
+{
+ int i, rc;
+ struct sbi_domain_memregion reg;
+
+ /* Sanity checks */
+ rc = imsic_data_check(imsic);
+ if (rc)
+ return rc;
+
+ /* We only initialize M-mode IMSIC */
+ if (!imsic->targets_mmode)
+ return SBI_EINVAL;
+
+ /* Setup external interrupt function for IMSIC */
+ sbi_trap_set_external_irqfn(imsic_external_irqfn);
+
+ /* Add IMSIC regions to the root domain */
+ for (i = 0; i < IMSIC_MAX_REGS && imsic->regs[i].size; i++) {
+ sbi_domain_memregion_init(imsic->regs[i].addr,
+ imsic->regs[i].size,
+ SBI_DOMAIN_MEMREGION_MMIO, ®);
+ rc = sbi_domain_root_add_memregion(®);
+ if (rc)
+ return rc;
+ }
+
+ /* Register IPI device */
+ sbi_ipi_set_device(&imsic_ipi_device);
+
+ return 0;
+}
diff --git a/lib/utils/irqchip/objects.mk b/lib/utils/irqchip/objects.mk
index 934f706..76a3c94 100644
--- a/lib/utils/irqchip/objects.mk
+++ b/lib/utils/irqchip/objects.mk
@@ -9,4 +9,5 @@
libsbiutils-objs-y += irqchip/fdt_irqchip.o
libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o
+libsbiutils-objs-y += irqchip/imsic.o
libsbiutils-objs-y += irqchip/plic.o
--
2.25.1
More information about the opensbi
mailing list