[PATCH v2 09/13] lib: utils/irqchip: Add FDT based driver for IMSIC

Anup Patel anup at brainfault.org
Tue Feb 15 07:20:21 PST 2022


On Sat, Feb 12, 2022 at 6:25 AM 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 FDT irqchip driver for IMSIC so that generic platform
> > (and other FDT based platforms) can utilize common IMIC library.
> >
> > Signed-off-by: Anup Patel <anup.patel at wdc.com>
> > Signed-off-by: Anup Patel <apatel at ventanamicro.com>
> > ---
> >  include/sbi_utils/fdt/fdt_helper.h    |   6 ++
> >  lib/utils/fdt/fdt_helper.c            | 103 +++++++++++++++++++++++++
> >  lib/utils/irqchip/fdt_irqchip.c       |   2 +
> >  lib/utils/irqchip/fdt_irqchip_imsic.c | 106 ++++++++++++++++++++++++++
> >  lib/utils/irqchip/objects.mk          |   1 +
> >  platform/generic/platform.c           |  12 +++
> >  6 files changed, 230 insertions(+)
> >  create mode 100644 lib/utils/irqchip/fdt_irqchip_imsic.c
> >
> > diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h
> > index 24fee7a..4c8d29e 100644
> > --- a/include/sbi_utils/fdt/fdt_helper.h
> > +++ b/include/sbi_utils/fdt/fdt_helper.h
> > @@ -68,6 +68,12 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
> >  int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
> >                        const char *compatible);
> >
> > +struct imsic_data;
> > +
> > +bool fdt_check_imsic_mlevel(void *fdt);
> > +
> > +int fdt_parse_imsic_node(void *fdt, int nodeoff, struct imsic_data *imsic);
> > +
> >  struct plic_data;
> >
> >  int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic);
> > diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
> > index 5bf4021..e179b79 100644
> > --- a/lib/utils/fdt/fdt_helper.c
> > +++ b/lib/utils/fdt/fdt_helper.c
> > @@ -13,6 +13,7 @@
> >  #include <sbi/sbi_platform.h>
> >  #include <sbi/sbi_scratch.h>
> >  #include <sbi_utils/fdt/fdt_helper.h>
> > +#include <sbi_utils/irqchip/imsic.h>
> >  #include <sbi_utils/irqchip/plic.h>
> >
> >  #define DEFAULT_UART_FREQ              0
> > @@ -465,6 +466,108 @@ int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
> >         return fdt_parse_uart8250_node(fdt, nodeoffset, uart);
> >  }
> >
> > +bool fdt_check_imsic_mlevel(void *fdt)
> > +{
> > +       const fdt32_t *val;
> > +       int i, len, noff = 0;
> > +
> > +       if (!fdt)
> > +               return false;
> > +
> > +       while ((noff = fdt_node_offset_by_compatible(fdt, noff,
> > +                                                    "riscv,imsics")) >= 0) {
> > +               val = fdt_getprop(fdt, noff, "interrupts-extended", &len);
> > +               if (val && len > sizeof(fdt32_t)) {
> > +                       len = len / sizeof(fdt32_t);
> > +                       for (i = 0; i < len; i += 2) {
> > +                               if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT)
> > +                                       return true;
> > +                       }
> > +               }
> > +       }
> > +
> > +       return false;
> > +}
> > +
> > +int fdt_parse_imsic_node(void *fdt, int nodeoff, struct imsic_data *imsic)
> > +{
> > +       const fdt32_t *val;
> > +       struct imsic_regs *regs;
> > +       uint64_t reg_addr, reg_size;
> > +       int i, rc, len, nr_parent_irqs;
> > +
> > +       if (nodeoff < 0 || !imsic || !fdt)
> > +               return SBI_ENODEV;
> > +
> > +       imsic->targets_mmode = false;
> > +       val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &len);
> > +       if (val && len > sizeof(fdt32_t)) {
> > +               len = len / sizeof(fdt32_t);
> > +               nr_parent_irqs = len / 2;
> > +               for (i = 0; i < len; i += 2) {
> > +                       if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT) {
> > +                               imsic->targets_mmode = true;
> > +                               break;
> > +                       }
> > +               }
> > +       } else
> > +               return SBI_EINVAL;
> > +
> > +       val = fdt_getprop(fdt, nodeoff, "riscv,guest-index-bits", &len);
> > +       if (val && len > 0)
> > +               imsic->guest_index_bits = fdt32_to_cpu(*val);
> > +       else
> > +               imsic->guest_index_bits = 0;
> > +
> > +       val = fdt_getprop(fdt, nodeoff, "riscv,hart-index-bits", &len);
> > +       if (val && len > 0) {
> > +               imsic->hart_index_bits = fdt32_to_cpu(*val);
> > +       } else {
> > +               imsic->hart_index_bits = sbi_fls(nr_parent_irqs);
> > +               if ((1UL << imsic->hart_index_bits) < nr_parent_irqs)
> > +                       imsic->hart_index_bits++;
> > +       }
> > +
> > +       val = fdt_getprop(fdt, nodeoff, "riscv,group-index-bits", &len);
> > +       if (val && len > 0)
> > +               imsic->group_index_bits = fdt32_to_cpu(*val);
> > +       else
> > +               imsic->group_index_bits = 0;
> > +
> > +       val = fdt_getprop(fdt, nodeoff, "riscv,group-index-shift", &len);
> > +       if (val && len > 0)
> > +               imsic->group_index_shift = fdt32_to_cpu(*val);
> > +       else
> > +               imsic->group_index_shift = 2 * IMSIC_MMIO_PAGE_SHIFT;
> > +
> > +       val = fdt_getprop(fdt, nodeoff, "riscv,num-ids", &len);
> > +       if (val && len > 0)
> > +               imsic->num_ids = fdt32_to_cpu(*val);
> > +       else
> > +               return SBI_EINVAL;
> > +
> > +       for (i = 0; i < IMSIC_MAX_REGS; i++) {
> > +               regs = &imsic->regs[i];
> > +               regs->addr = 0;
> > +               regs->size = 0;
> > +       }
> > +
> > +       for (i = 0; i < (IMSIC_MAX_REGS - 1); i++) {
> > +               regs = &imsic->regs[i];
> > +
> > +               rc = fdt_get_node_addr_size(fdt, nodeoff, i,
> > +                                           &reg_addr, &reg_size);
> > +               if (rc < 0 || !reg_addr || !reg_size)
> > +                       break;
> > +               regs->addr = reg_addr;
> > +               regs->size = reg_size;
> > +       };
> > +       if (!imsic->regs[0].size)
> > +               return SBI_EINVAL;
> > +
> > +       return 0;
> > +}
> > +
> >  int fdt_parse_plic_node(void *fdt, int nodeoffset, struct plic_data *plic)
> >  {
> >         int len, rc;
> > diff --git a/lib/utils/irqchip/fdt_irqchip.c b/lib/utils/irqchip/fdt_irqchip.c
> > index bf6969a..cf64a2e 100644
> > --- a/lib/utils/irqchip/fdt_irqchip.c
> > +++ b/lib/utils/irqchip/fdt_irqchip.c
> > @@ -12,9 +12,11 @@
> >  #include <sbi_utils/fdt/fdt_helper.h>
> >  #include <sbi_utils/irqchip/fdt_irqchip.h>
> >
> > +extern struct fdt_irqchip fdt_irqchip_imsic;
> >  extern struct fdt_irqchip fdt_irqchip_plic;
> >
> >  static struct fdt_irqchip *irqchip_drivers[] = {
> > +       &fdt_irqchip_imsic,
> >         &fdt_irqchip_plic
> >  };
> >
> > diff --git a/lib/utils/irqchip/fdt_irqchip_imsic.c b/lib/utils/irqchip/fdt_irqchip_imsic.c
> > new file mode 100644
> > index 0000000..b6962be
> > --- /dev/null
> > +++ b/lib/utils/irqchip/fdt_irqchip_imsic.c
> > @@ -0,0 +1,106 @@
> > +/*
> > + * 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 <libfdt.h>
> > +#include <sbi/riscv_asm.h>
> > +#include <sbi/sbi_error.h>
> > +#include <sbi/sbi_hartmask.h>
> > +#include <sbi_utils/fdt/fdt_helper.h>
> > +#include <sbi_utils/irqchip/fdt_irqchip.h>
> > +#include <sbi_utils/irqchip/imsic.h>
> > +
> > +#define IMSIC_MAX_NR                   16
> > +
> > +static unsigned long imsic_count = 0;
> > +static struct imsic_data imsic[IMSIC_MAX_NR];
> > +
> > +static int irqchip_imsic_update_hartid_table(void *fdt, int nodeoff,
> > +                                            struct imsic_data *id)
> > +{
> > +       const fdt32_t *val;
> > +       u32 phandle, hwirq, hartid;
> > +       int i, err, count, cpu_offset, cpu_intc_offset;
> > +
> > +       val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &count);
> > +       if (!val || count < sizeof(fdt32_t))
> > +               return SBI_EINVAL;
> > +       count = count / sizeof(fdt32_t);
> > +
> > +       for (i = 0; i < count; i += 2) {
> > +               phandle = fdt32_to_cpu(val[i]);
> > +               hwirq = fdt32_to_cpu(val[i + 1]);
> > +
> > +               cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
> > +               if (cpu_intc_offset < 0)
> > +                       continue;
> > +
> > +               cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset);
> > +               if (cpu_intc_offset < 0)
> > +                       continue;
> > +
> > +               err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
> > +               if (err)
> > +                       return SBI_EINVAL;
> > +               if (SBI_HARTMASK_MAX_BITS <= hartid)
> > +                       return SBI_EINVAL;
> > +
> > +               switch (hwirq) {
> > +               case IRQ_M_EXT:
> > +                       err = imsic_map_hartid_to_data(hartid, id, i / 2);
> > +                       if (err)
> > +                               return err;
> > +                       break;
> > +               default:
> > +                       break;
> > +               }
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int irqchip_imsic_cold_init(void *fdt, int nodeoff,
> > +                                   const struct fdt_match *match)
> > +{
> > +       int rc;
> > +       struct imsic_data *id;
> > +
> > +       if (IMSIC_MAX_NR <= imsic_count)
> > +               return SBI_ENOSPC;
> > +       id = &imsic[imsic_count];
> > +
> > +       rc = fdt_parse_imsic_node(fdt, nodeoff, id);
> > +       if (rc)
> > +               return rc;
> > +       if (!id->targets_mmode)
> > +               return 0;
> > +
> > +       rc = irqchip_imsic_update_hartid_table(fdt, nodeoff, id);
> > +       if (rc)
> > +               return rc;
> > +
> > +       rc = imsic_cold_irqchip_init(id);
> > +       if (rc)
> > +               return rc;
> > +
> > +       imsic_count++;
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct fdt_match irqchip_imsic_match[] = {
> > +       { .compatible = "riscv,imsics" },
> > +       { },
> > +};
> > +
> > +struct fdt_irqchip fdt_irqchip_imsic = {
> > +       .match_table = irqchip_imsic_match,
> > +       .cold_init = irqchip_imsic_cold_init,
> > +       .warm_init = imsic_warm_irqchip_init,
> > +};
> > diff --git a/lib/utils/irqchip/objects.mk b/lib/utils/irqchip/objects.mk
> > index 76a3c94..ae6f255 100644
> > --- a/lib/utils/irqchip/objects.mk
> > +++ b/lib/utils/irqchip/objects.mk
> > @@ -8,6 +8,7 @@
> >  #
> >
> >  libsbiutils-objs-y += irqchip/fdt_irqchip.o
> > +libsbiutils-objs-y += irqchip/fdt_irqchip_imsic.o
> >  libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o
> >  libsbiutils-objs-y += irqchip/imsic.o
> >  libsbiutils-objs-y += irqchip/plic.o
> > diff --git a/platform/generic/platform.c b/platform/generic/platform.c
> > index bc6e761..8a4fb70 100644
> > --- a/platform/generic/platform.c
> > +++ b/platform/generic/platform.c
> > @@ -18,6 +18,7 @@
> >  #include <sbi_utils/fdt/fdt_helper.h>
> >  #include <sbi_utils/fdt/fdt_pmu.h>
> >  #include <sbi_utils/irqchip/fdt_irqchip.h>
> > +#include <sbi_utils/irqchip/imsic.h>
> >  #include <sbi_utils/serial/fdt_serial.h>
> >  #include <sbi_utils/timer/fdt_timer.h>
> >  #include <sbi_utils/ipi/fdt_ipi.h>
> > @@ -56,6 +57,7 @@ static void fw_platform_lookup_special(void *fdt, int root_offset)
> >  }
> >
> >  extern struct sbi_platform platform;
> > +static bool platform_has_mlevel_imsic = false;
> >  static u32 generic_hart_index2id[SBI_HARTMASK_MAX_BITS] = { 0 };
> >
> >  /*
> > @@ -110,6 +112,8 @@ unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1,
> >
> >         platform.hart_count = hart_count;
> >
> > +       platform_has_mlevel_imsic = fdt_check_imsic_mlevel(fdt);
> > +
> >         /* Return original FDT pointer */
> >         return arg1;
> >
> > @@ -118,6 +122,13 @@ fail:
> >                 wfi();
> >  }
> >
> > +static int generic_nascent_init(void)
> > +{
> > +       if (platform_has_mlevel_imsic)
> > +               imsic_local_irqchip_init();
> > +       return 0;
> > +}
> > +
> >  static int generic_early_init(bool cold_boot)
> >  {
> >         if (!generic_plat || !generic_plat->early_init)
> > @@ -210,6 +221,7 @@ static uint64_t generic_pmu_xlate_to_mhpmevent(uint32_t event_idx,
> >  }
> >
> >  const struct sbi_platform_operations platform_ops = {
> > +       .nascent_init           = generic_nascent_init,
> >         .early_init             = generic_early_init,
> >         .final_init             = generic_final_init,
> >         .early_exit             = generic_early_exit,
> > --
> > 2.25.1
> >
>
> LGTM. Just a suggestion:
>
> I find it bit difficult to follow the code without the dt-bindings
> [1]. The DT bindings may also change
> in future based on feedback from the kernel/device tree mailing list.
> It may take some time to freeze
> the AIA spec as well.
>
> Should we keep the DT binding in OpenSBI as well so it is easier to
> follow the code without referring
> the DT binding in your tree.
>
> We can remove the DT binding once it is merged to upstream Linux to
> avoid duplication.
>
> [1] https://github.com/avpatel/linux/commit/059542fbcf644a42b135600ef3dd36d88e11f831
>
>
> 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