The firefly rk3399 board has a gic-v3, so I'd like to get Dale's
driver into the tree.  I did some further cleanup, renamed the driver
to agintc(4) (as jsg@ felt the "new" in angintc(4) did't make sense)
and fixed two bugs:

* prival in setipl() was used uninitialized

* apparently a dsb instruction is needed after reading the ICC_IAR1
  register

I deliberately tried to keep the differences to ampintc(4) as small as
possible such that it easier to compare the code.  I have a small
followup diff that will tweak ampintc(4) in the cases where the code
in agintc(4) was cleaner to start with.

There may be more bugs lurking in this code as I'm still seeing plenty
of weird behaviour.  But at least this will get me going.

ok?


Index: arch/arm64/dev/agintc.c
===================================================================
RCS file: arch/arm64/dev/agintc.c
diff -N arch/arm64/dev/agintc.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ arch/arm64/dev/agintc.c     29 Apr 2017 16:33:50 -0000
@@ -0,0 +1,785 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2007, 2009, 2011, 2017 Dale Rahn <dr...@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This is a device driver for the GICv3/GICv4 IP from ARM as specified
+ * in IHI0069C, an example of this hardware is the GIC 500.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/evcount.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/fdt.h>
+#include <dev/ofw/openfirm.h>
+
+#define ICC_PMR                s3_0_c4_c6_0
+#define ICC_IAR0       s3_0_c12_c8_0
+#define ICC_EOIR0      s3_0_c12_c8_1
+#define ICC_HPPIR0     s3_0_c12_c8_2
+#define ICC_BPR0       s3_0_c12_c8_3
+
+#define ICC_DIR                s3_0_c12_c11_1
+#define ICC_SGI1R      s3_0_c12_c11_5
+#define ICC_SGI0R      s3_0_c12_c11_7
+
+#define ICC_IAR1       s3_0_c12_c12_0
+#define ICC_EOIR1      s3_0_c12_c12_1
+#define ICC_HPPIR1     s3_0_c12_c12_2
+#define ICC_BPR1       s3_0_c12_c12_3
+#define ICC_CTLR       s3_0_c12_c12_4
+#define ICC_SRE_EL1    s3_0_c12_c12_5
+#define  ICC_SRE_EL1_EN                0x7
+#define ICC_IGRPEN0    s3_0_c12_c12_6
+#define ICC_IGRPEN1    s3_0_c12_c12_7
+
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+/* distributor registers */
+#define        GICD_CTLR               0x0000
+// non-secure
+#define  GICD_CTLR_RWP                 (1U << 31)
+#define         GICD_CTRL_EnableGrp1           (1 << 0)
+#define         GICD_CTRL_EnableGrp1A          (1 << 1)
+#define         GICD_CTRL_ARE_NS               (1 << 4)
+#define        GICD_TYPER              0x0004
+#define         GICD_TYPER_ITLINE_M            0xf
+#define        GICD_IIDR               0x0008
+#define        GICD_ISENABLER(i)       (0x0100 + (IRQ_TO_REG32(i) * 4))
+#define        GICD_ICENABLER(i)       (0x0180 + (IRQ_TO_REG32(i) * 4))
+#define        GICD_ISPENDR(i)         (0x0200 + (IRQ_TO_REG32(i) * 4))
+#define        GICD_ICPENDR(i)         (0x0280 + (IRQ_TO_REG32(i) * 4))
+#define        GICD_ISACTIVER(i)       (0x0300 + (IRQ_TO_REG32(i) * 4))
+#define        GICD_ICACTIVER(i)       (0x0380 + (IRQ_TO_REG32(i) * 4))
+#define        GICD_IPRIORITYR(i)      (0x0400 + (i))
+#define        GICD_ICFGR(i)           (0x0c00 + (IRQ_TO_REG16(i) * 4))
+#define        GICD_IROUTER(i)         (0x6000 + ((i) * 8))
+
+/* redistributor registers */
+#define GICR_CTLR              0x00000
+#define  GICR_CTLR_RWP                 ((1U << 31) | (1 << 3))
+#define GICR_IIDR              0x00004
+#define GICR_TYPER             0x00008
+#define  GICR_TYPER_LAST               (1 << 4)
+#define  GICR_TYPER_VLPIS              (1 << 1)
+#define GICR_WAKER             0x00014
+#define  GICR_WAKER_X31                        (1U << 31)
+#define  GICR_WAKER_CHILDRENASLEEP     (1 << 2)
+#define  GICR_WAKER_PROCESSORSLEEP     (1 << 1)
+#define  GICR_WAKER_X0                 (1 << 0)
+#define GICR_ISENABLE0         0x10100
+#define GICR_ICENABLE0         0x10180
+#define GICR_ISPENDR0          0x10200
+#define GICR_ICPENDR0          0x10280
+#define GICR_ISACTIVE0         0x10300
+#define GICR_ICACTIVE0         0x10380
+#define GICR_IPRIORITYR(i)     (0x10400 + (i))
+#define GICR_ICFGR0            0x10c00
+#define GICR_ICFGR1            0x10c04
+
+#define IRQ_TO_REG32(i)                (((i) >> 5) & 0x7)
+#define IRQ_TO_REG32BIT(i)     ((i) & 0x1f)
+
+#define IRQ_TO_REG16(i)                (((i) >> 4) & 0xf)
+#define IRQ_TO_REG16BIT(i)     ((i) & 0xf)
+
+#define IRQ_ENABLE     1
+#define IRQ_DISABLE    0
+
+/*
+ * This is not a true hard limit, but until bigger machines are supported
+ * there is no need for this to be 96+, which the interrupt controller
+ * does support. It may make sense to move to dynamic allocation of these 3
+ * fields in the future, eg when hardware with 96 cores are supported.
+ */
+#define MAX_CORES      16
+
+struct agintc_softc {
+       struct device            sc_dev;
+       struct intrq            *sc_agintc_handler;
+       bus_space_tag_t          sc_iot;
+       bus_space_handle_t       sc_d_ioh;
+       bus_space_handle_t       sc_r_ioh[MAX_CORES];
+       bus_space_handle_t       sc_redist_base;
+       uint64_t                 sc_affinity[MAX_CORES];
+       int                      sc_cpuremap[MAX_CORES]; /* bsd to redist */
+       int                      sc_nintr;
+       struct evcount           sc_spur;
+       int                      sc_ncells;
+       int                      sc_num_redist;
+       int                      sc_pri_shift;
+       struct interrupt_controller sc_ic;
+};
+struct agintc_softc *agintc_sc;
+
+struct intrhand {
+       TAILQ_ENTRY(intrhand)    ih_list;               /* link on intrq list */
+       int                     (*ih_func)(void *);     /* handler */
+       void                    *ih_arg;                /* arg for handler */
+       int                      ih_ipl;                /* IPL_* */
+       int                      ih_irq;                /* IRQ number */
+       struct evcount           ih_count;
+       char                    *ih_name;
+};
+
+struct intrq {
+       TAILQ_HEAD(, intrhand)  iq_list;        /* handler list */
+       int                     iq_irq;         /* IRQ to mask while handling */
+       int                     iq_levels;      /* IPL_*'s this IRQ has */
+       int                     iq_ist;         /* share type */
+};
+
+int            agintc_match(struct device *, void *, void *);
+void           agintc_attach(struct device *, struct device *, void *);
+void           agintc_cpuinit(void);
+int            agintc_spllower(int);
+void           agintc_splx(int);
+int            agintc_splraise(int);
+void           agintc_setipl(int);
+void           agintc_calc_mask(void);
+void           agintc_calc_irq(struct agintc_softc     *sc, int irq);
+void           *agintc_intr_establish(int, int, int (*)(void *), void *,
+                   char *);
+void           *agintc_intr_establish_fdt(void *cookie, int *cell, int level,
+                   int (*func)(void *), void *arg, char *name);
+void           agintc_intr_disestablish(void *);
+void           agintc_irq_handler(void *);
+uint32_t       agintc_iack(void);
+void           agintc_eoi(uint32_t);
+void           agintc_set_priority(struct agintc_softc *sc, int, int);
+void           agintc_intr_enable(struct agintc_softc *, int);
+void           agintc_intr_disable(struct agintc_softc *, int);
+void           agintc_route(struct agintc_softc *, int, int,
+                   struct cpu_info *);
+void           agintc_wait_rwp(struct agintc_softc *sc);
+void           agintc_r_wait_rwp(struct agintc_softc *sc);
+uint32_t       agintc_r_ictlr(void);
+
+struct cfattach        agintc_ca = {
+       sizeof (struct agintc_softc), agintc_match, agintc_attach
+};
+
+struct cfdriver agintc_cd = {
+       NULL, "agintc", DV_DULL
+};
+
+static char *agintc_compatibles[] = {
+       "arm,gic-v3",
+       "arm,gic-v4",
+       NULL
+};
+
+int
+agintc_match(struct device *parent, void *cfdata, void *aux)
+{
+       struct fdt_attach_args *faa = aux;
+       int i;
+
+       for (i = 0; agintc_compatibles[i]; i++)
+               if (OF_is_compatible(faa->fa_node, agintc_compatibles[i]))
+                       return (1);
+
+       return (0);
+}
+
+static void
+__isb(void)
+{
+       __asm volatile ("isb");
+}
+
+void
+agintc_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct agintc_softc     *sc = (struct agintc_softc *)self;
+       struct fdt_attach_args  *faa = aux;
+       int                      i, j, nintr;
+       int                      psw;
+       uint64_t                 icc_ctlr;
+       int                      offset, nredist;
+       int                      grp1enable;
+
+       psw = disable_interrupts();
+       arm_init_smask();
+
+       sc->sc_iot = faa->fa_iot;
+
+       /* First row: distributor */
+       if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+           faa->fa_reg[0].size, 0, &sc->sc_d_ioh))
+               panic("%s: ICD bus_space_map failed!", __func__);
+
+       /* Second row: redistributor */
+       if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
+           faa->fa_reg[1].size, 0, &sc->sc_redist_base))
+               panic("%s: ICP bus_space_map failed!", __func__);
+
+       evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
+
+       __asm volatile("msr "STR(ICC_SRE_EL1)", %x0" : : "r" (ICC_SRE_EL1_EN));
+       __isb();
+
+       nintr = 32 * (bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_TYPER) &
+           GICD_TYPER_ITLINE_M);
+
+       nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
+       sc->sc_nintr = nintr;
+
+       __asm volatile ("mrs %x0, "STR(ICC_CTLR) : "=r"(icc_ctlr));
+       sc->sc_pri_shift = 8 - (((icc_ctlr >> 8) & 0x7)) - 1;
+
+       agintc_sc = sc; /* save this for global access */
+
+       /* find and submap the redistributors. */
+       offset = 0;
+       for (nredist = 0; ; nredist++) {
+               int32_t sz = (64 * 1024 * 2);
+               uint64_t typer;
+
+               typer = bus_space_read_8(sc->sc_iot, sc->sc_redist_base,
+                   offset + GICR_TYPER);
+
+               if (typer & GICR_TYPER_VLPIS)
+                       sz += (64 * 1024 * 2);
+
+               sc->sc_affinity[nredist] = bus_space_read_8(sc->sc_iot,
+                   sc->sc_redist_base, offset + GICR_TYPER) >> 32;
+
+               bus_space_subregion(sc->sc_iot, sc->sc_redist_base,
+                   offset, sz, &sc->sc_r_ioh[nredist]);
+
+#ifdef DEBUG_AGINTC
+               printf("probing redistributor %d %x %p\n", nredist, offset,
+                   sc->sc_r_ioh[nredist]);
+#endif
+
+               offset += sz;
+
+               if (typer & GICR_TYPER_LAST) {
+                       sc->sc_num_redist = nredist + 1;
+                       break;
+               }
+       }
+
+       printf(" nirq %d, nredist %d, pri_shift %d\n", nintr,
+           sc->sc_num_redist, sc->sc_pri_shift);
+
+       /* Disable all interrupts, clear all pending */
+       for (i = 1; i < nintr / 32; i++) {
+               bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
+                   GICD_ICENABLER(i * 32), ~0);
+               for(j = 0; j < 32; j++) {
+                       __asm volatile("msr "STR(ICC_DIR)", %x0" : :
+                           "r" ((i * 32) + j));
+                       __isb();
+               }
+       }
+
+       for (i = 4; i < nintr; i += 4) {
+               /* lowest priority ?? */
+               bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
+                   GICD_IPRIORITYR(i), 0xffffffff);
+       }
+
+       for (i = 2; i < nintr / 16; i++) {
+               /* irq 32 - N */
+               bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
+                   GICD_ICFGR(i * 16), 0);
+       }
+
+       agintc_cpuinit();
+
+       sc->sc_agintc_handler = mallocarray(nintr,
+           sizeof(*sc->sc_agintc_handler), M_DEVBUF, M_ZERO | M_NOWAIT);
+       for (i = 0; i < nintr; i++)
+               TAILQ_INIT(&sc->sc_agintc_handler[i].iq_list);
+
+       /* set priority to IPL_HIGH until configure lowers to desired IPL */
+       agintc_setipl(IPL_HIGH); 
+
+       /* initialize all interrupts as disabled */
+       agintc_calc_mask();
+
+       /* insert self as interrupt handler */
+       arm_set_intr_handler(agintc_splraise, agintc_spllower, agintc_splx,
+           agintc_setipl, agintc_irq_handler);
+
+       /* enable interrupts */
+       bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR,
+          GICD_CTRL_ARE_NS|GICD_CTRL_EnableGrp1A|GICD_CTRL_EnableGrp1);
+
+       grp1enable = 1;
+       __asm volatile ("msr "STR(ICC_PMR)", %x0" :: "r"(0xff));
+       __asm volatile ("msr "STR(ICC_BPR1)", %x0" :: "r"(7));
+       __asm volatile ("msr "STR(ICC_IGRPEN1)", %x0" :: "r"(grp1enable));
+
+       sc->sc_ic.ic_node = faa->fa_node;
+       sc->sc_ic.ic_cookie = self;
+       sc->sc_ic.ic_establish = agintc_intr_establish_fdt;
+       sc->sc_ic.ic_disestablish = agintc_intr_disestablish;
+       arm_intr_register_fdt(&sc->sc_ic);
+
+       restore_interrupts(psw);
+}
+
+/* Initialize redistributors on each core. */
+void
+agintc_cpuinit(void)
+{
+       struct cpu_info *ci = curcpu();
+       struct agintc_softc *sc = agintc_sc;
+       uint64_t mpidr = READ_SPECIALREG(mpidr_el1);
+       uint32_t affinity, waker;
+       int timeout = 100000;
+       int hwcpu = -1;
+       int i;
+
+       /* match this processor to one of the redistributors */
+       affinity = (((mpidr >> 8) & 0xff000000) | (mpidr & 0x00ffffff));
+
+       for (i = 0; i < sc->sc_num_redist; i++) {
+               if (affinity == sc->sc_affinity[i]) {
+                       sc->sc_cpuremap[ci->ci_cpuid] = hwcpu = i;
+#ifdef DEBUG_AGINTC
+                       printf("found cpu%d at %d\n", ci->ci_cpuid, i);
+#endif
+                       break;
+               }
+       }
+
+       if (hwcpu == -1) {
+               printf("cpu mpidr not found mpidr %llx affinity %08x\n",
+                   mpidr, affinity);
+               for (i = 0; i < sc->sc_num_redist; i++)
+                       printf("rdist%d: %08x\n", i, sc->sc_affinity[i]);
+               panic("failed to indentify cpunumber %d \n", ci->ci_cpuid);
+       }
+
+       waker = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+           GICR_WAKER);
+       waker &= ~(GICR_WAKER_PROCESSORSLEEP);
+       bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], GICR_WAKER,
+           waker);
+
+       do {
+               waker = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+                   GICR_WAKER);
+       } while (--timeout && (waker & GICR_WAKER_CHILDRENASLEEP));
+       if (timeout == 0)
+               printf("%s: waker timed out\n", __func__);
+
+       bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+           GICR_ICENABLE0, ~0);
+       bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+           GICR_ICPENDR0, ~0);
+       bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+           GICR_ICACTIVE0, ~0);
+       for (i = 0; i < 32; i += 4) {
+               bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+                   GICR_IPRIORITYR(i), ~0);
+       }
+}
+
+void
+agintc_set_priority(struct agintc_softc *sc, int irq, int pri)
+{
+       struct cpu_info *ci = curcpu();
+       int              hwcpu = sc->sc_cpuremap[ci->ci_cpuid];
+       uint32_t         prival;
+
+       /*
+        * We only use 16 (13 really) interrupt priorities,
+        * and a CPU is only required to implement bit 4-7 of each field
+        * so shift into the top bits.
+        * also low values are higher priority thus NIPL - pri
+        */
+       prival = ((NIPL - pri) << sc->sc_pri_shift);
+       if (irq >= 32) {
+               bus_space_write_1(sc->sc_iot, sc->sc_d_ioh,
+                   GICD_IPRIORITYR(irq), prival);
+       } else  {
+               /* only sets local redistributor */
+               bus_space_write_1(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+                   GICR_IPRIORITYR(irq), prival);
+       }
+}
+
+void
+agintc_setipl(int new)
+{
+       struct cpu_info         *ci = curcpu();
+       struct agintc_softc     *sc = agintc_sc;
+       int                      psw;
+       uint32_t                 prival;
+
+       /* disable here is only to keep hardware in sync with ci->ci_cpl */
+       psw = disable_interrupts();
+       ci->ci_cpl = new;
+
+       /* low values are higher priority thus NIPL - pri */
+       if (new == IPL_NONE)
+               prival = 0xff;          /* minimum priority */
+       else
+               prival = ((NIPL - new) << sc->sc_pri_shift);
+
+       __asm volatile("msr "STR(ICC_PMR)", %x0" : : "r" (prival));
+       restore_interrupts(psw);
+}
+
+void
+agintc_intr_enable(struct agintc_softc *sc, int irq)
+{
+       struct cpu_info *ci = curcpu();
+       int hwcpu = sc->sc_cpuremap[ci->ci_cpuid];
+       int bit = 1 << IRQ_TO_REG32BIT(irq);
+
+       if (irq >= 32) {
+               bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
+                   GICD_ISENABLER(irq), bit);
+       } else {
+               bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+                   GICR_ISENABLE0, bit);
+       }
+}
+
+void
+agintc_intr_disable(struct agintc_softc *sc, int irq)
+{
+       struct cpu_info *ci = curcpu();
+       int hwcpu = sc->sc_cpuremap[ci->ci_cpuid];
+
+       if (irq >= 32) {
+               bus_space_write_4(sc->sc_iot, sc->sc_d_ioh,
+                   GICD_ICENABLER(irq), 1 << IRQ_TO_REG32BIT(irq));
+       } else {
+               bus_space_write_4(sc->sc_iot, sc->sc_r_ioh[hwcpu],
+                   GICR_ICENABLE0, 1 << IRQ_TO_REG32BIT(irq));
+       }
+}
+
+void
+agintc_calc_mask(void)
+{
+       struct agintc_softc     *sc = agintc_sc;
+       int                      irq;
+
+       for (irq = 0; irq < sc->sc_nintr; irq++)
+               agintc_calc_irq(sc, irq);
+}
+
+void
+agintc_calc_irq(struct agintc_softc *sc, int irq)
+{
+       struct cpu_info *ci = curcpu();
+       struct intrhand *ih;
+       int              max = IPL_NONE;
+       int              min = IPL_HIGH;
+       
+       TAILQ_FOREACH(ih, &sc->sc_agintc_handler[irq].iq_list, ih_list) {
+               if (ih->ih_ipl > max)
+                       max = ih->ih_ipl;
+
+               if (ih->ih_ipl < min)
+                       min = ih->ih_ipl;
+       }
+
+       if (sc->sc_agintc_handler[irq].iq_irq == max)
+               return;
+       sc->sc_agintc_handler[irq].iq_irq = max;
+
+       if (max == IPL_NONE)
+               min = IPL_NONE;
+
+#ifdef DEBUG_AGINTC
+       if (min != IPL_NONE)
+               printf("irq %d to block at %d %d \n", irq, max, min );
+#endif
+       /* Enable interrupts at lower levels, clear -> enable */
+       /* Set interrupt priority/enable */
+       if (min != IPL_NONE) {
+               agintc_set_priority(sc, irq, min);
+               agintc_route(sc, irq, IRQ_ENABLE, ci);
+               agintc_intr_enable(sc, irq);
+       } else {
+               agintc_intr_disable(sc, irq);
+               agintc_route(sc, irq, IRQ_DISABLE, ci);
+       }
+}
+
+void
+agintc_splx(int new)
+{
+       struct cpu_info *ci = curcpu();
+
+       if (ci->ci_ipending & arm_smask[new])
+               arm_do_pending_intr(new);
+
+       agintc_setipl(new);
+}
+
+int
+agintc_spllower(int new)
+{
+       struct cpu_info *ci = curcpu();
+       int old = ci->ci_cpl;
+
+       agintc_splx(new);
+       return (old);
+}
+
+int
+agintc_splraise(int new)
+{
+       struct cpu_info *ci = curcpu();
+       int old = ci->ci_cpl;
+
+       /*
+        * setipl must always be called because there is a race window
+        * where the variable is updated before the mask is set
+        * an interrupt occurs in that window without the mask always
+        * being set, the hardware might not get updated on the next
+        * splraise completely messing up spl protection.
+        */
+       if (old > new)
+               new = old;
+
+       agintc_setipl(new);
+       return (old);
+}
+
+uint32_t
+agintc_iack(void)
+{
+       int irq;
+
+       __asm volatile("mrs %x0, "STR(ICC_IAR1) : "=r" (irq));
+       __asm volatile("dsb sy");
+       return irq;
+}
+
+void
+agintc_route(struct agintc_softc *sc, int irq, int enable, struct cpu_info *ci)
+{
+       uint64_t  val;
+
+       /* XXX does not yet support 'participating node' */
+       if (irq >= 32) {
+               val = ((sc->sc_affinity[ci->ci_cpuid] & 0x00ffffff) |
+                   ((sc->sc_affinity[ci->ci_cpuid] & 0xff000000) << 8));
+#ifdef DEBUG_AGINTC
+               printf("router %x irq %d val %016llx\n", GICD_IROUTER(irq),irq,
+                   val);
+#endif
+               bus_space_write_8(sc->sc_iot, sc->sc_d_ioh,
+                   GICD_IROUTER(irq), val);
+       }
+}
+
+void
+agintc_irq_handler(void *frame)
+{
+       struct cpu_info         *ci = curcpu();
+       struct agintc_softc     *sc = agintc_sc;
+       struct intrhand         *ih;
+       void                    *arg;
+       int                      irq, pri, s, handled;
+
+       ci->ci_idepth++;
+       irq = agintc_iack();
+
+#ifdef DEBUG_AGINTC
+       if (irq != 30)
+               printf("irq  %d fired\n", irq);
+       else {
+               static int cnt = 0;
+               if ((cnt++ % 100) == 0) {
+                       printf("irq  %d fired * _100\n", irq);
+#ifdef DDB
+                       Debugger();
+#endif
+               }
+       }
+#endif
+
+       if (irq == 1023) {
+               sc->sc_spur.ec_count++;
+               return;
+       }
+
+       if (irq >= sc->sc_nintr)
+               return;
+
+       pri = sc->sc_agintc_handler[irq].iq_irq;
+       s = agintc_splraise(pri);
+       TAILQ_FOREACH(ih, &sc->sc_agintc_handler[irq].iq_list, ih_list) {
+               if (ih->ih_arg != 0)
+                       arg = ih->ih_arg;
+               else
+                       arg = frame;
+
+               enable_interrupts();
+               handled = ih->ih_func(arg);
+               disable_interrupts();
+               if (handled)
+                       ih->ih_count.ec_count++;
+
+       }
+       agintc_eoi(irq);
+
+       agintc_splx(s);
+       ci->ci_idepth--;
+}
+
+void *
+agintc_intr_establish_fdt(void *cookie, int *cell, int level,
+    int (*func)(void *), void *arg, char *name)
+{
+       struct agintc_softc     *sc = agintc_sc;
+       int                      irq;
+
+       /* 2nd cell contains the interrupt number */
+       irq = cell[1];
+
+       /* 1st cell contains type: 0 SPI (32-X), 1 PPI (16-31) */
+       if (cell[0] == 0)
+               irq += 32;
+       else if (cell[0] == 1)
+               irq += 16;
+       else
+               panic("%s: bogus interrupt type", sc->sc_dev.dv_xname);
+
+       return agintc_intr_establish(irq, level, func, arg, name);
+}
+
+void *
+agintc_intr_establish(int irqno, int level, int (*func)(void *),
+    void *arg, char *name)
+{
+       struct agintc_softc     *sc = agintc_sc;
+       struct intrhand         *ih;
+       int                      psw;
+
+       if (irqno < 0 || irqno >= sc->sc_nintr)
+               panic("agintc_intr_establish: bogus irqnumber %d: %s",
+                   irqno, name);
+
+       ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK);
+       ih->ih_func = func;
+       ih->ih_arg = arg;
+       ih->ih_ipl = level;
+       ih->ih_irq = irqno;
+       ih->ih_name = name;
+
+       psw = disable_interrupts();
+
+       TAILQ_INSERT_TAIL(&sc->sc_agintc_handler[irqno].iq_list, ih, ih_list);
+
+       if (name != NULL)
+               evcount_attach(&ih->ih_count, name, &ih->ih_irq);
+
+#ifdef DEBUG_AGINTC
+       printf("%s: irq %d level %d [%s]\n", __func__, irqno, level, name);
+#endif
+
+       agintc_calc_irq(sc, irqno);
+
+       restore_interrupts(psw);
+       return (ih);
+}
+
+void
+agintc_intr_disestablish(void *cookie)
+{
+       struct agintc_softc     *sc = agintc_sc;
+       struct intrhand         *ih = cookie;
+       int                      irqno = ih->ih_irq;
+       int                      psw;
+
+       psw = disable_interrupts();
+
+       TAILQ_REMOVE(&sc->sc_agintc_handler[irqno].iq_list, ih, ih_list);
+       if (ih->ih_name != NULL)
+               evcount_detach(&ih->ih_count);
+
+       agintc_calc_irq(sc, irqno);
+
+       restore_interrupts(psw);
+
+       free(ih, M_DEVBUF, 0);
+}
+
+void
+agintc_eoi(uint32_t eoi)
+{
+       __asm volatile("msr "STR(ICC_EOIR1)", %x0" :: "r" (eoi));
+       __isb();
+}
+
+uint32_t
+agintc_r_ictlr(void)
+{
+       int ictlr;
+
+       __asm volatile("mrs %x0, "STR(ICC_CTLR) : "=r" (ictlr));
+       __isb();
+       return ictlr;
+}
+
+void
+agintc_d_wait_rwp(struct agintc_softc *sc)
+{
+       int count = 100000;
+       uint32_t v;
+
+       do {
+           v = bus_space_read_4(sc->sc_iot, sc->sc_d_ioh, GICD_CTLR);
+       } while (--count && (v & GICD_CTLR_RWP));
+
+       if (count == 0)
+               panic("%s: RWP timed out 0x08%x\n", __func__, v);
+}
+
+void
+agintc_r_wait_rwp(struct agintc_softc *sc)
+{
+       struct cpu_info *ci = curcpu();
+       int hwcpu = sc->sc_cpuremap[ci->ci_cpuid];
+       int count = 100000;
+       uint32_t v;
+
+       do {
+           v = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], GICR_CTLR);
+       } while (--count && (v & GICR_CTLR_RWP));
+
+       if (count == 0)
+               panic("%s: RWP timed out 0x08%x\n", __func__, v);
+}
+
+void
+agintc_send_ipi(int sgi, int targetmask)
+{
+       int val = (sgi << 24) | (targetmask);
+
+       __asm volatile ("msr "STR(ICC_SGI1R)", %x0" ::"r" (val));
+}
Index: arch/arm64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v
retrieving revision 1.23
diff -u -p -r1.23 GENERIC
--- arch/arm64/conf/GENERIC     27 Apr 2017 10:23:19 -0000      1.23
+++ arch/arm64/conf/GENERIC     29 Apr 2017 16:33:50 -0000
@@ -47,6 +47,7 @@ uk*           at scsibus?
 
 ampintc*       at fdt?
 ampintcmsi*    at fdt?
