/** * Apply any hardware workarounds that are required upon detach or suspend * of the bridge device. */ static int bhndb_pci_wars_hwdown(struct bhndb_pci_softc *sc) { int error; /* Reduce L1 timer for better power savings. * TODO: We could enable/disable this on demand for better power * savings if we tie this to HT clock request handling */ if (BHNDB_PCIE_QUIRK(sc, L1_TIMER_PERF)) { uint32_t pmt; pmt = bhndb_pcie_read_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG); pmt &= ~BHND_PCIE_ASPMTIMER_EXTEND; bhndb_pcie_write_proto_reg(sc, BHND_PCIE_DLLP_PMTHRESHREG, pmt); } /* Disable clocks */ if (BHNDB_PCI_QUIRK(sc, EXT_CLOCK_GATING)) { if ((error = bhndb_disable_pci_clocks(sc))) { device_printf(sc->dev, "failed to disable clocks\n"); return (error); } } return (0); }
static int bhndb_pci_detach(device_t dev) { struct bhndb_pci_softc *sc; int error; sc = device_get_softc(dev); /* Attempt to detach our children */ if ((error = bus_generic_detach(dev))) return (error); /* Perform generic bridge detach */ if ((error = bhndb_generic_detach(dev))) return (error); /* Disable clocks (if required by this hardware) */ if ((error = bhndb_disable_pci_clocks(sc->dev))) return (error); /* Free our interrupt resources */ bhndb_free_intr_isrc(sc->isrc); /* Release MSI interrupts */ if (sc->msi_count > 0) pci_release_msi(sc->parent); /* Disable PCI bus mastering */ pci_disable_busmaster(sc->parent); BHNDB_PCI_LOCK_DESTROY(sc); return (0); }
/** * Default bhndb_pci implementation of device_probe(). * * Verifies that the parent is a PCI/PCIe device. */ static int bhndb_pci_probe(device_t dev) { struct bhndb_pci_probe *probe; struct bhndb_pci_core *entry; bhnd_devclass_t hostb_devclass; device_t parent, parent_bus; devclass_t pci, bus_devclass; int error; probe = NULL; /* Our parent must be a PCI/PCIe device. */ pci = devclass_find("pci"); parent = device_get_parent(dev); parent_bus = device_get_parent(parent); if (parent_bus == NULL) return (ENXIO); /* The bus device class may inherit from 'pci' */ for (bus_devclass = device_get_devclass(parent_bus); bus_devclass != NULL; bus_devclass = devclass_get_parent(bus_devclass)) { if (bus_devclass == pci) break; } if (bus_devclass != pci) return (ENXIO); /* Enable clocks */ if ((error = bhndb_enable_pci_clocks(dev))) return (error); /* Identify the chip and enumerate the bridged cores */ hostb_devclass = bhndb_expected_pci_devclass(dev); if ((error = bhndb_pci_probe_alloc(&probe, dev, hostb_devclass))) goto cleanup; /* Look for a matching core table entry */ if ((entry = bhndb_pci_find_core(&probe->hostb_core)) == NULL) { error = ENXIO; goto cleanup; } device_set_desc(dev, "PCI-BHND bridge"); /* fall-through */ error = BUS_PROBE_DEFAULT; cleanup: if (probe != NULL) bhndb_pci_probe_free(probe); bhndb_disable_pci_clocks(dev); return (error); }
static int bhndb_pci_suspend(device_t dev) { struct bhndb_pci_softc *sc; int error; sc = device_get_softc(dev); /* Disable clocks (if supported by this hardware) */ if ((error = bhndb_disable_pci_clocks(sc->dev))) return (error); /* Perform suspend */ return (bhndb_generic_suspend(dev)); }
static int bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child, bhnd_clock clock) { struct bhndb_pci_softc *sc = device_get_softc(dev); /* Only supported on PCI devices */ if (bhndb_is_pcie_attached(sc->dev)) return (ENODEV); /* Only HT is supported */ if (clock != BHND_CLOCK_HT) return (ENXIO); return (bhndb_disable_pci_clocks(sc->dev)); }
static int bhndb_pci_attach(device_t dev) { struct bhndb_pci_softc *sc; struct bhnd_chipid cid; struct bhnd_core_info *cores, hostb_core; bhnd_erom_class_t *erom_class; struct bhndb_pci_probe *probe; u_int ncores; int irq_rid; int error; sc = device_get_softc(dev); sc->dev = dev; sc->parent = device_get_parent(dev); sc->pci_devclass = bhndb_expected_pci_devclass(dev); sc->pci_quirks = 0; sc->set_regwin = NULL; BHNDB_PCI_LOCK_INIT(sc); probe = NULL; cores = NULL; /* Enable PCI bus mastering */ pci_enable_busmaster(sc->parent); /* Enable clocks (if required by this hardware) */ if ((error = bhndb_enable_pci_clocks(sc->dev))) goto cleanup; /* Identify the chip and enumerate the bridged cores */ error = bhndb_pci_probe_alloc(&probe, dev, sc->pci_devclass); if (error) goto cleanup; sc->pci_quirks = bhndb_pci_get_core_quirks(&probe->cid, &probe->hostb_core); /* Select the appropriate register window handler */ if (probe->cid.chip_type == BHND_CHIPTYPE_SIBA) { sc->set_regwin = bhndb_pci_compat_setregwin; } else { sc->set_regwin = bhndb_pci_fast_setregwin; } /* * Fix up our PCI base address in the SPROM shadow, if necessary. * * This must be done prior to accessing any static register windows * that map the PCI core. */ if ((error = bhndb_pci_srsh_pi_war(sc, probe))) goto cleanup; /* Set up PCI interrupt handling */ if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) { /* MSI uses resource IDs starting at 1 */ irq_rid = 1; device_printf(dev, "Using MSI interrupts on %s\n", device_get_nameunit(sc->parent)); } else { sc->msi_count = 0; irq_rid = 0; device_printf(dev, "Using INTx interrupts on %s\n", device_get_nameunit(sc->parent)); } sc->isrc = bhndb_alloc_intr_isrc(sc->parent, irq_rid, 0, RM_MAX_END, 1, RF_SHAREABLE | RF_ACTIVE); if (sc->isrc == NULL) { device_printf(sc->dev, "failed to allocate interrupt " "resource\n"); error = ENXIO; goto cleanup; } /* * Copy out the probe results and then free our probe state, releasing * its exclusive ownership of host bridge resources. * * This must be done prior to full configuration of the bridge via * bhndb_attach(). */ cid = probe->cid; erom_class = probe->erom_class; hostb_core = probe->hostb_core; error = bhndb_pci_probe_copy_core_table(probe, &cores, &ncores); if (error) { cores = NULL; goto cleanup; } bhndb_pci_probe_free(probe); probe = NULL; /* Perform bridge attach */ error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class); if (error) goto cleanup; /* Add any additional child devices */ if ((error = bhndb_pci_add_children(sc))) goto cleanup; /* Probe and attach our children */ if ((error = bus_generic_attach(dev))) goto cleanup; bhndb_pci_probe_free_core_table(cores); return (0); cleanup: device_delete_children(dev); if (sc->isrc != NULL) bhndb_free_intr_isrc(sc->isrc); if (sc->msi_count > 0) pci_release_msi(sc->parent); if (cores != NULL) bhndb_pci_probe_free_core_table(cores); if (probe != NULL) bhndb_pci_probe_free(probe); bhndb_disable_pci_clocks(sc->dev); pci_disable_busmaster(sc->parent); BHNDB_PCI_LOCK_DESTROY(sc); return (error); }