Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 15 Apr 2016 14:19:25 +0000 (UTC)
From:      Andrew Turner <andrew@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r298051 - head/sys/arm/arm
Message-ID:  <201604151419.u3FEJPfh055729@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: andrew
Date: Fri Apr 15 14:19:25 2016
New Revision: 298051
URL: https://svnweb.freebsd.org/changeset/base/298051

Log:
  Add initial GICv2m support to the arm GIC driver. This will be used to
  support MSI and MSI-X interrupts, however intrng needs updates before this
  can happen.
  
  For now we just attach the driver until the MSI API is ready.
  
  Obtained from:	ABT Systems Ltd
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D5950

Modified:
  head/sys/arm/arm/gic.c

Modified: head/sys/arm/arm/gic.c
==============================================================================
--- head/sys/arm/arm/gic.c	Fri Apr 15 13:34:59 2016	(r298050)
+++ head/sys/arm/arm/gic.c	Fri Apr 15 14:19:25 2016	(r298051)
@@ -134,6 +134,19 @@ u_int sgi_first_unused = GIC_FIRST_SGI;
 #endif
 #endif
 
+#ifdef ARM_INTRNG
+struct arm_gic_range {
+	uint64_t bus;
+	uint64_t host;
+	uint64_t size;
+};
+
+struct arm_gic_devinfo {
+	struct ofw_bus_devinfo	obdinfo;
+	struct resource_list	rl;
+};
+#endif
+
 struct arm_gic_softc {
 	device_t		gic_dev;
 #ifdef ARM_INTRNG
@@ -151,6 +164,14 @@ struct arm_gic_softc {
 #ifdef GIC_DEBUG_SPURIOUS
 	uint32_t		last_irq[MAXCPU];
 #endif
+
+#ifdef ARM_INTRNG
+	/* FDT child data */
+	pcell_t			addr_cells;
+	pcell_t			size_cells;
+	int			nranges;
+	struct arm_gic_range *	ranges;
+#endif
 };
 
 #ifdef ARM_INTRNG
@@ -195,6 +216,7 @@ static struct ofw_compat_data compat_dat
 	{"arm,cortex-a7-gic",	true},
 	{"arm,arm11mp-gic",	true},
 	{"brcm,brahma-b15-gic",	true},
+	{"qcom,msm-qgic2",	true},
 	{NULL,			false}
 };
 
@@ -437,6 +459,107 @@ arm_gic_register_isrcs(struct arm_gic_so
 	sc->nirqs = num;
 	return (0);
 }
