PERFORCE change 230948 for review
John Baldwin
jhb at FreeBSD.org
Wed Jul 10 03:39:57 UTC 2013
http://p4web.freebsd.org/@@230948?ac=10
Change 230948 by jhb at 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 */
More information about the p4-projects
mailing list