lib: utils/irqchip: Add APLIC initialization library
authorAnup Patel <apatel@ventanamicro.com>
Thu, 8 Apr 2021 05:58:02 +0000 (11:28 +0530)
committerAnup Patel <anup@brainfault.org>
Tue, 15 Feb 2022 15:07:55 +0000 (20:37 +0530)
We add simple APLIC initialization library which is independent of
hardware description format (FDT or ACPI). This APLIC initialization
library can be used by custom OpenSBI platform support to setup
APLIC domains.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Reviewed-by: Atish Patra <atishp@rivosinc.com>
include/sbi_utils/irqchip/aplic.h [new file with mode: 0644]
lib/utils/irqchip/aplic.c [new file with mode: 0644]
lib/utils/irqchip/objects.mk

diff --git a/include/sbi_utils/irqchip/aplic.h b/include/sbi_utils/irqchip/aplic.h
new file mode 100644 (file)
index 0000000..82682e8
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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@wdc.com>
+ */
+
+#ifndef __IRQCHIP_APLIC_H__
+#define __IRQCHIP_APLIC_H__
+
+#include <sbi/sbi_types.h>
+
+#define APLIC_MAX_DELEGATE     16
+
+struct aplic_msicfg_data {
+       unsigned long lhxs;
+       unsigned long lhxw;
+       unsigned long hhxs;
+       unsigned long hhxw;
+       unsigned long base_addr;
+};
+
+struct aplic_delegate_data {
+       u32 first_irq;
+       u32 last_irq;
+       u32 child_index;
+};
+
+struct aplic_data {
+       unsigned long addr;
+       unsigned long size;
+       unsigned long num_idc;
+       unsigned long num_source;
+       bool targets_mmode;
+       bool has_msicfg_mmode;
+       struct aplic_msicfg_data msicfg_mmode;
+       bool has_msicfg_smode;
+       struct aplic_msicfg_data msicfg_smode;
+       struct aplic_delegate_data delegate[APLIC_MAX_DELEGATE];
+};
+
+int aplic_cold_irqchip_init(struct aplic_data *aplic);
+
+#endif
diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c
new file mode 100644 (file)
index 0000000..0a8469b
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * 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@wdc.com>
+ */
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi_utils/irqchip/aplic.h>
+
+#define APLIC_MAX_IDC                  (1UL << 14)
+#define APLIC_MAX_SOURCE               1024
+
+#define APLIC_DOMAINCFG                0x0000
+#define APLIC_DOMAINCFG_IE             (1 << 8)
+#define APLIC_DOMAINCFG_DM             (1 << 2)
+#define APLIC_DOMAINCFG_BE             (1 << 0)
+
+#define APLIC_SOURCECFG_BASE           0x0004
+#define APLIC_SOURCECFG_D              (1 << 10)
+#define APLIC_SOURCECFG_CHILDIDX_MASK  0x000003ff
+#define APLIC_SOURCECFG_SM_MASK        0x00000007
+#define APLIC_SOURCECFG_SM_INACTIVE    0x0
+#define APLIC_SOURCECFG_SM_DETACH      0x1
+#define APLIC_SOURCECFG_SM_EDGE_RISE   0x4
+#define APLIC_SOURCECFG_SM_EDGE_FALL   0x5
+#define APLIC_SOURCECFG_SM_LEVEL_HIGH  0x6
+#define APLIC_SOURCECFG_SM_LEVEL_LOW   0x7
+
+#define APLIC_MMSICFGADDR              0x1bc0
+#define APLIC_MMSICFGADDRH             0x1bc4
+#define APLIC_SMSICFGADDR              0x1bc8
+#define APLIC_SMSICFGADDRH             0x1bcc
+
+#define APLIC_xMSICFGADDRH_L           (1UL << 31)
+#define APLIC_xMSICFGADDRH_HHXS_MASK   0x1f
+#define APLIC_xMSICFGADDRH_HHXS_SHIFT  24
+#define APLIC_xMSICFGADDRH_LHXS_MASK   0x7
+#define APLIC_xMSICFGADDRH_LHXS_SHIFT  20
+#define APLIC_xMSICFGADDRH_HHXW_MASK   0x7
+#define APLIC_xMSICFGADDRH_HHXW_SHIFT  16
+#define APLIC_xMSICFGADDRH_LHXW_MASK   0xf
+#define APLIC_xMSICFGADDRH_LHXW_SHIFT  12
+#define APLIC_xMSICFGADDRH_BAPPN_MASK  0xfff
+
+#define APLIC_xMSICFGADDR_PPN_SHIFT    12
+
+#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \
+       ((1UL << (__lhxs)) - 1)
+
+#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \
+       ((1UL << (__lhxw)) - 1)
+#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \
+       ((__lhxs))
+#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \
+       (APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \
+        APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs))
+
+#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \
+       ((1UL << (__hhxw)) - 1)
+#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \
+       ((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT)
+#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \
+       (APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \
+        APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs))
+
+#define APLIC_SETIP_BASE               0x1c00
+#define APLIC_SETIPNUM                 0x1cdc
+
+#define APLIC_CLRIP_BASE               0x1d00
+#define APLIC_CLRIPNUM                 0x1ddc
+
+#define APLIC_SETIE_BASE               0x1e00
+#define APLIC_SETIENUM                 0x1edc
+
+#define APLIC_CLRIE_BASE               0x1f00
+#define APLIC_CLRIENUM                 0x1fdc
+
+#define APLIC_SETIPNUM_LE              0x2000
+#define APLIC_SETIPNUM_BE              0x2004
+
+#define APLIC_TARGET_BASE              0x3004
+#define APLIC_TARGET_HART_IDX_SHIFT    18
+#define APLIC_TARGET_HART_IDX_MASK     0x3fff
+#define APLIC_TARGET_GUEST_IDX_SHIFT   12
+#define APLIC_TARGET_GUEST_IDX_MASK    0x3f
+#define APLIC_TARGET_IPRIO_MASK        0xff
+#define APLIC_TARGET_EIID_MASK 0x7ff
+
+#define APLIC_IDC_BASE                 0x4000
+#define APLIC_IDC_SIZE                 32
+
+#define APLIC_IDC_IDELIVERY            0x00
+
+#define APLIC_IDC_IFORCE               0x04
+
+#define APLIC_IDC_ITHRESHOLD           0x08
+
+#define APLIC_IDC_TOPI                 0x18
+#define APLIC_IDC_TOPI_ID_SHIFT        16
+#define APLIC_IDC_TOPI_ID_MASK 0x3ff
+#define APLIC_IDC_TOPI_PRIO_MASK       0xff
+
+#define APLIC_IDC_CLAIMI               0x1c
+
+#define APLIC_DEFAULT_PRIORITY         1
+#define APLIC_DISABLE_IDELIVERY                0
+#define APLIC_ENABLE_IDELIVERY         1
+#define APLIC_DISABLE_ITHRESHOLD       1
+#define APLIC_ENABLE_ITHRESHOLD                0
+
+static void aplic_writel_msicfg(struct aplic_msicfg_data *msicfg,
+                               void *msicfgaddr, void *msicfgaddrH)
+{
+       u32 val;
+       unsigned long base_ppn;
+
+       /* Check if MSI config is already locked */
+       if (readl(msicfgaddrH) & APLIC_xMSICFGADDRH_L)
+               return;
+
+       /* Compute the MSI base PPN */
+       base_ppn = msicfg->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
+       base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(msicfg->lhxs);
+       base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(msicfg->lhxw, msicfg->lhxs);
+       base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(msicfg->hhxw, msicfg->hhxs);
+
+       /* Write the lower MSI config register */
+       writel((u32)base_ppn, msicfgaddr);
+
+       /* Write the upper MSI config register */
+       val = (((u64)base_ppn) >> 32) &
+               APLIC_xMSICFGADDRH_BAPPN_MASK;
+       val |= (msicfg->lhxw & APLIC_xMSICFGADDRH_LHXW_MASK)
+               << APLIC_xMSICFGADDRH_LHXW_SHIFT;
+       val |= (msicfg->hhxw & APLIC_xMSICFGADDRH_HHXW_MASK)
+               << APLIC_xMSICFGADDRH_HHXW_SHIFT;
+       val |= (msicfg->lhxs & APLIC_xMSICFGADDRH_LHXS_MASK)
+               << APLIC_xMSICFGADDRH_LHXS_SHIFT;
+       val |= (msicfg->hhxs & APLIC_xMSICFGADDRH_HHXS_MASK)
+               << APLIC_xMSICFGADDRH_HHXS_SHIFT;
+       writel(val, msicfgaddrH);
+}
+
+static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg)
+{
+       if (APLIC_xMSICFGADDRH_LHXS_MASK < msicfg->lhxs)
+               return SBI_EINVAL;
+
+       if (APLIC_xMSICFGADDRH_LHXW_MASK < msicfg->lhxw)
+               return SBI_EINVAL;
+
+       if (APLIC_xMSICFGADDRH_HHXS_MASK < msicfg->hhxs)
+               return SBI_EINVAL;
+
+       if (APLIC_xMSICFGADDRH_HHXW_MASK < msicfg->hhxw)
+               return SBI_EINVAL;
+
+       return 0;
+}
+
+int aplic_cold_irqchip_init(struct aplic_data *aplic)
+{
+       int rc;
+       u32 i, j, tmp;
+       struct sbi_domain_memregion reg;
+       struct aplic_delegate_data *deleg;
+       u32 first_deleg_irq, last_deleg_irq;
+
+       /* Sanity checks */
+       if (!aplic ||
+           !aplic->num_source || APLIC_MAX_SOURCE <= aplic->num_source ||
+           APLIC_MAX_IDC <= aplic->num_idc)
+               return SBI_EINVAL;
+       if (aplic->targets_mmode && aplic->has_msicfg_mmode) {
+               rc = aplic_check_msicfg(&aplic->msicfg_mmode);
+               if (rc)
+                       return rc;
+       }
+       if (aplic->targets_mmode && aplic->has_msicfg_smode) {
+               rc = aplic_check_msicfg(&aplic->msicfg_smode);
+               if (rc)
+                       return rc;
+       }
+
+       /* Set domain configuration to 0 */
+       writel(0, (void *)(aplic->addr + APLIC_DOMAINCFG));
+
+       /* Disable all interrupts */
+       for (i = 0; i <= aplic->num_source; i++)
+               writel(-1U, (void *)(aplic->addr + APLIC_CLRIE_BASE +
+                                    (i / 32) * sizeof(u32)));
+
+       /* Set interrupt type and priority for all interrupts */
+       for (i = 1; i <= aplic->num_source; i++) {
+               /* Set IRQ source configuration to 0 */
+               writel(0, (void *)(aplic->addr + APLIC_SOURCECFG_BASE +
+                         (i - 1) * sizeof(u32)));
+               /* Set IRQ target hart index and priority to 1 */
+               writel(APLIC_DEFAULT_PRIORITY, (void *)(aplic->addr +
+                                               APLIC_TARGET_BASE +
+                                               (i - 1) * sizeof(u32)));
+       }
+
+       /* Configure IRQ delegation */
+       first_deleg_irq = -1U;
+       last_deleg_irq = 0;
+       for (i = 0; i < APLIC_MAX_DELEGATE; i++) {
+               deleg = &aplic->delegate[i];
+               if (!deleg->first_irq || !deleg->last_irq)
+                       continue;
+               if (aplic->num_source < deleg->first_irq ||
+                   aplic->num_source < deleg->last_irq)
+                       continue;
+               if (APLIC_SOURCECFG_CHILDIDX_MASK < deleg->child_index)
+                       continue;
+               if (deleg->first_irq > deleg->last_irq) {
+                       tmp = deleg->first_irq;
+                       deleg->first_irq = deleg->last_irq;
+                       deleg->last_irq = tmp;
+               }
+               if (deleg->first_irq < first_deleg_irq)
+                       first_deleg_irq = deleg->first_irq;
+               if (last_deleg_irq < deleg->last_irq)
+                       last_deleg_irq = deleg->last_irq;
+               for (j = deleg->first_irq; j <= deleg->last_irq; j++)
+                       writel(APLIC_SOURCECFG_D | deleg->child_index,
+                               (void *)(aplic->addr + APLIC_SOURCECFG_BASE +
+                               (j - 1) * sizeof(u32)));
+       }
+
+       /* Default initialization of IDC structures */
+       for (i = 0; i < aplic->num_idc; i++) {
+               writel(0, (void *)(aplic->addr + APLIC_IDC_BASE +
+                         i * APLIC_IDC_SIZE + APLIC_IDC_IDELIVERY));
+               writel(0, (void *)(aplic->addr + APLIC_IDC_BASE +
+                         i * APLIC_IDC_SIZE + APLIC_IDC_IFORCE));
+               writel(APLIC_DISABLE_ITHRESHOLD, (void *)(aplic->addr +
+                                                 APLIC_IDC_BASE +
+                                                 (i * APLIC_IDC_SIZE) +
+                                                 APLIC_IDC_ITHRESHOLD));
+       }
+
+       /* MSI configuration */
+       if (aplic->targets_mmode && aplic->has_msicfg_mmode) {
+               aplic_writel_msicfg(&aplic->msicfg_mmode,
+                               (void *)(aplic->addr + APLIC_MMSICFGADDR),
+                               (void *)(aplic->addr + APLIC_MMSICFGADDRH));
+       }
+       if (aplic->targets_mmode && aplic->has_msicfg_smode) {
+               aplic_writel_msicfg(&aplic->msicfg_smode,
+                               (void *)(aplic->addr + APLIC_SMSICFGADDR),
+                               (void *)(aplic->addr + APLIC_SMSICFGADDRH));
+       }
+
+       /*
+        * Add APLIC region to the root domain if:
+        * 1) It targets M-mode of any HART directly or via MSIs
+        * 2) All interrupts are delegated to some child APLIC
+        */
+       if (aplic->targets_mmode ||
+           ((first_deleg_irq < last_deleg_irq) &&
+           (last_deleg_irq == aplic->num_source) &&
+           (first_deleg_irq == 1))) {
+               sbi_domain_memregion_init(aplic->addr, aplic->size,
+                                         SBI_DOMAIN_MEMREGION_MMIO, &reg);
+               rc = sbi_domain_root_add_memregion(&reg);
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
index ae6f255..b2b3f79 100644 (file)
@@ -10,5 +10,6 @@
 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/aplic.o
 libsbiutils-objs-y += irqchip/imsic.o
 libsbiutils-objs-y += irqchip/plic.o