[PATCH v2 08/13] lib: utils/irqchip: Add IMSIC library
Anup Patel
anup at brainfault.org
Tue Feb 15 07:19:42 PST 2022
On Sat, Feb 12, 2022 at 2:39 PM Atish Patra <atishp at atishpatra.org> wrote:
>
> On Wed, Feb 9, 2022 at 7:05 AM Anup Patel <apatel at ventanamicro.com> wrote:
> >
> > 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
> >
>
>
> Reviewed-by: Atish Patra <atishp at rivosinc.com>
Applied this patch to the riscv/opensbi repo.
Regards,
Anup
>
> --
> Regards,
> Atish
More information about the opensbi
mailing list