svn commit: r261790 - in head/sys: amd64/include dev/acpica dev/cardbus dev/pccbb dev/pci i386/include sparc64/pci x86/include x86/pci x86/x86

John Baldwin jhb at FreeBSD.org
Wed Feb 12 04:30:40 UTC 2014


Author: jhb
Date: Wed Feb 12 04:30:37 2014
New Revision: 261790
URL: http://svnweb.freebsd.org/changeset/base/261790

Log:
  Add support for managing PCI bus numbers.  As with BARs and PCI-PCI bridge
  I/O windows, the default is to preserve the firmware-assigned resources.
  PCI bus numbers are only managed if NEW_PCIB is enabled and the architecture
  defines a PCI_RES_BUS resource type.
  - Add a helper API to create top-level PCI bus resource managers for each
    PCI domain/segment.  Host-PCI bridge drivers use this API to allocate
    bus numbers from their associated domain.
  - Change the PCI bus and CardBus drivers to allocate a bus resource for
    their bus number from the parent PCI bridge device.
  - Change the PCI-PCI and PCI-CardBus bridge drivers to allocate the
    full range of bus numbers from secbus to subbus from their parent bridge.
    The drivers also always program their primary bus register.  The bridge
    drivers also support growing their bus range by extending the bus resource
    and updating subbus to match the larger range.
  - Add support for managing PCI bus resources to the Host-PCI bridge drivers
    used for amd64 and i386 (acpi_pcib, mptable_pcib, legacy_pcib, and qpi_pcib).
  - Define a PCI_RES_BUS resource type for amd64 and i386.
  
  Reviewed by:	imp
  MFC after:	1 month

Modified:
  head/sys/amd64/include/resource.h
  head/sys/dev/acpica/acpi_pcib_acpi.c
  head/sys/dev/acpica/acpi_pcib_pci.c
  head/sys/dev/cardbus/cardbus.c
  head/sys/dev/cardbus/cardbusvar.h
  head/sys/dev/pccbb/pccbb.c
  head/sys/dev/pccbb/pccbb_isa.c
  head/sys/dev/pccbb/pccbb_pci.c
  head/sys/dev/pccbb/pccbbvar.h
  head/sys/dev/pci/pci.c
  head/sys/dev/pci/pci_pci.c
  head/sys/dev/pci/pci_private.h
  head/sys/dev/pci/pci_subr.c
  head/sys/dev/pci/pcib_private.h
  head/sys/i386/include/resource.h
  head/sys/sparc64/pci/apb.c
  head/sys/x86/include/legacyvar.h
  head/sys/x86/pci/pci_bus.c
  head/sys/x86/pci/qpi.c
  head/sys/x86/x86/mptable_pci.c

Modified: head/sys/amd64/include/resource.h
==============================================================================
--- head/sys/amd64/include/resource.h	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/amd64/include/resource.h	Wed Feb 12 04:30:37 2014	(r261790)
@@ -40,5 +40,8 @@
 #define	SYS_RES_DRQ	2	/* isa dma lines */
 #define	SYS_RES_MEMORY	3	/* i/o memory */
 #define	SYS_RES_IOPORT	4	/* i/o ports */
+#ifdef NEW_PCIB
+#define	PCI_RES_BUS	5	/* PCI bus numbers */
+#endif
 
 #endif /* !_MACHINE_RESOURCE_H_ */

Modified: head/sys/dev/acpica/acpi_pcib_acpi.c
==============================================================================
--- head/sys/dev/acpica/acpi_pcib_acpi.c	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/acpica/acpi_pcib_acpi.c	Wed Feb 12 04:30:37 2014	(r261790)
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/acpica/acpivar.h>
 
 #include <machine/pci_cfgreg.h>
+#include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
 #include <dev/pci/pcib_private.h>
 #include "pcib_if.h"
@@ -96,6 +97,11 @@ static struct resource *acpi_pcib_acpi_a
 static int		acpi_pcib_acpi_adjust_resource(device_t dev,
 			    device_t child, int type, struct resource *r,
 			    u_long start, u_long end);
