Date: Mon, 25 Jan 2021 10:15:41 GMT From: Andrew Turner <andrew@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 35ebd8d33ad2 - main - gic_v3: add message based interrupts support Message-ID: <202101251015.10PAFfTa050021@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by andrew: URL: https://cgit.FreeBSD.org/src/commit/?id=35ebd8d33ad2f7c2038f6bf9aa02eab21252f689 commit 35ebd8d33ad2f7c2038f6bf9aa02eab21252f689 Author: Cyprien Laplace <cyprien@cypou.net> AuthorDate: 2020-12-05 15:47:33 +0000 Commit: Andrew Turner <andrew@FreeBSD.org> CommitDate: 2021-01-25 10:08:47 +0000 gic_v3: add message based interrupts support Pull Request: https://github.com/freebsd/freebsd-src/pull/451 --- sys/arm64/arm64/gic_v3.c | 204 ++++++++++++++++++++++++++++++++++++++++++- sys/arm64/arm64/gic_v3_fdt.c | 20 +++++ sys/arm64/arm64/gic_v3_var.h | 5 ++ 3 files changed, 226 insertions(+), 3 deletions(-) diff --git a/sys/arm64/arm64/gic_v3.c b/sys/arm64/arm64/gic_v3.c index 8630a27102e3..954ed3cd878a 100644 --- a/sys/arm64/arm64/gic_v3.c +++ b/sys/arm64/arm64/gic_v3.c @@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$"); #endif #include "pic_if.h" +#include "msi_if.h" #include <arm/arm/gic_common.h> #include "gic_v3_reg.h" @@ -94,6 +95,12 @@ static pic_ipi_send_t gic_v3_ipi_send; static pic_ipi_setup_t gic_v3_ipi_setup; #endif +static msi_alloc_msi_t gic_v3_alloc_msi; +static msi_release_msi_t gic_v3_release_msi; +static msi_alloc_msix_t gic_v3_alloc_msix; +static msi_release_msix_t gic_v3_release_msix; +static msi_map_msi_t gic_v3_map_msi; + static u_int gic_irq_cpu; #ifdef SMP static u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1]; @@ -124,6 +131,13 @@ static device_method_t gic_v3_methods[] = { DEVMETHOD(pic_ipi_setup, gic_v3_ipi_setup), #endif + /* MSI/MSI-X */ + DEVMETHOD(msi_alloc_msi, gic_v3_alloc_msi), + DEVMETHOD(msi_release_msi, gic_v3_release_msi), + DEVMETHOD(msi_alloc_msix, gic_v3_alloc_msix), + DEVMETHOD(msi_release_msix, gic_v3_release_msix), + DEVMETHOD(msi_map_msi, gic_v3_map_msi), + /* End */ DEVMETHOD_END }; @@ -150,6 +164,11 @@ struct gic_v3_irqsrc { uint32_t gi_irq; enum intr_polarity gi_pol; enum intr_trigger gi_trig; +#define GI_FLAG_MSI (1 << 1) /* This interrupt source should only */ + /* be used for MSI/MSI-X interrupts */ +#define GI_FLAG_MSI_USED (1 << 2) /* This irq is already allocated */ + /* for a MSI/MSI-X interrupt */ + u_int gi_flags; }; /* Helper routines starting with gic_v3_ */ @@ -314,6 +333,22 @@ gic_v3_attach(device_t dev) } } + if (sc->gic_mbi_start > 0) { + /* Reserve these interrupts for MSI/MSI-X use */ + for (irq = sc->gic_mbi_start; irq <= sc->gic_mbi_end; irq++) { + sc->gic_irqs[irq].gi_pol = INTR_POLARITY_HIGH; + sc->gic_irqs[irq].gi_trig = INTR_TRIGGER_EDGE; + sc->gic_irqs[irq].gi_flags |= GI_FLAG_MSI; + } + + mtx_init(&sc->gic_mbi_mtx, "GICv3 mbi lock", NULL, MTX_DEF); + + if (bootverbose) { + device_printf(dev, "using spi %u to %u\n", sc->gic_mbi_start, + sc->gic_mbi_end); + } + } + /* * Read the Peripheral ID2 register. This is an implementation * defined register, but seems to be implemented in all GICv3 @@ -692,8 +727,11 @@ gic_v3_setup_intr(device_t dev, struct intr_irqsrc *isrc, return (0); } - gi->gi_pol = pol; - gi->gi_trig = trig; + /* For MSI/MSI-X we should have already configured these */ + if ((gi->gi_flags & GI_FLAG_MSI) == 0) { + gi->gi_pol = pol; + gi->gi_trig = trig; + } /* * XXX - In case that per CPU interrupt is going to be enabled in time @@ -742,7 +780,7 @@ gic_v3_teardown_intr(device_t dev, struct intr_irqsrc *isrc, { struct gic_v3_irqsrc *gi = (struct gic_v3_irqsrc *)isrc; - if (isrc->isrc_handlers == 0) { + if (isrc->isrc_handlers == 0 && (gi->gi_flags & GI_FLAG_MSI) == 0) { gi->gi_pol = INTR_POLARITY_CONFORM; gi->gi_trig = INTR_TRIGGER_CONFORM; } @@ -1270,3 +1308,163 @@ gic_v3_redist_init(struct gic_v3_softc *sc) return (0); } + +/* + * SPI-mapped Message Based Interrupts -- a GICv3 MSI/MSI-X controller. + */ + +static int +gic_v3_alloc_msi(device_t dev, device_t child, int count, int maxcount, + device_t *pic, struct intr_irqsrc **srcs) +{ + struct gic_v3_softc *sc; + int i, irq, end_irq; + bool found; + + KASSERT(powerof2(count), ("%s: bad count", __func__)); + KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__)); + + sc = device_get_softc(dev); + + mtx_lock(&sc->gic_mbi_mtx); + + found = false; + for (irq = sc->gic_mbi_start; irq < sc->gic_mbi_end; irq++) { + /* Start on an aligned interrupt */ + if ((irq & (maxcount - 1)) != 0) + continue; + + /* Assume we found a valid range until shown otherwise */ + found = true; + + /* Check this range is valid */ + for (end_irq = irq; end_irq != irq + count; end_irq++) { + /* No free interrupts */ + if (end_irq == sc->gic_mbi_end) { + found = false; + break; + } + + KASSERT((sc->gic_irqs[end_irq].gi_flags & GI_FLAG_MSI)!= 0, + ("%s: Non-MSI interrupt found", __func__)); + + /* This is already used */ + if ((sc->gic_irqs[end_irq].gi_flags & GI_FLAG_MSI_USED) == + GI_FLAG_MSI_USED) { + found = false; + break; + } + } + if (found) + break; + } + + /* Not enough interrupts were found */ + if (!found || irq == sc->gic_mbi_end) { + mtx_unlock(&sc->gic_mbi_mtx); + return (ENXIO); + } + + for (i = 0; i < count; i++) { + /* Mark the interrupt as used */ + sc->gic_irqs[irq + i].gi_flags |= GI_FLAG_MSI_USED; + } + mtx_unlock(&sc->gic_mbi_mtx); + + for (i = 0; i < count; i++) + srcs[i] = (struct intr_irqsrc *)&sc->gic_irqs[irq + i]; + *pic = dev; + + return (0); +} + +static int +gic_v3_release_msi(device_t dev, device_t child, int count, + struct intr_irqsrc **isrc) +{ + struct gic_v3_softc *sc; + struct gic_v3_irqsrc *gi; + int i; + + sc = device_get_softc(dev); + + mtx_lock(&sc->gic_mbi_mtx); + for (i = 0; i < count; i++) { + gi = (struct gic_v3_irqsrc *)isrc[i]; + + KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED, + ("%s: Trying to release an unused MSI-X interrupt", + __func__)); + + gi->gi_flags &= ~GI_FLAG_MSI_USED; + } + mtx_unlock(&sc->gic_mbi_mtx); + + return (0); +} + +static int +gic_v3_alloc_msix(device_t dev, device_t child, device_t *pic, + struct intr_irqsrc **isrcp) +{ + struct gic_v3_softc *sc; + int irq; + + sc = device_get_softc(dev); + + mtx_lock(&sc->gic_mbi_mtx); + /* Find an unused interrupt */ + for (irq = sc->gic_mbi_start; irq < sc->gic_mbi_end; irq++) { + KASSERT((sc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) != 0, + ("%s: Non-MSI interrupt found", __func__)); + if ((sc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) == 0) + break; + } + /* No free interrupt was found */ + if (irq == sc->gic_mbi_end) { + mtx_unlock(&sc->gic_mbi_mtx); + return (ENXIO); + } + + /* Mark the interrupt as used */ + sc->gic_irqs[irq].gi_flags |= GI_FLAG_MSI_USED; + mtx_unlock(&sc->gic_mbi_mtx); + + *isrcp = (struct intr_irqsrc *)&sc->gic_irqs[irq]; + *pic = dev; + + return (0); +} + +static int +gic_v3_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) +{ + struct gic_v3_softc *sc; + struct gic_v3_irqsrc *gi; + + sc = device_get_softc(dev); + gi = (struct gic_v3_irqsrc *)isrc; + + KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED, + ("%s: Trying to release an unused MSI-X interrupt", __func__)); + + mtx_lock(&sc->gic_mbi_mtx); + gi->gi_flags &= ~GI_FLAG_MSI_USED; + mtx_unlock(&sc->gic_mbi_mtx); + + return (0); +} + +static int +gic_v3_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, + uint64_t *addr, uint32_t *data) +{ + struct gic_v3_softc *sc = device_get_softc(dev); + struct gic_v3_irqsrc *gi = (struct gic_v3_irqsrc *)isrc; + +#define GICD_SETSPI_NSR 0x40 + *addr = vtophys(rman_get_virtual(sc->gic_dist)) + GICD_SETSPI_NSR; + *data = gi->gi_irq; + + return (0); +} diff --git a/sys/arm64/arm64/gic_v3_fdt.c b/sys/arm64/arm64/gic_v3_fdt.c index c8a9615a8a5f..483d67ba6deb 100644 --- a/sys/arm64/arm64/gic_v3_fdt.c +++ b/sys/arm64/arm64/gic_v3_fdt.c @@ -121,6 +121,8 @@ gic_v3_fdt_attach(device_t dev) pcell_t redist_regions; intptr_t xref; int err; + uint32_t *mbi_ranges; + ssize_t ret; sc = device_get_softc(dev); sc->dev = dev; @@ -135,6 +137,21 @@ gic_v3_fdt_attach(device_t dev) else sc->gic_redists.nregions = redist_regions; + /* Add Message Based Interrupts using SPIs. */ + ret = OF_getencprop_alloc_multi(ofw_bus_get_node(dev), "mbi-ranges", + sizeof(*mbi_ranges), (void **)&mbi_ranges); + if (ret > 0) { + if (ret % 2 == 0) { + /* Limit to a single range for now. */ + sc->gic_mbi_start = mbi_ranges[0]; + sc->gic_mbi_end = mbi_ranges[0] + mbi_ranges[1] - 1; + } else { + if (bootverbose) + device_printf(dev, "Malformed mbi-ranges property\n"); + } + free(mbi_ranges, M_OFWPROP); + } + err = gic_v3_attach(dev); if (err != 0) goto error; @@ -147,6 +164,9 @@ gic_v3_fdt_attach(device_t dev) goto error; } + if (sc->gic_mbi_start > 0) + intr_msi_register(dev, xref); + /* Register xref */ OF_device_register_xref(xref, dev); diff --git a/sys/arm64/arm64/gic_v3_var.h b/sys/arm64/arm64/gic_v3_var.h index f855e425d66d..1645c417fd8d 100644 --- a/sys/arm64/arm64/gic_v3_var.h +++ b/sys/arm64/arm64/gic_v3_var.h @@ -68,6 +68,11 @@ struct gic_v3_softc { /* Re-Distributors */ struct gic_redists gic_redists; + /* Message Based Interrupts */ + u_int gic_mbi_start; + u_int gic_mbi_end; + struct mtx gic_mbi_mtx; + uint32_t gic_pidr2; u_int gic_bus;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202101251015.10PAFfTa050021>