Implement support for MBIs. MBIs are message based interrupts that can be
authorkettenis <kettenis@openbsd.org>
Thu, 13 Oct 2022 07:04:53 +0000 (07:04 +0000)
committerkettenis <kettenis@openbsd.org>
Thu, 13 Oct 2022 07:04:53 +0000 (07:04 +0000)
used as an alternative implementation for MSIs on hardware that doesn't
implement an ITS (or where the ITS is broken such as on the Rochchip
RK3566 SoC).

Based on an earlier WIP diff from patrick@; I just cleaned it up a bit.

ok patrick@

sys/arch/arm64/dev/agintc.c

index 5840494..e342d23 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: agintc.c,v 1.42 2022/08/29 01:34:18 drahn Exp $ */
+/* $OpenBSD: agintc.c,v 1.43 2022/10/13 07:04:53 kettenis Exp $ */
 /*
  * Copyright (c) 2007, 2009, 2011, 2017 Dale Rahn <drahn@dalerahn.com>
  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
 #define  GICD_CTLR_ARE_NS              (1 << 4)
 #define  GICD_CTLR_DS                  (1 << 6)
 #define GICD_TYPER             0x0004
+#define  GICD_TYPER_MBIS               (1 << 16)
 #define  GICD_TYPER_LPIS               (1 << 17)
 #define  GICD_TYPER_ITLINE_M           0x1f
 #define GICD_IIDR              0x0008
+#define GICD_SETSPI_NSR                0x0040
+#define GICD_CLRSPI_NSR                0x0048
 #define GICD_IGROUPR(i)                (0x0080 + (IRQ_TO_REG32(i) * 4))
 #define GICD_ISENABLER(i)      (0x0100 + (IRQ_TO_REG32(i) * 4))
 #define GICD_ICENABLER(i)      (0x0180 + (IRQ_TO_REG32(i) * 4))
 #define IRQ_ENABLE     1
 #define IRQ_DISABLE    0
 
+struct agintc_mbi_range {
+       int                       mr_base;
+       int                       mr_span;
+       void                    **mr_mbi;
+};
+
 struct agintc_softc {
        struct simplebus_softc   sc_sbus;
        struct intrq            *sc_handler;
@@ -152,6 +161,9 @@ struct agintc_softc {
        int                      sc_cpuremap[MAXCPUS];
        int                      sc_nintr;
        int                      sc_nlpi;
+       bus_addr_t               sc_mbi_addr;
+       int                      sc_mbi_nranges;
+       struct agintc_mbi_range *sc_mbi_ranges;
        int                      sc_prio_shift;
        int                      sc_pmr_shift;
        int                      sc_rk3399_quirk;
@@ -206,6 +218,7 @@ void                agintc_dmamem_free(bus_dma_tag_t, struct agintc_dmamem *);
 
 int            agintc_match(struct device *, void *, void *);
 void           agintc_attach(struct device *, struct device *, void *);
+void           agintc_mbiinit(struct agintc_softc *, int, bus_addr_t);
 void           agintc_cpuinit(void);
 int            agintc_spllower(int);
 void           agintc_splx(int);
@@ -217,6 +230,8 @@ void                *agintc_intr_establish(int, int, int, struct cpu_info *,
                    int (*)(void *), void *, char *);
 void           *agintc_intr_establish_fdt(void *cookie, int *cell, int level,
                    struct cpu_info *, int (*func)(void *), void *arg, char *name);
+void           *agintc_intr_establish_mbi(void *, uint64_t *, uint64_t *,
+                   int , struct cpu_info *, int (*)(void *), void *, char *);
 void           agintc_intr_disestablish(void *);
 void           agintc_irq_handler(void *);
 uint32_t       agintc_iack(void);
@@ -328,6 +343,9 @@ agintc_attach(struct device *parent, struct device *self, void *aux)
                sc->sc_nlpi = 8192;
        }
 
+       if (typer & GICD_TYPER_MBIS)
+               agintc_mbiinit(sc, faa->fa_node, faa->fa_reg[0].addr);
+
        /*
         * We are guaranteed to have at least 16 priority levels, so
         * in principle we just want to use the top 4 bits of the
@@ -628,6 +646,8 @@ agintc_attach(struct device *parent, struct device *self, void *aux)
        sc->sc_ic.ic_route = agintc_route_irq;
        sc->sc_ic.ic_cpu_enable = agintc_cpuinit;
        sc->sc_ic.ic_barrier = agintc_intr_barrier;
+       if (sc->sc_mbi_nranges > 0)
+               sc->sc_ic.ic_establish_msi = agintc_intr_establish_mbi;
        arm_intr_register_fdt(&sc->sc_ic);
 
        intr_restore(psw);
@@ -656,6 +676,42 @@ unmap:
        bus_space_unmap(sc->sc_iot, sc->sc_d_ioh, faa->fa_reg[0].size);
 }
 
+void
+agintc_mbiinit(struct agintc_softc *sc, int node, bus_addr_t addr)
+{
+       uint32_t *ranges;
+       int i, len;
+
+       if (OF_getproplen(node, "msi-controller") != 0)
+               return;
+
+       len = OF_getproplen(node, "mbi-ranges");
+       if (len <= 0 || len % 2 * sizeof(uint32_t) != 0)
+               return;
+
+       ranges = malloc(len, M_TEMP, M_WAITOK);
+       OF_getpropintarray(node, "mbi-ranges", ranges, len);
+
+       sc->sc_mbi_nranges = len / (2 * sizeof(uint32_t));
+       sc->sc_mbi_ranges = mallocarray(sc->sc_mbi_nranges,
+           sizeof(struct agintc_mbi_range), M_DEVBUF, M_WAITOK);
+
+       for (i = 0; i < sc->sc_mbi_nranges; i++) {
+               sc->sc_mbi_ranges[i].mr_base = ranges[2 * i + 0];
+               sc->sc_mbi_ranges[i].mr_span = ranges[2 * i + 1];
+               sc->sc_mbi_ranges[i].mr_mbi =
+                   mallocarray(sc->sc_mbi_ranges[i].mr_span,
+                       sizeof(void *), M_DEVBUF, M_WAITOK | M_ZERO);
+       }
+
+       free(ranges, M_TEMP, len);
+
+       addr = OF_getpropint64(node, "mbi-alias", addr);
+       sc->sc_mbi_addr = addr + GICD_SETSPI_NSR;
+
+       printf(" mbi");
+}
+
 /* Initialize redistributors on each core. */
 void
 agintc_cpuinit(void)
