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>