git: acd69b070403 - stable/13 - gic_v3: add message based interrupts support

Ed Maste emaste at FreeBSD.org
Wed Feb 17 02:19:30 UTC 2021


The branch stable/13 has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=acd69b070403e12742ccea7b19d251320ed788d5

commit acd69b070403e12742ccea7b19d251320ed788d5
Author:     Cyprien Laplace <cyprien at cypou.net>
AuthorDate: 2020-12-05 15:47:33 +0000
Commit:     Ed Maste <emaste at FreeBSD.org>
CommitDate: 2021-02-17 00:07:51 +0000

    gic_v3: add message based interrupts support
    
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/451
    
    (cherry picked from commit 35ebd8d33ad2f7c2038f6bf9aa02eab21252f689)
---
 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;
 


More information about the dev-commits-src-all mailing list