diff options
Diffstat (limited to 'sys/arch/arm64/dev/ampintc.c')
-rw-r--r-- | sys/arch/arm64/dev/ampintc.c | 202 |
1 files changed, 187 insertions, 15 deletions
diff --git a/sys/arch/arm64/dev/ampintc.c b/sys/arch/arm64/dev/ampintc.c index 62bb06c3a35..f03985a7bd4 100644 --- a/sys/arch/arm64/dev/ampintc.c +++ b/sys/arch/arm64/dev/ampintc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ampintc.c,v 1.11 2018/01/12 22:20:28 kettenis Exp $ */ +/* $OpenBSD: ampintc.c,v 1.12 2018/01/31 10:52:12 kettenis Exp $ */ /* * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org> * @@ -134,13 +134,15 @@ struct ampintc_softc { struct simplebus_softc sc_sbus; - struct intrq *sc_ampintc_handler; + struct intrq *sc_handler; int sc_nintr; bus_space_tag_t sc_iot; bus_space_handle_t sc_d_ioh, sc_p_ioh; uint8_t sc_cpu_mask[ICD_ICTR_CPU_M + 1]; struct evcount sc_spur; struct interrupt_controller sc_ic; + int sc_ipi_reason[ICD_ICTR_CPU_M + 1]; + int sc_ipi_num[2]; }; struct ampintc_softc *ampintc; @@ -166,6 +168,7 @@ struct intrq { int ampintc_match(struct device *, void *, void *); void ampintc_attach(struct device *, struct device *, void *); +void ampintc_cpuinit(void); int ampintc_spllower(int); void ampintc_splx(int); int ampintc_splraise(int); @@ -187,6 +190,12 @@ void ampintc_intr_enable(int); void ampintc_intr_disable(int); void ampintc_intr_config(int, int); void ampintc_route(int, int, struct cpu_info *); +void ampintc_route_irq(void *, int, struct cpu_info *); + +int ampintc_ipi_combined(void *); +int ampintc_ipi_nop(void *); +int ampintc_ipi_ddb(void *); +void ampintc_send_ipi(struct cpu_info *, int); struct cfattach ampintc_ca = { sizeof (struct ampintc_softc), ampintc_match, ampintc_attach @@ -224,6 +233,9 @@ ampintc_attach(struct device *parent, struct device *self, void *aux) struct fdt_attach_args *faa = aux; int i, nintr, ncpu; uint32_t ictr; +#ifdef MULTIPROCESSOR + int nipi, ipiirq[2]; +#endif ampintc = sc; @@ -277,10 +289,10 @@ ampintc_attach(struct device *parent, struct device *self, void *aux) /* XXX - check power saving bit */ - sc->sc_ampintc_handler = mallocarray(nintr, - sizeof(*sc->sc_ampintc_handler), M_DEVBUF, M_ZERO | M_NOWAIT); + sc->sc_handler = mallocarray(nintr, sizeof(*sc->sc_handler), M_DEVBUF, + M_ZERO | M_NOWAIT); for (i = 0; i < nintr; i++) { - TAILQ_INIT(&sc->sc_ampintc_handler[i].iq_list); + TAILQ_INIT(&sc->sc_handler[i].iq_list); } ampintc_setipl(IPL_HIGH); /* XXX ??? */ @@ -290,6 +302,66 @@ ampintc_attach(struct device *parent, struct device *self, void *aux) arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx, ampintc_setipl, ampintc_irq_handler); +#ifdef MULTIPROCESSOR + /* setup IPI interrupts */ + + /* + * Ideally we want two IPI interrupts, one for NOP and one for + * DDB, however we can survive if only one is available it is + * possible that most are not available to the non-secure OS. + */ + nipi = 0; + for (i = 0; i < 16; i++) { + int reg, oldreg; + + oldreg = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, + ICD_IPRn(i)); + bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i), + oldreg ^ 0x20); + + /* if this interrupt is not usable, route will be zero */ + reg = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i)); + if (reg == oldreg) + continue; + + /* return to original value, will be set when used */ + bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(i), + oldreg); + + if (nipi == 0) + printf(" ipi: %d", i); + else + printf(", %d", i); + ipiirq[nipi++] = i; + if (nipi == 2) + break; + } + + if (nipi == 0) + panic ("no irq available for IPI"); + + switch (nipi) { + case 1: + ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING, + IPL_IPI|IPL_MPSAFE, ampintc_ipi_combined, sc, "ipi"); + sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0]; + sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[0]; + break; + case 2: + ampintc_intr_establish(ipiirq[0], IST_EDGE_RISING, + IPL_IPI|IPL_MPSAFE, ampintc_ipi_nop, sc, "ipinop"); + sc->sc_ipi_num[ARM_IPI_NOP] = ipiirq[0]; + ampintc_intr_establish(ipiirq[1], IST_EDGE_RISING, + IPL_IPI|IPL_MPSAFE, ampintc_ipi_ddb, sc, "ipiddb"); + sc->sc_ipi_num[ARM_IPI_DDB] = ipiirq[1]; + break; + default: + panic("nipi unexpected number %d", nipi); + } + + intr_send_ipi_func = ampintc_send_ipi; +#endif + /* enable interrupts */ bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_DCR, 3); bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1); @@ -299,6 +371,8 @@ ampintc_attach(struct device *parent, struct device *self, void *aux) sc->sc_ic.ic_cookie = self; sc->sc_ic.ic_establish = ampintc_intr_establish_fdt; sc->sc_ic.ic_disestablish = ampintc_intr_disestablish; + sc->sc_ic.ic_route = ampintc_route_irq; + sc->sc_ic.ic_cpu_enable = ampintc_cpuinit; arm_intr_register_fdt(&sc->sc_ic); /* attach GICv2M frame controller */ @@ -389,8 +463,7 @@ ampintc_calc_mask(void) for (irq = 0; irq < sc->sc_nintr; irq++) { int max = IPL_NONE; int min = IPL_HIGH; - TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, - ih_list) { + TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) { if (ih->ih_ipl > max) max = ih->ih_ipl; @@ -398,10 +471,10 @@ ampintc_calc_mask(void) min = ih->ih_ipl; } - if (sc->sc_ampintc_handler[irq].iq_irq == max) { + if (sc->sc_handler[irq].iq_irq == max) { continue; } - sc->sc_ampintc_handler[irq].iq_irq = max; + sc->sc_handler[irq].iq_irq = max; if (max == IPL_NONE) min = IPL_NONE; @@ -500,6 +573,47 @@ ampintc_route(int irq, int enable, struct cpu_info *ci) } void +ampintc_cpuinit() +{ + struct ampintc_softc *sc = ampintc; + int i; + + /* XXX - this is the only cpu specific call to set this */ + if (sc->sc_cpu_mask[cpu_number()] == 0) { + for (i = 0; i < 32; i++) { + int cpumask = + bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, + ICD_IPTRn(i)); + + if (cpumask != 0) { + sc->sc_cpu_mask[cpu_number()] = cpumask; + break; + } + } + } + + if (sc->sc_cpu_mask[cpu_number()] == 0) + panic("could not determine cpu target mask"); +} + +void +ampintc_route_irq(void *v, int enable, struct cpu_info *ci) +{ + struct ampintc_softc *sc = ampintc; + struct intrhand *ih = v; + + bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPICR, 1); + bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICRn(ih->ih_irq), 0); + if (enable) { + ampintc_set_priority(ih->ih_irq, + sc->sc_handler[ih->ih_irq].iq_irq); + ampintc_intr_enable(ih->ih_irq); + } + + ampintc_route(ih->ih_irq, enable, ci); +} + +void ampintc_irq_handler(void *frame) { struct ampintc_softc *sc = ampintc; @@ -534,9 +648,9 @@ ampintc_irq_handler(void *frame) if (irq >= sc->sc_nintr) return; - pri = sc->sc_ampintc_handler[irq].iq_irq; + pri = sc->sc_handler[irq].iq_irq; s = ampintc_splraise(pri); - TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, ih_list) { + TAILQ_FOREACH(ih, &sc->sc_handler[irq].iq_list, ih_list) { #ifdef MULTIPROCESSOR int need_lock; @@ -614,17 +728,25 @@ ampintc_intr_establish(int irqno, int type, int level, int (*func)(void *), panic("ampintc_intr_establish: bogus irqnumber %d: %s", irqno, name); + if (irqno < 16) { + /* SGI are only EDGE */ + type = IST_EDGE_RISING; + } else if (irqno < 32) { + /* PPI are only LEVEL */ + type = IST_LEVEL_HIGH; + } + ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); ih->ih_func = func; ih->ih_arg = arg; - ih->ih_ipl = level; - ih->ih_flags = 0; + ih->ih_ipl = level & IPL_IRQMASK; + ih->ih_flags = level & IPL_FLAGMASK; ih->ih_irq = irqno; ih->ih_name = name; psw = disable_interrupts(); - TAILQ_INSERT_TAIL(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list); + TAILQ_INSERT_TAIL(&sc->sc_handler[irqno].iq_list, ih, ih_list); if (name != NULL) evcount_attach(&ih->ih_count, name, &ih->ih_irq); @@ -655,7 +777,7 @@ ampintc_intr_disestablish(void *cookie) psw = disable_interrupts(); - TAILQ_REMOVE(&sc->sc_ampintc_handler[ih->ih_irq].iq_list, ih, ih_list); + TAILQ_REMOVE(&sc->sc_handler[ih->ih_irq].iq_list, ih, ih_list); if (ih->ih_name != NULL) evcount_detach(&ih->ih_count); free(ih, M_DEVBUF, sizeof(*ih)); @@ -787,3 +909,53 @@ ampintc_intr_disestablish_msi(void *cookie) ampintc_intr_disestablish(*(void **)cookie); *(void **)cookie = NULL; } + +#ifdef MULTIPROCESSOR +int +ampintc_ipi_ddb(void *v) +{ + /* XXX */ + db_enter(); + return 1; +} + +int +ampintc_ipi_nop(void *v) +{ + /* Nothing to do here, just enough to wake up from WFI */ + return 1; +} + +int +ampintc_ipi_combined(void *v) +{ + struct ampintc_softc *sc = (struct ampintc_softc *)v; + + if (sc->sc_ipi_reason[cpu_number()] == ARM_IPI_DDB) { + sc->sc_ipi_reason[cpu_number()] = ARM_IPI_NOP; + return ampintc_ipi_ddb(v); + } else { + return ampintc_ipi_nop(v); + } +} + +void +ampintc_send_ipi(struct cpu_info *ci, int id) +{ + struct ampintc_softc *sc = ampintc; + int sendmask; + + if (ci == curcpu() && id == ARM_IPI_NOP) + return; + + /* never overwrite IPI_DDB with IPI_NOP */ + if (id == ARM_IPI_DDB) + sc->sc_ipi_reason[ci->ci_cpuid] = id; + + /* currently will only send to one cpu */ + sendmask = 1 << (16 + ci->ci_cpuid); + sendmask |= sc->sc_ipi_num[id]; + + bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_SGIR, sendmask); +} +#endif |