14ad7e9b0SAdrian Chadd /*- 2bb64eeccSAdrian Chadd * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> 34ad7e9b0SAdrian Chadd * All rights reserved. 44ad7e9b0SAdrian Chadd * 54ad7e9b0SAdrian Chadd * Redistribution and use in source and binary forms, with or without 64ad7e9b0SAdrian Chadd * modification, are permitted provided that the following conditions 74ad7e9b0SAdrian Chadd * are met: 84ad7e9b0SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 94ad7e9b0SAdrian Chadd * notice, this list of conditions and the following disclaimer, 104ad7e9b0SAdrian Chadd * without modification. 114ad7e9b0SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 124ad7e9b0SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 134ad7e9b0SAdrian Chadd * redistribution must be conditioned upon including a substantially 144ad7e9b0SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 154ad7e9b0SAdrian Chadd * 164ad7e9b0SAdrian Chadd * NO WARRANTY 174ad7e9b0SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 184ad7e9b0SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 194ad7e9b0SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 204ad7e9b0SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 214ad7e9b0SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 224ad7e9b0SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 234ad7e9b0SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 244ad7e9b0SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 254ad7e9b0SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 264ad7e9b0SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 274ad7e9b0SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 284ad7e9b0SAdrian Chadd */ 294ad7e9b0SAdrian Chadd 304ad7e9b0SAdrian Chadd #include <sys/cdefs.h> 314ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$"); 324ad7e9b0SAdrian Chadd 334ad7e9b0SAdrian Chadd /* 344ad7e9b0SAdrian Chadd * PCI-specific implementation for the BHNDB bridge driver. 354ad7e9b0SAdrian Chadd * 364ad7e9b0SAdrian Chadd * Provides support for bridging from a PCI parent bus to a BHND-compatible 374ad7e9b0SAdrian Chadd * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point 384ad7e9b0SAdrian Chadd * mode. 394ad7e9b0SAdrian Chadd * 408ef24a0dSAdrian Chadd * This driver handles all initial generic host-level PCI interactions with a 418ef24a0dSAdrian Chadd * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4) 428ef24a0dSAdrian Chadd * bus has been enumerated, this driver works in tandem with a core-specific 438ef24a0dSAdrian Chadd * bhnd_pci_hostb driver to manage the PCI core. 444ad7e9b0SAdrian Chadd */ 454ad7e9b0SAdrian Chadd 464ad7e9b0SAdrian Chadd #include <sys/param.h> 474ad7e9b0SAdrian Chadd #include <sys/kernel.h> 484ad7e9b0SAdrian Chadd #include <sys/bus.h> 494ad7e9b0SAdrian Chadd #include <sys/limits.h> 504ad7e9b0SAdrian Chadd #include <sys/malloc.h> 514ad7e9b0SAdrian Chadd #include <sys/module.h> 524ad7e9b0SAdrian Chadd #include <sys/systm.h> 534ad7e9b0SAdrian Chadd 544ad7e9b0SAdrian Chadd #include <dev/pci/pcireg.h> 554ad7e9b0SAdrian Chadd #include <dev/pci/pcivar.h> 564ad7e9b0SAdrian Chadd 574ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h> 584ad7e9b0SAdrian Chadd 594ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h> 604ad7e9b0SAdrian Chadd 614ad7e9b0SAdrian Chadd #include "bhndb_pcireg.h" 624ad7e9b0SAdrian Chadd #include "bhndb_pcivar.h" 634ad7e9b0SAdrian Chadd #include "bhndb_private.h" 644ad7e9b0SAdrian Chadd 65*824b48efSLandon J. Fuller static int bhndb_pci_init_msi(struct bhndb_pci_softc *sc); 66111d7cb2SLandon J. Fuller static int bhndb_pci_add_children(struct bhndb_pci_softc *sc); 67111d7cb2SLandon J. Fuller 684ad7e9b0SAdrian Chadd static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc); 694ad7e9b0SAdrian Chadd static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc); 704ad7e9b0SAdrian Chadd 714ad7e9b0SAdrian Chadd static int bhndb_pci_compat_setregwin(struct bhndb_pci_softc *, 724ad7e9b0SAdrian Chadd const struct bhndb_regwin *, bhnd_addr_t); 734ad7e9b0SAdrian Chadd static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *, 744ad7e9b0SAdrian Chadd const struct bhndb_regwin *, bhnd_addr_t); 754ad7e9b0SAdrian Chadd 76e83ce340SAdrian Chadd static void bhndb_init_sromless_pci_config( 77e83ce340SAdrian Chadd struct bhndb_pci_softc *sc); 78e83ce340SAdrian Chadd 79e83ce340SAdrian Chadd static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc); 80f9cf87a0SAdrian Chadd static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc); 814ad7e9b0SAdrian Chadd 82*824b48efSLandon J. Fuller #define BHNDB_PCI_MSI_COUNT 1 83*824b48efSLandon J. Fuller 844ad7e9b0SAdrian Chadd /** 854ad7e9b0SAdrian Chadd * Default bhndb_pci implementation of device_probe(). 864ad7e9b0SAdrian Chadd * 874ad7e9b0SAdrian Chadd * Verifies that the parent is a PCI/PCIe device. 884ad7e9b0SAdrian Chadd */ 894ad7e9b0SAdrian Chadd static int 904ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev) 914ad7e9b0SAdrian Chadd { 924ad7e9b0SAdrian Chadd device_t parent; 934ad7e9b0SAdrian Chadd devclass_t parent_bus; 944ad7e9b0SAdrian Chadd devclass_t pci; 954ad7e9b0SAdrian Chadd 964ad7e9b0SAdrian Chadd /* Our parent must be a PCI/PCIe device. */ 974ad7e9b0SAdrian Chadd pci = devclass_find("pci"); 984ad7e9b0SAdrian Chadd parent = device_get_parent(dev); 994ad7e9b0SAdrian Chadd parent_bus = device_get_devclass(device_get_parent(parent)); 1004ad7e9b0SAdrian Chadd 1014ad7e9b0SAdrian Chadd if (parent_bus != pci) 1024ad7e9b0SAdrian Chadd return (ENXIO); 1034ad7e9b0SAdrian Chadd 1044ad7e9b0SAdrian Chadd device_set_desc(dev, "PCI-BHND bridge"); 1054ad7e9b0SAdrian Chadd 1064ad7e9b0SAdrian Chadd return (BUS_PROBE_DEFAULT); 1074ad7e9b0SAdrian Chadd } 1084ad7e9b0SAdrian Chadd 109*824b48efSLandon J. Fuller /* Configure MSI interrupts */ 110*824b48efSLandon J. Fuller static int 111*824b48efSLandon J. Fuller bhndb_pci_init_msi(struct bhndb_pci_softc *sc) 112*824b48efSLandon J. Fuller { 113*824b48efSLandon J. Fuller int error; 114*824b48efSLandon J. Fuller 115*824b48efSLandon J. Fuller /* Is MSI available? */ 116*824b48efSLandon J. Fuller if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT) 117*824b48efSLandon J. Fuller return (ENXIO); 118*824b48efSLandon J. Fuller 119*824b48efSLandon J. Fuller /* Allocate expected message count */ 120*824b48efSLandon J. Fuller sc->intr.msi_count = BHNDB_PCI_MSI_COUNT; 121*824b48efSLandon J. Fuller if ((error = pci_alloc_msi(sc->parent, &sc->intr.msi_count))) { 122*824b48efSLandon J. Fuller device_printf(sc->dev, "failed to allocate MSI interrupts: " 123*824b48efSLandon J. Fuller "%d\n", error); 124*824b48efSLandon J. Fuller return (error); 125*824b48efSLandon J. Fuller } 126*824b48efSLandon J. Fuller 127*824b48efSLandon J. Fuller if (sc->intr.msi_count < BHNDB_PCI_MSI_COUNT) 128*824b48efSLandon J. Fuller return (ENXIO); 129*824b48efSLandon J. Fuller 130*824b48efSLandon J. Fuller /* MSI uses resource IDs starting at 1 */ 131*824b48efSLandon J. Fuller sc->intr.intr_rid = 1; 132*824b48efSLandon J. Fuller 133*824b48efSLandon J. Fuller return (0); 134*824b48efSLandon J. Fuller } 135*824b48efSLandon J. Fuller 1364ad7e9b0SAdrian Chadd static int 1374ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev) 1384ad7e9b0SAdrian Chadd { 1394ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 1404ad7e9b0SAdrian Chadd int error, reg; 1414ad7e9b0SAdrian Chadd 1424ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1434ad7e9b0SAdrian Chadd sc->dev = dev; 144e83ce340SAdrian Chadd sc->parent = device_get_parent(dev); 1454ad7e9b0SAdrian Chadd sc->set_regwin = bhndb_pci_compat_setregwin; 1464ad7e9b0SAdrian Chadd 147*824b48efSLandon J. Fuller /* Enable PCI bus mastering */ 148*824b48efSLandon J. Fuller pci_enable_busmaster(sc->parent); 149*824b48efSLandon J. Fuller 150*824b48efSLandon J. Fuller /* Set up interrupt handling */ 151*824b48efSLandon J. Fuller if (bhndb_pci_init_msi(sc) == 0) { 152*824b48efSLandon J. Fuller device_printf(dev, "Using MSI interrupts on %s\n", 153*824b48efSLandon J. Fuller device_get_nameunit(sc->parent)); 154*824b48efSLandon J. Fuller } else { 155*824b48efSLandon J. Fuller device_printf(dev, "Using INTx interrupts on %s\n", 156*824b48efSLandon J. Fuller device_get_nameunit(sc->parent)); 157*824b48efSLandon J. Fuller sc->intr.intr_rid = 0; 158*824b48efSLandon J. Fuller } 159*824b48efSLandon J. Fuller 160*824b48efSLandon J. Fuller /* Determine our bridge device class */ 161*824b48efSLandon J. Fuller sc->pci_devclass = BHND_DEVCLASS_PCI; 162111d7cb2SLandon J. Fuller if (pci_find_cap(sc->parent, PCIY_EXPRESS, ®) == 0) 163111d7cb2SLandon J. Fuller sc->pci_devclass = BHND_DEVCLASS_PCIE; 164111d7cb2SLandon J. Fuller else 165111d7cb2SLandon J. Fuller sc->pci_devclass = BHND_DEVCLASS_PCI; 1664ad7e9b0SAdrian Chadd 167111d7cb2SLandon J. Fuller /* Enable clocks (if required by this hardware) */ 168111d7cb2SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(sc))) 169111d7cb2SLandon J. Fuller goto cleanup; 170111d7cb2SLandon J. Fuller 171111d7cb2SLandon J. Fuller /* Perform bridge attach, fully initializing the bridge 172111d7cb2SLandon J. Fuller * configuration. */ 173111d7cb2SLandon J. Fuller if ((error = bhndb_attach(dev, sc->pci_devclass))) 174111d7cb2SLandon J. Fuller goto cleanup; 175111d7cb2SLandon J. Fuller 176111d7cb2SLandon J. Fuller /* If supported, switch to faster regwin handling */ 1774ad7e9b0SAdrian Chadd if (sc->bhndb.chipid.chip_type != BHND_CHIPTYPE_SIBA) { 1784ad7e9b0SAdrian Chadd atomic_store_rel_ptr((volatile void *) &sc->set_regwin, 1794ad7e9b0SAdrian Chadd (uintptr_t) &bhndb_pci_fast_setregwin); 1804ad7e9b0SAdrian Chadd } 1814ad7e9b0SAdrian Chadd 182111d7cb2SLandon J. Fuller /* Enable PCI bus mastering */ 183111d7cb2SLandon J. Fuller pci_enable_busmaster(sc->parent); 1844ad7e9b0SAdrian Chadd 185bb64eeccSAdrian Chadd /* Fix-up power on defaults for SROM-less devices. */ 1864ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(sc); 1874ad7e9b0SAdrian Chadd 188111d7cb2SLandon J. Fuller /* Add any additional child devices */ 189111d7cb2SLandon J. Fuller if ((error = bhndb_pci_add_children(sc))) 190111d7cb2SLandon J. Fuller goto cleanup; 191111d7cb2SLandon J. Fuller 192111d7cb2SLandon J. Fuller /* Probe and attach our children */ 193111d7cb2SLandon J. Fuller if ((error = bus_generic_attach(dev))) 194111d7cb2SLandon J. Fuller goto cleanup; 195111d7cb2SLandon J. Fuller 196111d7cb2SLandon J. Fuller return (0); 197111d7cb2SLandon J. Fuller 198111d7cb2SLandon J. Fuller cleanup: 199111d7cb2SLandon J. Fuller device_delete_children(dev); 200111d7cb2SLandon J. Fuller bhndb_disable_pci_clocks(sc); 201*824b48efSLandon J. Fuller if (sc->intr.msi_count > 0) 202*824b48efSLandon J. Fuller pci_release_msi(dev); 203*824b48efSLandon J. Fuller 204111d7cb2SLandon J. Fuller pci_disable_busmaster(sc->parent); 205111d7cb2SLandon J. Fuller 206111d7cb2SLandon J. Fuller return (error); 207111d7cb2SLandon J. Fuller } 208111d7cb2SLandon J. Fuller 209111d7cb2SLandon J. Fuller static int 210111d7cb2SLandon J. Fuller bhndb_pci_detach(device_t dev) 211111d7cb2SLandon J. Fuller { 212111d7cb2SLandon J. Fuller struct bhndb_pci_softc *sc; 213111d7cb2SLandon J. Fuller int error; 214111d7cb2SLandon J. Fuller 215111d7cb2SLandon J. Fuller sc = device_get_softc(dev); 216111d7cb2SLandon J. Fuller 217111d7cb2SLandon J. Fuller /* Attempt to detach our children */ 218111d7cb2SLandon J. Fuller if ((error = bus_generic_detach(dev))) 219111d7cb2SLandon J. Fuller return (error); 220111d7cb2SLandon J. Fuller 221111d7cb2SLandon J. Fuller /* Perform generic bridge detach */ 222111d7cb2SLandon J. Fuller if ((error = bhndb_generic_detach(dev))) 223111d7cb2SLandon J. Fuller return (error); 224111d7cb2SLandon J. Fuller 225111d7cb2SLandon J. Fuller /* Disable clocks (if required by this hardware) */ 226111d7cb2SLandon J. Fuller if ((error = bhndb_disable_pci_clocks(sc))) 227111d7cb2SLandon J. Fuller return (error); 228111d7cb2SLandon J. Fuller 229*824b48efSLandon J. Fuller /* Release MSI interrupts */ 230*824b48efSLandon J. Fuller if (sc->intr.msi_count > 0) 231*824b48efSLandon J. Fuller pci_release_msi(dev); 232*824b48efSLandon J. Fuller 233111d7cb2SLandon J. Fuller /* Disable PCI bus mastering */ 234111d7cb2SLandon J. Fuller pci_disable_busmaster(sc->parent); 235111d7cb2SLandon J. Fuller 236111d7cb2SLandon J. Fuller return (0); 237111d7cb2SLandon J. Fuller } 238111d7cb2SLandon J. Fuller 239111d7cb2SLandon J. Fuller static int 240111d7cb2SLandon J. Fuller bhndb_pci_add_children(struct bhndb_pci_softc *sc) 241111d7cb2SLandon J. Fuller { 242111d7cb2SLandon J. Fuller bus_size_t nv_sz; 243111d7cb2SLandon J. Fuller int error; 244111d7cb2SLandon J. Fuller 245111d7cb2SLandon J. Fuller /** 246111d7cb2SLandon J. Fuller * If SPROM is mapped directly into BAR0, add child NVRAM 247111d7cb2SLandon J. Fuller * device. 248111d7cb2SLandon J. Fuller */ 249e83ce340SAdrian Chadd nv_sz = bhndb_pci_sprom_size(sc); 250e83ce340SAdrian Chadd if (nv_sz > 0) { 251e83ce340SAdrian Chadd struct bhndb_devinfo *dinfo; 252111d7cb2SLandon J. Fuller device_t child; 253e83ce340SAdrian Chadd 254e83ce340SAdrian Chadd if (bootverbose) { 255111d7cb2SLandon J. Fuller device_printf(sc->dev, "found SPROM (%ju bytes)\n", 256111d7cb2SLandon J. Fuller (uintmax_t)nv_sz); 257e83ce340SAdrian Chadd } 258e83ce340SAdrian Chadd 259111d7cb2SLandon J. Fuller /* Add sprom device, ordered early enough to be available 260111d7cb2SLandon J. Fuller * before the bridged bhnd(4) bus is attached. */ 261111d7cb2SLandon J. Fuller child = BUS_ADD_CHILD(sc->dev, 262111d7cb2SLandon J. Fuller BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1); 263111d7cb2SLandon J. Fuller if (child == NULL) { 264111d7cb2SLandon J. Fuller device_printf(sc->dev, "failed to add sprom device\n"); 265e83ce340SAdrian Chadd return (ENXIO); 266e83ce340SAdrian Chadd } 267e83ce340SAdrian Chadd 268e83ce340SAdrian Chadd /* Initialize device address space and resource covering the 269e83ce340SAdrian Chadd * BAR0 SPROM shadow. */ 270111d7cb2SLandon J. Fuller dinfo = device_get_ivars(child); 271e83ce340SAdrian Chadd dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; 272111d7cb2SLandon J. Fuller 273111d7cb2SLandon J. Fuller error = bus_set_resource(child, SYS_RES_MEMORY, 0, 274e83ce340SAdrian Chadd bhndb_pci_sprom_addr(sc), nv_sz); 275e83ce340SAdrian Chadd if (error) { 276111d7cb2SLandon J. Fuller device_printf(sc->dev, 277e83ce340SAdrian Chadd "failed to register sprom resources\n"); 278e83ce340SAdrian Chadd return (error); 279e83ce340SAdrian Chadd } 280e83ce340SAdrian Chadd } 281e83ce340SAdrian Chadd 2824ad7e9b0SAdrian Chadd return (0); 2834ad7e9b0SAdrian Chadd } 2844ad7e9b0SAdrian Chadd 285e83ce340SAdrian Chadd static const struct bhndb_regwin * 286e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc) 287e83ce340SAdrian Chadd { 288e83ce340SAdrian Chadd struct bhndb_resources *bres; 289e83ce340SAdrian Chadd const struct bhndb_hwcfg *cfg; 290e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 291e83ce340SAdrian Chadd 292e83ce340SAdrian Chadd bres = sc->bhndb.bus_res; 293e83ce340SAdrian Chadd cfg = bres->cfg; 294e83ce340SAdrian Chadd 295e83ce340SAdrian Chadd sprom_win = bhndb_regwin_find_type(cfg->register_windows, 296e83ce340SAdrian Chadd BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE); 297e83ce340SAdrian Chadd 298e83ce340SAdrian Chadd return (sprom_win); 299e83ce340SAdrian Chadd } 300e83ce340SAdrian Chadd 301e83ce340SAdrian Chadd static bus_addr_t 302e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc) 303e83ce340SAdrian Chadd { 304e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 305e83ce340SAdrian Chadd struct resource *r; 306e83ce340SAdrian Chadd 307e83ce340SAdrian Chadd /* Fetch the SPROM register window */ 308e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 309e83ce340SAdrian Chadd KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+")); 310e83ce340SAdrian Chadd 311e83ce340SAdrian Chadd /* Fetch the associated resource */ 312e83ce340SAdrian Chadd r = bhndb_find_regwin_resource(sc->bhndb.bus_res, sprom_win); 313e83ce340SAdrian Chadd KASSERT(r != NULL, ("missing resource for sprom window\n")); 314e83ce340SAdrian Chadd 315e83ce340SAdrian Chadd return (rman_get_start(r) + sprom_win->win_offset); 316e83ce340SAdrian Chadd } 317e83ce340SAdrian Chadd 318e83ce340SAdrian Chadd static bus_size_t 319e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc) 320e83ce340SAdrian Chadd { 321e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 322e83ce340SAdrian Chadd uint32_t sctl; 323e83ce340SAdrian Chadd bus_size_t sprom_sz; 324e83ce340SAdrian Chadd 325e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 326e83ce340SAdrian Chadd 327e83ce340SAdrian Chadd /* PCI_V2 and later devices map SPROM/OTP via ChipCommon */ 328e83ce340SAdrian Chadd if (sprom_win == NULL) 329e83ce340SAdrian Chadd return (0); 330e83ce340SAdrian Chadd 331e83ce340SAdrian Chadd /* Determine SPROM size */ 332e83ce340SAdrian Chadd sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4); 333e83ce340SAdrian Chadd if (sctl & BHNDB_PCI_SPROM_BLANK) 334e83ce340SAdrian Chadd return (0); 335e83ce340SAdrian Chadd 336e83ce340SAdrian Chadd switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) { 337e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_1KB: 338e83ce340SAdrian Chadd sprom_sz = (1 * 1024); 339e83ce340SAdrian Chadd break; 340e83ce340SAdrian Chadd 341e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_4KB: 342e83ce340SAdrian Chadd sprom_sz = (4 * 1024); 343e83ce340SAdrian Chadd break; 344e83ce340SAdrian Chadd 345e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_16KB: 346e83ce340SAdrian Chadd sprom_sz = (16 * 1024); 347e83ce340SAdrian Chadd break; 348e83ce340SAdrian Chadd 349e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_RESERVED: 350e83ce340SAdrian Chadd default: 351e83ce340SAdrian Chadd device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl); 352e83ce340SAdrian Chadd return (0); 353e83ce340SAdrian Chadd } 354e83ce340SAdrian Chadd 355e83ce340SAdrian Chadd if (sprom_sz > sprom_win->win_size) { 356e83ce340SAdrian Chadd device_printf(sc->dev, 357e83ce340SAdrian Chadd "PCI sprom size (0x%x) overruns defined register window\n", 358e83ce340SAdrian Chadd sctl); 359e83ce340SAdrian Chadd return (0); 360e83ce340SAdrian Chadd } 361e83ce340SAdrian Chadd 362e83ce340SAdrian Chadd return (sprom_sz); 363e83ce340SAdrian Chadd } 364e83ce340SAdrian Chadd 3654ad7e9b0SAdrian Chadd /* 3664ad7e9b0SAdrian Chadd * On devices without a SROM, the PCI(e) cores will be initialized with 367bb64eeccSAdrian Chadd * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows 368bb64eeccSAdrian Chadd * mapped to the wrong core. 3694ad7e9b0SAdrian Chadd * 370bb64eeccSAdrian Chadd * This function updates the SROM shadow to point the BAR0 windows at the 3714ad7e9b0SAdrian Chadd * current PCI core. 3724ad7e9b0SAdrian Chadd * 373bb64eeccSAdrian Chadd * Applies to all PCI/PCIe revisions. 3744ad7e9b0SAdrian Chadd */ 3754ad7e9b0SAdrian Chadd static void 3764ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc) 3774ad7e9b0SAdrian Chadd { 378bb64eeccSAdrian Chadd struct bhndb_resources *bres; 379bb64eeccSAdrian Chadd const struct bhndb_hwcfg *cfg; 380bb64eeccSAdrian Chadd const struct bhndb_regwin *win; 381111d7cb2SLandon J. Fuller struct bhnd_core_info hostb_core; 382bb64eeccSAdrian Chadd struct resource *core_regs; 383bb64eeccSAdrian Chadd bus_size_t srom_offset; 384bb64eeccSAdrian Chadd u_int pci_cidx, sprom_cidx; 3854ad7e9b0SAdrian Chadd uint16_t val; 386111d7cb2SLandon J. Fuller int error; 3874ad7e9b0SAdrian Chadd 388bb64eeccSAdrian Chadd bres = sc->bhndb.bus_res; 389bb64eeccSAdrian Chadd cfg = bres->cfg; 390bb64eeccSAdrian Chadd 391111d7cb2SLandon J. Fuller /* Find our hostb core */ 392111d7cb2SLandon J. Fuller error = BHNDB_GET_HOSTB_CORE(sc->dev, sc->bhndb.bus_dev, &hostb_core); 393111d7cb2SLandon J. Fuller if (error) { 394111d7cb2SLandon J. Fuller device_printf(sc->dev, "no host bridge device found\n"); 395111d7cb2SLandon J. Fuller return; 396111d7cb2SLandon J. Fuller } 397111d7cb2SLandon J. Fuller 398111d7cb2SLandon J. Fuller if (hostb_core.vendor != BHND_MFGID_BCM) 399bb64eeccSAdrian Chadd return; 400bb64eeccSAdrian Chadd 401111d7cb2SLandon J. Fuller switch (hostb_core.device) { 402bb64eeccSAdrian Chadd case BHND_COREID_PCI: 403bb64eeccSAdrian Chadd srom_offset = BHND_PCI_SRSH_PI_OFFSET; 404bb64eeccSAdrian Chadd break; 405bb64eeccSAdrian Chadd case BHND_COREID_PCIE: 406bb64eeccSAdrian Chadd srom_offset = BHND_PCIE_SRSH_PI_OFFSET; 407bb64eeccSAdrian Chadd break; 408bb64eeccSAdrian Chadd default: 409bb64eeccSAdrian Chadd device_printf(sc->dev, "unsupported PCI host bridge device\n"); 410bb64eeccSAdrian Chadd return; 411bb64eeccSAdrian Chadd } 412bb64eeccSAdrian Chadd 413bb64eeccSAdrian Chadd /* Locate the static register window mapping the PCI core */ 414bb64eeccSAdrian Chadd win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass, 415bb64eeccSAdrian Chadd 0, BHND_PORT_DEVICE, 0, 0); 416bb64eeccSAdrian Chadd if (win == NULL) { 417bb64eeccSAdrian Chadd device_printf(sc->dev, "missing PCI core register window\n"); 418bb64eeccSAdrian Chadd return; 419bb64eeccSAdrian Chadd } 420bb64eeccSAdrian Chadd 421bb64eeccSAdrian Chadd /* Fetch the resource containing the register window */ 422bb64eeccSAdrian Chadd core_regs = bhndb_find_regwin_resource(bres, win); 423bb64eeccSAdrian Chadd if (core_regs == NULL) { 424bb64eeccSAdrian Chadd device_printf(sc->dev, "missing PCI core register resource\n"); 425bb64eeccSAdrian Chadd return; 426bb64eeccSAdrian Chadd } 427bb64eeccSAdrian Chadd 4284ad7e9b0SAdrian Chadd /* Fetch the SPROM's configured core index */ 429bb64eeccSAdrian Chadd val = bus_read_2(core_regs, win->win_offset + srom_offset); 430bb64eeccSAdrian Chadd sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; 4314ad7e9b0SAdrian Chadd 4324ad7e9b0SAdrian Chadd /* If it doesn't match host bridge's core index, update the index 4334ad7e9b0SAdrian Chadd * value */ 434111d7cb2SLandon J. Fuller pci_cidx = hostb_core.core_idx; 435bb64eeccSAdrian Chadd if (sprom_cidx != pci_cidx) { 436bb64eeccSAdrian Chadd val &= ~BHND_PCI_SRSH_PI_MASK; 437bb64eeccSAdrian Chadd val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT); 438bb64eeccSAdrian Chadd bus_write_2(core_regs, 439bb64eeccSAdrian Chadd win->win_offset + srom_offset, val); 4404ad7e9b0SAdrian Chadd } 4414ad7e9b0SAdrian Chadd } 4424ad7e9b0SAdrian Chadd 4434ad7e9b0SAdrian Chadd static int 4444ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev) 4454ad7e9b0SAdrian Chadd { 4464ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 4474ad7e9b0SAdrian Chadd int error; 4484ad7e9b0SAdrian Chadd 4494ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 4504ad7e9b0SAdrian Chadd 451bb64eeccSAdrian Chadd /* Enable clocks (if supported by this hardware) */ 452bb64eeccSAdrian Chadd if ((error = bhndb_enable_pci_clocks(sc))) 4534ad7e9b0SAdrian Chadd return (error); 4544ad7e9b0SAdrian Chadd 455bb64eeccSAdrian Chadd /* Perform resume */ 456bb64eeccSAdrian Chadd return (bhndb_generic_resume(dev)); 457bb64eeccSAdrian Chadd } 458bb64eeccSAdrian Chadd 459bb64eeccSAdrian Chadd static int 460bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev) 461bb64eeccSAdrian Chadd { 462bb64eeccSAdrian Chadd struct bhndb_pci_softc *sc; 463bb64eeccSAdrian Chadd int error; 464bb64eeccSAdrian Chadd 465bb64eeccSAdrian Chadd sc = device_get_softc(dev); 466bb64eeccSAdrian Chadd 467bb64eeccSAdrian Chadd /* Disable clocks (if supported by this hardware) */ 468bb64eeccSAdrian Chadd if ((error = bhndb_disable_pci_clocks(sc))) 4694ad7e9b0SAdrian Chadd return (error); 4704ad7e9b0SAdrian Chadd 471bb64eeccSAdrian Chadd /* Perform suspend */ 472bb64eeccSAdrian Chadd return (bhndb_generic_suspend(dev)); 473bb64eeccSAdrian Chadd } 474bb64eeccSAdrian Chadd 475bb64eeccSAdrian Chadd static int 4764ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw, 4774ad7e9b0SAdrian Chadd bhnd_addr_t addr) 4784ad7e9b0SAdrian Chadd { 4794ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc = device_get_softc(dev); 4804ad7e9b0SAdrian Chadd return (sc->set_regwin(sc, rw, addr)); 4814ad7e9b0SAdrian Chadd } 4824ad7e9b0SAdrian Chadd 4834ad7e9b0SAdrian Chadd /** 4844ad7e9b0SAdrian Chadd * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. 4854ad7e9b0SAdrian Chadd * 4864ad7e9b0SAdrian Chadd * On siba(4) devices, it's possible that writing a PCI window register may 4874ad7e9b0SAdrian Chadd * not succeed; it's necessary to immediately read the configuration register 4884ad7e9b0SAdrian Chadd * and retry if not set to the desired value. 4894ad7e9b0SAdrian Chadd * 4904ad7e9b0SAdrian Chadd * This is not necessary on bcma(4) devices, but other than the overhead of 4914ad7e9b0SAdrian Chadd * validating the register, there's no harm in performing the verification. 4924ad7e9b0SAdrian Chadd */ 4934ad7e9b0SAdrian Chadd static int 4944ad7e9b0SAdrian Chadd bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc, 4954ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 4964ad7e9b0SAdrian Chadd { 4974ad7e9b0SAdrian Chadd int error; 498e83ce340SAdrian Chadd int reg; 4994ad7e9b0SAdrian Chadd 5004ad7e9b0SAdrian Chadd if (rw->win_type != BHNDB_REGWIN_T_DYN) 5014ad7e9b0SAdrian Chadd return (ENODEV); 5024ad7e9b0SAdrian Chadd 503e83ce340SAdrian Chadd reg = rw->d.dyn.cfg_offset; 5044ad7e9b0SAdrian Chadd for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { 5054ad7e9b0SAdrian Chadd if ((error = bhndb_pci_fast_setregwin(sc, rw, addr))) 5064ad7e9b0SAdrian Chadd return (error); 5074ad7e9b0SAdrian Chadd 508e83ce340SAdrian Chadd if (pci_read_config(sc->parent, reg, 4) == addr) 5094ad7e9b0SAdrian Chadd return (0); 5104ad7e9b0SAdrian Chadd 5114ad7e9b0SAdrian Chadd DELAY(10); 5124ad7e9b0SAdrian Chadd } 5134ad7e9b0SAdrian Chadd 5144ad7e9b0SAdrian Chadd /* Unable to set window */ 5154ad7e9b0SAdrian Chadd return (ENODEV); 5164ad7e9b0SAdrian Chadd } 5174ad7e9b0SAdrian Chadd 5184ad7e9b0SAdrian Chadd /** 5194ad7e9b0SAdrian Chadd * A bcma(4)-only bhndb_set_window_addr implementation. 5204ad7e9b0SAdrian Chadd */ 5214ad7e9b0SAdrian Chadd static int 5224ad7e9b0SAdrian Chadd bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc, 5234ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 5244ad7e9b0SAdrian Chadd { 5254ad7e9b0SAdrian Chadd /* The PCI bridge core only supports 32-bit addressing, regardless 5264ad7e9b0SAdrian Chadd * of the bus' support for 64-bit addressing */ 5274ad7e9b0SAdrian Chadd if (addr > UINT32_MAX) 5284ad7e9b0SAdrian Chadd return (ERANGE); 5294ad7e9b0SAdrian Chadd 5304ad7e9b0SAdrian Chadd switch (rw->win_type) { 5314ad7e9b0SAdrian Chadd case BHNDB_REGWIN_T_DYN: 5324ad7e9b0SAdrian Chadd /* Addresses must be page aligned */ 5334ad7e9b0SAdrian Chadd if (addr % rw->win_size != 0) 5344ad7e9b0SAdrian Chadd return (EINVAL); 5354ad7e9b0SAdrian Chadd 536e83ce340SAdrian Chadd pci_write_config(sc->parent, rw->d.dyn.cfg_offset, addr, 4); 5374ad7e9b0SAdrian Chadd break; 5384ad7e9b0SAdrian Chadd default: 5394ad7e9b0SAdrian Chadd return (ENODEV); 5404ad7e9b0SAdrian Chadd } 5414ad7e9b0SAdrian Chadd 5424ad7e9b0SAdrian Chadd return (0); 5434ad7e9b0SAdrian Chadd } 5444ad7e9b0SAdrian Chadd 545d567592bSAdrian Chadd static int 546d567592bSAdrian Chadd bhndb_pci_populate_board_info(device_t dev, device_t child, 547d567592bSAdrian Chadd struct bhnd_board_info *info) 548d567592bSAdrian Chadd { 549d567592bSAdrian Chadd struct bhndb_pci_softc *sc; 550d567592bSAdrian Chadd 551d567592bSAdrian Chadd sc = device_get_softc(dev); 552d567592bSAdrian Chadd 5538ef24a0dSAdrian Chadd /* 5548ef24a0dSAdrian Chadd * On a subset of Apple BCM4360 modules, always prefer the 5558ef24a0dSAdrian Chadd * PCI subdevice to the SPROM-supplied boardtype. 5568ef24a0dSAdrian Chadd * 5578ef24a0dSAdrian Chadd * TODO: 5588ef24a0dSAdrian Chadd * 5598ef24a0dSAdrian Chadd * Broadcom's own drivers implement this override, and then later use 5608ef24a0dSAdrian Chadd * the remapped BCM4360 board type to determine the required 5618ef24a0dSAdrian Chadd * board-specific workarounds. 5628ef24a0dSAdrian Chadd * 5638ef24a0dSAdrian Chadd * Without access to this hardware, it's unclear why this mapping 5648ef24a0dSAdrian Chadd * is done, and we must do the same. If we can survey the hardware 5658ef24a0dSAdrian Chadd * in question, it may be possible to replace this behavior with 5668ef24a0dSAdrian Chadd * explicit references to the SPROM-supplied boardtype(s) in our 5678ef24a0dSAdrian Chadd * quirk definitions. 5688ef24a0dSAdrian Chadd */ 5698ef24a0dSAdrian Chadd if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) { 5708ef24a0dSAdrian Chadd switch (info->board_type) { 5718ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X29C: 5728ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X29CP2: 5738ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X51: 5748ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X51P2: 5758ef24a0dSAdrian Chadd info->board_type = 0; /* allow override below */ 5768ef24a0dSAdrian Chadd break; 5778ef24a0dSAdrian Chadd default: 5788ef24a0dSAdrian Chadd break; 5798ef24a0dSAdrian Chadd } 5808ef24a0dSAdrian Chadd } 5818ef24a0dSAdrian Chadd 582d567592bSAdrian Chadd /* If NVRAM did not supply vendor/type info, provide the PCI 583d567592bSAdrian Chadd * subvendor/subdevice values. */ 584d567592bSAdrian Chadd if (info->board_vendor == 0) 585d567592bSAdrian Chadd info->board_vendor = pci_get_subvendor(sc->parent); 586d567592bSAdrian Chadd 587d567592bSAdrian Chadd if (info->board_type == 0) 588d567592bSAdrian Chadd info->board_type = pci_get_subdevice(sc->parent); 589d567592bSAdrian Chadd 590d567592bSAdrian Chadd return (0); 591d567592bSAdrian Chadd } 592d567592bSAdrian Chadd 5934ad7e9b0SAdrian Chadd /** 594bb64eeccSAdrian Chadd * Enable externally managed clocks, if required. 5954ad7e9b0SAdrian Chadd * 596bb64eeccSAdrian Chadd * Some PCI chipsets (BCM4306, possibly others) chips do not support 597bb64eeccSAdrian Chadd * the idle low-power clock. Clocking must be bootstrapped at 598bb64eeccSAdrian Chadd * attach/resume by directly adjusting GPIO registers exposed in the 599bb64eeccSAdrian Chadd * PCI config space, and correspondingly, explicitly shutdown at 600bb64eeccSAdrian Chadd * detach/suspend. 6014ad7e9b0SAdrian Chadd * 6024ad7e9b0SAdrian Chadd * @param sc Bridge driver state. 6034ad7e9b0SAdrian Chadd */ 6044ad7e9b0SAdrian Chadd static int 6054ad7e9b0SAdrian Chadd bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc) 6064ad7e9b0SAdrian Chadd { 6074ad7e9b0SAdrian Chadd uint32_t gpio_in, gpio_out, gpio_en; 6084ad7e9b0SAdrian Chadd uint32_t gpio_flags; 6094ad7e9b0SAdrian Chadd uint16_t pci_status; 6104ad7e9b0SAdrian Chadd 611bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 612bb64eeccSAdrian Chadd if (sc->pci_devclass != BHND_DEVCLASS_PCI) 613bb64eeccSAdrian Chadd return (0); 6144ad7e9b0SAdrian Chadd 6154ad7e9b0SAdrian Chadd /* Read state of XTAL pin */ 616e83ce340SAdrian Chadd gpio_in = pci_read_config(sc->parent, BHNDB_PCI_GPIO_IN, 4); 6174ad7e9b0SAdrian Chadd if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) 6184ad7e9b0SAdrian Chadd return (0); /* already enabled */ 6194ad7e9b0SAdrian Chadd 6204ad7e9b0SAdrian Chadd /* Fetch current config */ 621e83ce340SAdrian Chadd gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); 622e83ce340SAdrian Chadd gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4); 6234ad7e9b0SAdrian Chadd 6244ad7e9b0SAdrian Chadd /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ 6254ad7e9b0SAdrian Chadd gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 6264ad7e9b0SAdrian Chadd gpio_out |= gpio_flags; 6274ad7e9b0SAdrian Chadd gpio_en |= gpio_flags; 6284ad7e9b0SAdrian Chadd 629e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 630e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 6314ad7e9b0SAdrian Chadd DELAY(1000); 6324ad7e9b0SAdrian Chadd 6334ad7e9b0SAdrian Chadd /* Reset PLL_OFF */ 6344ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; 635e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 6364ad7e9b0SAdrian Chadd DELAY(5000); 6374ad7e9b0SAdrian Chadd 6384ad7e9b0SAdrian Chadd /* Clear any PCI 'sent target-abort' flag. */ 639e83ce340SAdrian Chadd pci_status = pci_read_config(sc->parent, PCIR_STATUS, 2); 6404ad7e9b0SAdrian Chadd pci_status &= ~PCIM_STATUS_STABORT; 641e83ce340SAdrian Chadd pci_write_config(sc->parent, PCIR_STATUS, pci_status, 2); 6424ad7e9b0SAdrian Chadd 6434ad7e9b0SAdrian Chadd return (0); 6444ad7e9b0SAdrian Chadd } 6454ad7e9b0SAdrian Chadd 6464ad7e9b0SAdrian Chadd /** 647bb64eeccSAdrian Chadd * Disable externally managed clocks, if required. 6484ad7e9b0SAdrian Chadd * 6494ad7e9b0SAdrian Chadd * @param sc Bridge driver state. 6504ad7e9b0SAdrian Chadd */ 6514ad7e9b0SAdrian Chadd static int 6524ad7e9b0SAdrian Chadd bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc) 6534ad7e9b0SAdrian Chadd { 6544ad7e9b0SAdrian Chadd uint32_t gpio_out, gpio_en; 6554ad7e9b0SAdrian Chadd 656bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 657bb64eeccSAdrian Chadd if (sc->pci_devclass != BHND_DEVCLASS_PCI) 658bb64eeccSAdrian Chadd return (0); 6594ad7e9b0SAdrian Chadd 6604ad7e9b0SAdrian Chadd /* Fetch current config */ 661e83ce340SAdrian Chadd gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); 662e83ce340SAdrian Chadd gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4); 6634ad7e9b0SAdrian Chadd 6644ad7e9b0SAdrian Chadd /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ 6654ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; 6664ad7e9b0SAdrian Chadd gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; 667e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 6684ad7e9b0SAdrian Chadd 6694ad7e9b0SAdrian Chadd /* Enable both output pins */ 6704ad7e9b0SAdrian Chadd gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 671e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 6724ad7e9b0SAdrian Chadd 6734ad7e9b0SAdrian Chadd return (0); 6744ad7e9b0SAdrian Chadd } 6754ad7e9b0SAdrian Chadd 676f90f4b65SLandon J. Fuller static bhnd_clksrc 677f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child, 678f90f4b65SLandon J. Fuller bhnd_clock clock) 679f90f4b65SLandon J. Fuller { 680f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc; 681f90f4b65SLandon J. Fuller uint32_t gpio_out; 682f90f4b65SLandon J. Fuller 683f90f4b65SLandon J. Fuller sc = device_get_softc(dev); 684f90f4b65SLandon J. Fuller 685f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 686f90f4b65SLandon J. Fuller if (sc->pci_devclass != BHND_DEVCLASS_PCI) 687f90f4b65SLandon J. Fuller return (ENODEV); 688f90f4b65SLandon J. Fuller 689f90f4b65SLandon J. Fuller /* Only ILP is supported */ 690f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_ILP) 691f90f4b65SLandon J. Fuller return (ENXIO); 692f90f4b65SLandon J. Fuller 693f90f4b65SLandon J. Fuller gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); 694f90f4b65SLandon J. Fuller if (gpio_out & BHNDB_PCI_GPIO_SCS) 695f90f4b65SLandon J. Fuller return (BHND_CLKSRC_PCI); 696f90f4b65SLandon J. Fuller else 697f90f4b65SLandon J. Fuller return (BHND_CLKSRC_XTAL); 698f90f4b65SLandon J. Fuller } 699f90f4b65SLandon J. Fuller 700f90f4b65SLandon J. Fuller static int 701f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child, 702f90f4b65SLandon J. Fuller bhnd_clock clock) 703f90f4b65SLandon J. Fuller { 704f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 705f90f4b65SLandon J. Fuller 706f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 707f90f4b65SLandon J. Fuller if (sc->pci_devclass != BHND_DEVCLASS_PCI) 708f90f4b65SLandon J. Fuller return (ENODEV); 709f90f4b65SLandon J. Fuller 710f90f4b65SLandon J. Fuller /* Only HT is supported */ 711f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_HT) 712f90f4b65SLandon J. Fuller return (ENXIO); 713f90f4b65SLandon J. Fuller 714f90f4b65SLandon J. Fuller return (bhndb_disable_pci_clocks(sc)); 715f90f4b65SLandon J. Fuller } 716f90f4b65SLandon J. Fuller 717f90f4b65SLandon J. Fuller static int 718f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child, 719f90f4b65SLandon J. Fuller bhnd_clock clock) 720f90f4b65SLandon J. Fuller { 721f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 722f90f4b65SLandon J. Fuller 723f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 724f90f4b65SLandon J. Fuller if (sc->pci_devclass != BHND_DEVCLASS_PCI) 725f90f4b65SLandon J. Fuller return (ENODEV); 726f90f4b65SLandon J. Fuller 727f90f4b65SLandon J. Fuller /* Only HT is supported */ 728f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_HT) 729f90f4b65SLandon J. Fuller return (ENXIO); 730f90f4b65SLandon J. Fuller 731f90f4b65SLandon J. Fuller return (bhndb_enable_pci_clocks(sc)); 732f90f4b65SLandon J. Fuller } 733f90f4b65SLandon J. Fuller 734*824b48efSLandon J. Fuller static int 735*824b48efSLandon J. Fuller bhndb_pci_assign_intr(device_t dev, device_t child, int rid) 736*824b48efSLandon J. Fuller { 737*824b48efSLandon J. Fuller struct bhndb_pci_softc *sc; 738*824b48efSLandon J. Fuller rman_res_t start, count; 739*824b48efSLandon J. Fuller int error; 740*824b48efSLandon J. Fuller 741*824b48efSLandon J. Fuller sc = device_get_softc(dev); 742*824b48efSLandon J. Fuller 743*824b48efSLandon J. Fuller /* Is the rid valid? */ 744*824b48efSLandon J. Fuller if (rid >= bhnd_get_intr_count(child)) 745*824b48efSLandon J. Fuller return (EINVAL); 746*824b48efSLandon J. Fuller 747*824b48efSLandon J. Fuller /* Fetch our common PCI interrupt's start/count. */ 748*824b48efSLandon J. Fuller error = bus_get_resource(sc->parent, SYS_RES_IRQ, sc->intr.intr_rid, 749*824b48efSLandon J. Fuller &start, &count); 750*824b48efSLandon J. Fuller if (error) 751*824b48efSLandon J. Fuller return (error); 752*824b48efSLandon J. Fuller 753*824b48efSLandon J. Fuller /* Add to child's resource list */ 754*824b48efSLandon J. Fuller return (bus_set_resource(child, SYS_RES_IRQ, rid, start, count)); 755*824b48efSLandon J. Fuller } 756*824b48efSLandon J. Fuller 7574ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = { 7584ad7e9b0SAdrian Chadd /* Device interface */ 7594ad7e9b0SAdrian Chadd DEVMETHOD(device_probe, bhndb_pci_probe), 7604ad7e9b0SAdrian Chadd DEVMETHOD(device_attach, bhndb_pci_attach), 7614ad7e9b0SAdrian Chadd DEVMETHOD(device_resume, bhndb_pci_resume), 762bb64eeccSAdrian Chadd DEVMETHOD(device_suspend, bhndb_pci_suspend), 763bb64eeccSAdrian Chadd DEVMETHOD(device_detach, bhndb_pci_detach), 7644ad7e9b0SAdrian Chadd 765f90f4b65SLandon J. Fuller /* BHND interface */ 766*824b48efSLandon J. Fuller DEVMETHOD(bhnd_bus_assign_intr, bhndb_pci_assign_intr), 767*824b48efSLandon J. Fuller 768f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhndb_pci_pwrctl_get_clksrc), 769f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhndb_pci_pwrctl_gate_clock), 770f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhndb_pci_pwrctl_ungate_clock), 771f90f4b65SLandon J. Fuller 7724ad7e9b0SAdrian Chadd /* BHNDB interface */ 7734ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), 774d567592bSAdrian Chadd DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info), 7754ad7e9b0SAdrian Chadd 7764ad7e9b0SAdrian Chadd DEVMETHOD_END 7774ad7e9b0SAdrian Chadd }; 7784ad7e9b0SAdrian Chadd 7794ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods, 7804ad7e9b0SAdrian Chadd sizeof(struct bhndb_pci_softc), bhndb_driver); 7814ad7e9b0SAdrian Chadd 7824ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1); 783148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1); 7844ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); 7854ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); 786148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1); 787