+#ifdef PCI_RES_BUS
+static int		acpi_pcib_acpi_release_resource(device_t dev,
+			    device_t child, int type, int rid,
+			    struct resource *r);
+#endif
 #endif
 
 static device_method_t acpi_pcib_acpi_methods[] = {
@@ -115,7 +121,11 @@ static device_method_t acpi_pcib_acpi_me
 #else
     DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
 #endif
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+    DEVMETHOD(bus_release_resource,	acpi_pcib_acpi_release_resource),
+#else
     DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
+#endif
     DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
     DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
     DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
@@ -271,6 +281,20 @@ acpi_pcib_producer_handler(ACPI_RESOURCE
 }
 #endif
 
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+static int
+first_decoded_bus(struct acpi_hpcib_softc *sc, u_long *startp)
+{
+	struct resource_list_entry *rle;
+
+	rle = resource_list_find(&sc->ap_host_res.hr_rl, PCI_RES_BUS, 0);
+	if (rle == NULL)
+		return (ENXIO);
+	*startp = rle->start;
+	return (0);
+}
+#endif
+
 static int
 acpi_pcib_acpi_attach(device_t dev)
 {
@@ -278,6 +302,11 @@ acpi_pcib_acpi_attach(device_t dev)
     ACPI_STATUS			status;
     static int bus0_seen = 0;
     u_int slot, func, busok;
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+    struct resource *bus_res;
+    u_long start;
+    int rid;
+#endif
     uint8_t busno;
 
     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
@@ -389,6 +418,39 @@ acpi_pcib_acpi_attach(device_t dev)
 	}
     }
 
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+    /*
+     * If nothing else worked, hope that ACPI at least lays out the
+     * Host-PCI bridges in order and that as a result the next free
+     * bus number is our bus number.
+     */
+    if (busok == 0) {
+	    /*
+	     * If we have a region of bus numbers, use the first
+	     * number for our bus.
+	     */
+	    if (first_decoded_bus(sc, &start) == 0)
+		    sc->ap_bus = start;
+	    else {
+		    rid = 0;
+		    bus_res = pci_domain_alloc_bus(sc->ap_segment, dev, &rid, 0,
+			PCI_BUSMAX, 1, 0);
+		    if (bus_res == NULL) {
+			    device_printf(dev,
+				"could not allocate bus number\n");
+			    pcib_host_res_free(dev, &sc->ap_host_res);
+			    return (ENXIO);
+		    }
+		    sc->ap_bus = rman_get_start(bus_res);
+		    pci_domain_release_bus(sc->ap_segment, dev, rid, bus_res);
+	    }
+    } else {
+#ifdef INVARIANTS
+	    if (first_decoded_bus(sc, &start) == 0)
+		    KASSERT(start == sc->ap_bus, ("bus number mismatch"));
+#endif
+    }
+#else
     /*
      * If nothing else worked, hope that ACPI at least lays out the
      * host-PCI bridges in order and that as a result our unit number
@@ -399,6 +461,7 @@ acpi_pcib_acpi_attach(device_t dev)
 	sc->ap_bus = device_get_unit(dev);
 	device_printf(dev, "trying bus number %d\n", sc->ap_bus);
     }
+#endif
 
     /* If this is bus 0 on segment 0, note that it has been seen already. */
     if (sc->ap_segment == 0 && sc->ap_bus == 0)
@@ -534,6 +597,11 @@ acpi_pcib_acpi_alloc_resource(device_t d
 
 #ifdef NEW_PCIB
     sc = device_get_softc(dev);
+#ifdef PCI_RES_BUS
+    if (type == PCI_RES_BUS)
+	return (pci_domain_alloc_bus(sc->ap_segment, child, rid, start, end,
+	    count, flags));
+#endif
     res = pcib_host_res_alloc(&sc->ap_host_res, child, type, rid, start, end,
 	count, flags);
 
@@ -562,7 +630,26 @@ acpi_pcib_acpi_adjust_resource(device_t 
 	struct acpi_hpcib_softc *sc;
 
 	sc = device_get_softc(dev);
+#ifdef PCI_RES_BUS
+	if (type == PCI_RES_BUS)
+		return (pci_domain_adjust_bus(sc->ap_segment, child, r, start,
+		    end));
+#endif
 	return (pcib_host_res_adjust(&sc->ap_host_res, child, type, r, start,
 	    end));
 }
+
+#ifdef PCI_RES_BUS
+int
+acpi_pcib_acpi_release_resource(device_t dev, device_t child, int type, int rid,
+    struct resource *r)
+{
+	struct acpi_hpcib_softc *sc;
+
+	sc = device_get_softc(dev);
+	if (type == PCI_RES_BUS)
+		return (pci_domain_release_bus(sc->ap_segment, child, rid, r));
+	return (bus_generic_release_resource(dev, child, type, rid, r));
+}
+#endif
 #endif

Modified: head/sys/dev/acpica/acpi_pcib_pci.c
==============================================================================
--- head/sys/dev/acpica/acpi_pcib_pci.c	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/acpica/acpi_pcib_pci.c	Wed Feb 12 04:30:37 2014	(r261790)
@@ -120,7 +120,7 @@ acpi_pcib_pci_attach(device_t dev)
     pcib_attach_common(dev);
     sc = device_get_softc(dev);
     sc->ap_handle = acpi_get_handle(dev);
-    return (acpi_pcib_attach(dev, &sc->ap_prt, sc->ap_pcibsc.secbus));
+    return (acpi_pcib_attach(dev, &sc->ap_prt, sc->ap_pcibsc.bus.sec));
 }
 
 static int

Modified: head/sys/dev/cardbus/cardbus.c
==============================================================================
--- head/sys/dev/cardbus/cardbus.c	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/cardbus/cardbus.c	Wed Feb 12 04:30:37 2014	(r261790)
@@ -96,17 +96,36 @@ static int
 cardbus_attach(device_t cbdev)
 {
 	struct cardbus_softc *sc;
+#ifdef PCI_RES_BUS
+	int rid;
+#endif
 
 	sc = device_get_softc(cbdev);
 	sc->sc_dev = cbdev;
+#ifdef PCI_RES_BUS
+	rid = 0;
+	sc->sc_bus = bus_alloc_resource(cbdev, PCI_RES_BUS, &rid,
+	    pcib_get_bus(cbdev), pcib_get_bus(cbdev), 1, 0);
+	if (sc->sc_bus == NULL) {
+		device_printf(cbdev, "failed to allocate bus number\n");
+		return (ENXIO);
+	}
+#endif
 	return (0);
 }
 
 static int
 cardbus_detach(device_t cbdev)
 {
+#ifdef PCI_RES_BUS
+	struct cardbus_softc *sc;
+#endif
 
 	cardbus_detach_card(cbdev);
+#ifdef PCI_RES_BUS
+	sc = device_get_softc(cbdev);
+	(void)bus_release_resource(cbdev, PCI_RES_BUS, 0, sc->sc_bus);
+#endif
 	return (0);
 }
 

