/** * Return all quirks known to be applicable to the host bridge. * * If the PCI bridge core has not yet been identified, no core-specific * quirk flags will be returned. This function may be called again to * rediscover applicable quirks after the host bridge core has been * identified. * * @param sc bhndb PCI driver state. * @param id The host bridge core's identification table entry, or NULL * if the host bridge core has not yet been identified. * * @return Returns the set of quirks applicable to the current hardware. */ static uint32_t bhndb_pci_discover_quirks(struct bhndb_pci_softc *sc, const struct bhndb_pci_id *id) { struct bhnd_device_quirk *qt; uint32_t quirks; uint8_t hwrev; quirks = BHNDB_PCI_QUIRK_NONE; /* Determine any device class-specific quirks */ switch (sc->pci_devclass) { case BHND_DEVCLASS_PCI: /* All PCI devices require external clock gating */ sc->quirks |= BHNDB_PCI_QUIRK_EXT_CLOCK_GATING; break; default: break; } // TODO: Additional quirk matching /* Determine any PCI core hwrev-specific device quirks */ if (id != NULL) { hwrev = bhnd_get_hwrev(sc->bhndb.hostb_dev); for (qt = id->quirks; qt->quirks != 0; qt++) { if (bhnd_hwrev_matches(hwrev, &qt->hwrev)) quirks |= qt->quirks; }; } return (quirks); }
/** * Default implementation of BUS_CHILD_PNPINFO_STR(). */ static int bhnd_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) { if (device_get_parent(child) != dev) { return (BUS_CHILD_PNPINFO_STR(device_get_parent(dev), child, buf, buflen)); } snprintf(buf, buflen, "vendor=0x%hx device=0x%hx rev=0x%hhx", bhnd_get_vendor(child), bhnd_get_device(child), bhnd_get_hwrev(child)); return (0); }
/** * Distribute @p clock on backplane. * * @param sc Driver instance state. * @param clock Clock to enable. * * @retval 0 success * @retval ENODEV If @p clock is unsupported, or if the device does not * support dynamic clock control. */ int bhnd_pwrctl_setclk(struct bhnd_pwrctl_softc *sc, bhnd_clock clock) { uint32_t scc; PWRCTL_LOCK_ASSERT(sc, MA_OWNED); /* Is dynamic clock control supported? */ if (PWRCTL_QUIRK(sc, FIXED_CLK)) return (ENODEV); /* Chips with ccrev 10 are EOL and they don't have SYCC_HR used below */ if (bhnd_get_hwrev(sc->chipc_dev) == 10) return (ENODEV); scc = bhnd_bus_read_4(sc->res, CHIPC_PLL_SLOWCLK_CTL); switch (clock) { case BHND_CLOCK_HT: /* fast (pll) clock */ if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { scc &= ~(CHIPC_SCC_XC | CHIPC_SCC_FS | CHIPC_SCC_IP); scc |= CHIPC_SCC_IP; /* force xtal back on before clearing SCC_DYN_XTAL.. */ bhnd_pwrctl_ungate_clock(sc->chipc_dev, BHND_CLOCK_HT); } else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { scc |= CHIPC_SYCC_HR; } else { return (ENODEV); } bhnd_bus_write_4(sc->res, CHIPC_PLL_SLOWCLK_CTL, scc); DELAY(CHIPC_PLL_DELAY); break; case BHND_CLOCK_DYN: /* enable dynamic clock control */ if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { scc &= ~(CHIPC_SCC_FS | CHIPC_SCC_IP | CHIPC_SCC_XC); if ((scc & CHIPC_SCC_SS_MASK) != CHIPC_SCC_SS_XTAL) scc |= CHIPC_SCC_XC; bhnd_bus_write_4(sc->res, CHIPC_PLL_SLOWCLK_CTL, scc); /* for dynamic control, we have to release our xtal_pu * "force on" */ if (scc & CHIPC_SCC_XC) { bhnd_pwrctl_gate_clock(sc->chipc_dev, BHND_CLOCK_HT); } } else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { /* Instaclock */ scc &= ~CHIPC_SYCC_HR; bhnd_bus_write_4(sc->res, CHIPC_SYS_CLK_CTL, scc); } else { return (ENODEV); } break; default: return (ENODEV); } return (0); }