@@ -1126,6 +1182,8 @@ agintc_intr_disestablish(void *cookie)
        struct intrhand         *ih = cookie;
        int                      irqno = ih->ih_irq;
        u_long                   psw;
+       struct agintc_mbi_range *mr;
+       int                      i;
 
        psw = intr_disable();
 
@@ -1135,11 +1193,57 @@ agintc_intr_disestablish(void *cookie)
 
        agintc_calc_irq(sc, irqno);
 
+       /* In case this is an MBI, free it */
+       for (i = 0; i < sc->sc_mbi_nranges; i++) {
+               mr = &sc->sc_mbi_ranges[i];
+               if (irqno < mr->mr_base)
+                       continue;
+               if (irqno >= mr->mr_base + mr->mr_span)
+                       break;
+               if (mr->mr_mbi[irqno - mr->mr_base] != NULL)
+                   mr->mr_mbi[irqno - mr->mr_base] = NULL;
+       }
+
        intr_restore(psw);
 
        free(ih, M_DEVBUF, 0);
 }
 
+void *
+agintc_intr_establish_mbi(void *self, uint64_t *addr, uint64_t *data,
+    int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
+{
+       struct agintc_softc *sc = agintc_sc;
+       struct agintc_mbi_range *mr;
+       void *cookie;
+       int i, j, hwcpu;
+
+       if (ci == NULL)
+               ci = &cpu_info_primary;
+       hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid];
+
+       for (i = 0; i < sc->sc_mbi_nranges; i++) {
+               mr = &sc->sc_mbi_ranges[i];
+               for (j = 0; j < mr->mr_span; j++) {
+                       if (mr->mr_mbi[j] != NULL)
+                               continue;
+
+                       cookie = agintc_intr_establish(mr->mr_base + j,
+                           IST_EDGE_RISING, level, ci, func, arg, name);
+                       if (cookie == NULL)
+                               return NULL;
+
+                       *addr = sc->sc_mbi_addr;
+                       *data = mr->mr_base + j;
+
+                       mr->mr_mbi[j] = cookie;
+                       return cookie;
+               }
+       }
+
+       return NULL;
+}
+
 void
 agintc_eoi(uint32_t eoi)
 {