Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 10 Jul 2013 03:39:56 GMT
From:      John Baldwin <jhb@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 230948 for review
Message-ID:  <201307100339.r6A3ducW029908@skunkworks.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@230948?ac=10

Change 230948 by jhb@jhb_pippin on 2013/07/10 03:39:28

	First cut at updating the PCI-PCI bridge driver to work with
	PCI_RES_BUS which includes bus renumbering support.

Affected files ...

.. //depot/projects/pci/sys/dev/pci/pci_pci.c#36 edit
.. //depot/projects/pci/sys/dev/pci/pci_subr.c#9 edit
.. //depot/projects/pci/sys/dev/pci/pcib_private.h#24 edit

Differences ...

==== //depot/projects/pci/sys/dev/pci/pci_pci.c#36 (text+ko) ====

@@ -529,6 +529,208 @@
 	}
 }
 
+#ifdef PCI_RES_BUS
+/*
+ * Allocate a suitable secondary bus for this bridge if needed and
+ * initialize the resource manager for the secondary bus range.
+ */
+static void
+pcib_setup_secbus(struct pcib_softc *sc)
+{
+	struct pcib_secbus *sec;
+	char buf[64];
+	int error, rid;
+
+	sec = &sc->bus;
+	sec->rman.rm_start = 0;
+	sec->rman.rm_end = PCI_BUSMAX;
+	sec->rman.rm_type = RMAN_ARRAY;
+	snprintf(buf, sizeof(buf), "%s bus numbers",
+	    device_get_nameunit(sc->dev));
+	sec->rman.rm_descr = strdup(buf, M_DEVBUF);
+	error = rman_init(&sec->rman);
+	if (error)
+		panic("Failed to initialize %s bus number rman",
+		    device_get_nameunit(sc->dev));
+
+#if 0
+	/*
+	 * XXX: Should we reset subbus to secbus if it is < secbus?
+	 * This would at least preserve the existing secbus if it is
+	 * valid.
+	 */
+	if (sc->subbus < sc->secbus)
+		sc->subbus = sc->secbus;
+#endif
+
+	/*
+	 * Allocate a resource for the existing secondary bus number
+	 * range if it is valid.
+	 */
+	if (sc->secbus != 0 && sc->subbus >= sc->secbus) {
+		rid = 0;
+		sec->res = bus_alloc_resource(sc->dev, PCI_RES_BUS, &rid,
+		    sc->secbus, sc->subbus, sc->subbus - sc->secbus + 1, 0);
+		if (sec->res == NULL) {
+			if (bootverbose || 1)
+				device_printf(sc->dev,
+				    "failed to allocate initial secbus range: %u-%u\n",
+				    sc->secbus, sc->subbus);
+#if 0
+			/* XXX: Should we clear these on failure? */
+			sc->secbus = 0;
+			sc->subbus = 0;
+#endif
+		}
+		/* XXX */
+		if (sec->res != NULL)
+			device_printf(sc->dev,
+			    "allocated initial secbus range %lu-%lu\n",
+			    rman_get_start(sec->res), rman_get_end(sec->res));
+	}
+
+	/*
+	 * If we don't have a valid resource, allocate a fresh resource
+	 * for just this bus.
+	 *
+	 * XXX: Should we always start with a bus higher than our primary
+	 * side bus number?  I'm not sure it is required by the spec but
+	 * it seems sensible.  OTOH, the existing rmans in any parent
+	 * PCI-PCI bridges should already enforce this.
+	 */
+	if (sec->res == NULL) {
+		/*
+		 * This doesn't use bus_alloc_resource_any() as the
+		 * count of 1 is explicit.
+		 */
+		rid = 0;
+		sec->res = bus_alloc_resource(sc->dev, PCI_RES_BUS, &rid, 0ul,
+		    ~0ul, 1, 0);
+		if (sec->res != NULL) {
+			KASSERT(rman_get_size(sec->res) == 1,
+			    ("more than one bus number"));
+			if (bootverbose || 1)
+				device_printf(sc->dev,
+				    "allocated initial secbus %lu\n",
+				    rman_get_start(sec->res));
+			sc->secbus = rman_get_start(sec->res);
+			sc->subbus = rman_get_end(sec->res);
+		} else
+			device_printf(sc->dev,
+			    "failed to allocate secondary bus number\n");
+	}
+
+	/*
+	 * Add the initial resource to the rman and write updated
+	 * secbus and subbus registers.
+	 */
+	if (sec->res != NULL) {
+		error = rman_manage_region(&sec->rman, rman_get_start(sec->res),
+		    rman_get_end(sec->res));
+		if (error)
+			panic("Failed to add resource to rman");
+	}
+	pci_write_config(sc->dev, PCIR_SECBUS_1, sc->secbus, 1);
+	pci_write_config(sc->dev, PCIR_SUBBUS_1, sc->subbus, 1);
+}
+
+static struct resource *
+pcib_suballoc_bus(struct pcib_softc *sc, device_t child, int *rid, u_long start,
+    u_long end, u_long count, u_int flags)
+{
+	struct resource *res;
+
+	res = rman_reserve_resource(&sc->bus.rman, start, end, count,
+	    flags, child);
+	if (res == NULL)
+		return (NULL);
+
+	if (bootverbose)
+		device_printf(sc->dev,
+		    "allocated bus range (%lu-%lu) for rid %d of %s\n",
+		    rman_get_start(res), rman_get_end(res), *rid,
+		    pcib_child_name(child));
+	rman_set_rid(res, *rid);
+	return (res);
+}
+
+/*
+ * Attempt to grow the secondary bus range.  This is much simpler than
+ * for I/O windows as the range can only be grown by increasing
+ * subbus.
+ */
+static int
+pcib_grow_subbus(struct pcib_softc *sc, u_long new_end)
+{
+	int error;
+
+	KASSERT(new_end > rman_get_end(sc->bus.res),
+	    ("attempt to shrink subbus"));
+	error = bus_adjust_resource(sc->dev, PCI_RES_BUS, sc->bus.res,
+	    rman_get_start(sc->bus.res), new_end);
+	if (error)
+		return (error);
+	if (bootverbose || 1)
+		device_printf(sc->dev, "grew bus range to %lu-%lu\n",
+		    rman_get_start(sc->bus.res), rman_get_end(sc->bus.res));
+	sc->subbus = rman_get_end(sc->bus.res);
+	pci_write_config(sc->dev, PCIR_SUBBUS_1, sc->subbus, 1);
+	return (0);
+}
+
+static struct resource *
+pcib_alloc_subbus(struct pcib_softc *sc, device_t child, int *rid, u_long start,
+    u_long end, u_long count, u_int flags)
+{
+	struct resource *res;
+	u_long start_free, end_free;
+
+	/*
+	 * First, see if the request can be satisified by the existing
+	 * bus range.
+	 */
+	res = pcib_suballoc_bus(sc, child, rid, start, end, count, flags);
+	if (res != NULL)
+		return (res);
+
+	/*
+	 * If this request is for a specific range, first attempt to
+	 * grow the existing resource to accomodate this request.  If
+	 * that fails, retry the allocation using an arbitrary range.
+	 */
+	if (start + count - 1 == end) {
+		if (end < sc->subbus && pcib_grow_subbus(sc, end) == 0) {
+			res = pcib_suballoc_bus(sc, child, rid, start, end,
+			    count, flags);
+			if (res != NULL)
+				return (res);
+		}
+
+		res = pcib_suballoc_bus(sc, child, rid, 0ul, ~0ul, count, flags);
+		if (res != NULL)
+			return (res);
+	}
+
+	/*
+	 * Finally, attempt to grow the existing resource to accomodate
+	 * the request using an arbitrary range.
+	 */
+	if (rman_last_free_region(&sc->bus.rman, &start_free, &end_free) != 0 ||
+	    end_free != sc->subbus)
+		start_free = sc->subbus + 1;
+	if (bootverbose || 1) {
+		device_printf(sc->dev,
+		    "attempting to grow bus range for %lu buses\n", count);
+		printf("\tback candidate range: %lu-%lu\n", start_free,
+		    start_free + count - 1);
+	}
+	if (pcib_grow_subbus(sc, start_free + count - 1) == 0)
+		return (pcib_suballoc_bus(sc, child, rid, 0ul, ~0ul, count,
+		    flags));
+	return (NULL);
+}
+#endif
+
 #else
 
 /*
@@ -830,6 +1032,9 @@
 	sc->flags |= PCIB_SUBTRACTIVE;
 
 #ifdef NEW_PCIB
+#ifdef PCI_RES_BUS
+    pcib_setup_secbus(sc);
+#endif
     pcib_probe_windows(sc);
 #endif
     if (bootverbose) {
@@ -876,20 +1081,6 @@
     }
 
     /*
-     * XXX If the secondary bus number is zero, we should assign a bus number
-     *     since the BIOS hasn't, then initialise the bridge.  A simple
-     *     bus_alloc_resource with the a couple of busses seems like the right
-     *     approach, but we don't know what busses the BIOS might have already
-     *     assigned to other bridges on this bus that probe later than we do.
-     *
-     *     If the subordinate bus number is less than the secondary bus number,
-     *     we should pick a better value.  One sensible alternative would be to
-     *     pick 255; the only tradeoff here is that configuration transactions
-     *     would be more widely routed than absolutely necessary.  We could
-     *     then do a walk of the tree later and fix it.
-     */
-
-    /*
      * Always enable busmastering on bridges so that transactions
      * initiated on the secondary bus are passed through to the
      * primary bus.
@@ -1382,6 +1573,11 @@
 	}
 
 	switch (type) {
+#ifdef PCI_RES_BUS
+	case PCI_RES_BUS:
+		return (pcib_alloc_subbus(sc, child, rid, start, end, count,
+		    flags));
+#endif
 	case SYS_RES_IOPORT:
 		r = pcib_suballoc_resource(sc, &sc->io, child, type, rid, start,
 		    end, count, flags);

==== //depot/projects/pci/sys/dev/pci/pci_subr.c#9 (text+ko) ====

@@ -320,7 +320,7 @@
 			return (d);
 	}
 
-	snprintf(buf, sizeof(buf), "PCI domain %d", domain);
+	snprintf(buf, sizeof(buf), "PCI domain %d bus numbers", domain);
 	d = malloc(sizeof(*d) + strlen(buf) + 1, M_DEVBUF, M_WAITOK | M_ZERO);
 	d->pd_domain = domain;
 	d->pd_bus_rman.rm_start = 0;

==== //depot/projects/pci/sys/dev/pci/pcib_private.h#24 (text+ko) ====

@@ -81,6 +81,14 @@
 	int		step;		/* log_2 of window granularity */
 	const char	*name;
 };
+
+#ifdef PCI_RES_BUS
+struct pcib_secbus {
+	struct rman	rman;
+	struct resource	*res;
+	const char	*name;
+};
+#endif
 #endif
 
 /*
@@ -101,6 +109,9 @@
     struct pcib_window io;	/* I/O port window */
     struct pcib_window mem;	/* memory window */
     struct pcib_window pmem;	/* prefetchable memory window */
+#ifdef PCI_RES_BUS
+    struct pcib_secbus bus;	/* secondary bus numbers */
+#endif
 #else
     pci_addr_t	pmembase;	/* base address of prefetchable memory */
     pci_addr_t	pmemlimit;	/* topmost address of prefetchable memory */



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