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