Modified: head/sys/dev/cardbus/cardbusvar.h
==============================================================================
--- head/sys/dev/cardbus/cardbusvar.h	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/cardbus/cardbusvar.h	Wed Feb 12 04:30:37 2014	(r261790)
@@ -69,6 +69,9 @@ struct cardbus_devinfo
 struct cardbus_softc 
 {
 	device_t	sc_dev;
+#ifdef PCI_RES_BUS
+	struct resource *sc_bus;
+#endif
 };
 
 /*

Modified: head/sys/dev/pccbb/pccbb.c
==============================================================================
--- head/sys/dev/pccbb/pccbb.c	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/pccbb/pccbb.c	Wed Feb 12 04:30:37 2014	(r261790)
@@ -97,6 +97,7 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
 
 #include <dev/pccard/pccardreg.h>
 #include <dev/pccard/pccardvar.h>
@@ -1548,7 +1549,7 @@ cbb_read_ivar(device_t brdev, device_t c
 		*result = sc->domain;
 		return (0);
 	case PCIB_IVAR_BUS:
-		*result = sc->secbus;
+		*result = sc->bus.sec;
 		return (0);
 	}
 	return (ENOENT);
@@ -1557,14 +1558,12 @@ cbb_read_ivar(device_t brdev, device_t c
 int
 cbb_write_ivar(device_t brdev, device_t child, int which, uintptr_t value)
 {
-	struct cbb_softc *sc = device_get_softc(brdev);
 
 	switch (which) {
 	case PCIB_IVAR_DOMAIN:
 		return (EINVAL);
 	case PCIB_IVAR_BUS:
-		sc->secbus = value;
-		return (0);
+		return (EINVAL);
 	}
 	return (ENOENT);
 }

Modified: head/sys/dev/pccbb/pccbb_isa.c
==============================================================================
--- head/sys/dev/pccbb/pccbb_isa.c	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/pccbb/pccbb_isa.c	Wed Feb 12 04:30:37 2014	(r261790)
@@ -51,6 +51,9 @@ __FBSDID("$FreeBSD$");
 
 #include <isa/isavar.h>
 
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
+
 #include <dev/pccard/pccardreg.h>
 #include <dev/pccard/pccardvar.h>
 

Modified: head/sys/dev/pccbb/pccbb_pci.c
==============================================================================
--- head/sys/dev/pccbb/pccbb_pci.c	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/pccbb/pccbb_pci.c	Wed Feb 12 04:30:37 2014	(r261790)
@@ -93,6 +93,7 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
 
 #include <dev/pccard/pccardreg.h>
 #include <dev/pccard/pccardvar.h>
@@ -303,13 +304,15 @@ cbb_print_config(device_t dev)
 static int
 cbb_pci_attach(device_t brdev)
 {
+#if !(defined(NEW_PCIB) && defined(PCI_RES_BUS))
 	static int curr_bus_number = 2; /* XXX EVILE BAD (see below) */
+	uint32_t pribus;
+#endif
 	struct cbb_softc *sc = (struct cbb_softc *)device_get_softc(brdev);
 	struct sysctl_ctx_list *sctx;
 	struct sysctl_oid *soid;
 	int rid;
 	device_t parent;
-	uint32_t pribus;
 
 	parent = device_get_parent(brdev);
 	mtx_init(&sc->mtx, device_get_nameunit(brdev), "cbb", MTX_DEF);
@@ -318,9 +321,13 @@ cbb_pci_attach(device_t brdev)
 	sc->cbdev = NULL;
 	sc->exca[0].pccarddev = NULL;
 	sc->domain = pci_get_domain(brdev);
-	sc->secbus = pci_read_config(brdev, PCIR_SECBUS_2, 1);
-	sc->subbus = pci_read_config(brdev, PCIR_SUBBUS_2, 1);
+	sc->bus.sec = pci_read_config(brdev, PCIR_SECBUS_2, 1);
+	sc->bus.sub = pci_read_config(brdev, PCIR_SUBBUS_2, 1);
 	sc->pribus = pcib_get_bus(parent);
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+	pci_write_config(brdev, PCIR_PRIBUS_2, sc->pribus, 1);
+	pcib_setup_secbus(brdev, &sc->bus, 1);
+#endif
 	SLIST_INIT(&sc->rl);
 	cbb_powerstate_d0(brdev);
 
@@ -352,9 +359,9 @@ cbb_pci_attach(device_t brdev)
 	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "pribus",
 	    CTLFLAG_RD, &sc->pribus, 0, "Primary bus number");
 	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "secbus",
-	    CTLFLAG_RD, &sc->secbus, 0, "Secondary bus number");
+	    CTLFLAG_RD, &sc->bus.sec, 0, "Secondary bus number");
 	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "subbus",
-	    CTLFLAG_RD, &sc->subbus, 0, "Subordinate bus number");
+	    CTLFLAG_RD, &sc->bus.sub, 0, "Subordinate bus number");
 #if 0
 	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "memory",
 	    CTLFLAG_RD, &sc->subbus, 0, "Memory window open");
@@ -366,15 +373,16 @@ cbb_pci_attach(device_t brdev)
 	    CTLFLAG_RD, &sc->subbus, 0, "io range 2 open");
 #endif
 