+
+static int
+arm_gic_fill_ranges(phandle_t node, struct arm_gic_softc *sc)
+{
+	pcell_t host_cells;
+	cell_t *base_ranges;
+	ssize_t nbase_ranges;
+	int i, j, k;
+
+	host_cells = 1;
+	OF_getencprop(OF_parent(node), "#address-cells", &host_cells,
+	    sizeof(host_cells));
+	sc->addr_cells = 2;
+	OF_getencprop(node, "#address-cells", &sc->addr_cells,
+	    sizeof(sc->addr_cells));
+	sc->size_cells = 2;
+	OF_getencprop(node, "#size-cells", &sc->size_cells,
+	    sizeof(sc->size_cells));
+
+	nbase_ranges = OF_getproplen(node, "ranges");
+	if (nbase_ranges < 0)
+		return (-1);
+	sc->nranges = nbase_ranges / sizeof(cell_t) /
+	    (sc->addr_cells + host_cells + sc->size_cells);
+	if (sc->nranges == 0)
+		return (0);
+
+	sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]),
+	    M_DEVBUF, M_WAITOK);
+	base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
+	OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
+
+	for (i = 0, j = 0; i < sc->nranges; i++) {
+		sc->ranges[i].bus = 0;
+		for (k = 0; k < sc->addr_cells; k++) {
+			sc->ranges[i].bus <<= 32;
+			sc->ranges[i].bus |= base_ranges[j++];
+		}
+		sc->ranges[i].host = 0;
+		for (k = 0; k < host_cells; k++) {
+			sc->ranges[i].host <<= 32;
+			sc->ranges[i].host |= base_ranges[j++];
+		}
+		sc->ranges[i].size = 0;
+		for (k = 0; k < sc->size_cells; k++) {
+			sc->ranges[i].size <<= 32;
+			sc->ranges[i].size |= base_ranges[j++];
+		}
+	}
+
+	free(base_ranges, M_DEVBUF);
+	return (sc->nranges);
+}
+
+static bool
+arm_gic_add_children(device_t dev)
+{
+	struct arm_gic_softc *sc;
+	struct arm_gic_devinfo *dinfo;
+	phandle_t child, node;
+	device_t cdev;
+
+	sc = device_get_softc(dev);
+	node = ofw_bus_get_node(dev);
+
+	/* If we have no children don't probe for them */
+	child = OF_child(node);
+	if (child == 0)
+		return (false);
+
+	if (arm_gic_fill_ranges(node, sc) < 0) {
+		device_printf(dev, "Have a child, but no ranges\n");
+		return (false);
+	}
+
+	for (; child != 0; child = OF_peer(child)) {
+		dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
+
+		if (ofw_bus_gen_setup_devinfo(&dinfo->obdinfo, child) != 0) {
+			free(dinfo, M_DEVBUF);
+			continue;
+		}
+
+		resource_list_init(&dinfo->rl);
+		ofw_bus_reg_to_rl(dev, child, sc->addr_cells,
+		    sc->size_cells, &dinfo->rl);
+
+		cdev = device_add_child(dev, NULL, -1);
+		if (cdev == NULL) {
+			device_printf(dev, "<%s>: device_add_child failed\n",
+			    dinfo->obdinfo.obd_name);
+			resource_list_free(&dinfo->rl);
+			ofw_bus_gen_destroy_devinfo(&dinfo->obdinfo);
+			free(dinfo, M_DEVBUF);
+			continue;
+		}
+		device_set_ivars(cdev, dinfo);
+	}
+
+	return (true);
+}
 #endif
 
 static int
@@ -578,6 +701,13 @@ arm_gic_attach(device_t dev)
 	}
 
 	OF_device_register_xref(xref, dev);
+
+	/* If we have children probe and attach them */
+	if (arm_gic_add_children(dev)) {
+		bus_generic_probe(dev);
+		return (bus_generic_attach(dev));
+	}
+
 	return (0);
 
 cleanup:
@@ -592,6 +722,75 @@ cleanup:
 }
 
 #ifdef ARM_INTRNG
+static struct resource *
+arm_gic_alloc_resource(device_t bus, device_t child, int type, int *rid,
+    rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
+{
+	struct arm_gic_softc *sc;
+	struct arm_gic_devinfo *di;
+	struct resource_list_entry *rle;
+	int j;
+
+	KASSERT(type == SYS_RES_MEMORY, ("Invalid resoure type %x", type));
+
+	sc = device_get_softc(bus);
+
+	/*
+	 * Request for the default allocation with a given rid: use resource
+	 * list stored in the local device info.
+	 */
+	if (RMAN_IS_DEFAULT_RANGE(start, end)) {
+		if ((di = device_get_ivars(child)) == NULL)
+			return (NULL);
+
+		if (type == SYS_RES_IOPORT)
+			type = SYS_RES_MEMORY;
+
+		rle = resource_list_find(&di->rl, type, *rid);
+		if (rle == NULL) {
+			if (bootverbose)
+				device_printf(bus, "no default resources for "
+				    "rid = %d, type = %d\n", *rid, type);
+			return (NULL);
+		}
+		start = rle->start;
+		end = rle->end;
+		count = rle->count;
+	}
+
+	/* Remap through ranges property */
+	for (j = 0; j < sc->nranges; j++) {
+		if (start >= sc->ranges[j].bus && end <
+		    sc->ranges[j].bus + sc->ranges[j].size) {
+			start -= sc->ranges[j].bus;
+			start += sc->ranges[j].host;
+			end -= sc->ranges[j].bus;
+			end += sc->ranges[j].host;
+			break;
+		}
+	}
+	if (j == sc->nranges && sc->nranges != 0) {
+		if (bootverbose)
+			device_printf(bus, "Could not map resource "
+			    "%#jx-%#jx\n", (uintmax_t)start, (uintmax_t)end);
+
+		return (NULL);
+	}
+
+	return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
+	    count, flags));
+}
+
+static const struct ofw_bus_devinfo *
+arm_gic_ofw_get_devinfo(device_t bus __unused, device_t child)
+{
+	struct arm_gic_devinfo *di;
+
+	di = device_get_ivars(child);
+
+	return (&di->obdinfo);
+}
+
 static int
 arm_gic_intr(void *arg)
 {
@@ -1231,7 +1430,22 @@ static device_method_t arm_gic_methods[]
 	/* Device interface */
 	DEVMETHOD(device_probe,		arm_gic_probe),
 	DEVMETHOD(device_attach,	arm_gic_attach),
+
 #ifdef ARM_INTRNG
+	/* Bus interface */
+	DEVMETHOD(bus_add_child,	bus_generic_add_child),
+	DEVMETHOD(bus_alloc_resource,	arm_gic_alloc_resource),
+	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
+	DEVMETHOD(bus_activate_resource,bus_generic_activate_resource),
+
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_devinfo,	arm_gic_ofw_get_devinfo),
+	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
+	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
+	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
+	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
+	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
+
 	/* Interrupt controller interface */
 	DEVMETHOD(pic_disable_intr,	arm_gic_disable_intr),
 	DEVMETHOD(pic_enable_intr,	arm_gic_enable_intr),
