14ad7e9b0SAdrian Chadd /*- 2bb64eeccSAdrian Chadd * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> 3*89294a78SLandon J. Fuller * Copyright (c) 2017 The FreeBSD Foundation 44ad7e9b0SAdrian Chadd * All rights reserved. 54ad7e9b0SAdrian Chadd * 6*89294a78SLandon J. Fuller * Portions of this software were developed by Landon Fuller 7*89294a78SLandon J. Fuller * under sponsorship from the FreeBSD Foundation. 8*89294a78SLandon J. Fuller * 94ad7e9b0SAdrian Chadd * Redistribution and use in source and binary forms, with or without 104ad7e9b0SAdrian Chadd * modification, are permitted provided that the following conditions 114ad7e9b0SAdrian Chadd * are met: 124ad7e9b0SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 134ad7e9b0SAdrian Chadd * notice, this list of conditions and the following disclaimer, 144ad7e9b0SAdrian Chadd * without modification. 154ad7e9b0SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 164ad7e9b0SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 174ad7e9b0SAdrian Chadd * redistribution must be conditioned upon including a substantially 184ad7e9b0SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 194ad7e9b0SAdrian Chadd * 204ad7e9b0SAdrian Chadd * NO WARRANTY 214ad7e9b0SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 224ad7e9b0SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 234ad7e9b0SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 244ad7e9b0SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 254ad7e9b0SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 264ad7e9b0SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 274ad7e9b0SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 284ad7e9b0SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 294ad7e9b0SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 304ad7e9b0SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 314ad7e9b0SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 324ad7e9b0SAdrian Chadd */ 334ad7e9b0SAdrian Chadd 344ad7e9b0SAdrian Chadd #include <sys/cdefs.h> 354ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$"); 364ad7e9b0SAdrian Chadd 374ad7e9b0SAdrian Chadd /* 384ad7e9b0SAdrian Chadd * PCI-specific implementation for the BHNDB bridge driver. 394ad7e9b0SAdrian Chadd * 404ad7e9b0SAdrian Chadd * Provides support for bridging from a PCI parent bus to a BHND-compatible 414ad7e9b0SAdrian Chadd * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point 424ad7e9b0SAdrian Chadd * mode. 434ad7e9b0SAdrian Chadd * 448ef24a0dSAdrian Chadd * This driver handles all initial generic host-level PCI interactions with a 458ef24a0dSAdrian Chadd * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4) 468ef24a0dSAdrian Chadd * bus has been enumerated, this driver works in tandem with a core-specific 478ef24a0dSAdrian Chadd * bhnd_pci_hostb driver to manage the PCI core. 484ad7e9b0SAdrian Chadd */ 494ad7e9b0SAdrian Chadd 504ad7e9b0SAdrian Chadd #include <sys/param.h> 514ad7e9b0SAdrian Chadd #include <sys/kernel.h> 524ad7e9b0SAdrian Chadd #include <sys/bus.h> 534ad7e9b0SAdrian Chadd #include <sys/limits.h> 544ad7e9b0SAdrian Chadd #include <sys/malloc.h> 554ad7e9b0SAdrian Chadd #include <sys/module.h> 564ad7e9b0SAdrian Chadd #include <sys/systm.h> 574ad7e9b0SAdrian Chadd 584ad7e9b0SAdrian Chadd #include <dev/pci/pcireg.h> 594ad7e9b0SAdrian Chadd #include <dev/pci/pcivar.h> 604ad7e9b0SAdrian Chadd 614ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h> 62*89294a78SLandon J. Fuller #include <dev/bhnd/bhndreg.h> 63*89294a78SLandon J. Fuller 64*89294a78SLandon J. Fuller #include <dev/bhnd/bhnd_erom.h> 65*89294a78SLandon J. Fuller #include <dev/bhnd/bhnd_eromvar.h> 664ad7e9b0SAdrian Chadd 674ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h> 684ad7e9b0SAdrian Chadd 694ad7e9b0SAdrian Chadd #include "bhndb_pcireg.h" 704ad7e9b0SAdrian Chadd #include "bhndb_pcivar.h" 714ad7e9b0SAdrian Chadd #include "bhndb_private.h" 724ad7e9b0SAdrian Chadd 73*89294a78SLandon J. Fuller struct bhndb_pci_eio; 74*89294a78SLandon J. Fuller 75824b48efSLandon J. Fuller static int bhndb_pci_init_msi(struct bhndb_pci_softc *sc); 76*89294a78SLandon J. Fuller static int bhndb_pci_read_core_table(device_t dev, 77*89294a78SLandon J. Fuller struct bhnd_chipid *chipid, 78*89294a78SLandon J. Fuller struct bhnd_core_info **cores, u_int *ncores, 79*89294a78SLandon J. Fuller bhnd_erom_class_t **eromcls); 80111d7cb2SLandon J. Fuller static int bhndb_pci_add_children(struct bhndb_pci_softc *sc); 81111d7cb2SLandon J. Fuller 82*89294a78SLandon J. Fuller static bool bhndb_is_pcie_attached(device_t dev); 834ad7e9b0SAdrian Chadd 84*89294a78SLandon J. Fuller static int bhndb_enable_pci_clocks(device_t dev); 85*89294a78SLandon J. Fuller static int bhndb_disable_pci_clocks(device_t dev); 86*89294a78SLandon J. Fuller 87*89294a78SLandon J. Fuller static int bhndb_pci_compat_setregwin(device_t dev, 88*89294a78SLandon J. Fuller device_t pci_dev, const struct bhndb_regwin *, 89*89294a78SLandon J. Fuller bhnd_addr_t); 90*89294a78SLandon J. Fuller static int bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev, 914ad7e9b0SAdrian Chadd const struct bhndb_regwin *, bhnd_addr_t); 924ad7e9b0SAdrian Chadd 93e83ce340SAdrian Chadd static void bhndb_init_sromless_pci_config( 94e83ce340SAdrian Chadd struct bhndb_pci_softc *sc); 95e83ce340SAdrian Chadd 96e83ce340SAdrian Chadd static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc); 97f9cf87a0SAdrian Chadd static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc); 984ad7e9b0SAdrian Chadd 99*89294a78SLandon J. Fuller static int bhndb_pci_eio_init(struct bhndb_pci_eio *pio, 100*89294a78SLandon J. Fuller device_t dev, device_t pci_dev, 101*89294a78SLandon J. Fuller struct bhndb_host_resources *hr); 102*89294a78SLandon J. Fuller static int bhndb_pci_eio_map(struct bhnd_erom_io *eio, 103*89294a78SLandon J. Fuller bhnd_addr_t addr, bhnd_size_t size); 104*89294a78SLandon J. Fuller static uint32_t bhndb_pci_eio_read(struct bhnd_erom_io *eio, 105*89294a78SLandon J. Fuller bhnd_size_t offset, u_int width); 106*89294a78SLandon J. Fuller 107824b48efSLandon J. Fuller #define BHNDB_PCI_MSI_COUNT 1 108824b48efSLandon J. Fuller 109*89294a78SLandon J. Fuller /* bhndb_pci erom I/O implementation */ 110*89294a78SLandon J. Fuller struct bhndb_pci_eio { 111*89294a78SLandon J. Fuller struct bhnd_erom_io eio; 112*89294a78SLandon J. Fuller device_t dev; /**< bridge device */ 113*89294a78SLandon J. Fuller device_t pci_dev; /**< parent PCI device */ 114*89294a78SLandon J. Fuller struct bhndb_host_resources *hr; /**< borrowed reference to host resources */ 115*89294a78SLandon J. Fuller const struct bhndb_regwin *win; /**< mapped register window, or NULL */ 116*89294a78SLandon J. Fuller struct resource *res; /**< resource containing the register window, or NULL if no window mapped */ 117*89294a78SLandon J. Fuller bhnd_addr_t res_target; /**< current target address (if mapped) */ 118*89294a78SLandon J. Fuller bool mapped; /**< true if a valid mapping exists, false otherwise */ 119*89294a78SLandon J. Fuller bhnd_addr_t addr; /**< mapped address */ 120*89294a78SLandon J. Fuller bhnd_size_t size; /**< mapped size */ 121*89294a78SLandon J. Fuller }; 122*89294a78SLandon J. Fuller 1234ad7e9b0SAdrian Chadd /** 1244ad7e9b0SAdrian Chadd * Default bhndb_pci implementation of device_probe(). 1254ad7e9b0SAdrian Chadd * 1264ad7e9b0SAdrian Chadd * Verifies that the parent is a PCI/PCIe device. 1274ad7e9b0SAdrian Chadd */ 1284ad7e9b0SAdrian Chadd static int 1294ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev) 1304ad7e9b0SAdrian Chadd { 1314ad7e9b0SAdrian Chadd device_t parent; 1324ad7e9b0SAdrian Chadd devclass_t parent_bus; 1334ad7e9b0SAdrian Chadd devclass_t pci; 1344ad7e9b0SAdrian Chadd 1354ad7e9b0SAdrian Chadd /* Our parent must be a PCI/PCIe device. */ 1364ad7e9b0SAdrian Chadd pci = devclass_find("pci"); 1374ad7e9b0SAdrian Chadd parent = device_get_parent(dev); 1384ad7e9b0SAdrian Chadd parent_bus = device_get_devclass(device_get_parent(parent)); 1394ad7e9b0SAdrian Chadd 1404ad7e9b0SAdrian Chadd if (parent_bus != pci) 1414ad7e9b0SAdrian Chadd return (ENXIO); 1424ad7e9b0SAdrian Chadd 1434ad7e9b0SAdrian Chadd device_set_desc(dev, "PCI-BHND bridge"); 1444ad7e9b0SAdrian Chadd 1454ad7e9b0SAdrian Chadd return (BUS_PROBE_DEFAULT); 1464ad7e9b0SAdrian Chadd } 1474ad7e9b0SAdrian Chadd 148824b48efSLandon J. Fuller /* Configure MSI interrupts */ 149824b48efSLandon J. Fuller static int 150824b48efSLandon J. Fuller bhndb_pci_init_msi(struct bhndb_pci_softc *sc) 151824b48efSLandon J. Fuller { 152824b48efSLandon J. Fuller int error; 153824b48efSLandon J. Fuller 154824b48efSLandon J. Fuller /* Is MSI available? */ 155824b48efSLandon J. Fuller if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT) 156824b48efSLandon J. Fuller return (ENXIO); 157824b48efSLandon J. Fuller 158824b48efSLandon J. Fuller /* Allocate expected message count */ 159824b48efSLandon J. Fuller sc->intr.msi_count = BHNDB_PCI_MSI_COUNT; 160824b48efSLandon J. Fuller if ((error = pci_alloc_msi(sc->parent, &sc->intr.msi_count))) { 161824b48efSLandon J. Fuller device_printf(sc->dev, "failed to allocate MSI interrupts: " 162824b48efSLandon J. Fuller "%d\n", error); 163824b48efSLandon J. Fuller return (error); 164824b48efSLandon J. Fuller } 165824b48efSLandon J. Fuller 166824b48efSLandon J. Fuller if (sc->intr.msi_count < BHNDB_PCI_MSI_COUNT) 167824b48efSLandon J. Fuller return (ENXIO); 168824b48efSLandon J. Fuller 169824b48efSLandon J. Fuller /* MSI uses resource IDs starting at 1 */ 170824b48efSLandon J. Fuller sc->intr.intr_rid = 1; 171824b48efSLandon J. Fuller 172824b48efSLandon J. Fuller return (0); 173824b48efSLandon J. Fuller } 174824b48efSLandon J. Fuller 1754ad7e9b0SAdrian Chadd static int 1764ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev) 1774ad7e9b0SAdrian Chadd { 1784ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 179*89294a78SLandon J. Fuller struct bhnd_chipid cid; 180*89294a78SLandon J. Fuller struct bhnd_core_info *cores, hostb_core; 181*89294a78SLandon J. Fuller bhnd_erom_class_t *erom_class; 182*89294a78SLandon J. Fuller u_int ncores; 1834ad7e9b0SAdrian Chadd int error, reg; 1844ad7e9b0SAdrian Chadd 1854ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1864ad7e9b0SAdrian Chadd sc->dev = dev; 187e83ce340SAdrian Chadd sc->parent = device_get_parent(dev); 188*89294a78SLandon J. Fuller sc->set_regwin = NULL; 189*89294a78SLandon J. Fuller 190*89294a78SLandon J. Fuller cores = NULL; 1914ad7e9b0SAdrian Chadd 192824b48efSLandon J. Fuller /* Enable PCI bus mastering */ 193824b48efSLandon J. Fuller pci_enable_busmaster(sc->parent); 194824b48efSLandon J. Fuller 195*89294a78SLandon J. Fuller /* Set up PCI interrupt handling */ 196824b48efSLandon J. Fuller if (bhndb_pci_init_msi(sc) == 0) { 197824b48efSLandon J. Fuller device_printf(dev, "Using MSI interrupts on %s\n", 198824b48efSLandon J. Fuller device_get_nameunit(sc->parent)); 199824b48efSLandon J. Fuller } else { 200824b48efSLandon J. Fuller device_printf(dev, "Using INTx interrupts on %s\n", 201824b48efSLandon J. Fuller device_get_nameunit(sc->parent)); 202824b48efSLandon J. Fuller sc->intr.intr_rid = 0; 203824b48efSLandon J. Fuller } 204824b48efSLandon J. Fuller 205824b48efSLandon J. Fuller /* Determine our bridge device class */ 206824b48efSLandon J. Fuller sc->pci_devclass = BHND_DEVCLASS_PCI; 207111d7cb2SLandon J. Fuller if (pci_find_cap(sc->parent, PCIY_EXPRESS, ®) == 0) 208111d7cb2SLandon J. Fuller sc->pci_devclass = BHND_DEVCLASS_PCIE; 209111d7cb2SLandon J. Fuller else 210111d7cb2SLandon J. Fuller sc->pci_devclass = BHND_DEVCLASS_PCI; 2114ad7e9b0SAdrian Chadd 212111d7cb2SLandon J. Fuller /* Enable clocks (if required by this hardware) */ 213*89294a78SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(sc->dev))) 214111d7cb2SLandon J. Fuller goto cleanup; 215111d7cb2SLandon J. Fuller 216*89294a78SLandon J. Fuller /* Identify the chip and enumerate the bridged cores */ 217*89294a78SLandon J. Fuller error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores, 218*89294a78SLandon J. Fuller &erom_class); 219*89294a78SLandon J. Fuller if (error) 220111d7cb2SLandon J. Fuller goto cleanup; 221111d7cb2SLandon J. Fuller 222*89294a78SLandon J. Fuller /* Select the appropriate register window handler */ 223*89294a78SLandon J. Fuller if (cid.chip_type == BHND_CHIPTYPE_SIBA) { 224*89294a78SLandon J. Fuller sc->set_regwin = bhndb_pci_compat_setregwin; 225*89294a78SLandon J. Fuller } else { 226*89294a78SLandon J. Fuller sc->set_regwin = bhndb_pci_fast_setregwin; 2274ad7e9b0SAdrian Chadd } 2284ad7e9b0SAdrian Chadd 229*89294a78SLandon J. Fuller /* Determine our host bridge core */ 230*89294a78SLandon J. Fuller error = bhndb_find_hostb_core(cores, ncores, sc->pci_devclass, 231*89294a78SLandon J. Fuller &hostb_core); 232*89294a78SLandon J. Fuller if (error) 233*89294a78SLandon J. Fuller goto cleanup; 234*89294a78SLandon J. Fuller 235*89294a78SLandon J. Fuller /* Perform bridge attach */ 236*89294a78SLandon J. Fuller error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class); 237*89294a78SLandon J. Fuller if (error) 238*89294a78SLandon J. Fuller goto cleanup; 2394ad7e9b0SAdrian Chadd 240bb64eeccSAdrian Chadd /* Fix-up power on defaults for SROM-less devices. */ 2414ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(sc); 2424ad7e9b0SAdrian Chadd 243111d7cb2SLandon J. Fuller /* Add any additional child devices */ 244111d7cb2SLandon J. Fuller if ((error = bhndb_pci_add_children(sc))) 245111d7cb2SLandon J. Fuller goto cleanup; 246111d7cb2SLandon J. Fuller 247111d7cb2SLandon J. Fuller /* Probe and attach our children */ 248111d7cb2SLandon J. Fuller if ((error = bus_generic_attach(dev))) 249111d7cb2SLandon J. Fuller goto cleanup; 250111d7cb2SLandon J. Fuller 251*89294a78SLandon J. Fuller free(cores, M_BHND); 252*89294a78SLandon J. Fuller 253111d7cb2SLandon J. Fuller return (0); 254111d7cb2SLandon J. Fuller 255111d7cb2SLandon J. Fuller cleanup: 256111d7cb2SLandon J. Fuller device_delete_children(dev); 257*89294a78SLandon J. Fuller bhndb_disable_pci_clocks(sc->dev); 258*89294a78SLandon J. Fuller 259824b48efSLandon J. Fuller if (sc->intr.msi_count > 0) 260824b48efSLandon J. Fuller pci_release_msi(dev); 261824b48efSLandon J. Fuller 262*89294a78SLandon J. Fuller if (cores != NULL) 263*89294a78SLandon J. Fuller free(cores, M_BHND); 264*89294a78SLandon J. Fuller 265111d7cb2SLandon J. Fuller pci_disable_busmaster(sc->parent); 266111d7cb2SLandon J. Fuller 267111d7cb2SLandon J. Fuller return (error); 268111d7cb2SLandon J. Fuller } 269111d7cb2SLandon J. Fuller 270111d7cb2SLandon J. Fuller static int 271111d7cb2SLandon J. Fuller bhndb_pci_detach(device_t dev) 272111d7cb2SLandon J. Fuller { 273111d7cb2SLandon J. Fuller struct bhndb_pci_softc *sc; 274111d7cb2SLandon J. Fuller int error; 275111d7cb2SLandon J. Fuller 276111d7cb2SLandon J. Fuller sc = device_get_softc(dev); 277111d7cb2SLandon J. Fuller 278111d7cb2SLandon J. Fuller /* Attempt to detach our children */ 279111d7cb2SLandon J. Fuller if ((error = bus_generic_detach(dev))) 280111d7cb2SLandon J. Fuller return (error); 281111d7cb2SLandon J. Fuller 282111d7cb2SLandon J. Fuller /* Perform generic bridge detach */ 283111d7cb2SLandon J. Fuller if ((error = bhndb_generic_detach(dev))) 284111d7cb2SLandon J. Fuller return (error); 285111d7cb2SLandon J. Fuller 286111d7cb2SLandon J. Fuller /* Disable clocks (if required by this hardware) */ 287*89294a78SLandon J. Fuller if ((error = bhndb_disable_pci_clocks(sc->dev))) 288111d7cb2SLandon J. Fuller return (error); 289111d7cb2SLandon J. Fuller 290824b48efSLandon J. Fuller /* Release MSI interrupts */ 291824b48efSLandon J. Fuller if (sc->intr.msi_count > 0) 292824b48efSLandon J. Fuller pci_release_msi(dev); 293824b48efSLandon J. Fuller 294111d7cb2SLandon J. Fuller /* Disable PCI bus mastering */ 295111d7cb2SLandon J. Fuller pci_disable_busmaster(sc->parent); 296111d7cb2SLandon J. Fuller 297111d7cb2SLandon J. Fuller return (0); 298111d7cb2SLandon J. Fuller } 299111d7cb2SLandon J. Fuller 300*89294a78SLandon J. Fuller /** 301*89294a78SLandon J. Fuller * Use the generic PCI bridge hardware configuration to enumerate the bridged 302*89294a78SLandon J. Fuller * bhnd(4) bus' core table. 303*89294a78SLandon J. Fuller * 304*89294a78SLandon J. Fuller * @note This function may be safely called prior to device attach, (e.g. 305*89294a78SLandon J. Fuller * from DEVICE_PROBE). 306*89294a78SLandon J. Fuller * @note This function requires exclusive ownership over allocating and 307*89294a78SLandon J. Fuller * configuring host bridge resources, and should only be called prior to 308*89294a78SLandon J. Fuller * completion of device attach and full configuration of the bridge. 309*89294a78SLandon J. Fuller * 310*89294a78SLandon J. Fuller * @param dev The bhndb_pci bridge device. 311*89294a78SLandon J. Fuller * @param[out] chipid On success, the parsed chip identification. 312*89294a78SLandon J. Fuller * @param[out] cores On success, the enumerated core table. The 313*89294a78SLandon J. Fuller * caller is responsible for freeing this table via 314*89294a78SLandon J. Fuller * bhndb_pci_free_core_table(). 315*89294a78SLandon J. Fuller * @param[out] ncores On success, the number of cores found in 316*89294a78SLandon J. Fuller * @p cores. 317*89294a78SLandon J. Fuller * @param[out] eromcls On success, a pointer to the erom class used to 318*89294a78SLandon J. Fuller * parse the device enumeration table. This 319*89294a78SLandon J. Fuller * argument may be NULL if the class is not 320*89294a78SLandon J. Fuller * desired. 321*89294a78SLandon J. Fuller * 322*89294a78SLandon J. Fuller * @retval 0 success 323*89294a78SLandon J. Fuller * @retval non-zero if enumerating the bridged bhnd(4) bus fails, a regular 324*89294a78SLandon J. Fuller * unix error code will be returned. 325*89294a78SLandon J. Fuller */ 326*89294a78SLandon J. Fuller static int 327*89294a78SLandon J. Fuller bhndb_pci_read_core_table(device_t dev, struct bhnd_chipid *chipid, 328*89294a78SLandon J. Fuller struct bhnd_core_info **cores, u_int *ncores, 329*89294a78SLandon J. Fuller bhnd_erom_class_t **eromcls) 330*89294a78SLandon J. Fuller { 331*89294a78SLandon J. Fuller const struct bhndb_hwcfg *cfg; 332*89294a78SLandon J. Fuller struct bhndb_host_resources *hr; 333*89294a78SLandon J. Fuller struct bhndb_pci_eio pio; 334*89294a78SLandon J. Fuller struct bhnd_core_info *erom_cores; 335*89294a78SLandon J. Fuller const struct bhnd_chipid *hint; 336*89294a78SLandon J. Fuller struct bhnd_chipid cid; 337*89294a78SLandon J. Fuller bhnd_erom_class_t *erom_class; 338*89294a78SLandon J. Fuller bhnd_erom_t *erom; 339*89294a78SLandon J. Fuller device_t parent_dev; 340*89294a78SLandon J. Fuller u_int erom_ncores; 341*89294a78SLandon J. Fuller int error; 342*89294a78SLandon J. Fuller 343*89294a78SLandon J. Fuller parent_dev = device_get_parent(dev); 344*89294a78SLandon J. Fuller erom = NULL; 345*89294a78SLandon J. Fuller erom_cores = NULL; 346*89294a78SLandon J. Fuller 347*89294a78SLandon J. Fuller /* Fetch our chipid hint (if any) and generic hardware configuration */ 348*89294a78SLandon J. Fuller cfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev); 349*89294a78SLandon J. Fuller hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev); 350*89294a78SLandon J. Fuller 351*89294a78SLandon J. Fuller /* Allocate our host resources */ 352*89294a78SLandon J. Fuller if ((error = bhndb_alloc_host_resources(parent_dev, cfg, &hr))) 353*89294a78SLandon J. Fuller return (error); 354*89294a78SLandon J. Fuller 355*89294a78SLandon J. Fuller /* Initialize our erom I/O state */ 356*89294a78SLandon J. Fuller if ((error = bhndb_pci_eio_init(&pio, dev, parent_dev, hr))) 357*89294a78SLandon J. Fuller goto failed; 358*89294a78SLandon J. Fuller 359*89294a78SLandon J. Fuller /* Map the first bus core from our bridged bhnd(4) bus */ 360*89294a78SLandon J. Fuller error = bhndb_pci_eio_map(&pio.eio, BHND_DEFAULT_CHIPC_ADDR, 361*89294a78SLandon J. Fuller BHND_DEFAULT_CORE_SIZE); 362*89294a78SLandon J. Fuller if (error) 363*89294a78SLandon J. Fuller goto failed; 364*89294a78SLandon J. Fuller 365*89294a78SLandon J. Fuller /* Probe for a usable EROM class, and read the chip identifier */ 366*89294a78SLandon J. Fuller erom_class = bhnd_erom_probe_driver_classes(device_get_devclass(dev), 367*89294a78SLandon J. Fuller &pio.eio, hint, &cid); 368*89294a78SLandon J. Fuller if (erom_class == NULL) { 369*89294a78SLandon J. Fuller device_printf(dev, "device enumeration unsupported; no " 370*89294a78SLandon J. Fuller "compatible driver found\n"); 371*89294a78SLandon J. Fuller 372*89294a78SLandon J. Fuller error = ENXIO; 373*89294a78SLandon J. Fuller goto failed; 374*89294a78SLandon J. Fuller } 375*89294a78SLandon J. Fuller 376*89294a78SLandon J. Fuller /* Allocate EROM parser */ 377*89294a78SLandon J. Fuller if ((erom = bhnd_erom_alloc(erom_class, &cid, &pio.eio)) == NULL) { 378*89294a78SLandon J. Fuller device_printf(dev, "failed to allocate device enumeration " 379*89294a78SLandon J. Fuller "table parser\n"); 380*89294a78SLandon J. Fuller error = ENXIO; 381*89294a78SLandon J. Fuller goto failed; 382*89294a78SLandon J. Fuller } 383*89294a78SLandon J. Fuller 384*89294a78SLandon J. Fuller /* Read the full core table */ 385*89294a78SLandon J. Fuller error = bhnd_erom_get_core_table(erom, &erom_cores, &erom_ncores); 386*89294a78SLandon J. Fuller if (error) { 387*89294a78SLandon J. Fuller device_printf(dev, "error fetching core table: %d\n", error); 388*89294a78SLandon J. Fuller goto failed; 389*89294a78SLandon J. Fuller } 390*89294a78SLandon J. Fuller 391*89294a78SLandon J. Fuller /* Provide the results to our caller */ 392*89294a78SLandon J. Fuller *cores = malloc(sizeof(erom_cores[0]) * erom_ncores, M_BHND, M_WAITOK); 393*89294a78SLandon J. Fuller memcpy(*cores, erom_cores, sizeof(erom_cores[0]) * erom_ncores); 394*89294a78SLandon J. Fuller *ncores = erom_ncores; 395*89294a78SLandon J. Fuller 396*89294a78SLandon J. Fuller *chipid = cid; 397*89294a78SLandon J. Fuller if (eromcls != NULL) 398*89294a78SLandon J. Fuller *eromcls = erom_class; 399*89294a78SLandon J. Fuller 400*89294a78SLandon J. Fuller /* Clean up */ 401*89294a78SLandon J. Fuller bhnd_erom_free_core_table(erom, erom_cores); 402*89294a78SLandon J. Fuller bhnd_erom_free(erom); 403*89294a78SLandon J. Fuller bhndb_release_host_resources(hr); 404*89294a78SLandon J. Fuller 405*89294a78SLandon J. Fuller return (0); 406*89294a78SLandon J. Fuller 407*89294a78SLandon J. Fuller failed: 408*89294a78SLandon J. Fuller if (erom_cores != NULL) 409*89294a78SLandon J. Fuller bhnd_erom_free_core_table(erom, erom_cores); 410*89294a78SLandon J. Fuller 411*89294a78SLandon J. Fuller if (erom != NULL) 412*89294a78SLandon J. Fuller bhnd_erom_free(erom); 413*89294a78SLandon J. Fuller 414*89294a78SLandon J. Fuller bhndb_release_host_resources(hr); 415*89294a78SLandon J. Fuller return (error); 416*89294a78SLandon J. Fuller } 417*89294a78SLandon J. Fuller 418111d7cb2SLandon J. Fuller static int 419111d7cb2SLandon J. Fuller bhndb_pci_add_children(struct bhndb_pci_softc *sc) 420111d7cb2SLandon J. Fuller { 421111d7cb2SLandon J. Fuller bus_size_t nv_sz; 422111d7cb2SLandon J. Fuller int error; 423111d7cb2SLandon J. Fuller 424111d7cb2SLandon J. Fuller /** 425111d7cb2SLandon J. Fuller * If SPROM is mapped directly into BAR0, add child NVRAM 426111d7cb2SLandon J. Fuller * device. 427111d7cb2SLandon J. Fuller */ 428e83ce340SAdrian Chadd nv_sz = bhndb_pci_sprom_size(sc); 429e83ce340SAdrian Chadd if (nv_sz > 0) { 430e83ce340SAdrian Chadd struct bhndb_devinfo *dinfo; 431111d7cb2SLandon J. Fuller device_t child; 432e83ce340SAdrian Chadd 433e83ce340SAdrian Chadd if (bootverbose) { 434111d7cb2SLandon J. Fuller device_printf(sc->dev, "found SPROM (%ju bytes)\n", 435111d7cb2SLandon J. Fuller (uintmax_t)nv_sz); 436e83ce340SAdrian Chadd } 437e83ce340SAdrian Chadd 438111d7cb2SLandon J. Fuller /* Add sprom device, ordered early enough to be available 439111d7cb2SLandon J. Fuller * before the bridged bhnd(4) bus is attached. */ 440111d7cb2SLandon J. Fuller child = BUS_ADD_CHILD(sc->dev, 441111d7cb2SLandon J. Fuller BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1); 442111d7cb2SLandon J. Fuller if (child == NULL) { 443111d7cb2SLandon J. Fuller device_printf(sc->dev, "failed to add sprom device\n"); 444e83ce340SAdrian Chadd return (ENXIO); 445e83ce340SAdrian Chadd } 446e83ce340SAdrian Chadd 447e83ce340SAdrian Chadd /* Initialize device address space and resource covering the 448e83ce340SAdrian Chadd * BAR0 SPROM shadow. */ 449111d7cb2SLandon J. Fuller dinfo = device_get_ivars(child); 450e83ce340SAdrian Chadd dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; 451111d7cb2SLandon J. Fuller 452111d7cb2SLandon J. Fuller error = bus_set_resource(child, SYS_RES_MEMORY, 0, 453e83ce340SAdrian Chadd bhndb_pci_sprom_addr(sc), nv_sz); 454e83ce340SAdrian Chadd if (error) { 455111d7cb2SLandon J. Fuller device_printf(sc->dev, 456e83ce340SAdrian Chadd "failed to register sprom resources\n"); 457e83ce340SAdrian Chadd return (error); 458e83ce340SAdrian Chadd } 459e83ce340SAdrian Chadd } 460e83ce340SAdrian Chadd 4614ad7e9b0SAdrian Chadd return (0); 4624ad7e9b0SAdrian Chadd } 4634ad7e9b0SAdrian Chadd 464e83ce340SAdrian Chadd static const struct bhndb_regwin * 465e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc) 466e83ce340SAdrian Chadd { 467e83ce340SAdrian Chadd struct bhndb_resources *bres; 468e83ce340SAdrian Chadd const struct bhndb_hwcfg *cfg; 469e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 470e83ce340SAdrian Chadd 471e83ce340SAdrian Chadd bres = sc->bhndb.bus_res; 472e83ce340SAdrian Chadd cfg = bres->cfg; 473e83ce340SAdrian Chadd 474e83ce340SAdrian Chadd sprom_win = bhndb_regwin_find_type(cfg->register_windows, 475e83ce340SAdrian Chadd BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE); 476e83ce340SAdrian Chadd 477e83ce340SAdrian Chadd return (sprom_win); 478e83ce340SAdrian Chadd } 479e83ce340SAdrian Chadd 480e83ce340SAdrian Chadd static bus_addr_t 481e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc) 482e83ce340SAdrian Chadd { 483e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 484e83ce340SAdrian Chadd struct resource *r; 485e83ce340SAdrian Chadd 486e83ce340SAdrian Chadd /* Fetch the SPROM register window */ 487e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 488e83ce340SAdrian Chadd KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+")); 489e83ce340SAdrian Chadd 490e83ce340SAdrian Chadd /* Fetch the associated resource */ 491*89294a78SLandon J. Fuller r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win); 492e83ce340SAdrian Chadd KASSERT(r != NULL, ("missing resource for sprom window\n")); 493e83ce340SAdrian Chadd 494e83ce340SAdrian Chadd return (rman_get_start(r) + sprom_win->win_offset); 495e83ce340SAdrian Chadd } 496e83ce340SAdrian Chadd 497e83ce340SAdrian Chadd static bus_size_t 498e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc) 499e83ce340SAdrian Chadd { 500e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 501e83ce340SAdrian Chadd uint32_t sctl; 502e83ce340SAdrian Chadd bus_size_t sprom_sz; 503e83ce340SAdrian Chadd 504e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 505e83ce340SAdrian Chadd 506e83ce340SAdrian Chadd /* PCI_V2 and later devices map SPROM/OTP via ChipCommon */ 507e83ce340SAdrian Chadd if (sprom_win == NULL) 508e83ce340SAdrian Chadd return (0); 509e83ce340SAdrian Chadd 510e83ce340SAdrian Chadd /* Determine SPROM size */ 511e83ce340SAdrian Chadd sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4); 512e83ce340SAdrian Chadd if (sctl & BHNDB_PCI_SPROM_BLANK) 513e83ce340SAdrian Chadd return (0); 514e83ce340SAdrian Chadd 515e83ce340SAdrian Chadd switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) { 516e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_1KB: 517e83ce340SAdrian Chadd sprom_sz = (1 * 1024); 518e83ce340SAdrian Chadd break; 519e83ce340SAdrian Chadd 520e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_4KB: 521e83ce340SAdrian Chadd sprom_sz = (4 * 1024); 522e83ce340SAdrian Chadd break; 523e83ce340SAdrian Chadd 524e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_16KB: 525e83ce340SAdrian Chadd sprom_sz = (16 * 1024); 526e83ce340SAdrian Chadd break; 527e83ce340SAdrian Chadd 528e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_RESERVED: 529e83ce340SAdrian Chadd default: 530e83ce340SAdrian Chadd device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl); 531e83ce340SAdrian Chadd return (0); 532e83ce340SAdrian Chadd } 533e83ce340SAdrian Chadd 534e83ce340SAdrian Chadd if (sprom_sz > sprom_win->win_size) { 535e83ce340SAdrian Chadd device_printf(sc->dev, 536e83ce340SAdrian Chadd "PCI sprom size (0x%x) overruns defined register window\n", 537e83ce340SAdrian Chadd sctl); 538e83ce340SAdrian Chadd return (0); 539e83ce340SAdrian Chadd } 540e83ce340SAdrian Chadd 541e83ce340SAdrian Chadd return (sprom_sz); 542e83ce340SAdrian Chadd } 543e83ce340SAdrian Chadd 5444ad7e9b0SAdrian Chadd /* 5454ad7e9b0SAdrian Chadd * On devices without a SROM, the PCI(e) cores will be initialized with 546bb64eeccSAdrian Chadd * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows 547bb64eeccSAdrian Chadd * mapped to the wrong core. 5484ad7e9b0SAdrian Chadd * 549bb64eeccSAdrian Chadd * This function updates the SROM shadow to point the BAR0 windows at the 5504ad7e9b0SAdrian Chadd * current PCI core. 5514ad7e9b0SAdrian Chadd * 552bb64eeccSAdrian Chadd * Applies to all PCI/PCIe revisions. 5534ad7e9b0SAdrian Chadd */ 5544ad7e9b0SAdrian Chadd static void 5554ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc) 5564ad7e9b0SAdrian Chadd { 557bb64eeccSAdrian Chadd struct bhndb_resources *bres; 558bb64eeccSAdrian Chadd const struct bhndb_hwcfg *cfg; 559bb64eeccSAdrian Chadd const struct bhndb_regwin *win; 560111d7cb2SLandon J. Fuller struct bhnd_core_info hostb_core; 561bb64eeccSAdrian Chadd struct resource *core_regs; 562bb64eeccSAdrian Chadd bus_size_t srom_offset; 563bb64eeccSAdrian Chadd u_int pci_cidx, sprom_cidx; 5644ad7e9b0SAdrian Chadd uint16_t val; 565111d7cb2SLandon J. Fuller int error; 5664ad7e9b0SAdrian Chadd 567bb64eeccSAdrian Chadd bres = sc->bhndb.bus_res; 568bb64eeccSAdrian Chadd cfg = bres->cfg; 569bb64eeccSAdrian Chadd 570111d7cb2SLandon J. Fuller /* Find our hostb core */ 571111d7cb2SLandon J. Fuller error = BHNDB_GET_HOSTB_CORE(sc->dev, sc->bhndb.bus_dev, &hostb_core); 572111d7cb2SLandon J. Fuller if (error) { 573111d7cb2SLandon J. Fuller device_printf(sc->dev, "no host bridge device found\n"); 574111d7cb2SLandon J. Fuller return; 575111d7cb2SLandon J. Fuller } 576111d7cb2SLandon J. Fuller 577111d7cb2SLandon J. Fuller if (hostb_core.vendor != BHND_MFGID_BCM) 578bb64eeccSAdrian Chadd return; 579bb64eeccSAdrian Chadd 580111d7cb2SLandon J. Fuller switch (hostb_core.device) { 581bb64eeccSAdrian Chadd case BHND_COREID_PCI: 582bb64eeccSAdrian Chadd srom_offset = BHND_PCI_SRSH_PI_OFFSET; 583bb64eeccSAdrian Chadd break; 584bb64eeccSAdrian Chadd case BHND_COREID_PCIE: 585bb64eeccSAdrian Chadd srom_offset = BHND_PCIE_SRSH_PI_OFFSET; 586bb64eeccSAdrian Chadd break; 587bb64eeccSAdrian Chadd default: 588bb64eeccSAdrian Chadd device_printf(sc->dev, "unsupported PCI host bridge device\n"); 589bb64eeccSAdrian Chadd return; 590bb64eeccSAdrian Chadd } 591bb64eeccSAdrian Chadd 592bb64eeccSAdrian Chadd /* Locate the static register window mapping the PCI core */ 593bb64eeccSAdrian Chadd win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass, 594bb64eeccSAdrian Chadd 0, BHND_PORT_DEVICE, 0, 0); 595bb64eeccSAdrian Chadd if (win == NULL) { 596bb64eeccSAdrian Chadd device_printf(sc->dev, "missing PCI core register window\n"); 597bb64eeccSAdrian Chadd return; 598bb64eeccSAdrian Chadd } 599bb64eeccSAdrian Chadd 600bb64eeccSAdrian Chadd /* Fetch the resource containing the register window */ 601*89294a78SLandon J. Fuller core_regs = bhndb_host_resource_for_regwin(bres->res, win); 602bb64eeccSAdrian Chadd if (core_regs == NULL) { 603bb64eeccSAdrian Chadd device_printf(sc->dev, "missing PCI core register resource\n"); 604bb64eeccSAdrian Chadd return; 605bb64eeccSAdrian Chadd } 606bb64eeccSAdrian Chadd 6074ad7e9b0SAdrian Chadd /* Fetch the SPROM's configured core index */ 608bb64eeccSAdrian Chadd val = bus_read_2(core_regs, win->win_offset + srom_offset); 609bb64eeccSAdrian Chadd sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; 6104ad7e9b0SAdrian Chadd 6114ad7e9b0SAdrian Chadd /* If it doesn't match host bridge's core index, update the index 6124ad7e9b0SAdrian Chadd * value */ 613111d7cb2SLandon J. Fuller pci_cidx = hostb_core.core_idx; 614bb64eeccSAdrian Chadd if (sprom_cidx != pci_cidx) { 615bb64eeccSAdrian Chadd val &= ~BHND_PCI_SRSH_PI_MASK; 616bb64eeccSAdrian Chadd val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT); 617bb64eeccSAdrian Chadd bus_write_2(core_regs, 618bb64eeccSAdrian Chadd win->win_offset + srom_offset, val); 6194ad7e9b0SAdrian Chadd } 6204ad7e9b0SAdrian Chadd } 6214ad7e9b0SAdrian Chadd 6224ad7e9b0SAdrian Chadd static int 6234ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev) 6244ad7e9b0SAdrian Chadd { 6254ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 6264ad7e9b0SAdrian Chadd int error; 6274ad7e9b0SAdrian Chadd 6284ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 6294ad7e9b0SAdrian Chadd 630bb64eeccSAdrian Chadd /* Enable clocks (if supported by this hardware) */ 631*89294a78SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(sc->dev))) 6324ad7e9b0SAdrian Chadd return (error); 6334ad7e9b0SAdrian Chadd 634bb64eeccSAdrian Chadd /* Perform resume */ 635bb64eeccSAdrian Chadd return (bhndb_generic_resume(dev)); 636bb64eeccSAdrian Chadd } 637bb64eeccSAdrian Chadd 638bb64eeccSAdrian Chadd static int 639bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev) 640bb64eeccSAdrian Chadd { 641bb64eeccSAdrian Chadd struct bhndb_pci_softc *sc; 642bb64eeccSAdrian Chadd int error; 643bb64eeccSAdrian Chadd 644bb64eeccSAdrian Chadd sc = device_get_softc(dev); 645bb64eeccSAdrian Chadd 646bb64eeccSAdrian Chadd /* Disable clocks (if supported by this hardware) */ 647*89294a78SLandon J. Fuller if ((error = bhndb_disable_pci_clocks(sc->dev))) 6484ad7e9b0SAdrian Chadd return (error); 6494ad7e9b0SAdrian Chadd 650bb64eeccSAdrian Chadd /* Perform suspend */ 651bb64eeccSAdrian Chadd return (bhndb_generic_suspend(dev)); 652bb64eeccSAdrian Chadd } 653bb64eeccSAdrian Chadd 654bb64eeccSAdrian Chadd static int 6554ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw, 6564ad7e9b0SAdrian Chadd bhnd_addr_t addr) 6574ad7e9b0SAdrian Chadd { 6584ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc = device_get_softc(dev); 659*89294a78SLandon J. Fuller return (sc->set_regwin(sc->dev, sc->parent, rw, addr)); 6604ad7e9b0SAdrian Chadd } 6614ad7e9b0SAdrian Chadd 6624ad7e9b0SAdrian Chadd /** 6634ad7e9b0SAdrian Chadd * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. 6644ad7e9b0SAdrian Chadd * 6654ad7e9b0SAdrian Chadd * On siba(4) devices, it's possible that writing a PCI window register may 6664ad7e9b0SAdrian Chadd * not succeed; it's necessary to immediately read the configuration register 6674ad7e9b0SAdrian Chadd * and retry if not set to the desired value. 6684ad7e9b0SAdrian Chadd * 6694ad7e9b0SAdrian Chadd * This is not necessary on bcma(4) devices, but other than the overhead of 6704ad7e9b0SAdrian Chadd * validating the register, there's no harm in performing the verification. 6714ad7e9b0SAdrian Chadd */ 6724ad7e9b0SAdrian Chadd static int 673*89294a78SLandon J. Fuller bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev, 6744ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 6754ad7e9b0SAdrian Chadd { 6764ad7e9b0SAdrian Chadd int error; 677e83ce340SAdrian Chadd int reg; 6784ad7e9b0SAdrian Chadd 6794ad7e9b0SAdrian Chadd if (rw->win_type != BHNDB_REGWIN_T_DYN) 6804ad7e9b0SAdrian Chadd return (ENODEV); 6814ad7e9b0SAdrian Chadd 682e83ce340SAdrian Chadd reg = rw->d.dyn.cfg_offset; 6834ad7e9b0SAdrian Chadd for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { 684*89294a78SLandon J. Fuller if ((error = bhndb_pci_fast_setregwin(dev, pci_dev, rw, addr))) 6854ad7e9b0SAdrian Chadd return (error); 6864ad7e9b0SAdrian Chadd 687*89294a78SLandon J. Fuller if (pci_read_config(pci_dev, reg, 4) == addr) 6884ad7e9b0SAdrian Chadd return (0); 6894ad7e9b0SAdrian Chadd 6904ad7e9b0SAdrian Chadd DELAY(10); 6914ad7e9b0SAdrian Chadd } 6924ad7e9b0SAdrian Chadd 6934ad7e9b0SAdrian Chadd /* Unable to set window */ 6944ad7e9b0SAdrian Chadd return (ENODEV); 6954ad7e9b0SAdrian Chadd } 6964ad7e9b0SAdrian Chadd 6974ad7e9b0SAdrian Chadd /** 6984ad7e9b0SAdrian Chadd * A bcma(4)-only bhndb_set_window_addr implementation. 6994ad7e9b0SAdrian Chadd */ 7004ad7e9b0SAdrian Chadd static int 701*89294a78SLandon J. Fuller bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev, 7024ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 7034ad7e9b0SAdrian Chadd { 7044ad7e9b0SAdrian Chadd /* The PCI bridge core only supports 32-bit addressing, regardless 7054ad7e9b0SAdrian Chadd * of the bus' support for 64-bit addressing */ 7064ad7e9b0SAdrian Chadd if (addr > UINT32_MAX) 7074ad7e9b0SAdrian Chadd return (ERANGE); 7084ad7e9b0SAdrian Chadd 7094ad7e9b0SAdrian Chadd switch (rw->win_type) { 7104ad7e9b0SAdrian Chadd case BHNDB_REGWIN_T_DYN: 7114ad7e9b0SAdrian Chadd /* Addresses must be page aligned */ 7124ad7e9b0SAdrian Chadd if (addr % rw->win_size != 0) 7134ad7e9b0SAdrian Chadd return (EINVAL); 7144ad7e9b0SAdrian Chadd 715*89294a78SLandon J. Fuller pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4); 7164ad7e9b0SAdrian Chadd break; 7174ad7e9b0SAdrian Chadd default: 7184ad7e9b0SAdrian Chadd return (ENODEV); 7194ad7e9b0SAdrian Chadd } 7204ad7e9b0SAdrian Chadd 7214ad7e9b0SAdrian Chadd return (0); 7224ad7e9b0SAdrian Chadd } 7234ad7e9b0SAdrian Chadd 724d567592bSAdrian Chadd static int 725d567592bSAdrian Chadd bhndb_pci_populate_board_info(device_t dev, device_t child, 726d567592bSAdrian Chadd struct bhnd_board_info *info) 727d567592bSAdrian Chadd { 728d567592bSAdrian Chadd struct bhndb_pci_softc *sc; 729d567592bSAdrian Chadd 730d567592bSAdrian Chadd sc = device_get_softc(dev); 731d567592bSAdrian Chadd 7328ef24a0dSAdrian Chadd /* 7338ef24a0dSAdrian Chadd * On a subset of Apple BCM4360 modules, always prefer the 7348ef24a0dSAdrian Chadd * PCI subdevice to the SPROM-supplied boardtype. 7358ef24a0dSAdrian Chadd * 7368ef24a0dSAdrian Chadd * TODO: 7378ef24a0dSAdrian Chadd * 7388ef24a0dSAdrian Chadd * Broadcom's own drivers implement this override, and then later use 7398ef24a0dSAdrian Chadd * the remapped BCM4360 board type to determine the required 7408ef24a0dSAdrian Chadd * board-specific workarounds. 7418ef24a0dSAdrian Chadd * 7428ef24a0dSAdrian Chadd * Without access to this hardware, it's unclear why this mapping 7438ef24a0dSAdrian Chadd * is done, and we must do the same. If we can survey the hardware 7448ef24a0dSAdrian Chadd * in question, it may be possible to replace this behavior with 7458ef24a0dSAdrian Chadd * explicit references to the SPROM-supplied boardtype(s) in our 7468ef24a0dSAdrian Chadd * quirk definitions. 7478ef24a0dSAdrian Chadd */ 7488ef24a0dSAdrian Chadd if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) { 7498ef24a0dSAdrian Chadd switch (info->board_type) { 7508ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X29C: 7518ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X29CP2: 7528ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X51: 7538ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X51P2: 7548ef24a0dSAdrian Chadd info->board_type = 0; /* allow override below */ 7558ef24a0dSAdrian Chadd break; 7568ef24a0dSAdrian Chadd default: 7578ef24a0dSAdrian Chadd break; 7588ef24a0dSAdrian Chadd } 7598ef24a0dSAdrian Chadd } 7608ef24a0dSAdrian Chadd 761d567592bSAdrian Chadd /* If NVRAM did not supply vendor/type info, provide the PCI 762d567592bSAdrian Chadd * subvendor/subdevice values. */ 763d567592bSAdrian Chadd if (info->board_vendor == 0) 764d567592bSAdrian Chadd info->board_vendor = pci_get_subvendor(sc->parent); 765d567592bSAdrian Chadd 766d567592bSAdrian Chadd if (info->board_type == 0) 767d567592bSAdrian Chadd info->board_type = pci_get_subdevice(sc->parent); 768d567592bSAdrian Chadd 769d567592bSAdrian Chadd return (0); 770d567592bSAdrian Chadd } 771d567592bSAdrian Chadd 7724ad7e9b0SAdrian Chadd /** 773*89294a78SLandon J. Fuller * Return true if the bridge device @p bhndb is attached via PCIe, 774*89294a78SLandon J. Fuller * false otherwise. 775*89294a78SLandon J. Fuller * 776*89294a78SLandon J. Fuller * @param dev The bhndb bridge device 777*89294a78SLandon J. Fuller */ 778*89294a78SLandon J. Fuller static bool 779*89294a78SLandon J. Fuller bhndb_is_pcie_attached(device_t dev) 780*89294a78SLandon J. Fuller { 781*89294a78SLandon J. Fuller int reg; 782*89294a78SLandon J. Fuller 783*89294a78SLandon J. Fuller if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, ®) == 0) 784*89294a78SLandon J. Fuller return (true); 785*89294a78SLandon J. Fuller 786*89294a78SLandon J. Fuller return (false); 787*89294a78SLandon J. Fuller } 788*89294a78SLandon J. Fuller 789*89294a78SLandon J. Fuller /** 790bb64eeccSAdrian Chadd * Enable externally managed clocks, if required. 7914ad7e9b0SAdrian Chadd * 792bb64eeccSAdrian Chadd * Some PCI chipsets (BCM4306, possibly others) chips do not support 793bb64eeccSAdrian Chadd * the idle low-power clock. Clocking must be bootstrapped at 794bb64eeccSAdrian Chadd * attach/resume by directly adjusting GPIO registers exposed in the 795bb64eeccSAdrian Chadd * PCI config space, and correspondingly, explicitly shutdown at 796bb64eeccSAdrian Chadd * detach/suspend. 7974ad7e9b0SAdrian Chadd * 798*89294a78SLandon J. Fuller * @note This function may be safely called prior to device attach, (e.g. 799*89294a78SLandon J. Fuller * from DEVICE_PROBE). 800*89294a78SLandon J. Fuller * 801*89294a78SLandon J. Fuller * @param dev The bhndb bridge device 8024ad7e9b0SAdrian Chadd */ 8034ad7e9b0SAdrian Chadd static int 804*89294a78SLandon J. Fuller bhndb_enable_pci_clocks(device_t dev) 8054ad7e9b0SAdrian Chadd { 806*89294a78SLandon J. Fuller device_t pci_dev; 8074ad7e9b0SAdrian Chadd uint32_t gpio_in, gpio_out, gpio_en; 8084ad7e9b0SAdrian Chadd uint32_t gpio_flags; 8094ad7e9b0SAdrian Chadd uint16_t pci_status; 8104ad7e9b0SAdrian Chadd 811*89294a78SLandon J. Fuller pci_dev = device_get_parent(dev); 812*89294a78SLandon J. Fuller 813bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 814*89294a78SLandon J. Fuller if (!bhndb_is_pcie_attached(dev)) 815bb64eeccSAdrian Chadd return (0); 8164ad7e9b0SAdrian Chadd 8174ad7e9b0SAdrian Chadd /* Read state of XTAL pin */ 818*89294a78SLandon J. Fuller gpio_in = pci_read_config(pci_dev, BHNDB_PCI_GPIO_IN, 4); 8194ad7e9b0SAdrian Chadd if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) 8204ad7e9b0SAdrian Chadd return (0); /* already enabled */ 8214ad7e9b0SAdrian Chadd 8224ad7e9b0SAdrian Chadd /* Fetch current config */ 823*89294a78SLandon J. Fuller gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4); 824*89294a78SLandon J. Fuller gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4); 8254ad7e9b0SAdrian Chadd 8264ad7e9b0SAdrian Chadd /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ 8274ad7e9b0SAdrian Chadd gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 8284ad7e9b0SAdrian Chadd gpio_out |= gpio_flags; 8294ad7e9b0SAdrian Chadd gpio_en |= gpio_flags; 8304ad7e9b0SAdrian Chadd 831*89294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 832*89294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 8334ad7e9b0SAdrian Chadd DELAY(1000); 8344ad7e9b0SAdrian Chadd 8354ad7e9b0SAdrian Chadd /* Reset PLL_OFF */ 8364ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; 837*89294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 8384ad7e9b0SAdrian Chadd DELAY(5000); 8394ad7e9b0SAdrian Chadd 8404ad7e9b0SAdrian Chadd /* Clear any PCI 'sent target-abort' flag. */ 841*89294a78SLandon J. Fuller pci_status = pci_read_config(pci_dev, PCIR_STATUS, 2); 8424ad7e9b0SAdrian Chadd pci_status &= ~PCIM_STATUS_STABORT; 843*89294a78SLandon J. Fuller pci_write_config(pci_dev, PCIR_STATUS, pci_status, 2); 8444ad7e9b0SAdrian Chadd 8454ad7e9b0SAdrian Chadd return (0); 8464ad7e9b0SAdrian Chadd } 8474ad7e9b0SAdrian Chadd 8484ad7e9b0SAdrian Chadd /** 849bb64eeccSAdrian Chadd * Disable externally managed clocks, if required. 8504ad7e9b0SAdrian Chadd * 851*89294a78SLandon J. Fuller * This function may be safely called prior to device attach, (e.g. 852*89294a78SLandon J. Fuller * from DEVICE_PROBE). 853*89294a78SLandon J. Fuller * 854*89294a78SLandon J. Fuller * @param dev The bhndb bridge device 8554ad7e9b0SAdrian Chadd */ 8564ad7e9b0SAdrian Chadd static int 857*89294a78SLandon J. Fuller bhndb_disable_pci_clocks(device_t dev) 8584ad7e9b0SAdrian Chadd { 859*89294a78SLandon J. Fuller device_t pci_dev; 8604ad7e9b0SAdrian Chadd uint32_t gpio_out, gpio_en; 8614ad7e9b0SAdrian Chadd 862*89294a78SLandon J. Fuller pci_dev = device_get_parent(dev); 863*89294a78SLandon J. Fuller 864bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 865*89294a78SLandon J. Fuller if (bhndb_is_pcie_attached(dev)) 866bb64eeccSAdrian Chadd return (0); 8674ad7e9b0SAdrian Chadd 8684ad7e9b0SAdrian Chadd /* Fetch current config */ 869*89294a78SLandon J. Fuller gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4); 870*89294a78SLandon J. Fuller gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4); 8714ad7e9b0SAdrian Chadd 8724ad7e9b0SAdrian Chadd /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ 8734ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; 8744ad7e9b0SAdrian Chadd gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; 875*89294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 8764ad7e9b0SAdrian Chadd 8774ad7e9b0SAdrian Chadd /* Enable both output pins */ 8784ad7e9b0SAdrian Chadd gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 879*89294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 8804ad7e9b0SAdrian Chadd 8814ad7e9b0SAdrian Chadd return (0); 8824ad7e9b0SAdrian Chadd } 8834ad7e9b0SAdrian Chadd 884f90f4b65SLandon J. Fuller static bhnd_clksrc 885f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child, 886f90f4b65SLandon J. Fuller bhnd_clock clock) 887f90f4b65SLandon J. Fuller { 888f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc; 889f90f4b65SLandon J. Fuller uint32_t gpio_out; 890f90f4b65SLandon J. Fuller 891f90f4b65SLandon J. Fuller sc = device_get_softc(dev); 892f90f4b65SLandon J. Fuller 893f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 894*89294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 895f90f4b65SLandon J. Fuller return (ENODEV); 896f90f4b65SLandon J. Fuller 897f90f4b65SLandon J. Fuller /* Only ILP is supported */ 898f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_ILP) 899f90f4b65SLandon J. Fuller return (ENXIO); 900f90f4b65SLandon J. Fuller 901f90f4b65SLandon J. Fuller gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); 902f90f4b65SLandon J. Fuller if (gpio_out & BHNDB_PCI_GPIO_SCS) 903f90f4b65SLandon J. Fuller return (BHND_CLKSRC_PCI); 904f90f4b65SLandon J. Fuller else 905f90f4b65SLandon J. Fuller return (BHND_CLKSRC_XTAL); 906f90f4b65SLandon J. Fuller } 907f90f4b65SLandon J. Fuller 908f90f4b65SLandon J. Fuller static int 909f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child, 910f90f4b65SLandon J. Fuller bhnd_clock clock) 911f90f4b65SLandon J. Fuller { 912f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 913f90f4b65SLandon J. Fuller 914f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 915*89294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 916f90f4b65SLandon J. Fuller return (ENODEV); 917f90f4b65SLandon J. Fuller 918f90f4b65SLandon J. Fuller /* Only HT is supported */ 919f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_HT) 920f90f4b65SLandon J. Fuller return (ENXIO); 921f90f4b65SLandon J. Fuller 922*89294a78SLandon J. Fuller return (bhndb_disable_pci_clocks(sc->dev)); 923f90f4b65SLandon J. Fuller } 924f90f4b65SLandon J. Fuller 925f90f4b65SLandon J. Fuller static int 926f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child, 927f90f4b65SLandon J. Fuller bhnd_clock clock) 928f90f4b65SLandon J. Fuller { 929f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 930f90f4b65SLandon J. Fuller 931f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 932*89294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 933f90f4b65SLandon J. Fuller return (ENODEV); 934f90f4b65SLandon J. Fuller 935f90f4b65SLandon J. Fuller /* Only HT is supported */ 936f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_HT) 937f90f4b65SLandon J. Fuller return (ENXIO); 938f90f4b65SLandon J. Fuller 939*89294a78SLandon J. Fuller return (bhndb_enable_pci_clocks(sc->dev)); 940f90f4b65SLandon J. Fuller } 941f90f4b65SLandon J. Fuller 942824b48efSLandon J. Fuller static int 943824b48efSLandon J. Fuller bhndb_pci_assign_intr(device_t dev, device_t child, int rid) 944824b48efSLandon J. Fuller { 945824b48efSLandon J. Fuller struct bhndb_pci_softc *sc; 946824b48efSLandon J. Fuller rman_res_t start, count; 947824b48efSLandon J. Fuller int error; 948824b48efSLandon J. Fuller 949824b48efSLandon J. Fuller sc = device_get_softc(dev); 950824b48efSLandon J. Fuller 951824b48efSLandon J. Fuller /* Is the rid valid? */ 952824b48efSLandon J. Fuller if (rid >= bhnd_get_intr_count(child)) 953824b48efSLandon J. Fuller return (EINVAL); 954824b48efSLandon J. Fuller 955824b48efSLandon J. Fuller /* Fetch our common PCI interrupt's start/count. */ 956824b48efSLandon J. Fuller error = bus_get_resource(sc->parent, SYS_RES_IRQ, sc->intr.intr_rid, 957824b48efSLandon J. Fuller &start, &count); 958824b48efSLandon J. Fuller if (error) 959824b48efSLandon J. Fuller return (error); 960824b48efSLandon J. Fuller 961824b48efSLandon J. Fuller /* Add to child's resource list */ 962824b48efSLandon J. Fuller return (bus_set_resource(child, SYS_RES_IRQ, rid, start, count)); 963824b48efSLandon J. Fuller } 964824b48efSLandon J. Fuller 965*89294a78SLandon J. Fuller /** 966*89294a78SLandon J. Fuller * Initialize a new bhndb PCI bridge EROM I/O instance. This EROM I/O 967*89294a78SLandon J. Fuller * implementation supports mapping of the device enumeration table via the 968*89294a78SLandon J. Fuller * @p hr host resources. 969*89294a78SLandon J. Fuller * 970*89294a78SLandon J. Fuller * @param pio The instance to be initialized. 971*89294a78SLandon J. Fuller * @param dev The bridge device. 972*89294a78SLandon J. Fuller * @param pci_dev The bridge's parent PCI device. 973*89294a78SLandon J. Fuller * @param hr The host resources to be used to map the device 974*89294a78SLandon J. Fuller * enumeration table. 975*89294a78SLandon J. Fuller */ 976*89294a78SLandon J. Fuller static int 977*89294a78SLandon J. Fuller bhndb_pci_eio_init(struct bhndb_pci_eio *pio, device_t dev, device_t pci_dev, 978*89294a78SLandon J. Fuller struct bhndb_host_resources *hr) 979*89294a78SLandon J. Fuller { 980*89294a78SLandon J. Fuller memset(&pio->eio, sizeof(pio->eio), 0); 981*89294a78SLandon J. Fuller pio->eio.map = bhndb_pci_eio_map; 982*89294a78SLandon J. Fuller pio->eio.read = bhndb_pci_eio_read; 983*89294a78SLandon J. Fuller pio->eio.fini = NULL; 984*89294a78SLandon J. Fuller 985*89294a78SLandon J. Fuller pio->dev = dev; 986*89294a78SLandon J. Fuller pio->pci_dev = pci_dev; 987*89294a78SLandon J. Fuller pio->hr = hr; 988*89294a78SLandon J. Fuller pio->win = NULL; 989*89294a78SLandon J. Fuller pio->res = NULL; 990*89294a78SLandon J. Fuller 991*89294a78SLandon J. Fuller return (0); 992*89294a78SLandon J. Fuller } 993*89294a78SLandon J. Fuller 994*89294a78SLandon J. Fuller /** 995*89294a78SLandon J. Fuller * Attempt to adjust the dynamic register window backing @p pio to permit 996*89294a78SLandon J. Fuller * reading @p size bytes at @p addr. 997*89294a78SLandon J. Fuller * 998*89294a78SLandon J. Fuller * If @p addr or @p size fall outside the existing mapped range, or if 999*89294a78SLandon J. Fuller * @p pio is not backed by a dynamic register window, ENXIO will be returned. 1000*89294a78SLandon J. Fuller * 1001*89294a78SLandon J. Fuller * @param pio The bhndb PCI erom I/O state to be modified. 1002*89294a78SLandon J. Fuller * @param addr The address to be include 1003*89294a78SLandon J. Fuller */ 1004*89294a78SLandon J. Fuller static int 1005*89294a78SLandon J. Fuller bhndb_pci_eio_adjust_mapping(struct bhndb_pci_eio *pio, bhnd_addr_t addr, 1006*89294a78SLandon J. Fuller bhnd_size_t size) 1007*89294a78SLandon J. Fuller { 1008*89294a78SLandon J. Fuller bhnd_addr_t target; 1009*89294a78SLandon J. Fuller bhnd_size_t offset; 1010*89294a78SLandon J. Fuller int error; 1011*89294a78SLandon J. Fuller 1012*89294a78SLandon J. Fuller 1013*89294a78SLandon J. Fuller KASSERT(pio->win != NULL, ("missing register window")); 1014*89294a78SLandon J. Fuller KASSERT(pio->res != NULL, ("missing regwin resource")); 1015*89294a78SLandon J. Fuller KASSERT(pio->win->win_type == BHNDB_REGWIN_T_DYN, 1016*89294a78SLandon J. Fuller ("unexpected window type %d", pio->win->win_type)); 1017*89294a78SLandon J. Fuller 1018*89294a78SLandon J. Fuller /* The requested subrange must fall within the total mapped range */ 1019*89294a78SLandon J. Fuller if (addr < pio->addr || (addr - pio->addr) > pio->size || 1020*89294a78SLandon J. Fuller size > pio->size || (addr - pio->addr) - pio->size < size) 1021*89294a78SLandon J. Fuller { 1022*89294a78SLandon J. Fuller return (ENXIO); 1023*89294a78SLandon J. Fuller } 1024*89294a78SLandon J. Fuller 1025*89294a78SLandon J. Fuller /* Do we already have a useable mapping? */ 1026*89294a78SLandon J. Fuller if (addr >= pio->res_target && 1027*89294a78SLandon J. Fuller addr <= pio->res_target + pio->win->win_size && 1028*89294a78SLandon J. Fuller (pio->res_target + pio->win->win_size) - addr >= size) 1029*89294a78SLandon J. Fuller { 1030*89294a78SLandon J. Fuller return (0); 1031*89294a78SLandon J. Fuller } 1032*89294a78SLandon J. Fuller 1033*89294a78SLandon J. Fuller /* Page-align the target address */ 1034*89294a78SLandon J. Fuller offset = addr % pio->win->win_size; 1035*89294a78SLandon J. Fuller target = addr - offset; 1036*89294a78SLandon J. Fuller 1037*89294a78SLandon J. Fuller /* Configure the register window */ 1038*89294a78SLandon J. Fuller error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, pio->win, 1039*89294a78SLandon J. Fuller target); 1040*89294a78SLandon J. Fuller if (error) { 1041*89294a78SLandon J. Fuller device_printf(pio->dev, "failed to configure dynamic register " 1042*89294a78SLandon J. Fuller "window: %d\n", error); 1043*89294a78SLandon J. Fuller return (error); 1044*89294a78SLandon J. Fuller } 1045*89294a78SLandon J. Fuller 1046*89294a78SLandon J. Fuller pio->res_target = target; 1047*89294a78SLandon J. Fuller return (0); 1048*89294a78SLandon J. Fuller } 1049*89294a78SLandon J. Fuller 1050*89294a78SLandon J. Fuller /* bhnd_erom_io_map() implementation */ 1051*89294a78SLandon J. Fuller static int 1052*89294a78SLandon J. Fuller bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 1053*89294a78SLandon J. Fuller bhnd_size_t size) 1054*89294a78SLandon J. Fuller { 1055*89294a78SLandon J. Fuller struct bhndb_pci_eio *pio; 1056*89294a78SLandon J. Fuller const struct bhndb_regwin *regwin; 1057*89294a78SLandon J. Fuller struct resource *r; 1058*89294a78SLandon J. Fuller bhnd_addr_t target; 1059*89294a78SLandon J. Fuller bhnd_size_t offset; 1060*89294a78SLandon J. Fuller int error; 1061*89294a78SLandon J. Fuller 1062*89294a78SLandon J. Fuller pio = (struct bhndb_pci_eio *)eio; 1063*89294a78SLandon J. Fuller 1064*89294a78SLandon J. Fuller /* Locate a useable dynamic register window */ 1065*89294a78SLandon J. Fuller regwin = bhndb_regwin_find_type(pio->hr->cfg->register_windows, 1066*89294a78SLandon J. Fuller BHNDB_REGWIN_T_DYN, MIN(size, BHND_DEFAULT_CORE_SIZE)); 1067*89294a78SLandon J. Fuller if (regwin == NULL) { 1068*89294a78SLandon J. Fuller device_printf(pio->dev, "unable to map %#jx+%#jx; no " 1069*89294a78SLandon J. Fuller "usable dynamic register window found\n", addr, size); 1070*89294a78SLandon J. Fuller return (ENXIO); 1071*89294a78SLandon J. Fuller } 1072*89294a78SLandon J. Fuller 1073*89294a78SLandon J. Fuller /* Locate the host resource mapping our register window */ 1074*89294a78SLandon J. Fuller if ((r = bhndb_host_resource_for_regwin(pio->hr, regwin)) == NULL) { 1075*89294a78SLandon J. Fuller device_printf(pio->dev, "unable to map %#jx+%#jx; no " 1076*89294a78SLandon J. Fuller "usable register resource found\n", addr, size); 1077*89294a78SLandon J. Fuller return (ENXIO); 1078*89294a78SLandon J. Fuller } 1079*89294a78SLandon J. Fuller 1080*89294a78SLandon J. Fuller /* Page-align the target address */ 1081*89294a78SLandon J. Fuller offset = addr % regwin->win_size; 1082*89294a78SLandon J. Fuller target = addr - offset; 1083*89294a78SLandon J. Fuller 1084*89294a78SLandon J. Fuller /* Configure the register window */ 1085*89294a78SLandon J. Fuller error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, regwin, 1086*89294a78SLandon J. Fuller target); 1087*89294a78SLandon J. Fuller if (error) { 1088*89294a78SLandon J. Fuller device_printf(pio->dev, "failed to configure dynamic register " 1089*89294a78SLandon J. Fuller "window: %d\n", error); 1090*89294a78SLandon J. Fuller return (error); 1091*89294a78SLandon J. Fuller } 1092*89294a78SLandon J. Fuller 1093*89294a78SLandon J. Fuller /* Update our mapping state */ 1094*89294a78SLandon J. Fuller pio->win = regwin; 1095*89294a78SLandon J. Fuller pio->res = r; 1096*89294a78SLandon J. Fuller pio->addr = addr; 1097*89294a78SLandon J. Fuller pio->size = size; 1098*89294a78SLandon J. Fuller pio->res_target = target; 1099*89294a78SLandon J. Fuller 1100*89294a78SLandon J. Fuller return (0); 1101*89294a78SLandon J. Fuller } 1102*89294a78SLandon J. Fuller 1103*89294a78SLandon J. Fuller /* bhnd_erom_io_read() implementation */ 1104*89294a78SLandon J. Fuller static uint32_t 1105*89294a78SLandon J. Fuller bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 1106*89294a78SLandon J. Fuller { 1107*89294a78SLandon J. Fuller struct bhndb_pci_eio *pio; 1108*89294a78SLandon J. Fuller bhnd_addr_t addr; 1109*89294a78SLandon J. Fuller bus_size_t res_offset; 1110*89294a78SLandon J. Fuller int error; 1111*89294a78SLandon J. Fuller 1112*89294a78SLandon J. Fuller pio = (struct bhndb_pci_eio *)eio; 1113*89294a78SLandon J. Fuller 1114*89294a78SLandon J. Fuller /* Calculate absolute address */ 1115*89294a78SLandon J. Fuller if (BHND_SIZE_MAX - offset < pio->addr) { 1116*89294a78SLandon J. Fuller device_printf(pio->dev, "invalid offset %#jx+%#jx\n", pio->addr, 1117*89294a78SLandon J. Fuller offset); 1118*89294a78SLandon J. Fuller return (UINT32_MAX); 1119*89294a78SLandon J. Fuller } 1120*89294a78SLandon J. Fuller 1121*89294a78SLandon J. Fuller addr = pio->addr + offset; 1122*89294a78SLandon J. Fuller 1123*89294a78SLandon J. Fuller /* Adjust the mapping for our read */ 1124*89294a78SLandon J. Fuller if ((error = bhndb_pci_eio_adjust_mapping(pio, addr, width))) { 1125*89294a78SLandon J. Fuller device_printf(pio->dev, "failed to adjust register mapping: " 1126*89294a78SLandon J. Fuller "%d\n", error); 1127*89294a78SLandon J. Fuller return (UINT32_MAX); 1128*89294a78SLandon J. Fuller } 1129*89294a78SLandon J. Fuller 1130*89294a78SLandon J. Fuller KASSERT(pio->res_target <= addr, ("invalid mapping (%#jx vs. %#jx)", 1131*89294a78SLandon J. Fuller pio->res_target, addr)); 1132*89294a78SLandon J. Fuller 1133*89294a78SLandon J. Fuller /* Determine the actual read offset within our register window 1134*89294a78SLandon J. Fuller * resource */ 1135*89294a78SLandon J. Fuller res_offset = (addr - pio->res_target) + pio->win->win_offset; 1136*89294a78SLandon J. Fuller 1137*89294a78SLandon J. Fuller /* Perform our read */ 1138*89294a78SLandon J. Fuller switch (width) { 1139*89294a78SLandon J. Fuller case 1: 1140*89294a78SLandon J. Fuller return (bus_read_1(pio->res, res_offset)); 1141*89294a78SLandon J. Fuller case 2: 1142*89294a78SLandon J. Fuller return (bus_read_2(pio->res, res_offset)); 1143*89294a78SLandon J. Fuller case 4: 1144*89294a78SLandon J. Fuller return (bus_read_4(pio->res, res_offset)); 1145*89294a78SLandon J. Fuller default: 1146*89294a78SLandon J. Fuller panic("unsupported width: %u", width); 1147*89294a78SLandon J. Fuller } 1148*89294a78SLandon J. Fuller } 1149*89294a78SLandon J. Fuller 11504ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = { 11514ad7e9b0SAdrian Chadd /* Device interface */ 11524ad7e9b0SAdrian Chadd DEVMETHOD(device_probe, bhndb_pci_probe), 11534ad7e9b0SAdrian Chadd DEVMETHOD(device_attach, bhndb_pci_attach), 11544ad7e9b0SAdrian Chadd DEVMETHOD(device_resume, bhndb_pci_resume), 1155bb64eeccSAdrian Chadd DEVMETHOD(device_suspend, bhndb_pci_suspend), 1156bb64eeccSAdrian Chadd DEVMETHOD(device_detach, bhndb_pci_detach), 11574ad7e9b0SAdrian Chadd 1158f90f4b65SLandon J. Fuller /* BHND interface */ 1159824b48efSLandon J. Fuller DEVMETHOD(bhnd_bus_assign_intr, bhndb_pci_assign_intr), 1160824b48efSLandon J. Fuller 1161f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhndb_pci_pwrctl_get_clksrc), 1162f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhndb_pci_pwrctl_gate_clock), 1163f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhndb_pci_pwrctl_ungate_clock), 1164f90f4b65SLandon J. Fuller 11654ad7e9b0SAdrian Chadd /* BHNDB interface */ 11664ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), 1167d567592bSAdrian Chadd DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info), 11684ad7e9b0SAdrian Chadd 11694ad7e9b0SAdrian Chadd DEVMETHOD_END 11704ad7e9b0SAdrian Chadd }; 11714ad7e9b0SAdrian Chadd 11724ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods, 11734ad7e9b0SAdrian Chadd sizeof(struct bhndb_pci_softc), bhndb_driver); 11744ad7e9b0SAdrian Chadd 11754ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1); 1176148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1); 11774ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); 11784ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); 1179148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1); 1180