+#if !(defined(NEW_PCIB) && defined(PCI_RES_BUS))
 	/*
 	 * This is a gross hack.  We should be scanning the entire pci
 	 * tree, assigning bus numbers in a way such that we (1) can
 	 * reserve 1 extra bus just in case and (2) all sub busses
 	 * are in an appropriate range.
 	 */
-	DEVPRINTF((brdev, "Secondary bus is %d\n", sc->secbus));
+	DEVPRINTF((brdev, "Secondary bus is %d\n", sc->bus.sec));
 	pribus = pci_read_config(brdev, PCIR_PRIBUS_2, 1);
-	if (sc->secbus == 0 || sc->pribus != pribus) {
+	if (sc->bus.sec == 0 || sc->pribus != pribus) {
 		if (curr_bus_number <= sc->pribus)
 			curr_bus_number = sc->pribus + 1;
 		if (pribus != sc->pribus) {
@@ -382,13 +390,14 @@ cbb_pci_attach(device_t brdev)
 			    sc->pribus));
 			pci_write_config(brdev, PCIR_PRIBUS_2, sc->pribus, 1);
 		}
-		sc->secbus = curr_bus_number++;
-		sc->subbus = curr_bus_number++;
+		sc->bus.sec = curr_bus_number++;
+		sc->bus.sub = curr_bus_number++;
 		DEVPRINTF((brdev, "Secondary bus set to %d subbus %d\n",
-		    sc->secbus, sc->subbus));
-		pci_write_config(brdev, PCIR_SECBUS_2, sc->secbus, 1);
-		pci_write_config(brdev, PCIR_SUBBUS_2, sc->subbus, 1);
+		    sc->bus.sec, sc->bus.sub));
+		pci_write_config(brdev, PCIR_SECBUS_2, sc->bus.sec, 1);
+		pci_write_config(brdev, PCIR_SUBBUS_2, sc->bus.sub, 1);
 	}
+#endif
 
 	/* attach children */
 	sc->cbdev = device_add_child(brdev, "cardbus", -1);
@@ -467,8 +476,8 @@ cbb_chipinit(struct cbb_softc *sc)
 
 	/* Restore bus configuration */
 	pci_write_config(sc->dev, PCIR_PRIBUS_2, sc->pribus, 1);
-	pci_write_config(sc->dev, PCIR_SECBUS_2, sc->secbus, 1);
-	pci_write_config(sc->dev, PCIR_SUBBUS_2, sc->subbus, 1);
+	pci_write_config(sc->dev, PCIR_SECBUS_2, sc->bus.sec, 1);
+	pci_write_config(sc->dev, PCIR_SUBBUS_2, sc->bus.sub, 1);
 
 	/* Enable memory access */
 	pci_enable_busmaster(sc->dev);
@@ -783,6 +792,58 @@ cbb_pci_filt(void *arg)
 	return retval;
 }
 
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+static struct resource *
+cbb_pci_alloc_resource(device_t bus, device_t child, int type, int *rid,
+    u_long start, u_long end, u_long count, u_int flags)
+{
+	struct cbb_softc *sc;
+
+	sc = device_get_softc(bus);
+	if (type == PCI_RES_BUS)
+		return (pcib_alloc_subbus(&sc->bus, child, rid, start, end,
+		    count, flags));
+	return (cbb_alloc_resource(bus, child, type, rid, start, end, count,
+	    flags));
+}
+
+static int
+cbb_pci_adjust_resource(device_t bus, device_t child, int type,
+    struct resource *r, u_long start, u_long end)
+{
+	struct cbb_softc *sc;
+
+	sc = device_get_softc(bus);
+	if (type == PCI_RES_BUS) {
+		if (!rman_is_region_manager(r, &sc->bus.rman))
+			return (EINVAL);
+		return (rman_adjust_resource(r, start, end));
+	}
+	return (bus_generic_adjust_resource(bus, child, type, r, start, end));
+}
+
+static int
+cbb_pci_release_resource(device_t bus, device_t child, int type, int rid,
+    struct resource *r)
+{
+	struct cbb_softc *sc;
+	int error;
+
+	sc = device_get_softc(bus);
+	if (type == PCI_RES_BUS) {
+		if (!rman_is_region_manager(r, &sc->bus.rman))
+			return (EINVAL);
+		if (rman_get_flags(r) & RF_ACTIVE) {
+			error = bus_deactivate_resource(child, type, rid, r);
+			if (error)
+				return (error);
+		}
+		return (rman_release_resource(r));
+	}
+	return (cbb_release_resource(bus, child, type, rid, r));
+}
+#endif
+
 /************************************************************************/
 /* PCI compat methods							*/
 /************************************************************************/
