aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/pci/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/pci.c')
-rw-r--r--sys/dev/pci/pci.c216
1 files changed, 216 insertions, 0 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index f1cbe6c1bb10..41bb4b11b1fc 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