+agintc*                at fdt?
 agtimer*       at fdt?
 ahci*          at fdt?
 pciecam*       at fdt?
Index: arch/arm64/conf/RAMDISK
===================================================================
RCS file: /cvs/src/sys/arch/arm64/conf/RAMDISK,v
retrieving revision 1.20
diff -u -p -r1.20 RAMDISK
--- arch/arm64/conf/RAMDISK     27 Apr 2017 10:23:19 -0000      1.20
+++ arch/arm64/conf/RAMDISK     29 Apr 2017 16:33:50 -0000
@@ -57,6 +57,7 @@ uk*           at scsibus?
 
 ampintc*       at fdt?
 ampintcmsi*    at fdt?
+agintc*                at fdt?
 agtimer*       at fdt?
 ahci*          at fdt?
 pciecam*       at fdt?
Index: arch/arm64/conf/files.arm64
===================================================================
RCS file: /cvs/src/sys/arch/arm64/conf/files.arm64,v
retrieving revision 1.12
diff -u -p -r1.12 files.arm64
--- arch/arm64/conf/files.arm64 27 Apr 2017 10:23:19 -0000      1.12
+++ arch/arm64/conf/files.arm64 29 Apr 2017 16:33:50 -0000
@@ -125,6 +125,10 @@ device     ampintcmsi
 attach ampintcmsi at fdt
 file   arch/arm64/dev/ampintc.c                ampintc | ampintcmsi
 
+device agintc
+attach agintc at fdt
+file   arch/arm64/dev/agintc.c                 agintc
+
 device agtimer
 attach agtimer at fdt
 file   arch/arm64/dev/agtimer.c                agtimer

Reply via email to