diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2020-07-14 16:01:09 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2020-07-14 16:01:09 +0000 |
commit | 1536ba3241baaf6d44ff25926b7367faca75f180 (patch) | |
tree | aaac0cb25ce9b4f0588421a23463835774f81193 | |
parent | 3e71783dcfd0d2185a30190024131721ffdf8acf (diff) |
Add support for routing interrupts to other CPUs in agintc(4). Most of the
supporting code was already there. The driver supports establishing multiple
handlers on the same pin. Make sure that a single pin can only be established
on a specific core by recording the struct cpu_info * of the first establish,
and returning NULL if someone tries to share the pin with a different core.
For LPIs, typically used for MSIs, the routing is done by targetting an LPI
to a specific "collection". We create a collection per core, indexing it by
cpu_number().
For this we need to know a CPU's "processor number", unless GITS_TYPER_PTA is
set. Since we now attach CPUs early, and the redistributors are not banked,
we can retrieve that information early on. It's important to move this as far
up as possible, as it's not as easy as on ampintc(4) to re-route LPIs.
To establish an LPI on a different core, we now only have the pass the CPU's
number as part of the map command which is sent to the hardware.
Prompted by dlg@
ok kettenis@
-rw-r--r-- | sys/arch/arm64/dev/agintc.c | 101 |
1 files changed, 54 insertions, 47 deletions
diff --git a/sys/arch/arm64/dev/agintc.c b/sys/arch/arm64/dev/agintc.c index 3477b635c9f..222e786d30a 100644 --- a/sys/arch/arm64/dev/agintc.c +++ b/sys/arch/arm64/dev/agintc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: agintc.c,v 1.23 2020/07/14 15:34:14 patrick Exp $ */ +/* $OpenBSD: agintc.c,v 1.24 2020/07/14 16:01:08 patrick Exp $ */ /* * Copyright (c) 2007, 2009, 2011, 2017 Dale Rahn <drahn@dalerahn.com> * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> @@ -142,7 +142,7 @@ struct agintc_softc { bus_space_handle_t *sc_r_ioh; bus_space_handle_t sc_redist_base; bus_dma_tag_t sc_dmat; - uint64_t *sc_affinity; + uint16_t *sc_processor; int sc_cpuremap[MAXCPUS]; int sc_nintr; int sc_nlpi; @@ -174,6 +174,7 @@ struct intrhand { struct intrq { TAILQ_HEAD(, intrhand) iq_list; /* handler list */ + struct cpu_info *iq_ci; /* CPU the IRQ runs on */ int iq_irq_max; /* IRQ to mask while handling */ int iq_irq_min; /* lowest IRQ when shared */ int iq_ist; /* share type */ @@ -266,10 +267,13 @@ agintc_attach(struct device *parent, struct device *self, void *aux) { struct agintc_softc *sc = (struct agintc_softc *)self; struct fdt_attach_args *faa = aux; + struct cpu_info *ci; + CPU_INFO_ITERATOR cii; uint32_t typer; uint32_t nsacr, oldnsacr; uint32_t pmr, oldpmr; uint32_t ctrl, bits; + uint32_t affinity; int i, j, nbits, nintr; int psw; int offset, nredist; @@ -416,8 +420,8 @@ agintc_attach(struct device *parent, struct device *self, void *aux) sc->sc_r_ioh = mallocarray(sc->sc_num_redist, sizeof(*sc->sc_r_ioh), M_DEVBUF, M_WAITOK); - sc->sc_affinity = mallocarray(sc->sc_num_redist, - sizeof(*sc->sc_affinity), M_DEVBUF, M_WAITOK); + sc->sc_processor = mallocarray(sc->sc_num_redist, + sizeof(*sc->sc_processor), M_DEVBUF, M_WAITOK); /* submap and configure the redistributors. */ offset = 0; @@ -431,8 +435,20 @@ agintc_attach(struct device *parent, struct device *self, void *aux) if (typer & GICR_TYPER_VLPIS) sz += (64 * 1024 * 2); - sc->sc_affinity[nredist] = bus_space_read_8(sc->sc_iot, + affinity = bus_space_read_8(sc->sc_iot, sc->sc_redist_base, offset + GICR_TYPER) >> 32; + CPU_INFO_FOREACH(cii, ci) { + if (affinity == (((ci->ci_mpidr >> 8) & 0xff000000) | + (ci->ci_mpidr & 0x00ffffff))) + break; + } + if (ci == NULL) + panic("%s: no CPU found for affinity %08x", + sc->sc_sbus.sc_dev.dv_xname, affinity); + + sc->sc_cpuremap[ci->ci_cpuid] = nredist; + sc->sc_processor[nredist] = bus_space_read_8(sc->sc_iot, + sc->sc_redist_base, offset + GICR_TYPER) >> 8; bus_space_subregion(sc->sc_iot, sc->sc_redist_base, offset, sz, &sc->sc_r_ioh[nredist]); @@ -592,9 +608,9 @@ unmap: free(sc->sc_r_ioh, M_DEVBUF, sc->sc_num_redist * sizeof(*sc->sc_r_ioh)); } - if (sc->sc_affinity) { - free(sc->sc_affinity, M_DEVBUF, - sc->sc_num_redist * sizeof(*sc->sc_affinity)); + if (sc->sc_processor) { + free(sc->sc_processor, M_DEVBUF, + sc->sc_num_redist * sizeof(*sc->sc_processor)); } if (sc->sc_pend) @@ -610,35 +626,13 @@ unmap: 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; + uint32_t waker; int timeout = 100000; - int hwcpu = -1; + int hwcpu; 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: %016llx\n", i, sc->sc_affinity[i]); - panic("failed to indentify cpunumber %d", ci->ci_cpuid); - } - + hwcpu = sc->sc_cpuremap[cpu_number()]; waker = bus_space_read_4(sc->sc_iot, sc->sc_r_ioh[hwcpu], GICR_WAKER); waker &= ~(GICR_WAKER_PROCESSORSLEEP); @@ -756,7 +750,7 @@ agintc_calc_mask(void) void agintc_calc_irq(struct agintc_softc *sc, int irq) { - struct cpu_info *ci = curcpu(); + struct cpu_info *ci = sc->sc_handler[irq].iq_ci; struct intrhand *ih; int max = IPL_NONE; int min = IPL_HIGH; @@ -1012,9 +1006,16 @@ agintc_intr_establish(int irqno, int level, struct cpu_info *ci, psw = disable_interrupts(); - if (irqno < LPI_BASE) + if (irqno < LPI_BASE) { + if (!TAILQ_EMPTY(&sc->sc_handler[irqno].iq_list) && + sc->sc_handler[irqno].iq_ci != ci) { + free(ih, M_DEVBUF, sizeof *ih); + restore_interrupts(psw); + return NULL; + } TAILQ_INSERT_TAIL(&sc->sc_handler[irqno].iq_list, ih, ih_list); - else + sc->sc_handler[irqno].iq_ci = ci; + } else sc->sc_lpi_handler[irqno - LPI_BASE] = ih; if (name != NULL) @@ -1274,7 +1275,7 @@ agintc_msi_attach(struct device *parent, struct device *self, void *aux) struct gits_cmd cmd; uint32_t pre_its[2]; uint64_t typer; - int i; + int i, hwcpu; if (faa->fa_nreg < 1) { printf(": no registers\n"); @@ -1298,7 +1299,7 @@ agintc_msi_attach(struct device *parent, struct device *self, void *aux) typer = bus_space_read_8(sc->sc_iot, sc->sc_ioh, GITS_TYPER); if ((typer & GITS_TYPER_PHYS) == 0 || typer & GITS_TYPER_PTA || - GITS_TYPER_HCC(typer) == 0 || typer & GITS_TYPER_CIL) { + GITS_TYPER_HCC(typer) < ncpus || typer & GITS_TYPER_CIL) { printf(": unsupported type 0x%016llx\n", typer); goto unmap; } @@ -1387,12 +1388,17 @@ agintc_msi_attach(struct device *parent, struct device *self, void *aux) LIST_INIT(&sc->sc_msi_devices); - /* Map collection 0 to redistributor 0. */ - memset(&cmd, 0, sizeof(cmd)); - cmd.cmd = MAPC; - cmd.dw2 = GITS_CMD_VALID; - agintc_msi_send_cmd(sc, &cmd); - agintc_msi_wait_cmd(sc); + /* Create one collection per core. */ + KASSERT(ncpus <= agintc_sc->sc_num_redist); + for (i = 0; i < ncpus; i++) { + hwcpu = agintc_sc->sc_cpuremap[i]; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = MAPC; + cmd.dw2 = GITS_CMD_VALID | + (agintc_sc->sc_processor[hwcpu] << 16) | i; + agintc_msi_send_cmd(sc, &cmd); + agintc_msi_wait_cmd(sc); + } printf("\n"); @@ -1494,10 +1500,11 @@ agintc_intr_establish_msi(void *self, uint64_t *addr, uint64_t *data, uint32_t deviceid = *data; uint32_t eventid; void *cookie; - int i; + int i, hwcpu; if (ci == NULL) ci = &cpu_info_primary; + hwcpu = agintc_sc->sc_cpuremap[ci->ci_cpuid]; md = agintc_msi_find_device(sc, deviceid); if (md == NULL) @@ -1521,12 +1528,12 @@ agintc_intr_establish_msi(void *self, uint64_t *addr, uint64_t *data, cmd.deviceid = deviceid; cmd.eventid = eventid; cmd.intid = LPI_BASE + i; - cmd.dw2 = 0; + cmd.dw2 = ci->ci_cpuid; agintc_msi_send_cmd(sc, &cmd); memset(&cmd, 0, sizeof(cmd)); cmd.cmd = SYNC; - cmd.dw2 = 0; + cmd.dw2 = agintc_sc->sc_processor[hwcpu] << 16; agintc_msi_send_cmd(sc, &cmd); agintc_msi_wait_cmd(sc); |