summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2014-02-12 04:30:37 +0000
committerjhb <jhb@FreeBSD.org>2014-02-12 04:30:37 +0000
commit6e6e271c34f0bc0881fd2171f9d0709306b2eb8c (patch)
tree4ed418e09d2abca6c5033a32710e2f5170bc5bb4 /sys
parentb3e5336bce93d0b0b1c4b9735fe419d1585ae53e (diff)
downloadFreeBSD-src-6e6e271c34f0bc0881fd2171f9d0709306b2eb8c.zip
FreeBSD-src-6e6e271c34f0bc0881fd2171f9d0709306b2eb8c.tar.gz
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
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/include/resource.h3
-rw-r--r--sys/dev/acpica/acpi_pcib_acpi.c87
-rw-r--r--sys/dev/acpica/acpi_pcib_pci.c2
-rw-r--r--sys/dev/cardbus/cardbus.c19
-rw-r--r--sys/dev/cardbus/cardbusvar.h3
-rw-r--r--sys/dev/pccbb/pccbb.c7
-rw-r--r--sys/dev/pccbb/pccbb_isa.c3
-rw-r--r--sys/dev/pccbb/pccbb_pci.c95
-rw-r--r--sys/dev/pccbb/pccbbvar.h3
-rw-r--r--sys/dev/pci/pci.c216
-rw-r--r--sys/dev/pci/pci_pci.c238
-rw-r--r--sys/dev/pci/pci_private.h3
-rw-r--r--sys/dev/pci/pci_subr.c99
-rw-r--r--sys/dev/pci/pcib_private.h34
-rw-r--r--sys/i386/include/resource.h3
-rw-r--r--sys/sparc64/pci/apb.c12
-rw-r--r--sys/x86/include/legacyvar.h4
-rw-r--r--sys/x86/pci/pci_bus.c38
-rw-r--r--sys/x86/pci/qpi.c20
-rw-r--r--sys/x86/x86/mptable_pci.c13
20 files changed, 835 insertions, 67 deletions
diff --git a/sys/amd64/include/resource.h b/sys/amd64/include/resource.h
index edde5eb..dfcc902 100644
--- a/sys/amd64/include/resource.h
+++ b/sys/amd64/include/resource.h
@@ -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_ */
diff --git a/sys/dev/acpica/acpi_pcib_acpi.c b/sys/dev/acpica/acpi_pcib_acpi.c
index 882834d..545f641 100644
--- a/sys/dev/acpica/acpi_pcib_acpi.c
+++ b/sys/dev/acpica/acpi_pcib_acpi.c
@@ -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_alloc_resource(device_t dev,
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_methods[] = {
#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 *res, void *context)
}
#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 dev, device_t child, int type, int *rid,
#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 dev, device_t child, int type,
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
diff --git a/sys/dev/acpica/acpi_pcib_pci.c b/sys/dev/acpica/acpi_pcib_pci.c
index de26a4a..d3f4cbc 100644
--- a/sys/dev/acpica/acpi_pcib_pci.c
+++ b/sys/dev/acpica/acpi_pcib_pci.c
@@ -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
diff --git a/sys/dev/cardbus/cardbus.c b/sys/dev/cardbus/cardbus.c
index 608c1fe..00d4a19 100644
--- a/sys/dev/cardbus/cardbus.c
+++ b/sys/dev/cardbus/cardbus.c
@@ -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);
}
diff --git a/sys/dev/cardbus/cardbusvar.h b/sys/dev/cardbus/cardbusvar.h
index 6264f69..7730ffd 100644
--- a/sys/dev/cardbus/cardbusvar.h
+++ b/sys/dev/cardbus/cardbusvar.h
@@ -69,6 +69,9 @@ struct cardbus_devinfo
struct cardbus_softc
{
device_t sc_dev;
+#ifdef PCI_RES_BUS
+ struct resource *sc_bus;
+#endif
};
/*
diff --git a/sys/dev/pccbb/pccbb.c b/sys/dev/pccbb/pccbb.c
index 5715df3..092d3ef 100644
--- a/sys/dev/pccbb/pccbb.c
+++ b/sys/dev/pccbb/pccbb.c
@@ -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 child, int which, uintptr_t *result)
*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 child, int which, uintptr_t *result)
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);
}
diff --git a/sys/dev/pccbb/pccbb_isa.c b/sys/dev/pccbb/pccbb_isa.c
index fa990e0..1d0a698 100644
--- a/sys/dev/pccbb/pccbb_isa.c
+++ b/sys/dev/pccbb/pccbb_isa.c
@@ -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>
diff --git a/sys/dev/pccbb/pccbb_pci.c b/sys/dev/pccbb/pccbb_pci.c
index 0b02286..85928f9 100644
--- a/sys/dev/pccbb/pccbb_pci.c
+++ b/sys/dev/pccbb/pccbb_pci.c
@@ -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),
diff --git a/sys/dev/pccbb/pccbbvar.h b/sys/dev/pccbb/pccbbvar.h
index 64e0727..c3e2d24 100644
--- a/sys/dev/pccbb/pccbbvar.h
+++ b/sys/dev/pccbb/pccbbvar.h
@@ -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;
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index f1cbe6c..41bb4b1 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -92,6 +92,9 @@ static int pci_add_map(device_t bus, device_t dev, int reg,
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_clear_bars);
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 dev, int force, uint32_t prefetchmask)
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_t child)
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_t child, int type, int *rid,
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
diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c
index 5fa886e..684e6b7 100644
--- a/sys/dev/pci/pci_pci.c
+++ b/sys/dev/pci/pci_pci.c
@@ -119,6 +119,10 @@ pcib_is_resource_managed(struct pcib_softc *sc, int type, struct resource *r)
{
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;
}
break;
}
+#endif
/*
* The i82380FB mobile docking controller is a PCI-PCI bridge,
@@ -781,6 +961,7 @@ pcib_attach_common(device_t dev)
sc->flags |= PCIB_SUBTRACTIVE;
break;
+#if !defined(NEW_PCIB) && !defined(PCI_RES_BUS)
/* Compaq R3000 BIOS sets wrong subordinate bus number. */
case 0x00dd10de:
{
@@ -800,12 +981,13 @@ pcib_attach_common(device_t dev)
break;
}
freeenv(cp);
- if (sc->subbus < 0xa) {
+ if (sc->bus.sub < 0xa) {
pci_write_config(dev, PCIR_SUBBUS_1, 0xa, 1);
- sc->subbus = pci_read_config(dev, PCIR_SUBBUS_1, 1);
+ sc->bus.sub = pci_read_config(dev, PCIR_SUBBUS_1, 1);
}
break;
}
+#endif
}
if (pci_msi_device_blacklisted(dev))
@@ -827,12 +1009,15 @@ pcib_attach_common(device_t dev)
sc->flags |= PCIB_SUBTRACTIVE;
#ifdef NEW_PCIB
+#ifdef PCI_RES_BUS
+ pcib_setup_secbus(dev, &sc->bus, 1);
+#endif
pcib_probe_windows(sc);
#endif
if (bootverbose) {
device_printf(dev, " domain %d\n", sc->domain);
- device_printf(dev, " secondary bus %d\n", sc->secbus);
- device_printf(dev, " subordinate bus %d\n", sc->subbus);
+ device_printf(dev, " secondary bus %d\n", sc->bus.sec);
+ device_printf(dev, " subordinate bus %d\n", sc->bus.sub);
#ifdef NEW_PCIB
if (pcib_is_window_open(&sc->io))
device_printf(dev, " I/O decode 0x%jx-0x%jx\n",
@@ -873,20 +1058,6 @@ pcib_attach_common(device_t dev)
}
/*
- * 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.
@@ -902,8 +1073,8 @@ pcib_attach(device_t dev)
pcib_attach_common(dev);
sc = device_get_softc(dev);
- if (sc->secbus != 0) {
- child = device_add_child(dev, "pci", sc->secbus);
+ if (sc->bus.sec != 0) {
+ child = device_add_child(dev, "pci", sc->bus.sec);
if (child != NULL)
return(bus_generic_attach(dev));
}
@@ -953,7 +1124,7 @@ pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
*result = sc->domain;
return(0);
case PCIB_IVAR_BUS:
- *result = sc->secbus;
+ *result = sc->bus.sec;
return(0);
}
return(ENOENT);
@@ -962,14 +1133,12 @@ pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
int
pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
{
- struct pcib_softc *sc = device_get_softc(dev);
switch (which) {
case PCIB_IVAR_DOMAIN:
return(EINVAL);
case PCIB_IVAR_BUS:
- sc->secbus = value;
- return(0);
+ return(EINVAL);
}
return(ENOENT);
}
@@ -1379,6 +1548,11 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
}
switch (type) {
+#ifdef PCI_RES_BUS
+ case PCI_RES_BUS:
+ return (pcib_alloc_subbus(&sc->bus, child, rid, start, end,
+ count, flags));
+#endif
case SYS_RES_IOPORT:
if (pcib_is_isa_range(sc, start, end, count))
return (NULL);
diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h
index 1502288..0223ee8 100644
--- a/sys/dev/pci/pci_private.h
+++ b/sys/dev/pci/pci_private.h
@@ -40,6 +40,9 @@ DECLARE_CLASS(pci_driver);
struct pci_softc {
bus_dma_tag_t sc_dma_tag;
+#ifdef PCI_RES_BUS
+ struct resource *sc_bus;
+#endif
};
extern int pci_do_power_resume;
diff --git a/sys/dev/pci/pci_subr.c b/sys/dev/pci/pci_subr.c
index 3d13fb9..5d0db36 100644
--- a/sys/dev/pci/pci_subr.c
+++ b/sys/dev/pci/pci_subr.c
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bus.h>
+#include <sys/malloc.h>
#include <sys/rman.h>
#include <sys/systm.h>
@@ -282,4 +283,102 @@ pcib_host_res_adjust(struct pcib_host_resources *hr, device_t dev, int type,
}
return (ERANGE);
}
+
+#ifdef PCI_RES_BUS
+struct pci_domain {
+ int pd_domain;
+ struct rman pd_bus_rman;
+ TAILQ_ENTRY(pci_domain) pd_link;
+};
+
+static TAILQ_HEAD(, pci_domain) domains = TAILQ_HEAD_INITIALIZER(domains);
+
+/*
+ * Each PCI domain maintains its own resource manager for PCI bus
+ * numbers in that domain. Domain objects are created on first use.
+ * Host to PCI bridge drivers and PCI-PCI bridge drivers should
+ * allocate their bus ranges from their domain.
+ */
+static struct pci_domain *
+pci_find_domain(int domain)
+{
+ struct pci_domain *d;
+ char buf[64];
+ int error;
+
+ TAILQ_FOREACH(d, &domains, pd_link) {
+ if (d->pd_domain == domain)
+ return (d);
+ }
+
+ 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;
+ d->pd_bus_rman.rm_end = PCI_BUSMAX;
+ d->pd_bus_rman.rm_type = RMAN_ARRAY;
+ strcpy((char *)(d + 1), buf);
+ d->pd_bus_rman.rm_descr = (char *)(d + 1);
+ error = rman_init(&d->pd_bus_rman);
+ if (error == 0)
+ error = rman_manage_region(&d->pd_bus_rman, 0, PCI_BUSMAX);
+ if (error)
+ panic("Failed to initialize PCI domain %d rman", domain);
+ TAILQ_INSERT_TAIL(&domains, d, pd_link);
+ return (d);
+}
+
+struct resource *
+pci_domain_alloc_bus(int domain, device_t dev, int *rid, u_long start,
+ u_long end, u_long count, u_int flags)
+{
+ struct pci_domain *d;
+ struct resource *res;
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (NULL);
+ d = pci_find_domain(domain);
+ res = rman_reserve_resource(&d->pd_bus_rman, start, end, count, flags,
+ dev);
+ if (res == NULL)
+ return (NULL);
+
+ rman_set_rid(res, *rid);
+ return (res);
+}
+
+int
+pci_domain_adjust_bus(int domain, device_t dev, struct resource *r,
+ u_long start, u_long end)
+{
+#ifdef INVARIANTS
+ struct pci_domain *d;
+#endif
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (EINVAL);
+#ifdef INVARIANTS
+ d = pci_find_domain(domain);
+ KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
+#endif
+ return (rman_adjust_resource(r, start, end));
+}
+
+int
+pci_domain_release_bus(int domain, device_t dev, int rid, struct resource *r)
+{
+#ifdef INVARIANTS
+ struct pci_domain *d;
+#endif
+
+ if (domain < 0 || domain > PCI_DOMAINMAX)
+ return (EINVAL);
+#ifdef INVARIANTS
+ d = pci_find_domain(domain);
+ KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource"));
+#endif
+ return (rman_release_resource(r));
+}
+#endif /* PCI_RES_BUS */
+
#endif /* NEW_PCIB */
diff --git a/sys/dev/pci/pcib_private.h b/sys/dev/pci/pcib_private.h
index e9d4c4b..e74f149 100644
--- a/sys/dev/pci/pcib_private.h
+++ b/sys/dev/pci/pcib_private.h
@@ -83,6 +83,18 @@ struct pcib_window {
};
#endif
+struct pcib_secbus {
+ u_int sec;
+ u_int sub;
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ device_t dev;
+ struct rman rman;
+ struct resource *res;
+ const char *name;
+ int sub_reg;
+#endif
+};
+
/*
* Bridge-specific data.
*/
@@ -96,8 +108,7 @@ struct pcib_softc
uint16_t command; /* command register */
u_int domain; /* domain number */
u_int pribus; /* primary bus number */
- u_int secbus; /* secondary bus number */
- u_int subbus; /* subordinate bus number */
+ struct pcib_secbus bus; /* secondary bus numbers */
#ifdef NEW_PCIB
struct pcib_window io; /* I/O port window */
struct pcib_window mem; /* memory window */
@@ -117,13 +128,26 @@ struct pcib_softc
typedef uint32_t pci_read_config_fn(int b, int s, int f, int reg, int width);
-#ifdef NEW_PCIB
-const char *pcib_child_name(device_t child);
-#endif
int host_pcib_get_busno(pci_read_config_fn read_config, int bus,
int slot, int func, uint8_t *busnum);
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+struct resource *pci_domain_alloc_bus(int domain, device_t dev, int *rid,
+ u_long start, u_long end, u_long count, u_int flags);
+int pci_domain_adjust_bus(int domain, device_t dev,
+ struct resource *r, u_long start, u_long end);
+int pci_domain_release_bus(int domain, device_t dev, int rid,
+ struct resource *r);
+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);
+void pcib_setup_secbus(device_t dev, struct pcib_secbus *bus,
+ int min_count);
+#endif
int pcib_attach(device_t dev);
void pcib_attach_common(device_t dev);
+#ifdef NEW_PCIB
+const char *pcib_child_name(device_t child);
+#endif
int pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result);
int pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t value);
struct resource *pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
diff --git a/sys/i386/include/resource.h b/sys/i386/include/resource.h
index edde5eb..dfcc902 100644
--- a/sys/i386/include/resource.h
+++ b/sys/i386/include/resource.h
@@ -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_ */
diff --git a/sys/sparc64/pci/apb.c b/sys/sparc64/pci/apb.c
index a6854e9..9b3fa44 100644
--- a/sys/sparc64/pci/apb.c
+++ b/sys/sparc64/pci/apb.c
@@ -177,9 +177,9 @@ apb_attach(device_t dev)
pci_read_config(dev, PCIR_COMMAND, 2);
sc->sc_bsc.ops_pcib_sc.pribus =
pci_read_config(dev, PCIR_PRIBUS_1, 1);
- sc->sc_bsc.ops_pcib_sc.secbus =
+ sc->sc_bsc.ops_pcib_sc.bus.sec =
pci_read_config(dev, PCIR_SECBUS_1, 1);
- sc->sc_bsc.ops_pcib_sc.subbus =
+ sc->sc_bsc.ops_pcib_sc.bus.sub =
pci_read_config(dev, PCIR_SUBBUS_1, 1);
sc->sc_bsc.ops_pcib_sc.bridgectl =
pci_read_config(dev, PCIR_BRIDGECTL_1, 2);
@@ -200,10 +200,10 @@ apb_attach(device_t dev)
CTLFLAG_RD, &sc->sc_bsc.ops_pcib_sc.pribus, 0,
"Primary bus number");
SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "secbus",
- CTLFLAG_RD, &sc->sc_bsc.ops_pcib_sc.secbus, 0,
+ CTLFLAG_RD, &sc->sc_bsc.ops_pcib_sc.bus.sec, 0,
"Secondary bus number");
SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "subbus",
- CTLFLAG_RD, &sc->sc_bsc.ops_pcib_sc.subbus, 0,
+ CTLFLAG_RD, &sc->sc_bsc.ops_pcib_sc.bus.sub, 0,
"Subordinate bus number");
ofw_pcib_gen_setup(dev);
@@ -212,9 +212,9 @@ apb_attach(device_t dev)
device_printf(dev, " domain %d\n",
sc->sc_bsc.ops_pcib_sc.domain);
device_printf(dev, " secondary bus %d\n",
- sc->sc_bsc.ops_pcib_sc.secbus);
+ sc->sc_bsc.ops_pcib_sc.bus.sec);
device_printf(dev, " subordinate bus %d\n",
- sc->sc_bsc.ops_pcib_sc.subbus);
+ sc->sc_bsc.ops_pcib_sc.bus.sub);
device_printf(dev, " I/O decode ");
apb_map_print(sc->sc_iomap, APB_IO_SCALE);
printf("\n");
diff --git a/sys/x86/include/legacyvar.h b/sys/x86/include/legacyvar.h
index 5f2e29c..856b352 100644
--- a/sys/x86/include/legacyvar.h
+++ b/sys/x86/include/legacyvar.h
@@ -57,6 +57,10 @@ int legacy_pcib_write_ivar(device_t dev, device_t child, int which,
uintptr_t value);
struct resource *legacy_pcib_alloc_resource(device_t dev, device_t child,
int type, int *rid, u_long start, u_long end, u_long count, u_int flags);
+int legacy_pcib_adjust_resource(device_t dev, device_t child, int type,
+ struct resource *r, u_long start, u_long end);
+int legacy_pcib_release_resource(device_t dev, device_t child, int type,
+ int rid, struct resource *r);
int legacy_pcib_alloc_msi(device_t pcib, device_t dev, int count,
int maxcount, int *irqs);
int legacy_pcib_alloc_msix(device_t pcib, device_t dev, int *irq);
diff --git a/sys/x86/pci/pci_bus.c b/sys/x86/pci/pci_bus.c
index 53be8c2..88cabc7 100644
--- a/sys/x86/pci/pci_bus.c
+++ b/sys/x86/pci/pci_bus.c
@@ -597,10 +597,37 @@ legacy_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
u_long start, u_long end, u_long count, u_int flags)
{
- start = hostb_alloc_start(type, start, end, count);
- return (bus_generic_alloc_resource(dev, child, type, rid, start, end,
- count, flags));
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ if (type == PCI_RES_BUS)
+ return (pci_domain_alloc_bus(0, child, rid, start, end, count,
+ flags));
+#endif
+ start = hostb_alloc_start(type, start, end, count);
+ return (bus_generic_alloc_resource(dev, child, type, rid, start, end,
+ count, flags));
+}
+
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+int
+legacy_pcib_adjust_resource(device_t dev, device_t child, int type,
+ struct resource *r, u_long start, u_long end)
+{
+
+ if (type == PCI_RES_BUS)
+ return (pci_domain_adjust_bus(0, child, r, start, end));
+ return (bus_generic_adjust_resource(dev, child, type, r, start, end));
+}
+
+int
+legacy_pcib_release_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *r)
+{
+
+ if (type == PCI_RES_BUS)
+ return (pci_domain_release_bus(0, child, rid, r));
+ return (bus_generic_release_resource(dev, child, type, rid, r));
}
+#endif
static device_method_t legacy_pcib_methods[] = {
/* Device interface */
@@ -615,8 +642,13 @@ static device_method_t legacy_pcib_methods[] = {
DEVMETHOD(bus_read_ivar, legacy_pcib_read_ivar),
DEVMETHOD(bus_write_ivar, legacy_pcib_write_ivar),
DEVMETHOD(bus_alloc_resource, legacy_pcib_alloc_resource),
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ DEVMETHOD(bus_adjust_resource, legacy_pcib_adjust_resource),
+ DEVMETHOD(bus_release_resource, legacy_pcib_release_resource),
+#else
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
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),
diff --git a/sys/x86/pci/qpi.c b/sys/x86/pci/qpi.c
index 21b2c43..2f88891 100644
--- a/sys/x86/pci/qpi.c
+++ b/sys/x86/pci/qpi.c
@@ -238,6 +238,20 @@ qpi_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
}
}
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+static struct resource *
+qpi_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+
+ if (type == PCI_RES_BUS)
+ return (pci_domain_alloc_bus(0, child, rid, start, end, count,
+ flags));
+ return (bus_generic_alloc_resource(dev, child, type, rid, start, end,
+ count, flags));
+}
+#endif
+
static int
qpi_pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr,
uint32_t *data)
@@ -258,8 +272,14 @@ static device_method_t qpi_pcib_methods[] = {
/* Bus interface */
DEVMETHOD(bus_read_ivar, qpi_pcib_read_ivar),
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ DEVMETHOD(bus_alloc_resource, qpi_pcib_alloc_resource),
+ DEVMETHOD(bus_adjust_resource, legacy_pcib_adjust_resource),
+ DEVMETHOD(bus_release_resource, legacy_pcib_release_resource),
+#else
DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
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),
diff --git a/sys/x86/x86/mptable_pci.c b/sys/x86/x86/mptable_pci.c
index 2110b10..3e2c1cb 100644
--- a/sys/x86/x86/mptable_pci.c
+++ b/sys/x86/x86/mptable_pci.c
@@ -105,6 +105,11 @@ mptable_hostb_alloc_resource(device_t dev, device_t child, int type, int *rid,
{
struct mptable_hostb_softc *sc;
+#ifdef PCI_RES_BUS
+ if (type == PCI_RES_BUS)
+ return (pci_domain_alloc_bus(0, child, rid, start, end, count,
+ flags));
+#endif
sc = device_get_softc(dev);
if (type == SYS_RES_IOPORT && start + count - 1 == end) {
if (mptable_is_isa_range(start, end)) {
@@ -141,6 +146,10 @@ mptable_hostb_adjust_resource(device_t dev, device_t child, int type,
{
struct mptable_hostb_softc *sc;
+#ifdef PCI_RES_BUS
+ if (type == PCI_RES_BUS)
+ return (pci_domain_adjust_bus(0, child, r, start, end));
+#endif
sc = device_get_softc(dev);
return (pcib_host_res_adjust(&sc->sc_host_res, child, type, r, start,
end));
@@ -165,7 +174,11 @@ static device_method_t mptable_hostb_methods[] = {
DEVMETHOD(bus_alloc_resource, legacy_pcib_alloc_resource),
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource),
#endif
+#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
+ DEVMETHOD(bus_release_resource, legacy_pcib_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),
OpenPOWER on IntegriCloud