@@ -826,8 +887,14 @@ static device_method_t cbb_methods[] = {
 	/* bus methods */
 	DEVMETHOD(bus_read_ivar,		cbb_read_ivar),
 	DEVMETHOD(bus_write_ivar,		cbb_write_ivar),
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+	DEVMETHOD(bus_alloc_resource,		cbb_pci_alloc_resource),
+	DEVMETHOD(bus_adjust_resource,		cbb_pci_adjust_resource),
+	DEVMETHOD(bus_release_resource,		cbb_pci_release_resource),
+#else
 	DEVMETHOD(bus_alloc_resource,		cbb_alloc_resource),
 	DEVMETHOD(bus_release_resource,		cbb_release_resource),
+#endif
 	DEVMETHOD(bus_activate_resource,	cbb_activate_resource),
 	DEVMETHOD(bus_deactivate_resource,	cbb_deactivate_resource),
 	DEVMETHOD(bus_driver_added,		cbb_driver_added),

Modified: head/sys/dev/pccbb/pccbbvar.h
==============================================================================
--- head/sys/dev/pccbb/pccbbvar.h	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/pccbb/pccbbvar.h	Wed Feb 12 04:30:37 2014	(r261790)
@@ -64,8 +64,7 @@ struct cbb_softc {
 	bus_space_handle_t bsh;
 	uint32_t	domain;
 	unsigned int	pribus;
-	unsigned int	secbus;
-	unsigned int	subbus;
+	struct pcib_secbus bus;
 	struct mtx	mtx;
 	int		cardok;
 	u_int32_t	flags;

Modified: head/sys/dev/pci/pci.c
==============================================================================
--- head/sys/dev/pci/pci.c	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/pci/pci.c	Wed Feb 12 04:30:37 2014	(r261790)
@@ -92,6 +92,9 @@ static int		pci_add_map(device_t bus, de
 			    struct resource_list *rl, int force, int prefetch);
 static int		pci_probe(device_t dev);
 static int		pci_attach(device_t dev);
+#ifdef PCI_RES_BUS
+static int		pci_detach(device_t dev);
+#endif
 static void		pci_load_vendor_data(void);
 static int		pci_describe_parse_line(char **ptr, int *vendor,
 			    int *device, char **desc);
@@ -125,7 +128,11 @@ static device_method_t pci_methods[] = {
 	/* Device interface */
 	DEVMETHOD(device_probe,		pci_probe),
 	DEVMETHOD(device_attach,	pci_attach),
+#ifdef PCI_RES_BUS
+	DEVMETHOD(device_detach,	pci_detach),
+#else
 	DEVMETHOD(device_detach,	bus_generic_detach),
+#endif
 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
 	DEVMETHOD(device_suspend,	pci_suspend),
 	DEVMETHOD(device_resume,	pci_resume),
@@ -337,6 +344,13 @@ TUNABLE_INT("hw.pci.clear_bars", &pci_cl
 SYSCTL_INT(_hw_pci, OID_AUTO, clear_bars, CTLFLAG_RDTUN, &pci_clear_bars, 0,
     "Ignore firmware-assigned resources for BARs.");
 
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+static int pci_clear_buses;
+TUNABLE_INT("hw.pci.clear_buses", &pci_clear_buses);
+SYSCTL_INT(_hw_pci, OID_AUTO, clear_buses, CTLFLAG_RDTUN, &pci_clear_buses, 0,
+    "Ignore firmware-assigned bus numbers.");
+#endif
+
 static int
 pci_has_quirk(uint32_t devid, int quirk)
 {
@@ -3197,6 +3211,164 @@ xhci_early_takeover(device_t self)
 	bus_release_resource(self, SYS_RES_MEMORY, rid, res);
 }
 
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+static void
+pci_reserve_secbus(device_t bus, device_t dev, pcicfgregs *cfg,
+    struct resource_list *rl)
+{
+	struct resource *res;
+	char *cp;
+	u_long start, end, count;
+	int rid, sec_bus, sec_reg, sub_bus, sub_reg, sup_bus;
+
+	switch (cfg->hdrtype & PCIM_HDRTYPE) {
+	case PCIM_HDRTYPE_BRIDGE:
+		sec_reg = PCIR_SECBUS_1;
+		sub_reg = PCIR_SUBBUS_1;
+		break;
+	case PCIM_HDRTYPE_CARDBUS:
+		sec_reg = PCIR_SECBUS_2;
+		sub_reg = PCIR_SUBBUS_2;
+		break;
+	default:
+		return;
+	}
+
+	/*
+	 * If the existing bus range is valid, attempt to reserve it
+	 * from our parent.  If this fails for any reason, clear the
+	 * secbus and subbus registers.
+	 *
+	 * XXX: Should we reset sub_bus to sec_bus if it is < sec_bus?
+	 * This would at least preserve the existing sec_bus if it is
+	 * valid.
+	 */
+	sec_bus = PCI_READ_CONFIG(bus, dev, sec_reg, 1);
+	sub_bus = PCI_READ_CONFIG(bus, dev, sub_reg, 1);
+
+	/* Quirk handling. */
+	switch (pci_get_devid(dev)) {
+	case 0x12258086:		/* Intel 82454KX/GX (Orion) */
+		sup_bus = pci_read_config(dev, 0x41, 1);
+		if (sup_bus != 0xff) {
+			sec_bus = sup_bus + 1;
+			sub_bus = sup_bus + 1;
+			PCI_WRITE_CONFIG(bus, dev, sec_reg, sec_bus, 1);
+			PCI_WRITE_CONFIG(bus, dev, sub_reg, sub_bus, 1);
+		}
+		break;
+
+	case 0x00dd10de:
+		/* Compaq R3000 BIOS sets wrong subordinate bus number. */
+		if ((cp = getenv("smbios.planar.maker")) == NULL)
+			break;
+		if (strncmp(cp, "Compal", 6) != 0) {
+			freeenv(cp);
+			break;
+		}
+		freeenv(cp);
+		if ((cp = getenv("smbios.planar.product")) == NULL)
+			break;
+		if (strncmp(cp, "08A0", 4) != 0) {
+			freeenv(cp);
+			break;
+		}
+		freeenv(cp);
+		if (sub_bus < 0xa) {
+			sub_bus = 0xa;
+			PCI_WRITE_CONFIG(bus, dev, sub_reg, sub_bus, 1);
+		}
+		break;
+	}
+
+	if (bootverbose)
+		printf("\tsecbus=%d, subbus=%d\n", sec_bus, sub_bus);
+	if (sec_bus > 0 && sub_bus >= sec_bus) {
+		start = sec_bus;
+		end = sub_bus;
+		count = end - start + 1;
+
+		resource_list_add(rl, PCI_RES_BUS, 0, 0ul, ~0ul, count);
+
+		/*
+		 * If requested, clear secondary bus registers in
+		 * bridge devices to force a complete renumbering
+		 * rather than reserving the existing range.  However,
+		 * preserve the existing size.
+		 */
+		if (pci_clear_buses)
+			goto clear;
+
+		rid = 0;
+		res = resource_list_reserve(rl, bus, dev, PCI_RES_BUS, &rid,
+		    start, end, count, 0);
+		if (res != NULL)
+			return;
+
+		if (bootverbose)
+			device_printf(bus,
+			    "pci%d:%d:%d:%d secbus failed to allocate\n",
+			    pci_get_domain(dev), pci_get_bus(dev),
+			    pci_get_slot(dev), pci_get_function(dev));
+	}
+
+clear:
+	PCI_WRITE_CONFIG(bus, dev, sec_reg, 0, 1);
+	PCI_WRITE_CONFIG(bus, dev, sub_reg, 0, 1);
+}
+
+static struct resource *
+pci_alloc_secbus(device_t dev, device_t child, int *rid, u_long start,
+    u_long end, u_long count, u_int flags)
+{
+	struct pci_devinfo *dinfo;
+	pcicfgregs *cfg;
+	struct resource_list *rl;
+	struct resource *res;
+	int sec_reg, sub_reg;
+
+	dinfo = device_get_ivars(child);
+	cfg = &dinfo->cfg;
+	rl = &dinfo->resources;
+	switch (cfg->hdrtype & PCIM_HDRTYPE) {
+	case PCIM_HDRTYPE_BRIDGE:
+		sec_reg = PCIR_SECBUS_1;
+		sub_reg = PCIR_SUBBUS_1;
+		break;
+	case PCIM_HDRTYPE_CARDBUS:
+		sec_reg = PCIR_SECBUS_2;
+		sub_reg = PCIR_SUBBUS_2;
+		break;
+	default:
+		return (NULL);
+	}
+
+	if (*rid != 0)
+		return (NULL);
+
+	if (resource_list_find(rl, PCI_RES_BUS, *rid) == NULL)
+		resource_list_add(rl, PCI_RES_BUS, *rid, start, end, count);
+	if (!resource_list_reserved(rl, PCI_RES_BUS, *rid)) {
+		res = resource_list_reserve(rl, dev, child, PCI_RES_BUS, rid,
+		    start, end, count, flags & ~RF_ACTIVE);
+		if (res == NULL) {
+			resource_list_delete(rl, PCI_RES_BUS, *rid);
+			device_printf(child, "allocating %lu bus%s failed\n",
+			    count, count == 1 ? "" : "es");
+			return (NULL);
+		}
+		if (bootverbose)
+			device_printf(child,
+			    "Lazy allocation of %lu bus%s at %lu\n", count,
+			    count == 1 ? "" : "es", rman_get_start(res));
+		PCI_WRITE_CONFIG(dev, child, sec_reg, rman_get_start(res), 1);
+		PCI_WRITE_CONFIG(dev, child, sub_reg, rman_get_end(res), 1);
+	}
+	return (resource_list_alloc(rl, dev, child, PCI_RES_BUS, rid, start,
+	    end, count, flags));
+}
+#endif
+
 void
 pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask)
 {
@@ -3269,6 +3441,14 @@ pci_add_resources(device_t bus, device_t
 		else if (pci_get_progif(dev) == PCIP_SERIALBUS_USB_UHCI)
 			uhci_early_takeover(dev);
 	}
+
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+	/*
+	 * Reserve resources for secondary bus ranges behind bridge
+	 * devices.
+	 */
+	pci_reserve_secbus(bus, dev, cfg, rl);
+#endif
 }
 
 void
@@ -3334,10 +3514,22 @@ pci_attach_common(device_t dev)
 #ifdef PCI_DMA_BOUNDARY
 	int error, tag_valid;
 #endif
+#ifdef PCI_RES_BUS
+	int rid;
+#endif
 
 	sc = device_get_softc(dev);
 	domain = pcib_get_domain(dev);
 	busno = pcib_get_bus(dev);
+#ifdef PCI_RES_BUS
+	rid = 0;
+	sc->sc_bus = bus_alloc_resource(dev, PCI_RES_BUS, &rid, busno, busno,
+	    1, 0);
+	if (sc->sc_bus == NULL) {
+		device_printf(dev, "failed to allocate bus number\n");
+		return (ENXIO);
+	}
+#endif
 	if (bootverbose)
 		device_printf(dev, "domain=%d, physical bus=%d\n",
 		    domain, busno);
@@ -3382,6 +3574,21 @@ pci_attach(device_t dev)
 	return (bus_generic_attach(dev));
 }
 
+#ifdef PCI_RES_BUS
+static int
+pci_detach(device_t dev)
+{
+	struct pci_softc *sc;
+	int error;
+
+	error = bus_generic_detach(dev);
+	if (error)
+		return (error);
+	sc = device_get_softc(dev);
+	return (bus_release_resource(dev, PCI_RES_BUS, 0, sc->sc_bus));
+}
+#endif
+
 static void
 pci_set_power_children(device_t dev, device_t *devlist, int numdevs,
     int state)
@@ -3879,6 +4086,10 @@ pci_child_detached(device_t dev, device_
 		pci_printf(&dinfo->cfg, "Device leaked memory resources\n");
 	if (resource_list_release_active(rl, dev, child, SYS_RES_IOPORT) != 0)
 		pci_printf(&dinfo->cfg, "Device leaked I/O resources\n");
+#ifdef PCI_RES_BUS
+	if (resource_list_release_active(rl, dev, child, PCI_RES_BUS) != 0)
+		pci_printf(&dinfo->cfg, "Device leaked PCI bus numbers\n");
+#endif
 
 	pci_cfg_save(child, dinfo, 1);
 }
@@ -4295,6 +4506,11 @@ pci_alloc_resource(device_t dev, device_
 	rl = &dinfo->resources;
 	cfg = &dinfo->cfg;
 	switch (type) {
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+	case PCI_RES_BUS:
+		return (pci_alloc_secbus(dev, child, rid, start, end, count,
+		    flags));
+#endif
 	case SYS_RES_IRQ:
 		/*
 		 * Can't alloc legacy interrupt once MSI messages have

Modified: head/sys/dev/pci/pci_pci.c
==============================================================================
--- head/sys/dev/pci/pci_pci.c	Wed Feb 12 03:19:35 2014	(r261789)
+++ head/sys/dev/pci/pci_pci.c	Wed Feb 12 04:30:37 2014	(r261790)
@@ -119,6 +119,10 @@ pcib_is_resource_managed(struct pcib_sof
 {
 
 	switch (type) {
+#ifdef PCI_RES_BUS
+	case PCI_RES_BUS:
+		return (rman_is_region_manager(r, &sc->bus.rman));
+#endif
 	case SYS_RES_IOPORT:
 		return (rman_is_region_manager(r, &sc->io.rman));
 	case SYS_RES_MEMORY:
@@ -523,6 +527,173 @@ pcib_probe_windows(struct pcib_softc *sc
 	}
 }
 
+#ifdef PCI_RES_BUS
+/*
+ * Allocate a suitable secondary bus for this bridge if needed and
+ * initialize the resource manager for the secondary bus range.  Note
+ * that the minimum count is a desired value and this may allocate a
+ * smaller range.
+ */
+void
+pcib_setup_secbus(device_t dev, struct pcib_secbus *bus, int min_count)
+{
+	char buf[64];
+	int error, rid;
+
+	switch (pci_read_config(dev, PCIR_HDRTYPE, 1) & PCIM_HDRTYPE) {
+	case PCIM_HDRTYPE_BRIDGE:
+		bus->sub_reg = PCIR_SUBBUS_1;
+		break;
+	case PCIM_HDRTYPE_CARDBUS:
+		bus->sub_reg = PCIR_SUBBUS_2;
+		break;
+	default:
+		panic("not a PCI bridge");
+	}
+	bus->dev = dev;
+	bus->rman.rm_start = 0;
+	bus->rman.rm_end = PCI_BUSMAX;
+	bus->rman.rm_type = RMAN_ARRAY;
+	snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev));
+	bus->rman.rm_descr = strdup(buf, M_DEVBUF);
+	error = rman_init(&bus->rman);
+	if (error)
+		panic("Failed to initialize %s bus number rman",
+		    device_get_nameunit(dev));
+
+	/*
+	 * Allocate a bus range.  This will return an existing bus range
+	 * if one exists, or a new bus range if one does not.
+	 */
+	rid = 0;
+	bus->res = bus_alloc_resource(dev, PCI_RES_BUS, &rid, 0ul, ~0ul,
+	    min_count, 0);
+	if (bus->res == NULL) {
+		/*
+		 * Fall back to just allocating a range of a single bus
+		 * number.
+		 */
+		bus->res = bus_alloc_resource(dev, PCI_RES_BUS, &rid, 0ul, ~0ul,
+		    1, 0);
+	} else if (rman_get_size(bus->res) < min_count)
+		/*
+		 * Attempt to grow the existing range to satisfy the
+		 * minimum desired count.
+		 */
+		(void)bus_adjust_resource(dev, PCI_RES_BUS, bus->res,
+		    rman_get_start(bus->res), rman_get_start(bus->res) +
+		    min_count - 1);
+
+	/*
+	 * Add the initial resource to the rman.
+	 */
+	if (bus->res != NULL) {
+		error = rman_manage_region(&bus->rman, rman_get_start(bus->res),
+		    rman_get_end(bus->res));
+		if (error)
+			panic("Failed to add resource to rman");
+		bus->sec = rman_get_start(bus->res);
+		bus->sub = rman_get_end(bus->res);
+	}
+}
+
+static struct resource *
+pcib_suballoc_bus(struct pcib_secbus *bus, device_t child, int *rid,
+    u_long start, u_long end, u_long count, u_int flags)
+{
+	struct resource *res;
+
+	res = rman_reserve_resource(&bus->rman, start, end, count, flags,
+	    child);
+	if (res == NULL)
+		return (NULL);
+
+	if (bootverbose)
+		device_printf(bus->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_secbus *bus, u_long new_end)
+{
+	u_long old_end;
+	int error;
+
+	old_end = rman_get_end(bus->res);
+	KASSERT(new_end > old_end, ("attempt to shrink subbus"));
+	error = bus_adjust_resource(bus->dev, PCI_RES_BUS, bus->res,
+	    rman_get_start(bus->res), new_end);
+	if (error)
+		return (error);
+	if (bootverbose)
+		device_printf(bus->dev, "grew bus range to %lu-%lu\n",
+		    rman_get_start(bus->res), rman_get_end(bus->res));
+	error = rman_manage_region(&bus->rman, old_end + 1,
+	    rman_get_end(bus->res));
+	if (error)
+		panic("Failed to add resource to rman");
+	bus->sub = rman_get_end(bus->res);
+	pci_write_config(bus->dev, bus->sub_reg, bus->sub, 1);
+	return (0);
+}
+
+struct resource *
+pcib_alloc_subbus(struct pcib_secbus *bus, 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, new_end;
+
+	/*
+	 * First, see if the request can be satisified by the existing
+	 * bus range.
+	 */
+	res = pcib_suballoc_bus(bus, child, rid, start, end, count, flags);
+	if (res != NULL)
+		return (res);
+
+	/*
+	 * Figure out a range to grow the bus range.  First, find the
+	 * first bus number after the last allocated bus in the rman and
+	 * enforce that as a minimum starting point for the range.
+	 */
+	if (rman_last_free_region(&bus->rman, &start_free, &end_free) != 0 ||
+	    end_free != bus->sub)
+		start_free = bus->sub + 1;
+	if (start_free < start)
+		start_free = start;
+	new_end = start_free + count - 1;
+
+	/*
+	 * See if this new range would satisfy the request if it
+	 * succeeds.
+	 */
+	if (new_end > end)
+		return (NULL);
+
+	/* Finally, attempt to grow the existing resource. */
+	if (bootverbose) {
+		device_printf(bus->dev,
+		    "attempting to grow bus range for %lu buses\n", count);
+		printf("\tback candidate range: %lu-%lu\n", start_free,
+		    new_end);
+	}
+	if (pcib_grow_subbus(bus, new_end) == 0)
+		return (pcib_suballoc_bus(bus, child, rid, start, end, count,
+		    flags));
+	return (NULL);
+}
+#endif
+
 #else
 
 /*
@@ -669,8 +840,8 @@ pcib_cfg_save(struct pcib_softc *sc)
 
 	sc->command = pci_read_config(dev, PCIR_COMMAND, 2);
 	sc->pribus = pci_read_config(dev, PCIR_PRIBUS_1, 1);
-	sc->secbus = pci_read_config(dev, PCIR_SECBUS_1, 1);
-	sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1);
+	sc->bus.sec = pci_read_config(dev, PCIR_SECBUS_1, 1);
+	sc->bus.sub = pci_read_config(dev, PCIR_SUBBUS_1, 1);
 	sc->bridgectl = pci_read_config(dev, PCIR_BRIDGECTL_1, 2);
 	sc->seclat = pci_read_config(dev, PCIR_SECLAT_1, 1);
 #ifndef NEW_PCIB
@@ -693,8 +864,8 @@ pcib_cfg_restore(struct pcib_softc *sc)
 
 	pci_write_config(dev, PCIR_COMMAND, sc->command, 2);
 	pci_write_config(dev, PCIR_PRIBUS_1, sc->pribus, 1);
-	pci_write_config(dev, PCIR_SECBUS_1, sc->secbus, 1);
-	pci_write_config(dev, PCIR_SUBBUS_1, sc->subbus, 1);
+	pci_write_config(dev, PCIR_SECBUS_1, sc->bus.sec, 1);
+	pci_write_config(dev, PCIR_SUBBUS_1, sc->bus.sub, 1);
 	pci_write_config(dev, PCIR_BRIDGECTL_1, sc->bridgectl, 2);
 	pci_write_config(dev, PCIR_SECLAT_1, sc->seclat, 1);
 #ifdef NEW_PCIB
@@ -740,6 +911,13 @@ pcib_attach_common(device_t dev)
     pcib_cfg_save(sc);
 
     /*
+     * The primary bus register should always be the bus of the
+     * parent.
+     */
+    sc->pribus = pci_get_bus(dev);
+    pci_write_config(dev, PCIR_PRIBUS_1, sc->pribus, 1);
+
+    /*
      * Setup sysctl reporting nodes
      */
     sctx = device_get_sysctl_ctx(dev);
@@ -749,25 +927,27 @@ pcib_attach_common(device_t dev)
     SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "pribus",
       CTLFLAG_RD, &sc->pribus, 0, "Primary bus number");
     SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "secbus",
-      CTLFLAG_RD, &sc->secbus, 0, "Secondary bus number");
+      CTLFLAG_RD, &sc->bus.sec, 0, "Secondary bus number");
     SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "subbus",
-      CTLFLAG_RD, &sc->subbus, 0, "Subordinate bus number");
+      CTLFLAG_RD, &sc->bus.sub, 0, "Subordinate bus number");
 
     /*
      * Quirk handling.
      */
     switch (pci_get_devid(dev)) {
+#if !defined(NEW_PCIB) && !defined(PCI_RES_BUS)
     case 0x12258086:		/* Intel 82454KX/GX (Orion) */
 	{
 	    uint8_t	supbus;
 
 	    supbus = pci_read_config(dev, 0x41, 1);
 	    if (supbus != 0xff) {
-		sc->secbus = supbus + 1;
-		sc->subbus = supbus + 1;
+		sc->bus.sec = supbus + 1;
+		sc->bus.sub = supbus + 1;
 	    }

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list