[PATCH 08/13] lib: utils/irqchip: Add IMSIC library

Anup Patel apatel at ventanamicro.com
Tue Jan 4 02:13:18 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..f87321f
--- /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	0
+#define IMSIC_ENABLE_EITHRESHOLD	IMSIC_MAX_ID
+
+#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, &reg);
+		rc = sbi_domain_root_add_memregion(&reg);
+		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