[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, &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
> >
>
>
> 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