/** * 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_resume(device_t dev) { struct bhndb_pci_softc *sc; int error; sc = device_get_softc(dev); /* Enable clocks (if supported by this hardware) */ if ((error = bhndb_enable_pci_clocks(sc->dev))) return (error); /* Perform resume */ return (bhndb_generic_resume(dev)); }
static int bhndb_pci_pwrctl_ungate_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_enable_pci_clocks(sc->dev)); }
/** * Initialize the full bridge configuration. * * This is called during the DEVICE_ATTACH() process by the bridged bhndb(4) * bus, prior to probe/attachment of child cores. * * At this point, we can introspect the enumerated cores, find our host * bridge device, and apply any bridge-level hardware workarounds required * for proper operation of the bridged device cores. */ static int bhndb_pci_init_full_config(device_t dev, device_t child, const struct bhndb_hw_priority *prio_table) { struct bhnd_core_info core; const struct bhndb_pci_id *id; struct bhndb_pci_softc *sc; struct bhndb_region *pcir; bhnd_addr_t pcir_addr; bhnd_size_t pcir_size; int error; sc = device_get_softc(dev); /* Let bhndb perform full discovery and initialization of the * available register windows and bridge resources. */ if ((error = bhndb_generic_init_full_config(dev, child, prio_table))) return (error); /* * Identify our PCI bridge core, its register family, and any * applicable hardware quirks. */ KASSERT(sc->bhndb.hostb_dev, ("missing hostb device\n")); core = bhnd_get_core_info(sc->bhndb.hostb_dev); id = bhndb_pci_find_core_id(&core); if (id == NULL) { device_printf(dev, "%s %s hostb core is not recognized\n", bhnd_vendor_name(core.vendor), bhnd_core_name(&core)); } sc->regfmt = id->regfmt; /* Now that we've identified the PCI bridge core, we can determine the * full set of device quirks */ sc->quirks = bhndb_pci_discover_quirks(sc, id); /* * Determine and save a reference to the bhndb resource and offset * at which the bridge core's device registers are mapped. * * All known bhnd(4) hardware provides a fixed static mapping of * the PCI core's registers. If this changes in the future -- which * is unlikely -- this driver will need to be adjusted to use * dynamic register windows. */ /* Find base address and size of the PCI core's register block. */ error = bhnd_get_region_addr(sc->bhndb.hostb_dev, BHND_PORT_DEVICE, 0, 0, &pcir_addr, &pcir_size); if (error) { device_printf(dev, "failed to locate PCI core registers\n"); return (error); } /* Find the bhndb_region that statically maps this block */ pcir = bhndb_find_resource_region(sc->bhndb.bus_res, pcir_addr, pcir_size); if (pcir == NULL || pcir->static_regwin == NULL) { device_printf(dev, "missing static PCI core register window\n"); return (ENXIO); } /* Save borrowed reference to the mapped PCI core registers */ sc->mem_off = pcir->static_regwin->win_offset; sc->mem_res = bhndb_find_regwin_resource(sc->bhndb.bus_res, pcir->static_regwin); if (sc->mem_res == NULL || !(rman_get_flags(sc->mem_res) & RF_ACTIVE)) { device_printf(dev, "no active resource maps the PCI core register window\n"); return (ENXIO); } /* Configure a direct bhnd_resource wrapper that we can pass to * bhnd_resource APIs */ sc->bhnd_mem_res = (struct bhnd_resource) { .res = sc->mem_res, .direct = true }; /* * Attach MMIO device (if this is a PCIe device), which is used for * access to the PCIe SerDes required by the quirk workarounds. */ if (sc->pci_devclass == BHND_DEVCLASS_PCIE) { sc->mdio = device_add_child(dev, devclass_get_name(bhnd_mdio_pci_devclass), 0); if (sc->mdio == NULL) return (ENXIO); if ((error = device_probe_and_attach(sc->mdio))) { device_printf(dev, "failed to attach MDIO device\n"); return (error); } } /* Apply any early one-time quirk workarounds */ if ((error = bhndb_pci_wars_early_once(sc))) return (error); /* Apply attach-time quirk workarounds, required before the bridged * bhnd(4) bus itself performs a full attach(). */ if ((error = bhndb_pci_wars_hwup(sc))) return (error); return (0); } /** * Apply any hardware workarounds that must be executed prior to attempting * register access on the bridged chipset. * * This must be called very early in attach() or resume(), after the basic * set of applicable device quirks has been determined. */ static int bhndb_pci_wars_register_access(struct bhndb_pci_softc *sc) { int error; if (BHNDB_PCI_QUIRK(sc, EXT_CLOCK_GATING)) { if ((error = bhndb_enable_pci_clocks(sc))) { device_printf(sc->dev, "failed to enable clocks\n"); return (error); } } return (0); }
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); }