@@ -1263,3 +1477,89 @@ EARLY_DRIVER_MODULE(gic, simplebus, arm_
     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
 EARLY_DRIVER_MODULE(gic, ofwbus, arm_gic_driver, arm_gic_devclass, 0, 0,
     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+
+#ifdef ARM_INTRNG
+/*
+ * GICv2m support -- the GICv2 MSI/MSI-X controller.
+ */
+
+#define	GICV2M_MSI_TYPER	0x008
+#define	 MSI_TYPER_SPI_BASE(x)	(((x) >> 16) & 0x3ff)
+#define	 MSI_TYPER_SPI_COUNT(x)	(((x) >> 0) & 0x3ff)
+#define	GICv2M_MSI_SETSPI_NS	0x040
+#define	GICV2M_MSI_IIDR		0xFCC
+
+struct arm_gicv2m_softc {
+	struct resource	*sc_mem;
+	struct mtx	sc_mutex;
+	u_int		sc_spi_start;
+	u_int		sc_spi_count;
+	u_int		sc_spi_offset;
+};
+
+static struct ofw_compat_data gicv2m_compat_data[] = {
+	{"arm,gic-v2m-frame",	true},
+	{NULL,			false}
+};
+
+static int
+arm_gicv2m_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_search_compatible(dev, gicv2m_compat_data)->ocd_data)
+		return (ENXIO);
+
+	device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+arm_gicv2m_attach(device_t dev)
+{
+	struct arm_gicv2m_softc *sc;
+	uint32_t typer;
+	int rid;
+
+	sc = device_get_softc(dev);
+
+	rid = 0;
+	sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->sc_mem == NULL) {
+		device_printf(dev, "Unable to allocate resources\n");
+		return (ENXIO);
+	}
+
+	typer = bus_read_4(sc->sc_mem, GICV2M_MSI_TYPER);
+	sc->sc_spi_start = MSI_TYPER_SPI_BASE(typer);
+	sc->sc_spi_count = MSI_TYPER_SPI_COUNT(typer);
+
+	mtx_init(&sc->sc_mutex, "GICv2m lock", "", MTX_DEF);
+
+	if (bootverbose)
+		device_printf(dev, "using spi %u to %u\n", sc->sc_spi_start,
+		    sc->sc_spi_start + sc->sc_spi_count - 1);
+
+	return (0);
+}
+
+static device_method_t arm_gicv2m_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		arm_gicv2m_probe),
+	DEVMETHOD(device_attach,	arm_gicv2m_attach),
+
+	/* End */
+	DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(gicv2m, arm_gicv2m_driver, arm_gicv2m_methods,
+    sizeof(struct arm_gicv2m_softc));
+
+static devclass_t arm_gicv2m_devclass;
+
+EARLY_DRIVER_MODULE(gicv2m, gic, arm_gicv2m_driver,
+    arm_gicv2m_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
+#endif



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201604151419.u3FEJPfh055729>