Index: head/sys/dev/bhnd/bhndb/bhndb_pci.c =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 328070) +++ head/sys/dev/bhnd/bhndb/bhndb_pci.c (revision 328071) @@ -1,1499 +1,1731 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Landon Fuller * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * PCI-specific implementation for the BHNDB bridge driver. * * Provides support for bridging from a PCI parent bus to a BHND-compatible * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point * mode. * * This driver handles all initial generic host-level PCI interactions with a * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4) * bus has been enumerated, this driver works in tandem with a core-specific * bhnd_pci_hostb driver to manage the PCI core. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bhnd_pwrctl_hostb_if.h" #include "bhndb_pcireg.h" #include "bhndb_pcivar.h" #include "bhndb_private.h" struct bhndb_pci_eio; +struct bhndb_pci_probe; static int bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count); -static int bhndb_pci_read_core_table(device_t dev, - struct bhnd_chipid *chipid, - struct bhnd_core_info **cores, u_int *ncores, - bhnd_erom_class_t **eromcls); + static int bhndb_pci_add_children(struct bhndb_pci_softc *sc); static bhnd_devclass_t bhndb_expected_pci_devclass(device_t dev); static bool bhndb_is_pcie_attached(device_t dev); static int bhndb_enable_pci_clocks(device_t dev); static int bhndb_disable_pci_clocks(device_t dev); static int bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev, const struct bhndb_regwin *, bhnd_addr_t); static int bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev, const struct bhndb_regwin *, bhnd_addr_t); static void bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_size_t offset, uint32_t value, u_int width); static uint32_t bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_size_t offset, u_int width); -static void bhndb_init_sromless_pci_config( - struct bhndb_pci_softc *sc); +static int bhndb_pci_srsh_pi_war(struct bhndb_pci_softc *sc, + struct bhndb_pci_probe *probe); static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc); static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc); -static int bhndb_pci_eio_init(struct bhndb_pci_eio *pio, - device_t dev, device_t pci_dev, - struct bhndb_host_resources *hr); +static int bhndb_pci_probe_alloc(struct bhndb_pci_probe **probe, + device_t dev, bhnd_devclass_t pci_devclass); +static void bhndb_pci_probe_free(struct bhndb_pci_probe *probe); + +static int bhndb_pci_probe_copy_core_table( + struct bhndb_pci_probe *probe, + struct bhnd_core_info **cores, u_int *ncores); +static void bhndb_pci_probe_free_core_table( + struct bhnd_core_info *cores); + +static void bhndb_pci_probe_write(struct bhndb_pci_probe *sc, + bhnd_addr_t addr, bhnd_size_t offset, + uint32_t value, u_int width); +static uint32_t bhndb_pci_probe_read(struct bhndb_pci_probe *sc, + bhnd_addr_t addr, bhnd_size_t offset, u_int width); + +static void bhndb_pci_eio_init(struct bhndb_pci_eio *eio, + struct bhndb_pci_probe *probe); static int bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size); static uint32_t bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width); #define BHNDB_PCI_MSI_COUNT 1 static struct bhndb_pci_quirk bhndb_pci_quirks[]; static struct bhndb_pci_quirk bhndb_pcie_quirks[]; static struct bhndb_pci_quirk bhndb_pcie2_quirks[]; static struct bhndb_pci_core bhndb_pci_cores[] = { - BHNDB_PCI_CORE(PCI, BHND_PCI_SRSH_PI_OFFSET, bhndb_pci_quirks), - BHNDB_PCI_CORE(PCIE, BHND_PCIE_SRSH_PI_OFFSET, bhndb_pcie_quirks), - BHNDB_PCI_CORE(PCIE2, BHND_PCIE_SRSH_PI_OFFSET, bhndb_pcie2_quirks), + BHNDB_PCI_CORE(PCI, bhndb_pci_quirks), + BHNDB_PCI_CORE(PCIE, bhndb_pcie_quirks), + BHNDB_PCI_CORE(PCIE2, bhndb_pcie2_quirks), BHNDB_PCI_CORE_END }; /* bhndb_pci erom I/O instance state */ struct bhndb_pci_eio { struct bhnd_erom_io eio; - device_t dev; /**< bridge device */ - device_t pci_dev; /**< parent PCI device */ - struct bhndb_host_resources *hr; /**< borrowed reference to host resources */ - const struct bhndb_regwin *win; /**< mapped register window, or NULL */ - struct resource *res; /**< resource containing the register window, or NULL if no window mapped */ - bhnd_addr_t res_target; /**< current target address (if mapped) */ - bool mapped; /**< true if a valid mapping exists, false otherwise */ bhnd_addr_t addr; /**< mapped address */ bhnd_size_t size; /**< mapped size */ + struct bhndb_pci_probe *probe; /**< borrowed probe reference */ }; +/** + * Provides early bus access to the bridged device's cores and core enumeration + * table. + * + * May be safely used during probe or early device attach, prior to calling + * bhndb_attach(). + */ +struct bhndb_pci_probe { + device_t dev; /**< bridge device */ + device_t pci_dev; /**< parent PCI device */ + struct bhnd_chipid cid; /**< chip identification */ + struct bhnd_core_info hostb_core; /**< PCI bridge core info */ + + struct bhndb_pci_eio erom_io; /**< erom I/O instance */ + bhnd_erom_class_t *erom_class; /**< probed erom class */ + bhnd_erom_t *erom; /**< erom parser */ + struct bhnd_core_info *cores; /**< erom-owned core table */ + u_int ncores; /**< number of cores */ + + const struct bhndb_regwin *m_win; /**< mapped register window, or NULL if no mapping */ + struct resource *m_res; /**< resource containing the register window, or NULL if no window mapped */ + bhnd_addr_t m_target; /**< base address mapped by m_win */ + bhnd_addr_t m_addr; /**< mapped address */ + bhnd_size_t m_size; /**< mapped size */ + bool m_valid; /**< true if a valid mapping exists, false otherwise */ + + struct bhndb_host_resources *hr; /**< backing host resources */ +}; + + static struct bhndb_pci_quirk bhndb_pci_quirks[] = { /* Backplane interrupt flags must be routed via siba-specific * SIBA_CFG0_INTVEC configuration register; the BHNDB_PCI_INT_MASK * PCI configuration register is unsupported. */ {{ BHND_MATCH_CHIP_TYPE (SIBA) }, { BHND_MATCH_CORE_REV (HWREV_LTE(5)) }, BHNDB_PCI_QUIRK_SIBA_INTVEC }, /* All PCI core revisions require the SRSH work-around */ BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR), BHNDB_PCI_QUIRK_END }; static struct bhndb_pci_quirk bhndb_pcie_quirks[] = { /* All PCIe-G1 core revisions require the SRSH work-around */ BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR), BHNDB_PCI_QUIRK_END }; static struct bhndb_pci_quirk bhndb_pcie2_quirks[] = { - /* All PCIe-G2 core revisions require the SRSH work-around */ - BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR), BHNDB_PCI_QUIRK_END }; - /** * Return the device table entry for @p ci, or NULL if none. */ static struct bhndb_pci_core * bhndb_pci_find_core(struct bhnd_core_info *ci) { for (size_t i = 0; !BHNDB_PCI_IS_CORE_END(&bhndb_pci_cores[i]); i++) { struct bhndb_pci_core *entry = &bhndb_pci_cores[i]; if (bhnd_core_matches(ci, &entry->match)) return (entry); } return (NULL); } /** * Return all quirk flags for the given @p cid and @p ci. */ static uint32_t bhndb_pci_get_core_quirks(struct bhnd_chipid *cid, struct bhnd_core_info *ci) { struct bhndb_pci_core *entry; struct bhndb_pci_quirk *qtable; uint32_t quirks; quirks = 0; /* No core entry? */ if ((entry = bhndb_pci_find_core(ci)) == NULL) return (quirks); /* No quirks? */ if ((qtable = entry->quirks) == NULL) return (quirks); for (size_t i = 0; !BHNDB_PCI_IS_QUIRK_END(&qtable[i]); i++) { struct bhndb_pci_quirk *q = &qtable[i]; if (!bhnd_chip_matches(cid, &q->chip_desc)) continue; if (!bhnd_core_matches(ci, &q->core_desc)) continue; quirks |= q->quirks; } return (quirks); } /** * 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 bhnd_chipid cid; - struct bhnd_core_info *cores, hostb_core; + struct bhndb_pci_probe *probe; struct bhndb_pci_core *entry; bhnd_devclass_t hostb_devclass; - u_int ncores; device_t parent; devclass_t parent_bus, pci; int error; - cores = NULL; + probe = NULL; /* Our parent must be a PCI/PCIe device. */ pci = devclass_find("pci"); parent = device_get_parent(dev); parent_bus = device_get_devclass(device_get_parent(parent)); if (parent_bus != pci) return (ENXIO); /* Enable clocks */ if ((error = bhndb_enable_pci_clocks(dev))) return (error); /* Identify the chip and enumerate the bridged cores */ - error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores, NULL); - if (error) - goto cleanup; - - /* Search our core table for the host bridge core */ hostb_devclass = bhndb_expected_pci_devclass(dev); - error = bhndb_find_hostb_core(cores, ncores, hostb_devclass, - &hostb_core); - if (error) + 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(&hostb_core)) == NULL) { + 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); - if (cores != NULL) - free(cores, M_BHND); return (error); } /** * Attempt to allocate MSI interrupts, returning the count in @p msi_count * on success. */ static int bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count) { int error, count; /* Is MSI available? */ if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT) return (ENXIO); /* Allocate expected message count */ count = BHNDB_PCI_MSI_COUNT; if ((error = pci_alloc_msi(sc->parent, &count))) { device_printf(sc->dev, "failed to allocate MSI interrupts: " "%d\n", error); return (error); } if (count < BHNDB_PCI_MSI_COUNT) { pci_release_msi(sc->parent); return (ENXIO); } *msi_count = count; 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; } - /* Enable clocks (if required by this hardware) */ - if ((error = bhndb_enable_pci_clocks(sc->dev))) - 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; - /* Identify the chip and enumerate the bridged cores */ - error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores, - &erom_class); - if (error) + error = bhndb_pci_probe_copy_core_table(probe, &cores, &ncores); + if (error) { + cores = NULL; goto cleanup; - - /* Select the appropriate register window handler */ - if (cid.chip_type == BHND_CHIPTYPE_SIBA) { - sc->set_regwin = bhndb_pci_compat_setregwin; - } else { - sc->set_regwin = bhndb_pci_fast_setregwin; } - /* Determine our host bridge core and populate our quirk flags */ - error = bhndb_find_hostb_core(cores, ncores, sc->pci_devclass, - &hostb_core); - if (error) - goto cleanup; + bhndb_pci_probe_free(probe); + probe = NULL; - sc->pci_quirks = bhndb_pci_get_core_quirks(&cid, &hostb_core); - /* Perform bridge attach */ error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class); if (error) goto cleanup; - /* Fix-up power on defaults for SROM-less devices. */ - bhndb_init_sromless_pci_config(sc); - /* 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; - free(cores, M_BHND); + bhndb_pci_probe_free_core_table(cores); return (0); cleanup: device_delete_children(dev); - bhndb_disable_pci_clocks(sc->dev); if (sc->isrc != NULL) bhndb_free_intr_isrc(sc->isrc); if (sc->msi_count > 0) pci_release_msi(sc->parent); if (cores != NULL) - free(cores, M_BHND); + 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); } 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); } -/** - * Use the generic PCI bridge hardware configuration to enumerate the bridged - * bhnd(4) bus' core table. - * - * @note This function may be safely called prior to device attach, (e.g. - * from DEVICE_PROBE). - * @note This function requires exclusive ownership over allocating and - * configuring host bridge resources, and should only be called prior to - * completion of device attach and full configuration of the bridge. - * - * @param dev The bhndb_pci bridge device. - * @param[out] chipid On success, the parsed chip identification. - * @param[out] cores On success, the enumerated core table. The - * caller is responsible for freeing this table via - * bhndb_pci_free_core_table(). - * @param[out] ncores On success, the number of cores found in - * @p cores. - * @param[out] eromcls On success, a pointer to the erom class used to - * parse the device enumeration table. This - * argument may be NULL if the class is not - * desired. - * - * @retval 0 success - * @retval non-zero if enumerating the bridged bhnd(4) bus fails, a regular - * unix error code will be returned. - */ static int -bhndb_pci_read_core_table(device_t dev, struct bhnd_chipid *chipid, - struct bhnd_core_info **cores, u_int *ncores, - bhnd_erom_class_t **eromcls) -{ - const struct bhndb_hwcfg *cfg; - struct bhndb_host_resources *hr; - struct bhndb_pci_eio pio; - struct bhnd_core_info *erom_cores; - const struct bhnd_chipid *hint; - struct bhnd_chipid cid; - bhnd_erom_class_t *erom_class; - bhnd_erom_t *erom; - device_t parent_dev; - u_int erom_ncores; - int error; - - parent_dev = device_get_parent(dev); - erom = NULL; - erom_cores = NULL; - - /* Fetch our chipid hint (if any) and generic hardware configuration */ - cfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev); - hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev); - - /* Allocate our host resources */ - if ((error = bhndb_alloc_host_resources(&hr, dev, parent_dev, cfg))) - return (error); - - /* Initialize our erom I/O state */ - if ((error = bhndb_pci_eio_init(&pio, dev, parent_dev, hr))) - goto failed; - - /* Map the first bus core from our bridged bhnd(4) bus */ - error = bhndb_pci_eio_map(&pio.eio, BHND_DEFAULT_CHIPC_ADDR, - BHND_DEFAULT_CORE_SIZE); - if (error) - goto failed; - - /* Probe for a usable EROM class, and read the chip identifier */ - erom_class = bhnd_erom_probe_driver_classes(device_get_devclass(dev), - &pio.eio, hint, &cid); - if (erom_class == NULL) { - device_printf(dev, "device enumeration unsupported; no " - "compatible driver found\n"); - - error = ENXIO; - goto failed; - } - - /* Allocate EROM parser */ - if ((erom = bhnd_erom_alloc(erom_class, &cid, &pio.eio)) == NULL) { - device_printf(dev, "failed to allocate device enumeration " - "table parser\n"); - error = ENXIO; - goto failed; - } - - /* Read the full core table */ - error = bhnd_erom_get_core_table(erom, &erom_cores, &erom_ncores); - if (error) { - device_printf(dev, "error fetching core table: %d\n", error); - goto failed; - } - - /* Provide the results to our caller */ - *cores = malloc(sizeof(erom_cores[0]) * erom_ncores, M_BHND, M_WAITOK); - memcpy(*cores, erom_cores, sizeof(erom_cores[0]) * erom_ncores); - *ncores = erom_ncores; - - *chipid = cid; - if (eromcls != NULL) - *eromcls = erom_class; - - /* Clean up */ - bhnd_erom_free_core_table(erom, erom_cores); - bhnd_erom_free(erom); - bhndb_release_host_resources(hr); - - return (0); - -failed: - if (erom_cores != NULL) - bhnd_erom_free_core_table(erom, erom_cores); - - if (erom != NULL) - bhnd_erom_free(erom); - - bhndb_release_host_resources(hr); - return (error); -} - -static int bhndb_pci_add_children(struct bhndb_pci_softc *sc) { bus_size_t nv_sz; int error; /** * If SPROM is mapped directly into BAR0, add child NVRAM * device. */ nv_sz = bhndb_pci_sprom_size(sc); if (nv_sz > 0) { struct bhndb_devinfo *dinfo; device_t child; if (bootverbose) { device_printf(sc->dev, "found SPROM (%ju bytes)\n", (uintmax_t)nv_sz); } /* Add sprom device, ordered early enough to be available * before the bridged bhnd(4) bus is attached. */ child = BUS_ADD_CHILD(sc->dev, BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1); if (child == NULL) { device_printf(sc->dev, "failed to add sprom device\n"); return (ENXIO); } /* Initialize device address space and resource covering the * BAR0 SPROM shadow. */ dinfo = device_get_ivars(child); dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; error = bus_set_resource(child, SYS_RES_MEMORY, 0, bhndb_pci_sprom_addr(sc), nv_sz); if (error) { device_printf(sc->dev, "failed to register sprom resources\n"); return (error); } } return (0); } static const struct bhndb_regwin * bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc) { struct bhndb_resources *bres; const struct bhndb_hwcfg *cfg; const struct bhndb_regwin *sprom_win; bres = sc->bhndb.bus_res; cfg = bres->cfg; sprom_win = bhndb_regwin_find_type(cfg->register_windows, BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE); return (sprom_win); } static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc) { const struct bhndb_regwin *sprom_win; struct resource *r; /* Fetch the SPROM register window */ sprom_win = bhndb_pci_sprom_regwin(sc); KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+")); /* Fetch the associated resource */ r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win); KASSERT(r != NULL, ("missing resource for sprom window\n")); return (rman_get_start(r) + sprom_win->win_offset); } static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc) { const struct bhndb_regwin *sprom_win; uint32_t sctl; bus_size_t sprom_sz; sprom_win = bhndb_pci_sprom_regwin(sc); /* PCI_V2 and later devices map SPROM/OTP via ChipCommon */ if (sprom_win == NULL) return (0); /* Determine SPROM size */ sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4); if (sctl & BHNDB_PCI_SPROM_BLANK) return (0); switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) { case BHNDB_PCI_SPROM_SZ_1KB: sprom_sz = (1 * 1024); break; case BHNDB_PCI_SPROM_SZ_4KB: sprom_sz = (4 * 1024); break; case BHNDB_PCI_SPROM_SZ_16KB: sprom_sz = (16 * 1024); break; case BHNDB_PCI_SPROM_SZ_RESERVED: default: device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl); return (0); } if (sprom_sz > sprom_win->win_size) { device_printf(sc->dev, "PCI sprom size (0x%x) overruns defined register window\n", sctl); return (0); } return (sprom_sz); } /** * Return the host resource providing a static mapping of the PCI core's * registers. * * @param sc bhndb PCI driver state. * @param offset The required readable offset within the PCI core * register block. * @param size The required readable size at @p offset. * @param[out] res On success, the host resource containing our PCI * core's register window. * @param[out] res_offset On success, the @p offset relative to @p res. * * @retval 0 success * @retval ENXIO if a valid static register window mapping the PCI core * registers is not available. */ static int bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, bus_size_t offset, bus_size_t size, struct resource **res, bus_size_t *res_offset) { const struct bhndb_regwin *win; struct resource *r; /* Locate the static register window mapping the requested offset */ win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows, sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0, offset, size); if (win == NULL) { device_printf(sc->dev, "missing PCI core register window\n"); return (ENXIO); } /* Fetch the resource containing the register window */ r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, win); if (r == NULL) { device_printf(sc->dev, "missing PCI core register resource\n"); return (ENXIO); } KASSERT(offset >= win->d.core.offset, ("offset %#jx outside of " "register window", (uintmax_t)offset)); *res = r; *res_offset = win->win_offset + (offset - win->d.core.offset); return (0); } /** * Write a 1, 2, or 4 byte data item to the PCI core's registers at @p offset. * * @param sc bhndb PCI driver state. * @param offset register write offset. * @param value value to be written. * @param width item width (1, 2, or 4 bytes). */ static void bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_size_t offset, uint32_t value, u_int width) { struct resource *r; bus_size_t r_offset; int error; error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset); if (error) { panic("no PCI register window mapping %#jx+%#x: %d", (uintmax_t)offset, width, error); } switch (width) { case 1: bus_write_1(r, r_offset, value); break; case 2: bus_write_2(r, r_offset, value); break; case 4: bus_write_4(r, r_offset, value); break; default: panic("invalid width: %u", width); } } /** * Read a 1, 2, or 4 byte data item from the PCI core's registers * at @p offset. * * @param sc bhndb PCI driver state. * @param offset register read offset. * @param width item width (1, 2, or 4 bytes). */ static uint32_t bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_size_t offset, u_int width) { struct resource *r; bus_size_t r_offset; int error; error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset); if (error) { panic("no PCI register window mapping %#jx+%#x: %d", (uintmax_t)offset, width, error); } switch (width) { case 1: return (bus_read_1(r, r_offset)); case 2: return (bus_read_2(r, r_offset)); case 4: return (bus_read_4(r, r_offset)); default: panic("invalid width: %u", width); } } -/* - * On devices without a SROM, the PCI(e) cores will be initialized with - * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows - * mapped to the wrong core. +/** + * Fix-up power on defaults for SPROM-less devices. + * + * On SPROM-less devices, the PCI(e) cores will be initialized with their their + * Power-on-Reset defaults; this can leave the BHND_PCI_SRSH_PI value pointing + * to the wrong backplane address. This value is used by the PCI core when + * performing address translation between static register windows in BAR0 that + * map the PCI core's register block, and backplane address space. + * + * When translating accesses via these BAR0 regions, the PCI bridge determines + * the base address of the PCI core by concatenating: + * + * [bits] [source] + * 31:16 bits [31:16] of the enumeration space address (e.g. 0x18000000) + * 15:12 value of BHND_PCI_SRSH_PI from the PCI core's SPROM shadow + * 11:0 bits [11:0] of the PCI bus address + * + * For example, on a PCI_V0 device, the following PCI core register offsets are + * mapped into BAR0: + * + * [BAR0 offset] [description] [PCI core offset] + * 0x1000-0x17FF sprom shadow 0x800-0xFFF + * 0x1800-0x1DFF device registers 0x000-0x5FF + * 0x1E00+0x1FFF siba config registers 0xE00-0xFFF + * + * This function checks -- and if necessary, corrects -- the BHND_PCI_SRSH_PI + * value in the SPROM shadow. + * + * This workaround must applied prior to accessing any static register windows + * that map the PCI core. * - * This function updates the SROM shadow to point the BAR0 windows at the - * current PCI core. - * - * Applies to all PCI/PCIe revisions. + * Applies to all PCI and PCIe-G1 core revisions. */ -static void -bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc) +static int +bhndb_pci_srsh_pi_war(struct bhndb_pci_softc *sc, + struct bhndb_pci_probe *probe) { - const struct bhndb_pci_core *pci_core; - bus_size_t srsh_offset; - u_int pci_cidx, sprom_cidx; - uint16_t val; + struct bhnd_core_match md; + bhnd_addr_t pci_addr; + bhnd_size_t pci_size; + bus_size_t srsh_offset; + uint16_t srsh_val, pci_val; + uint16_t val; + int error; if ((sc->pci_quirks & BHNDB_PCI_QUIRK_SRSH_WAR) == 0) - return; + return (0); - /* Determine the correct register offset for our PCI core */ - pci_core = bhndb_pci_find_core(&sc->bhndb.bridge_core); - KASSERT(pci_core != NULL, ("missing core table entry")); + /* Use an equality match descriptor to look up our PCI core's base + * address in the EROM */ + md = bhnd_core_get_match_desc(&probe->hostb_core); + error = bhnd_erom_lookup_core_addr(probe->erom, &md, BHND_PORT_DEVICE, + 0, 0, NULL, &pci_addr, &pci_size); + if (error) { + device_printf(sc->dev, "no base address found for the PCI host " + "bridge core: %d\n", error); + return (error); + } - srsh_offset = pci_core->srsh_offset; + /* Fetch the SPROM SRSH_PI value */ + srsh_offset = BHND_PCI_SPROM_SHADOW + BHND_PCI_SRSH_PI_OFFSET; + val = bhndb_pci_probe_read(probe, pci_addr, srsh_offset, sizeof(val)); + srsh_val = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; - /* Fetch the SPROM's configured core index */ - val = bhndb_pci_read_core(sc, srsh_offset, sizeof(val)); - sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; - - /* If it doesn't match host bridge's core index, update the index - * value */ - pci_cidx = sc->bhndb.bridge_core.core_idx; - if (sprom_cidx != pci_cidx) { + /* If it doesn't match PCI core's base address, update the SPROM + * shadow */ + pci_val = (pci_addr & BHND_PCI_SRSH_PI_ADDR_MASK) >> + BHND_PCI_SRSH_PI_ADDR_SHIFT; + if (srsh_val != pci_val) { val &= ~BHND_PCI_SRSH_PI_MASK; - val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT); - bhndb_pci_write_core(sc, srsh_offset, val, sizeof(val)); + val |= (pci_val << BHND_PCI_SRSH_PI_SHIFT); + bhndb_pci_probe_write(probe, pci_addr, srsh_offset, val, + sizeof(val)); } + + return (0); } 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_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_set_window_addr(device_t dev, const struct bhndb_regwin *rw, bhnd_addr_t addr) { struct bhndb_pci_softc *sc = device_get_softc(dev); return (sc->set_regwin(sc->dev, sc->parent, rw, addr)); } /** * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. * * On siba(4) devices, it's possible that writing a PCI window register may * not succeed; it's necessary to immediately read the configuration register * and retry if not set to the desired value. * * This is not necessary on bcma(4) devices, but other than the overhead of * validating the register, there's no harm in performing the verification. */ static int bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev, const struct bhndb_regwin *rw, bhnd_addr_t addr) { int error; int reg; if (rw->win_type != BHNDB_REGWIN_T_DYN) return (ENODEV); reg = rw->d.dyn.cfg_offset; for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { if ((error = bhndb_pci_fast_setregwin(dev, pci_dev, rw, addr))) return (error); if (pci_read_config(pci_dev, reg, 4) == addr) return (0); DELAY(10); } /* Unable to set window */ return (ENODEV); } /** * A bcma(4)-only bhndb_set_window_addr implementation. */ static int bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev, const struct bhndb_regwin *rw, bhnd_addr_t addr) { /* The PCI bridge core only supports 32-bit addressing, regardless * of the bus' support for 64-bit addressing */ if (addr > UINT32_MAX) return (ERANGE); switch (rw->win_type) { case BHNDB_REGWIN_T_DYN: /* Addresses must be page aligned */ if (addr % rw->win_size != 0) return (EINVAL); pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4); break; default: return (ENODEV); } return (0); } static int bhndb_pci_populate_board_info(device_t dev, device_t child, struct bhnd_board_info *info) { struct bhndb_pci_softc *sc; sc = device_get_softc(dev); /* * On a subset of Apple BCM4360 modules, always prefer the * PCI subdevice to the SPROM-supplied boardtype. * * TODO: * * Broadcom's own drivers implement this override, and then later use * the remapped BCM4360 board type to determine the required * board-specific workarounds. * * Without access to this hardware, it's unclear why this mapping * is done, and we must do the same. If we can survey the hardware * in question, it may be possible to replace this behavior with * explicit references to the SPROM-supplied boardtype(s) in our * quirk definitions. */ if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) { switch (info->board_type) { case BHND_BOARD_BCM94360X29C: case BHND_BOARD_BCM94360X29CP2: case BHND_BOARD_BCM94360X51: case BHND_BOARD_BCM94360X51P2: info->board_type = 0; /* allow override below */ break; default: break; } } /* If NVRAM did not supply vendor/type/devid info, provide the PCI * subvendor/subdevice/device values. */ if (info->board_vendor == 0) info->board_vendor = pci_get_subvendor(sc->parent); if (info->board_type == 0) info->board_type = pci_get_subdevice(sc->parent); if (info->board_devid == 0) info->board_devid = pci_get_device(sc->parent); return (0); } /** * Examine the bridge device @p dev and return the expected host bridge * device class. * * @param dev The bhndb bridge device */ static bhnd_devclass_t bhndb_expected_pci_devclass(device_t dev) { if (bhndb_is_pcie_attached(dev)) return (BHND_DEVCLASS_PCIE); else return (BHND_DEVCLASS_PCI); } /** * Return true if the bridge device @p dev is attached via PCIe, * false otherwise. * * @param dev The bhndb bridge device */ static bool bhndb_is_pcie_attached(device_t dev) { int reg; if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, ®) == 0) return (true); return (false); } /** * Enable externally managed clocks, if required. * * Some PCI chipsets (BCM4306, possibly others) chips do not support * the idle low-power clock. Clocking must be bootstrapped at * attach/resume by directly adjusting GPIO registers exposed in the * PCI config space, and correspondingly, explicitly shutdown at * detach/suspend. * * @note This function may be safely called prior to device attach, (e.g. * from DEVICE_PROBE). * * @param dev The bhndb bridge device */ static int bhndb_enable_pci_clocks(device_t dev) { device_t pci_dev; uint32_t gpio_in, gpio_out, gpio_en; uint32_t gpio_flags; uint16_t pci_status; pci_dev = device_get_parent(dev); /* Only supported and required on PCI devices */ if (bhndb_is_pcie_attached(dev)) return (0); /* Read state of XTAL pin */ gpio_in = pci_read_config(pci_dev, BHNDB_PCI_GPIO_IN, 4); if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) return (0); /* already enabled */ /* Fetch current config */ gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4); gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4); /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); gpio_out |= gpio_flags; gpio_en |= gpio_flags; pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); DELAY(1000); /* Reset PLL_OFF */ gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); DELAY(5000); /* Clear any PCI 'sent target-abort' flag. */ pci_status = pci_read_config(pci_dev, PCIR_STATUS, 2); pci_status &= ~PCIM_STATUS_STABORT; pci_write_config(pci_dev, PCIR_STATUS, pci_status, 2); return (0); } /** * Disable externally managed clocks, if required. * * This function may be safely called prior to device attach, (e.g. * from DEVICE_PROBE). * * @param dev The bhndb bridge device */ static int bhndb_disable_pci_clocks(device_t dev) { device_t pci_dev; uint32_t gpio_out, gpio_en; pci_dev = device_get_parent(dev); /* Only supported and required on PCI devices */ if (bhndb_is_pcie_attached(dev)) return (0); /* Fetch current config */ gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4); gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4); /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); /* Enable both output pins */ gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); return (0); } static bhnd_clksrc bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child, bhnd_clock clock) { struct bhndb_pci_softc *sc; uint32_t gpio_out; sc = device_get_softc(dev); /* Only supported on PCI devices */ if (bhndb_is_pcie_attached(sc->dev)) return (BHND_CLKSRC_UNKNOWN); /* Only ILP is supported */ if (clock != BHND_CLOCK_ILP) return (BHND_CLKSRC_UNKNOWN); gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); if (gpio_out & BHNDB_PCI_GPIO_SCS) return (BHND_CLKSRC_PCI); else return (BHND_CLKSRC_XTAL); } 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_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)); } /** * BHNDB_MAP_INTR_ISRC() */ static int bhndb_pci_map_intr_isrc(device_t dev, struct resource *irq, struct bhndb_intr_isrc **isrc) { struct bhndb_pci_softc *sc = device_get_softc(dev); /* There's only one bridged interrupt to choose from */ *isrc = sc->isrc; return (0); } /* siba-specific implementation of BHNDB_ROUTE_INTERRUPTS() */ static int bhndb_pci_route_siba_interrupts(struct bhndb_pci_softc *sc, device_t child) { uint32_t sbintvec; u_int ivec; int error; KASSERT(sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC, ("route_siba_interrupts not supported by this hardware")); /* Fetch the sbflag# for the child */ if ((error = bhnd_get_intr_ivec(child, 0, &ivec))) return (error); if (ivec > (sizeof(sbintvec)*8) - 1 /* aka '31' */) { /* This should never be an issue in practice */ device_printf(sc->dev, "cannot route interrupts to high " "sbflag# %u\n", ivec); return (ENXIO); } BHNDB_PCI_LOCK(sc); sbintvec = bhndb_pci_read_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), 4); sbintvec |= (1 << ivec); bhndb_pci_write_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), sbintvec, 4); BHNDB_PCI_UNLOCK(sc); return (0); } /* BHNDB_ROUTE_INTERRUPTS() */ static int bhndb_pci_route_interrupts(device_t dev, device_t child) { struct bhndb_pci_softc *sc; struct bhnd_core_info core; uint32_t core_bit; uint32_t intmask; sc = device_get_softc(dev); if (sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC) return (bhndb_pci_route_siba_interrupts(sc, child)); core = bhnd_get_core_info(child); if (core.core_idx > BHNDB_PCI_SBIM_COREIDX_MAX) { /* This should never be an issue in practice */ device_printf(dev, "cannot route interrupts to high core " "index %u\n", core.core_idx); return (ENXIO); } BHNDB_PCI_LOCK(sc); core_bit = (1<parent, BHNDB_PCI_INT_MASK, 4); intmask |= core_bit; pci_write_config(sc->parent, BHNDB_PCI_INT_MASK, intmask, 4); BHNDB_PCI_UNLOCK(sc); return (0); } /** - * Initialize a new bhndb PCI bridge EROM I/O instance. This EROM I/O - * implementation supports mapping of the device enumeration table via the - * @p hr host resources. + * Using the generic PCI bridge hardware configuration, allocate, initialize + * and return a new bhndb_pci probe state instance. * - * @param pio The instance to be initialized. - * @param dev The bridge device. - * @param pci_dev The bridge's parent PCI device. - * @param hr The host resources to be used to map the device - * enumeration table. + * On success, the caller assumes ownership of the returned probe instance, and + * is responsible for releasing this reference using bhndb_pci_probe_free(). + * + * @param[out] probe On success, the newly allocated probe instance. + * @param dev The bhndb_pci bridge device. + * @param hostb_devclass The expected device class of the bridge core. + * + * @retval 0 success + * @retval non-zero if allocating the probe state fails, a regular + * unix error code will be returned. + * + * @note This function requires exclusive ownership over allocating and + * configuring host bridge resources, and should only be called prior to + * completion of device attach and full configuration of the bridge. */ static int -bhndb_pci_eio_init(struct bhndb_pci_eio *pio, device_t dev, device_t pci_dev, - struct bhndb_host_resources *hr) +bhndb_pci_probe_alloc(struct bhndb_pci_probe **probe, device_t dev, + bhnd_devclass_t hostb_devclass) { - memset(&pio->eio, 0, sizeof(pio->eio)); - pio->eio.map = bhndb_pci_eio_map; - pio->eio.read = bhndb_pci_eio_read; - pio->eio.fini = NULL; + struct bhndb_pci_probe *p; + struct bhnd_erom_io *eio; + const struct bhndb_hwcfg *hwcfg; + const struct bhnd_chipid *hint; + device_t parent_dev; + int error; - pio->dev = dev; - pio->pci_dev = pci_dev; - pio->hr = hr; - pio->win = NULL; - pio->res = NULL; + parent_dev = device_get_parent(dev); + eio = NULL; + p = malloc(sizeof(*p), M_BHND, M_ZERO|M_WAITOK); + p->dev = dev; + p->pci_dev = parent_dev; + + /* Our register window mapping state must be initialized at this point, + * as bhndb_pci_eio will begin making calls into + * bhndb_pci_probe_(read|write|get_mapping) */ + p->m_win = NULL; + p->m_res = NULL; + p->m_valid = false; + + bhndb_pci_eio_init(&p->erom_io, p); + eio = &p->erom_io.eio; + + /* Fetch our chipid hint (if any) and generic hardware configuration */ + hwcfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev); + hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev); + + /* Allocate our host resources */ + error = bhndb_alloc_host_resources(&p->hr, dev, parent_dev, hwcfg); + if (error) { + p->hr = NULL; + goto failed; + } + + /* Map the first bus core from our bridged bhnd(4) bus */ + error = bhnd_erom_io_map(eio, BHND_DEFAULT_CHIPC_ADDR, + BHND_DEFAULT_CORE_SIZE); + if (error) + goto failed; + + /* Probe for a usable EROM class, and read the chip identifier */ + p->erom_class = bhnd_erom_probe_driver_classes( + device_get_devclass(dev), eio, hint, &p->cid); + if (p->erom_class == NULL) { + device_printf(dev, "device enumeration unsupported; no " + "compatible driver found\n"); + + error = ENXIO; + goto failed; + } + + /* Allocate EROM parser */ + p->erom = bhnd_erom_alloc(p->erom_class, &p->cid, eio); + if (p->erom == NULL) { + device_printf(dev, "failed to allocate device enumeration " + "table parser\n"); + error = ENXIO; + goto failed; + } + + /* The EROM I/O instance is now owned by our EROM parser */ + eio = NULL; + + /* Read the full core table */ + error = bhnd_erom_get_core_table(p->erom, &p->cores, &p->ncores); + if (error) { + device_printf(p->dev, "error fetching core table: %d\n", + error); + + p->cores = NULL; + goto failed; + } + + /* Identify the host bridge core */ + error = bhndb_find_hostb_core(p->cores, p->ncores, hostb_devclass, + &p->hostb_core); + if (error) { + device_printf(dev, "failed to identify the host bridge " + "core: %d\n", error); + + goto failed; + } + + *probe = p; return (0); + +failed: + if (eio != NULL) { + KASSERT(p->erom == NULL, ("I/O instance will be freed by " + "its owning parser")); + + bhnd_erom_io_fini(eio); + } + + if (p->erom != NULL) { + if (p->cores != NULL) + bhnd_erom_free_core_table(p->erom, p->cores); + + bhnd_erom_free(p->erom); + } else { + KASSERT(p->cores == NULL, ("cannot free erom-owned core table " + "without erom reference")); + } + + if (p->hr != NULL) + bhndb_release_host_resources(p->hr); + + free(p, M_BHND); + + return (error); } /** - * Attempt to adjust the dynamic register window backing @p pio to permit - * reading @p size bytes at @p addr. + * Free the given @p probe instance and any associated host bridge resources. + */ +static void +bhndb_pci_probe_free(struct bhndb_pci_probe *probe) +{ + bhnd_erom_free_core_table(probe->erom, probe->cores); + bhnd_erom_free(probe->erom); + bhndb_release_host_resources(probe->hr); + free(probe, M_BHND); +} + +/** + * Return a copy of probed core table from @p probe. * - * If @p addr or @p size fall outside the existing mapped range, or if - * @p pio is not backed by a dynamic register window, ENXIO will be returned. + * @param probe The probe instance. + * @param[out] cores On success, a copy of the probed core table. The + * caller is responsible for freeing this table + * bhndb_pci_probe_free_core_table(). + * @param[out] ncores On success, the number of cores found in + * @p cores. * - * @param pio The bhndb PCI erom I/O state to be modified. - * @param addr The address to be include + * @retval 0 success + * @retval non-zero if enumerating the bridged bhnd(4) bus fails, a regular + * unix error code will be returned. */ static int -bhndb_pci_eio_adjust_mapping(struct bhndb_pci_eio *pio, bhnd_addr_t addr, - bhnd_size_t size) +bhndb_pci_probe_copy_core_table(struct bhndb_pci_probe *probe, + struct bhnd_core_info **cores, u_int *ncores) { - bhnd_addr_t target; - bhnd_size_t offset; - int error; + size_t len = sizeof(**cores) * probe->ncores; + *cores = malloc(len, M_BHND, M_WAITOK); + memcpy(*cores, probe->cores, len); - KASSERT(pio->win != NULL, ("missing register window")); - KASSERT(pio->res != NULL, ("missing regwin resource")); - KASSERT(pio->win->win_type == BHNDB_REGWIN_T_DYN, - ("unexpected window type %d", pio->win->win_type)); + *ncores = probe->ncores; - /* The requested subrange must fall within the total mapped range */ - if (addr < pio->addr || (addr - pio->addr) > pio->size || - size > pio->size || (addr - pio->addr) - pio->size < size) - { - return (ENXIO); - } + return (0); +} - /* Do we already have a useable mapping? */ - if (addr >= pio->res_target && - addr <= pio->res_target + pio->win->win_size && - (pio->res_target + pio->win->win_size) - addr >= size) - { - return (0); - } +/** + * Free a core table previously returned by bhndb_pci_probe_copy_core_table(). + * + * @param cores The core table to be freed. + */ +static void +bhndb_pci_probe_free_core_table(struct bhnd_core_info *cores) +{ + free(cores, M_BHND); +} - /* Page-align the target address */ - offset = addr % pio->win->win_size; - target = addr - offset; +/** + * Return true if @p addr and @p size are mapped by the dynamic register window + * backing @p probe. + */ +static bool +bhndb_pci_probe_has_mapping(struct bhndb_pci_probe *probe, bhnd_addr_t addr, + bhnd_size_t size) +{ + if (!probe->m_valid) + return (false); - /* Configure the register window */ - error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, pio->win, - target); - if (error) { - device_printf(pio->dev, "failed to configure dynamic register " - "window: %d\n", error); - return (error); - } + KASSERT(probe->m_win != NULL, ("missing register window")); + KASSERT(probe->m_res != NULL, ("missing regwin resource")); + KASSERT(probe->m_win->win_type == BHNDB_REGWIN_T_DYN, + ("unexpected window type %d", probe->m_win->win_type)); - pio->res_target = target; - return (0); + if (addr < probe->m_target) + return (false); + + if (addr >= probe->m_target + probe->m_win->win_size) + return (false); + + if ((probe->m_target + probe->m_win->win_size) - addr < size) + return (false); + + return (true); } -/* bhnd_erom_io_map() implementation */ +/** + * Attempt to adjust the dynamic register window backing @p probe to permit + * accessing @p size bytes at @p addr. + * + * @param probe The bhndb_pci probe state to be modified. + * @param addr The address at which @p size bytes will mapped. + * @param size The number of bytes to be mapped. + * @param[out] res On success, will be set to the host resource + * mapping @p size bytes at @p addr. + * @param[out] res_offset On success, will be set to the offset of @addr + * within @p res. + * + * @retval 0 success + * @retval non-zero if an error occurs adjusting the backing dynamic + * register window. + */ static int -bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, - bhnd_size_t size) +bhndb_pci_probe_map(struct bhndb_pci_probe *probe, bhnd_addr_t addr, + bhnd_size_t offset, bhnd_size_t size, struct resource **res, + bus_size_t *res_offset) { - struct bhndb_pci_eio *pio; - const struct bhndb_regwin *regwin; - struct resource *r; + const struct bhndb_regwin *regwin, *regwin_table; + struct resource *regwin_res; bhnd_addr_t target; - bhnd_size_t offset; int error; - pio = (struct bhndb_pci_eio *)eio; + /* Determine the absolute address */ + if (BHND_SIZE_MAX - offset < addr) { + device_printf(probe->dev, "invalid offset %#jx+%#jx\n", addr, + offset); + return (ENXIO); + } + addr += offset; + + /* Can we use the existing mapping? */ + if (bhndb_pci_probe_has_mapping(probe, addr, size)) { + *res = probe->m_res; + *res_offset = (addr - probe->m_target) + + probe->m_win->win_offset; + + return (0); + } + /* Locate a useable dynamic register window */ - regwin = bhndb_regwin_find_type(pio->hr->cfg->register_windows, - BHNDB_REGWIN_T_DYN, MIN(size, BHND_DEFAULT_CORE_SIZE)); + regwin_table = probe->hr->cfg->register_windows; + regwin = bhndb_regwin_find_type(regwin_table, + BHNDB_REGWIN_T_DYN, size); if (regwin == NULL) { - device_printf(pio->dev, "unable to map %#jx+%#jx; no " - "usable dynamic register window found\n", addr, size); + device_printf(probe->dev, "unable to map %#jx+%#jx; no " + "usable dynamic register window found\n", addr, + size); return (ENXIO); } /* Locate the host resource mapping our register window */ - if ((r = bhndb_host_resource_for_regwin(pio->hr, regwin)) == NULL) { - device_printf(pio->dev, "unable to map %#jx+%#jx; no " + regwin_res = bhndb_host_resource_for_regwin(probe->hr, regwin); + if (regwin_res == NULL) { + device_printf(probe->dev, "unable to map %#jx+%#jx; no " "usable register resource found\n", addr, size); return (ENXIO); } /* Page-align the target address */ - offset = addr % regwin->win_size; - target = addr - offset; + target = addr - (addr % regwin->win_size); /* Configure the register window */ - error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, regwin, - target); + error = bhndb_pci_compat_setregwin(probe->dev, probe->pci_dev, + regwin, target); if (error) { - device_printf(pio->dev, "failed to configure dynamic register " - "window: %d\n", error); + device_printf(probe->dev, "failed to configure dynamic " + "register window: %d\n", error); return (error); } /* Update our mapping state */ - pio->win = regwin; - pio->res = r; - pio->addr = addr; - pio->size = size; - pio->res_target = target; + probe->m_win = regwin; + probe->m_res = regwin_res; + probe->m_addr = addr; + probe->m_size = size; + probe->m_target = target; + probe->m_valid = true; + *res = regwin_res; + *res_offset = (addr - target) + regwin->win_offset; + return (0); } -/* bhnd_erom_io_read() implementation */ -static uint32_t -bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) +/** + * Write a data item to the bridged address space at the given @p offset from + * @p addr. + * + * A dynamic register window will be used to map @p addr. + * + * @param probe The bhndb_pci probe state to be used to perform the + * write. + * @param addr The base address. + * @param offset The offset from @p addr at which @p value will be + * written. + * @param value The data item to be written. + * @param width The data item width (1, 2, or 4 bytes). + */ +static void +bhndb_pci_probe_write(struct bhndb_pci_probe *probe, bhnd_addr_t addr, + bhnd_size_t offset, uint32_t value, u_int width) { - struct bhndb_pci_eio *pio; - bhnd_addr_t addr; - bus_size_t res_offset; - int error; + struct resource *r; + bus_size_t res_offset; + int error; - pio = (struct bhndb_pci_eio *)eio; + /* Map the target address */ + error = bhndb_pci_probe_map(probe, addr, offset, width, &r, + &res_offset); + if (error) { + device_printf(probe->dev, "error mapping %#jx+%#jx for " + "writing: %d\n", addr, offset, error); + return; + } - /* Calculate absolute address */ - if (BHND_SIZE_MAX - offset < pio->addr) { - device_printf(pio->dev, "invalid offset %#jx+%#jx\n", pio->addr, - offset); - return (UINT32_MAX); + /* Perform write */ + switch (width) { + case 1: + return (bus_write_1(r, res_offset, value)); + case 2: + return (bus_write_2(r, res_offset, value)); + case 4: + return (bus_write_4(r, res_offset, value)); + default: + panic("unsupported width: %u", width); } +} - addr = pio->addr + offset; +/** + * Read a data item from the bridged address space at the given @p offset + * from @p addr. + * + * A dynamic register window will be used to map @p addr. + * + * @param probe The bhndb_pci probe state to be used to perform the + * read. + * @param addr The base address. + * @param offset The offset from @p addr at which to read a data item of + * @p width bytes. + * @param width Item width (1, 2, or 4 bytes). + */ +static uint32_t +bhndb_pci_probe_read(struct bhndb_pci_probe *probe, bhnd_addr_t addr, + bhnd_size_t offset, u_int width) +{ + struct resource *r; + bus_size_t res_offset; + int error; - /* Adjust the mapping for our read */ - if ((error = bhndb_pci_eio_adjust_mapping(pio, addr, width))) { - device_printf(pio->dev, "failed to adjust register mapping: " - "%d\n", error); + /* Map the target address */ + error = bhndb_pci_probe_map(probe, addr, offset, width, &r, + &res_offset); + if (error) { + device_printf(probe->dev, "error mapping %#jx+%#jx for " + "reading: %d\n", addr, offset, error); return (UINT32_MAX); } - KASSERT(pio->res_target <= addr, ("invalid mapping (%#jx vs. %#jx)", - pio->res_target, addr)); - - /* Determine the actual read offset within our register window - * resource */ - res_offset = (addr - pio->res_target) + pio->win->win_offset; - - /* Perform our read */ + /* Perform read */ switch (width) { case 1: - return (bus_read_1(pio->res, res_offset)); + return (bus_read_1(r, res_offset)); case 2: - return (bus_read_2(pio->res, res_offset)); + return (bus_read_2(r, res_offset)); case 4: - return (bus_read_4(pio->res, res_offset)); + return (bus_read_4(r, res_offset)); default: panic("unsupported width: %u", width); } +} + +/** + * Initialize a new bhndb PCI bridge EROM I/O instance. All I/O will be + * performed using @p probe. + * + * @param pio The instance to be initialized. + * @param probe The bhndb_pci probe state to be used to perform all + * I/O. + */ +static void +bhndb_pci_eio_init(struct bhndb_pci_eio *pio, struct bhndb_pci_probe *probe) +{ + memset(pio, 0, sizeof(*pio)); + + pio->eio.map = bhndb_pci_eio_map; + pio->eio.read = bhndb_pci_eio_read; + pio->eio.fini = NULL; + + pio->addr = 0; + pio->size = 0; + pio->probe = probe; +} + +/* bhnd_erom_io_map() implementation */ +static int +bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, + bhnd_size_t size) +{ + struct bhndb_pci_eio *pio = (struct bhndb_pci_eio *)eio; + + if (BHND_ADDR_MAX - addr < size) + return (EINVAL); /* addr+size would overflow */ + + pio->addr = addr; + pio->size = size; + + return (0); +} + +/* bhnd_erom_io_read() implementation */ +static uint32_t +bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) +{ + struct bhndb_pci_eio *pio = (struct bhndb_pci_eio *)eio; + + /* The requested subrange must fall within the existing mapped range */ + if (offset > pio->size || + width > pio->size || + pio->size - offset < width) + { + return (ENXIO); + } + + return (bhndb_pci_probe_read(pio->probe, pio->addr, offset, width)); } static device_method_t bhndb_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bhndb_pci_probe), DEVMETHOD(device_attach, bhndb_pci_attach), DEVMETHOD(device_resume, bhndb_pci_resume), DEVMETHOD(device_suspend, bhndb_pci_suspend), DEVMETHOD(device_detach, bhndb_pci_detach), /* BHNDB interface */ DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info), DEVMETHOD(bhndb_map_intr_isrc, bhndb_pci_map_intr_isrc), DEVMETHOD(bhndb_route_interrupts, bhndb_pci_route_interrupts), /* BHND PWRCTL hostb interface */ DEVMETHOD(bhnd_pwrctl_hostb_get_clksrc, bhndb_pci_pwrctl_get_clksrc), DEVMETHOD(bhnd_pwrctl_hostb_gate_clock, bhndb_pci_pwrctl_gate_clock), DEVMETHOD(bhnd_pwrctl_hostb_ungate_clock, bhndb_pci_pwrctl_ungate_clock), DEVMETHOD_END }; DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods, sizeof(struct bhndb_pci_softc), bhndb_driver); MODULE_VERSION(bhndb_pci, 1); MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1); MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1); Index: head/sys/dev/bhnd/bhndb/bhndb_pcivar.h =================================================================== --- head/sys/dev/bhnd/bhndb/bhndb_pcivar.h (revision 328070) +++ head/sys/dev/bhnd/bhndb/bhndb_pcivar.h (revision 328071) @@ -1,133 +1,131 @@ /*- * Copyright (c) 2015-2016 Landon Fuller * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Landon Fuller * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. * * $FreeBSD$ */ #ifndef _BHND_BHNDB_PCIVAR_H_ #define _BHND_BHNDB_PCIVAR_H_ #include "bhndbvar.h" /* * bhndb(4) PCI driver subclass. */ DECLARE_CLASS(bhndb_pci_driver); struct bhndb_pci_softc; /* * An interconnect-specific function implementing BHNDB_SET_WINDOW_ADDR */ typedef int (*bhndb_pci_set_regwin_t)(device_t dev, device_t pci_dev, const struct bhndb_regwin *rw, bhnd_addr_t addr); /** * PCI/PCIe bridge-level device quirks */ enum { /** No quirks */ BHNDB_PCI_QUIRK_NONE = 0, /** * The core requires fixup of the BAR0 SROM shadow to point at the * current PCI core. */ BHNDB_PCI_QUIRK_SRSH_WAR = (1<<0), /** * The PCI (rev <= 5) core does not provide interrupt status/mask * registers; these siba-only devices require routing backplane * interrupt flags via the SIBA_CFG0_INTVEC register. */ BHNDB_PCI_QUIRK_SIBA_INTVEC = (1<<1), }; /** bhndb_pci quirk table entry */ struct bhndb_pci_quirk { struct bhnd_chip_match chip_desc; /**< chip match descriptor */ struct bhnd_core_match core_desc; /**< core match descriptor */ uint32_t quirks; /**< quirk flags */ }; #define BHNDB_PCI_QUIRK(_rev, _flags) { \ { BHND_MATCH_ANY }, \ { BHND_MATCH_CORE_REV(_rev) }, \ _flags, \ } #define BHNDB_PCI_QUIRK_END \ { { BHND_MATCH_ANY }, { BHND_MATCH_ANY }, 0 } #define BHNDB_PCI_IS_QUIRK_END(_q) \ (BHND_MATCH_IS_ANY(&(_q)->core_desc) && \ BHND_MATCH_IS_ANY(&(_q)->chip_desc) && \ (_q)->quirks == 0) /** bhndb_pci core table entry */ struct bhndb_pci_core { struct bhnd_core_match match; /**< core match descriptor */ - bus_size_t srsh_offset; /**< offset to SRSH_PI register, if any */ struct bhndb_pci_quirk *quirks; /**< quirk table */ }; -#define BHNDB_PCI_CORE(_device, _srsh, _quirks) { \ +#define BHNDB_PCI_CORE(_device, _quirks) { \ { BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_ ## _device) }, \ - _srsh, \ _quirks \ } -#define BHNDB_PCI_CORE_END { { BHND_MATCH_ANY }, 0, NULL } +#define BHNDB_PCI_CORE_END { { BHND_MATCH_ANY }, NULL } #define BHNDB_PCI_IS_CORE_END(_c) BHND_MATCH_IS_ANY(&(_c)->match) struct bhndb_pci_softc { struct bhndb_softc bhndb; /**< parent softc */ device_t dev; /**< bridge device */ device_t parent; /**< parent PCI device */ bhnd_devclass_t pci_devclass; /**< PCI core's devclass */ uint32_t pci_quirks; /**< PCI bridge-level quirks */ int msi_count; /**< MSI count, or 0 */ struct bhndb_intr_isrc *isrc; /**< host interrupt source */ struct mtx mtx; bhndb_pci_set_regwin_t set_regwin; /**< regwin handler */ }; #define BHNDB_PCI_LOCK_INIT(sc) \ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ "bhndb_pc state", MTX_DEF) #define BHNDB_PCI_LOCK(sc) mtx_lock(&(sc)->mtx) #define BHNDB_PCI_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define BHNDB_PCI_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what) #define BHNDB_PCI_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx) #endif /* _BHND_BHNDB_PCIVAR_H_ */ Index: head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h =================================================================== --- head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h (revision 328070) +++ head/sys/dev/bhnd/cores/pci/bhnd_pcireg.h (revision 328071) @@ -1,419 +1,421 @@ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2015 Landon Fuller * Copyright (c) 2010 Broadcom Corporation * All rights reserved. * * This file is derived from the hndsoc.h, pci_core.h, and pcie_core.h headers * distributed with Broadcom's initial brcm80211 Linux driver release, as * contributed to the Linux staging repository. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #ifndef _BHND_CORES_PCI_BHND_PCIREG_H_ #define _BHND_CORES_PCI_BHND_PCIREG_H_ /* * PCI/PCIe-Gen1 DMA Constants */ #define BHND_PCI_DMA32_TRANSLATION 0x40000000 /**< PCI DMA32 address translation (sbtopci2) */ #define BHND_PCI_DMA32_MASK BHND_PCI_SBTOPCI2_MASK /**< PCI DMA32 translation mask */ #define BHND_PCIE_DMA32_TRANSLATION 0x80000000 /**< PCIe-Gen1 DMA32 address translation (sb2pcitranslation2) */ #define BHND_PCIE_DMA32_MASK BHND_PCIE_SBTOPCI2_MASK /**< PCIe-Gen1 DMA32 translation mask */ #define BHND_PCIE_DMA64_TRANSLATION _BHND_PCIE_DMA64(TRANSLATION) /**< PCIe-Gen1 DMA64 address translation (sb2pcitranslation2) */ #define BHND_PCIE_DMA64_MASK _BHND_PCIE_DMA64(MASK) /**< PCIe-Gen1 DMA64 translation mask */ #define _BHND_PCIE_DMA64(_x) ((uint64_t)BHND_PCIE_DMA32_ ## _x << 32) /* * PCI Core Registers */ #define BHND_PCI_CTL 0x000 /**< PCI core control*/ #define BHND_PCI_ARB_CTL 0x010 /**< PCI arbiter control */ #define BHND_PCI_CLKRUN_CTL 0x014 /**< PCI clckrun control (>= rev11) */ #define BHND_PCI_INTR_STATUS 0x020 /**< Interrupt status */ #define BHND_PCI_INTR_MASK 0x024 /**< Interrupt mask */ #define BHND_PCI_SBTOPCI_MBOX 0x028 /**< Sonics to PCI mailbox */ #define BHND_PCI_BCAST_ADDR 0x050 /**< Sonics broadcast address (pci) */ #define BHND_PCI_BCAST_DATA 0x054 /**< Sonics broadcast data (pci) */ #define BHND_PCI_GPIO_IN 0x060 /**< GPIO input (>= rev2) */ #define BHND_PCI_GPIO_OUT 0x064 /**< GPIO output (>= rev2) */ #define BHND_PCI_GPIO_EN 0x068 /**< GPIO output enable (>= rev2) */ #define BHND_PCI_GPIO_CTL 0x06C /**< GPIO control (>= rev2) */ #define BHND_PCI_SBTOPCI0 0x100 /**< Sonics to PCI translation 0 */ #define BHND_PCI_SBTOPCI1 0x104 /**< Sonics to PCI translation 1 */ #define BHND_PCI_SBTOPCI2 0x108 /**< Sonics to PCI translation 2 */ #define BHND_PCI_FUNC0_CFG 0x400 /**< PCI function 0 cfg space (>= rev8) */ #define BHND_PCI_FUNC1_CFG 0x500 /**< PCI function 1 cfg space (>= rev8) */ #define BHND_PCI_FUNC2_CFG 0x600 /**< PCI function 2 cfg space (>= rev8) */ #define BHND_PCI_FUNC3_CFG 0x700 /**< PCI function 3 cfg space (>= rev8) */ #define BHND_PCI_SPROM_SHADOW 0x800 /**< PCI SPROM shadow */ /* BHND_PCI_CTL */ #define BHND_PCI_CTL_RST_OE 0x01 /* When set, drives PCI_RESET out to pin */ #define BHND_PCI_CTL_RST 0x02 /* Value driven out to pin */ #define BHND_PCI_CTL_CLK_OE 0x04 /* When set, drives clock as gated by PCI_CLK out to pin */ #define BHND_PCI_CTL_CLK 0x08 /* Gate for clock driven out to pin */ /* BHND_PCI_ARB_CTL */ #define BHND_PCI_ARB_INT 0x01 /* When set, use an internal arbiter */ #define BHND_PCI_ARB_EXT 0x02 /* When set, use an external arbiter */ /* BHND_PCI_ARB_CTL - ParkID (>= rev8) */ #define BHND_PCI_ARB_PARKID_MASK 0x1c /* Selects which agent is parked on an idle bus */ #define BHND_PCI_ARB_PARKID_SHIFT 2 #define BHND_PCI_ARB_PARKID_EXT0 0 /* External master 0 */ #define BHND_PCI_ARB_PARKID_EXT1 1 /* External master 1 */ #define BHND_PCI_ARB_PARKID_EXT2 2 /* External master 2 */ #define BHND_PCI_ARB_PARKID_EXT3 3 /* External master 3 (rev >= 11) */ #define BHND_PCI_ARB_PARKID_INT_r10 3 /* Internal master (rev < 11) */ #define BHND_PCI_ARB_PARKID_INT_r11 4 /* Internal master (rev >= 11) */ #define BHND_PCI_ARB_PARKID_LAST_r10 4 /* Last active master (rev < 11) */ #define BHND_PCI_ARB_PARKID_LAST_r11 5 /* Last active master (rev >= 11) */ /* BHND_PCI_CLKRUN_CTL */ #define BHND_PCI_CLKRUN_DSBL 0x8000 /* Bit 15 forceClkrun */ /* BHND_PCI_INTR_STATUS / BHND_PCI_INTR_MASK */ #define BHND_PCI_INTR_A 0x01 /* PCI INTA# is asserted */ #define BHND_PCI_INTR_B 0x02 /* PCI INTB# is asserted */ #define BHND_PCI_INTR_SERR 0x04 /* PCI SERR# has been asserted (write one to clear) */ #define BHND_PCI_INTR_PERR 0x08 /* PCI PERR# has been asserted (write one to clear) */ /* BHND_PCI_SBTOPCI_MBOX * (General) PCI/SB mailbox interrupts, two bits per pci function */ #define BHND_PCI_SBTOPCI_MBOX_F0_0 0x100 /* function 0, int 0 */ #define BHND_PCI_SBTOPCI_MBOX_F0_1 0x200 /* function 0, int 1 */ #define BHND_PCI_SBTOPCI_MBOX_F1_0 0x400 /* function 1, int 0 */ #define BHND_PCI_SBTOPCI_MBOX_F1_1 0x800 /* function 1, int 1 */ #define BHND_PCI_SBTOPCI_MBOX_F2_0 0x1000 /* function 2, int 0 */ #define BHND_PCI_SBTOPCI_MBOX_F2_1 0x2000 /* function 2, int 1 */ #define BHND_PCI_SBTOPCI_MBOX_F3_0 0x4000 /* function 3, int 0 */ #define BHND_PCI_SBTOPCI_MBOX_F3_1 0x8000 /* function 3, int 1 */ /* BHND_PCI_BCAST_ADDR */ #define BHNC_PCI_BCAST_ADDR_MASK 0xFF /* Broadcast register address */ /* Sonics to PCI translation types */ #define BHND_PCI_SBTOPCI0_MASK 0xfc000000 #define BHND_PCI_SBTOPCI1_MASK 0xfc000000 #define BHND_PCI_SBTOPCI2_MASK 0xc0000000 /* Access type bits (0:1) */ #define BHND_PCI_SBTOPCI_MEM 0 #define BHND_PCI_SBTOPCI_IO 1 #define BHND_PCI_SBTOPCI_CFG0 2 #define BHND_PCI_SBTOPCI_CFG1 3 #define BHND_PCI_SBTOPCI_PREF 0x4 /* prefetch enable */ #define BHND_PCI_SBTOPCI_BURST 0x8 /* burst enable */ #define BHND_PCI_SBTOPCI_RC_MASK 0x30 /* read command (>= rev11) */ #define BHND_PCI_SBTOPCI_RC_READ 0x00 /* memory read */ #define BHND_PCI_SBTOPCI_RC_READLINE 0x10 /* memory read line */ #define BHND_PCI_SBTOPCI_RC_READMULTI 0x20 /* memory read multiple */ -/* PCI core index in SROM shadow area */ +/* PCI base address bits in SPROM shadow area */ #define BHND_PCI_SRSH_PI_OFFSET 0 /* first word */ #define BHND_PCI_SRSH_PI_MASK 0xf000 /* bit 15:12 */ #define BHND_PCI_SRSH_PI_SHIFT 12 /* bit 15:12 */ +#define BHND_PCI_SRSH_PI_ADDR_MASK 0x0000F000 +#define BHND_PCI_SRSH_PI_ADDR_SHIFT 12 - - /* * PCIe-Gen1 Core Registers */ #define BHND_PCIE_CTL BHND_PCI_CTL /**< PCI core control*/ #define BHND_PCIE_BIST_STATUS 0x00C /**< BIST status */ #define BHND_PCIE_GPIO_SEL 0x010 /**< GPIO select */ #define BHND_PCIE_GPIO_OUT_EN 0x014 /**< GPIO output enable */ #define BHND_PCIE_INTR_STATUS BHND_PCI_INTR_STATUS /**< Interrupt status */ #define BHND_PCIE_INTR_MASK BHND_PCI_INTR_MASK /**< Interrupt mask */ #define BHND_PCIE_SBTOPCI_MBOX BHND_PCI_SBTOPCI_MBOX /**< Sonics to PCI mailbox */ #define BHND_PCIE_SBTOPCI0 BHND_PCI_SBTOPCI0 /**< Sonics to PCI translation 0 */ #define BHND_PCIE_SBTOPCI1 BHND_PCI_SBTOPCI1 /**< Sonics to PCI translation 1 */ #define BHND_PCIE_SBTOPCI2 BHND_PCI_SBTOPCI2 /**< Sonics to PCI translation 2 */ /* indirect pci config space access */ #define BHND_PCIE_CFG_ADDR 0x120 /**< pcie config space address */ #define BHND_PCIE_CFG_DATA 0x124 /**< pcie config space data */ /* mdio register access */ #define BHND_PCIE_MDIO_CTL 0x128 /**< mdio control */ #define BHND_PCIE_MDIO_DATA 0x12C /**< mdio data */ /* indirect protocol phy/dllp/tlp register access */ #define BHND_PCIE_IND_ADDR 0x130 /**< internal protocol register address */ #define BHND_PCIE_IND_DATA 0x134 /**< internal protocol register data */ #define BHND_PCIE_CLKREQEN_CTL 0x138 /**< clkreq rdma control */ #define BHND_PCIE_FUNC0_CFG BHND_PCI_FUNC0_CFG /**< PCI function 0 cfg space */ #define BHND_PCIE_FUNC1_CFG BHND_PCI_FUNC1_CFG /**< PCI function 1 cfg space */ #define BHND_PCIE_FUNC2_CFG BHND_PCI_FUNC2_CFG /**< PCI function 2 cfg space */ #define BHND_PCIE_FUNC3_CFG BHND_PCI_FUNC3_CFG /**< PCI function 3 cfg space */ #define BHND_PCIE_SPROM_SHADOW BHND_PCI_SPROM_SHADOW /**< PCI SPROM shadow */ /* BHND_PCIE_CTL */ #define BHND_PCIE_CTL_RST_OE BHND_PCI_CTL_RST_OE /* When set, drives PCI_RESET out to pin */ #define BHND_PCIE_CTL_RST BHND_PCI_CTL_RST_OE /* Value driven out to pin */ /* BHND_PCI_INTR_STATUS / BHND_PCI_INTR_MASK */ #define BHND_PCIE_INTR_A BHND_PCI_INTR_A /* PCIE INTA message is received */ #define BHND_PCIE_INTR_B BHND_PCI_INTR_B /* PCIE INTB message is received */ #define BHND_PCIE_INTR_FATAL 0x04 /* PCIE INTFATAL message is received */ #define BHND_PCIE_INTR_NFATAL 0x08 /* PCIE INTNONFATAL message is received */ #define BHND_PCIE_INTR_CORR 0x10 /* PCIE INTCORR message is received */ #define BHND_PCIE_INTR_PME 0x20 /* PCIE INTPME message is received */ /* SB to PCIE translation masks */ #define BHND_PCIE_SBTOPCI0_MASK BHND_PCI_SBTOPCI0_MASK #define BHND_PCIE_SBTOPCI1_MASK BHND_PCI_SBTOPCI1_MASK #define BHND_PCIE_SBTOPCI2_MASK BHND_PCI_SBTOPCI2_MASK /* Access type bits (0:1) */ #define BHND_PCIE_SBTOPCI_MEM BHND_PCI_SBTOPCI_MEM #define BHND_PCIE_SBTOPCI_IO BHND_PCI_SBTOPCI_IO #define BHND_PCIE_SBTOPCI_CFG0 BHND_PCI_SBTOPCI_CFG0 #define BHND_PCIE_SBTOPCI_CFG1 BHND_PCI_SBTOPCI_CFG1 #define BHND_PCIE_SBTOPCI_PREF BHND_PCI_SBTOPCI_PREF /* prefetch enable */ #define BHND_PCIE_SBTOPCI_BURST BHND_PCI_SBTOPCI_BURST /* burst enable */ /* BHND_PCIE_CFG_ADDR / BHND_PCIE_CFG_DATA */ #define BHND_PCIE_CFG_ADDR_FUNC_MASK 0x7000 #define BHND_PCIE_CFG_ADDR_FUNC_SHIFT 12 #define BHND_PCIE_CFG_ADDR_REG_MASK 0x0FFF #define BHND_PCIE_CFG_ADDR_REG_SHIFT 0 #define BHND_PCIE_CFG_OFFSET(f, r) \ ((((f) & BHND_PCIE_CFG_ADDR_FUNC_MASK) << BHND_PCIE_CFG_ADDR_FUNC_SHIFT) | \ (((r) & BHND_PCIE_CFG_ADDR_FUNC_SHIFT) << BHND_PCIE_CFG_ADDR_REG_SHIFT)) /* BHND_PCIE_MDIO_CTL control */ #define BHND_PCIE_MDIOCTL_DIVISOR_MASK 0x7f /* clock divisor mask */ #define BHND_PCIE_MDIOCTL_DIVISOR_VAL 0x2 /* default clock divisor */ #define BHND_PCIE_MDIOCTL_PREAM_EN 0x80 /* enable preamble mode */ #define BHND_PCIE_MDIOCTL_DONE 0x100 /* tranaction completed */ /* PCIe BHND_PCIE_MDIO_DATA Data */ #define BHND_PCIE_MDIODATA_PHYADDR_MASK 0x0f800000 /* phy addr */ #define BHND_PCIE_MDIODATA_PHYADDR_SHIFT 23 #define BHND_PCIE_MDIODATA_REGADDR_MASK 0x007c0000 /* reg/dev addr */ #define BHND_PCIE_MDIODATA_REGADDR_SHIFT 18 #define BHND_PCIE_MDIODATA_DATA_MASK 0x0000ffff /* data */ #define BHND_PCIE_MDIODATA_TA 0x00020000 /* slave turnaround time */ #define BHND_PCIE_MDIODATA_START 0x40000000 /* start of transaction */ #define BHND_PCIE_MDIODATA_CMD_WRITE 0x10000000 /* write command */ #define BHND_PCIE_MDIODATA_CMD_READ 0x20000000 /* read command */ #define BHND_PCIE_MDIODATA_ADDR(_phyaddr, _regaddr) ( \ (((_phyaddr) << BHND_PCIE_MDIODATA_PHYADDR_SHIFT) & \ BHND_PCIE_MDIODATA_PHYADDR_MASK) | \ (((_regaddr) << BHND_PCIE_MDIODATA_REGADDR_SHIFT) & \ BHND_PCIE_MDIODATA_REGADDR_MASK) \ ) /* PCIE protocol PHY diagnostic registers */ #define BHND_PCIE_PLP_MODEREG 0x200 /* Mode */ #define BHND_PCIE_PLP_STATUSREG 0x204 /* Status */ #define BHND_PCIE_PLP_LTSSMCTRLREG 0x208 /* LTSSM control */ #define BHND_PCIE_PLP_LTLINKNUMREG 0x20c /* Link Training Link number */ #define BHND_PCIE_PLP_LTLANENUMREG 0x210 /* Link Training Lane number */ #define BHND_PCIE_PLP_LTNFTSREG 0x214 /* Link Training N_FTS */ #define BHND_PCIE_PLP_ATTNREG 0x218 /* Attention */ #define BHND_PCIE_PLP_ATTNMASKREG 0x21C /* Attention Mask */ #define BHND_PCIE_PLP_RXERRCTR 0x220 /* Rx Error */ #define BHND_PCIE_PLP_RXFRMERRCTR 0x224 /* Rx Framing Error */ #define BHND_PCIE_PLP_RXERRTHRESHREG 0x228 /* Rx Error threshold */ #define BHND_PCIE_PLP_TESTCTRLREG 0x22C /* Test Control reg */ #define BHND_PCIE_PLP_SERDESCTRLOVRDREG 0x230 /* SERDES Control Override */ #define BHND_PCIE_PLP_TIMINGOVRDREG 0x234 /* Timing param override */ #define BHND_PCIE_PLP_RXTXSMDIAGREG 0x238 /* RXTX State Machine Diag */ #define BHND_PCIE_PLP_LTSSMDIAGREG 0x23C /* LTSSM State Machine Diag */ /* PCIE protocol DLLP diagnostic registers */ #define BHND_PCIE_DLLP_LCREG 0x100 /* Link Control */ #define BHND_PCIE_DLLP_LCREG_PCIPM_EN 0x40 /* Enable PCI-PM power management */ #define BHND_PCIE_DLLP_LSREG 0x104 /* Link Status */ #define BHND_PCIE_DLLP_LAREG 0x108 /* Link Attention */ #define BHND_PCIE_DLLP_LAMASKREG 0x10C /* Link Attention Mask */ #define BHND_PCIE_DLLP_NEXTTXSEQNUMREG 0x110 /* Next Tx Seq Num */ #define BHND_PCIE_DLLP_ACKEDTXSEQNUMREG 0x114 /* Acked Tx Seq Num */ #define BHND_PCIE_DLLP_PURGEDTXSEQNUMREG 0x118 /* Purged Tx Seq Num */ #define BHND_PCIE_DLLP_RXSEQNUMREG 0x11C /* Rx Sequence Number */ #define BHND_PCIE_DLLP_LRREG 0x120 /* Link Replay */ #define BHND_PCIE_DLLP_LACKTOREG 0x124 /* Link Ack Timeout */ #define BHND_PCIE_DLLP_PMTHRESHREG 0x128 /* Power Management Threshold */ #define BHND_PCIE_L0THRESHOLDTIME_MASK 0xFF00 /* bits 0 - 7 */ #define BHND_PCIE_L1THRESHOLDTIME_MASK 0xFF00 /* bits 8 - 15 */ #define BHND_PCIE_L1THRESHOLDTIME_SHIFT 8 /* PCIE_L1THRESHOLDTIME_SHIFT */ #define BHND_PCIE_L1THRESHOLD_WARVAL 0x72 /* WAR value */ #define BHND_PCIE_ASPMTIMER_EXTEND 0x1000000 /* > rev7: enable extend ASPM timer */ #define BHND_PCIE_DLLP_RTRYWPREG 0x12C /* Retry buffer write ptr */ #define BHND_PCIE_DLLP_RTRYRPREG 0x130 /* Retry buffer Read ptr */ #define BHND_PCIE_DLLP_RTRYPPREG 0x134 /* Retry buffer Purged ptr */ #define BHND_PCIE_DLLP_RTRRWREG 0x138 /* Retry buffer Read/Write */ #define BHND_PCIE_DLLP_ECTHRESHREG 0x13C /* Error Count Threshold */ #define BHND_PCIE_DLLP_TLPERRCTRREG 0x140 /* TLP Error Counter */ #define BHND_PCIE_DLLP_ERRCTRREG 0x144 /* Error Counter */ #define BHND_PCIE_DLLP_NAKRXCTRREG 0x148 /* NAK Received Counter */ #define BHND_PCIE_DLLP_TESTREG 0x14C /* Test */ #define BHND_PCIE_DLLP_PKTBIST 0x150 /* Packet BIST */ #define BHND_PCIE_DLLP_PCIE11 0x154 /* DLLP PCIE 1.1 reg */ #define BHND_PCIE_DLLP_LSREG_LINKUP (1 << 16) /* PCIE protocol TLP diagnostic registers */ #define BHND_PCIE_TLP_CONFIGREG 0x000 /* Configuration */ #define BHND_PCIE_TLP_WORKAROUNDSREG 0x004 /* TLP Workarounds */ #define BHND_PCIE_TLP_WORKAROUND_URBIT 0x8 /* If enabled, UR status bit is set * on memory access of an unmatched * address */ #define BHND_PCIE_TLP_WRDMAUPPER 0x010 /* Write DMA Upper Address */ #define BHND_PCIE_TLP_WRDMALOWER 0x014 /* Write DMA Lower Address */ #define BHND_PCIE_TLP_WRDMAREQ_LBEREG 0x018 /* Write DMA Len/ByteEn Req */ #define BHND_PCIE_TLP_RDDMAUPPER 0x01C /* Read DMA Upper Address */ #define BHND_PCIE_TLP_RDDMALOWER 0x020 /* Read DMA Lower Address */ #define BHND_PCIE_TLP_RDDMALENREG 0x024 /* Read DMA Len Req */ #define BHND_PCIE_TLP_MSIDMAUPPER 0x028 /* MSI DMA Upper Address */ #define BHND_PCIE_TLP_MSIDMALOWER 0x02C /* MSI DMA Lower Address */ #define BHND_PCIE_TLP_MSIDMALENREG 0x030 /* MSI DMA Len Req */ #define BHND_PCIE_TLP_SLVREQLENREG 0x034 /* Slave Request Len */ #define BHND_PCIE_TLP_FCINPUTSREQ 0x038 /* Flow Control Inputs */ #define BHND_PCIE_TLP_TXSMGRSREQ 0x03C /* Tx StateMachine and Gated Req */ #define BHND_PCIE_TLP_ADRACKCNTARBLEN 0x040 /* Address Ack XferCnt and ARB Len */ #define BHND_PCIE_TLP_DMACPLHDR0 0x044 /* DMA Completion Hdr 0 */ #define BHND_PCIE_TLP_DMACPLHDR1 0x048 /* DMA Completion Hdr 1 */ #define BHND_PCIE_TLP_DMACPLHDR2 0x04C /* DMA Completion Hdr 2 */ #define BHND_PCIE_TLP_DMACPLMISC0 0x050 /* DMA Completion Misc0 */ #define BHND_PCIE_TLP_DMACPLMISC1 0x054 /* DMA Completion Misc1 */ #define BHND_PCIE_TLP_DMACPLMISC2 0x058 /* DMA Completion Misc2 */ #define BHND_PCIE_TLP_SPTCTRLLEN 0x05C /* Split Controller Req len */ #define BHND_PCIE_TLP_SPTCTRLMSIC0 0x060 /* Split Controller Misc 0 */ #define BHND_PCIE_TLP_SPTCTRLMSIC1 0x064 /* Split Controller Misc 1 */ #define BHND_PCIE_TLP_BUSDEVFUNC 0x068 /* Bus/Device/Func */ #define BHND_PCIE_TLP_RESETCTR 0x06C /* Reset Counter */ #define BHND_PCIE_TLP_RTRYBUF 0x070 /* Retry Buffer value */ #define BHND_PCIE_TLP_TGTDEBUG1 0x074 /* Target Debug Reg1 */ #define BHND_PCIE_TLP_TGTDEBUG2 0x078 /* Target Debug Reg2 */ #define BHND_PCIE_TLP_TGTDEBUG3 0x07C /* Target Debug Reg3 */ #define BHND_PCIE_TLP_TGTDEBUG4 0x080 /* Target Debug Reg4 */ /* * PCIe-G1 SerDes MDIO Registers (>= rev10) */ #define BHND_PCIE_PHYADDR_SD 0x0 /* serdes PHY address */ #define BHND_PCIE_SD_ADDREXT 0x1F /* serdes address extension register */ #define BHND_PCIE_SD_REGS_IEEE0 0x0000 /* IEEE0 AN CTRL block */ #define BHND_PCIE_SD_REGS_IEEE1 0x0010 /* IEEE1 AN ADV block */ #define BHND_PCIE_SD_REGS_BLK0 0x8000 /* ??? */ #define BHND_PCIE_SD_REGS_BLK1 0x8010 /* ??? */ #define BHND_PCIE_SD_REGS_BLK2 0x8020 /* ??? */ #define BHND_PCIE_SD_REGS_BLK3 0x8030 /* ??? */ #define BHND_PCIE_SD_REGS_BLK4 0x8040 /* ??? */ #define BHND_PCIE_SD_REGS_PLL 0x8080 /* (?) PLL register block */ #define BHND_PCIE_SD_REGS_TX0 0x8200 /* (?) Transmit 0 block */ #define BHND_PCIE_SD_REGS_SERDESID 0x8310 /* ??? */ #define BHND_PCIE_SD_REGS_RX0 0x8400 /* (?) Receive 0 register block */ /* The interpretation of these registers and values are just guesses based on * the limited available documentation from other (likely similar) Broadcom * SerDes IP. */ #define BHND_PCIE_SD_TX_DRIVER 0x17 /* TX transmit driver register */ #define BHND_PCIE_SD_TX_DRIVER_IFIR_MASK 0x000E /* unconfirmed */ #define BHND_PCIE_SD_TX_DRIVER_IFIR_SHIFT 1 /* unconfirmed */ #define BHND_PCIE_SD_TX_DRIVER_IPRE_MASK 0x00F0 /* unconfirmed */ #define BHND_PCIE_SD_TX_DRIVER_IPRE_SHIFT 4 /* unconfirmed */ #define BHND_PCIE_SD_TX_DRIVER_IDRIVER_MASK 0x0F00 /* unconfirmed */ #define BHND_PCIE_SD_TX_DRIVER_IDRIVER_SHIFT 8 /* unconfirmed */ #define BHND_PCIE_SD_TX_DRIVER_P2_COEFF_SHIFT 12 /* unconfirmed */ #define BHND_PCIE_SD_TX_DRIVER_P2_COEFF_MASK 0xF000 /* unconfirmed */ /* Constants used with host bridge quirk handling */ #define BHND_PCIE_APPLE_TX_P2_COEFF_MAX 0x7 /* 9.6dB pre-emphassis coeff (???) */ #define BHND_PCIE_APPLE_TX_IDRIVER_MAX 0xF /* 1400mV voltage range (???) */ #define BHND_PCIE_APPLE_TX_P2_COEFF_700MV 0x7 /* 2.3dB pre-emphassis coeff (???) */ #define BHND_PCIE_APPLE_TX_IDRIVER_700MV 0x0 /* 670mV voltage range (???) */ /* * PCIe-G1 SerDes-R9 MDIO Registers (<= rev9) * * These register definitions appear to match those provided in the * "PCI Express SerDes Registers" section of the BCM5761 Ethernet Controller * Programmer's Reference Guide. */ #define BHND_PCIE_PHY_SDR9_PLL 0x1C /* SerDes PLL PHY Address*/ #define BHND_PCIE_SDR9_PLL_CTRL 0x17 /* PLL control reg */ #define BHND_PCIE_SDR9_PLL_CTRL_FREQDET_EN 0x4000 /* bit 14 is FREQDET on */ #define BHND_PCIE_PHY_SDR9_TXRX 0x0F /* SerDes RX/TX PHY Address */ #define BHND_PCIE_SDR9_RX_CTRL 0x11 /* RX ctrl register */ #define BHND_PCIE_SDR9_RX_CTRL_FORCE 0x80 /* rxpolarity_force */ #define BHND_PCIE_SDR9_RX_CTRL_POLARITY_INV 0x40 /* rxpolarity_value (if set, inverse polarity) */ #define BHND_PCIE_SDR9_RX_CDR 0x16 /* RX CDR ctrl register */ #define BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_EN 0x0100 /* freq_override_en flag */ #define BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_MASK 0x00FF /* freq_override_val */ #define BHND_PCIE_SDR9_RX_CDR_FREQ_OVR_SHIFT 0 #define BHND_PCIE_SDR9_RX_CDRBW 0x17 /* RX CDR bandwidth (PLL tuning) */ #define BHND_PCIE_SDR9_RX_CDRBW_INTGTRK_MASK 0x7000 /* integral loop bandwidth (phase tracking mode) */ #define BHND_PCIE_SDR9_RX_CDRBW_INTGTRK_SHIFT 11 #define BHND_PCIE_SDR9_RX_CDRBW_INTGACQ_MASK 0x0700 /* integral loop bandwidth (phase acquisition mode) */ #define BHND_PCIE_SDR9_RX_CDRBW_INTGACQ_SHIFT 8 #define BHND_PCIE_SDR9_RX_CDRBW_PROPTRK_MASK 0x0070 /* proportional loop bandwidth (phase tracking mode) */ #define BHND_PCIE_SDR9_RX_CDRBW_PROPTRK_SHIFT 4 #define BHND_PCIE_SDR9_RX_CDRBW_PROPACQ_MASK 0x0007 /* proportional loop bandwidth (phase acquisition mode) */ #define BHND_PCIE_SDR9_RX_CDRBW_PROPACQ_SHIFT 0 #define BHND_PCIE_SDR9_RX_TIMER1 0x12 /* timer1 register */ #define BHND_PCIE_SDR9_RX_TIMER1_LKTRK_MASK 0xFF00 /* phase tracking delay before asserting RX seq completion (in 16ns units) */ #define BHND_PCIE_SDR9_RX_TIMER1_LKTRK_SHIFT 8 #define BHND_PCIE_SDR9_RX_TIMER1_LKACQ_MASK 0x00FF /* phase acquisition mode time (in 1024ns units) */ #define BHND_PCIE_SDR9_RX_TIMER1_LKACQ_SHIFT 0 /* SPROM offsets */ -#define BHND_PCIE_SRSH_PI_OFFSET BHND_PCI_SRSH_PI_OFFSET /**< PCI core index in SROM shadow area */ -#define BHND_PCIE_SRSH_PI_MASK BHND_PCI_SRSH_PI_MASK +#define BHND_PCIE_SRSH_PI_OFFSET BHND_PCI_SRSH_PI_OFFSET /**< PCI base address bits in SPROM shadow area */ +#define BHND_PCIE_SRSH_PI_MASK BHND_PCI_SRSH_PI_MASK /**< bits 15:12 of the PCI core address */ #define BHND_PCIE_SRSH_PI_SHIFT BHND_PCI_SRSH_PI_SHIFT +#define BHND_PCIE_SRSH_PI_ADDR_MASK BHND_PCI_SRSH_PI_ADDR_MASK +#define BHND_PCIE_SRSH_PI_ADDR_SHIFT BHND_PCI_SRSH_PI_ADDR_SHIFT #define BHND_PCIE_SRSH_ASPM_OFFSET 8 /* word 4 */ #define BHND_PCIE_SRSH_ASPM_ENB 0x18 /* bit 3, 4 */ #define BHND_PCIE_SRSH_ASPM_L1_ENB 0x10 /* bit 4 */ #define BHND_PCIE_SRSH_ASPM_L0s_ENB 0x8 /* bit 3 */ #define BHND_PCIE_SRSH_PCIE_MISC_CONFIG 10 /* word 5 */ #define BHND_PCIE_SRSH_L23READY_EXIT_NOPRST 0x8000 /* bit 15 */ #define BHND_PCIE_SRSH_CLKREQ_OFFSET_R5 40 /* word 20 for srom rev <= 5 */ #define BHND_PCIE_SRSH_CLKREQ_OFFSET_R8 104 /* word 52 for srom rev 8 */ #define BHND_PCIE_SRSH_CLKREQ_ENB 0x0800 /* bit 11 */ #define BHND_PCIE_SRSH_BD_OFFSET 12 /* word 6 */ #define BHND_PCIE_SRSH_AUTOINIT_OFFSET 36 /* auto initialization enable */ /* Status reg PCIE_PLP_STATUSREG */ #define BHND_PCIE_PLP_POLARITY_INV 0x10 /* lane polarity is inverted */ #endif /* _BHND_CORES_PCI_BHND_PCIREG_H_ */