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 * 40bb64eeccSAdrian Chadd * This driver handles all host-level PCI interactions with a PCI/PCIe bridge 41bb64eeccSAdrian Chadd * core operating in endpoint mode. On the bridged bhnd bus, the PCI core 42bb64eeccSAdrian Chadd * device will be managed by a bhnd_pci_hostb driver. 434ad7e9b0SAdrian Chadd */ 444ad7e9b0SAdrian Chadd 454ad7e9b0SAdrian Chadd #include <sys/param.h> 464ad7e9b0SAdrian Chadd #include <sys/kernel.h> 474ad7e9b0SAdrian Chadd #include <sys/bus.h> 484ad7e9b0SAdrian Chadd #include <sys/limits.h> 494ad7e9b0SAdrian Chadd #include <sys/malloc.h> 504ad7e9b0SAdrian Chadd #include <sys/module.h> 514ad7e9b0SAdrian Chadd #include <sys/systm.h> 524ad7e9b0SAdrian Chadd 534ad7e9b0SAdrian Chadd #include <dev/pci/pcireg.h> 544ad7e9b0SAdrian Chadd #include <dev/pci/pcivar.h> 554ad7e9b0SAdrian Chadd 564ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h> 574ad7e9b0SAdrian Chadd 584ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h> 594ad7e9b0SAdrian Chadd 604ad7e9b0SAdrian Chadd #include "bhndb_pcireg.h" 614ad7e9b0SAdrian Chadd #include "bhndb_pcivar.h" 624ad7e9b0SAdrian Chadd #include "bhndb_private.h" 634ad7e9b0SAdrian Chadd 644ad7e9b0SAdrian Chadd static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc); 654ad7e9b0SAdrian Chadd static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc); 664ad7e9b0SAdrian Chadd 674ad7e9b0SAdrian Chadd static int bhndb_pci_compat_setregwin(struct bhndb_pci_softc *, 684ad7e9b0SAdrian Chadd const struct bhndb_regwin *, bhnd_addr_t); 694ad7e9b0SAdrian Chadd static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *, 704ad7e9b0SAdrian Chadd const struct bhndb_regwin *, bhnd_addr_t); 714ad7e9b0SAdrian Chadd 72*e83ce340SAdrian Chadd static void bhndb_init_sromless_pci_config( 73*e83ce340SAdrian Chadd struct bhndb_pci_softc *sc); 74*e83ce340SAdrian Chadd 75*e83ce340SAdrian Chadd static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc); 76*e83ce340SAdrian Chadd static size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc); 774ad7e9b0SAdrian Chadd 784ad7e9b0SAdrian Chadd /** 794ad7e9b0SAdrian Chadd * Default bhndb_pci implementation of device_probe(). 804ad7e9b0SAdrian Chadd * 814ad7e9b0SAdrian Chadd * Verifies that the parent is a PCI/PCIe device. 824ad7e9b0SAdrian Chadd */ 834ad7e9b0SAdrian Chadd static int 844ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev) 854ad7e9b0SAdrian Chadd { 864ad7e9b0SAdrian Chadd device_t parent; 874ad7e9b0SAdrian Chadd devclass_t parent_bus; 884ad7e9b0SAdrian Chadd devclass_t pci; 894ad7e9b0SAdrian Chadd 904ad7e9b0SAdrian Chadd /* Our parent must be a PCI/PCIe device. */ 914ad7e9b0SAdrian Chadd pci = devclass_find("pci"); 924ad7e9b0SAdrian Chadd parent = device_get_parent(dev); 934ad7e9b0SAdrian Chadd parent_bus = device_get_devclass(device_get_parent(parent)); 944ad7e9b0SAdrian Chadd 954ad7e9b0SAdrian Chadd if (parent_bus != pci) 964ad7e9b0SAdrian Chadd return (ENXIO); 974ad7e9b0SAdrian Chadd 984ad7e9b0SAdrian Chadd device_set_desc(dev, "PCI-BHND bridge"); 994ad7e9b0SAdrian Chadd 1004ad7e9b0SAdrian Chadd return (BUS_PROBE_DEFAULT); 1014ad7e9b0SAdrian Chadd } 1024ad7e9b0SAdrian Chadd 1034ad7e9b0SAdrian Chadd static int 1044ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev) 1054ad7e9b0SAdrian Chadd { 1064ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 1074ad7e9b0SAdrian Chadd int error, reg; 1084ad7e9b0SAdrian Chadd 1094ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1104ad7e9b0SAdrian Chadd sc->dev = dev; 111*e83ce340SAdrian Chadd sc->parent = device_get_parent(dev); 1124ad7e9b0SAdrian Chadd 1134ad7e9b0SAdrian Chadd /* Enable PCI bus mastering */ 114*e83ce340SAdrian Chadd pci_enable_busmaster(sc->parent); 1154ad7e9b0SAdrian Chadd 1164ad7e9b0SAdrian Chadd /* Determine our bridge device class */ 1174ad7e9b0SAdrian Chadd sc->pci_devclass = BHND_DEVCLASS_PCI; 118*e83ce340SAdrian Chadd if (pci_find_cap(sc->parent, PCIY_EXPRESS, ®) == 0) 1194ad7e9b0SAdrian Chadd sc->pci_devclass = BHND_DEVCLASS_PCIE; 1204ad7e9b0SAdrian Chadd 121bb64eeccSAdrian Chadd /* Enable clocks (if supported by this hardware) */ 122bb64eeccSAdrian Chadd if ((error = bhndb_enable_pci_clocks(sc))) 1234ad7e9b0SAdrian Chadd return (error); 1244ad7e9b0SAdrian Chadd 1254ad7e9b0SAdrian Chadd /* Use siba(4)-compatible regwin handling until we know 1264ad7e9b0SAdrian Chadd * what kind of bus is attached */ 1274ad7e9b0SAdrian Chadd sc->set_regwin = bhndb_pci_compat_setregwin; 1284ad7e9b0SAdrian Chadd 1294ad7e9b0SAdrian Chadd /* Perform full bridge attach. This should call back into our 1304ad7e9b0SAdrian Chadd * bhndb_pci_init_full_config() implementation once the bridged 1314ad7e9b0SAdrian Chadd * bhnd(4) bus has been enumerated, but before any devices have been 1324ad7e9b0SAdrian Chadd * probed or attached. */ 1334ad7e9b0SAdrian Chadd if ((error = bhndb_attach(dev, sc->pci_devclass))) 1344ad7e9b0SAdrian Chadd return (error); 1354ad7e9b0SAdrian Chadd 1364ad7e9b0SAdrian Chadd /* If supported, switch to the faster regwin handling */ 1374ad7e9b0SAdrian Chadd if (sc->bhndb.chipid.chip_type != BHND_CHIPTYPE_SIBA) { 1384ad7e9b0SAdrian Chadd atomic_store_rel_ptr((volatile void *) &sc->set_regwin, 1394ad7e9b0SAdrian Chadd (uintptr_t) &bhndb_pci_fast_setregwin); 1404ad7e9b0SAdrian Chadd } 1414ad7e9b0SAdrian Chadd 1424ad7e9b0SAdrian Chadd return (0); 1434ad7e9b0SAdrian Chadd } 1444ad7e9b0SAdrian Chadd 1454ad7e9b0SAdrian Chadd static int 1464ad7e9b0SAdrian Chadd bhndb_pci_init_full_config(device_t dev, device_t child, 147bb64eeccSAdrian Chadd const struct bhndb_hw_priority *hw_prio_table) 1484ad7e9b0SAdrian Chadd { 1494ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 150*e83ce340SAdrian Chadd device_t nv_dev; 151*e83ce340SAdrian Chadd bus_size_t nv_sz; 1524ad7e9b0SAdrian Chadd int error; 1534ad7e9b0SAdrian Chadd 1544ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1554ad7e9b0SAdrian Chadd 156bb64eeccSAdrian Chadd /* Let our parent perform standard initialization first */ 157bb64eeccSAdrian Chadd if ((error = bhndb_generic_init_full_config(dev, child, hw_prio_table))) 1584ad7e9b0SAdrian Chadd return (error); 1594ad7e9b0SAdrian Chadd 160bb64eeccSAdrian Chadd /* Fix-up power on defaults for SROM-less devices. */ 1614ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(sc); 1624ad7e9b0SAdrian Chadd 163*e83ce340SAdrian Chadd /* If SPROM is mapped directly into BAR0, add NVRAM device. */ 164*e83ce340SAdrian Chadd nv_sz = bhndb_pci_sprom_size(sc); 165*e83ce340SAdrian Chadd if (nv_sz > 0) { 166*e83ce340SAdrian Chadd struct bhndb_devinfo *dinfo; 167*e83ce340SAdrian Chadd const char *dname; 168*e83ce340SAdrian Chadd 169*e83ce340SAdrian Chadd if (bootverbose) { 170*e83ce340SAdrian Chadd device_printf(dev, "found SPROM (%zu bytes)\n", nv_sz); 171*e83ce340SAdrian Chadd } 172*e83ce340SAdrian Chadd 173*e83ce340SAdrian Chadd /* Add sprom device */ 174*e83ce340SAdrian Chadd dname = "bhnd_nvram"; 175*e83ce340SAdrian Chadd if ((nv_dev = BUS_ADD_CHILD(dev, 0, dname, -1)) == NULL) { 176*e83ce340SAdrian Chadd device_printf(dev, "failed to add sprom device\n"); 177*e83ce340SAdrian Chadd return (ENXIO); 178*e83ce340SAdrian Chadd } 179*e83ce340SAdrian Chadd 180*e83ce340SAdrian Chadd /* Initialize device address space and resource covering the 181*e83ce340SAdrian Chadd * BAR0 SPROM shadow. */ 182*e83ce340SAdrian Chadd dinfo = device_get_ivars(nv_dev); 183*e83ce340SAdrian Chadd dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; 184*e83ce340SAdrian Chadd error = bus_set_resource(nv_dev, SYS_RES_MEMORY, 0, 185*e83ce340SAdrian Chadd bhndb_pci_sprom_addr(sc), nv_sz); 186*e83ce340SAdrian Chadd 187*e83ce340SAdrian Chadd if (error) { 188*e83ce340SAdrian Chadd device_printf(dev, 189*e83ce340SAdrian Chadd "failed to register sprom resources\n"); 190*e83ce340SAdrian Chadd return (error); 191*e83ce340SAdrian Chadd } 192*e83ce340SAdrian Chadd 193*e83ce340SAdrian Chadd /* Attach the device */ 194*e83ce340SAdrian Chadd if ((error = device_probe_and_attach(nv_dev))) { 195*e83ce340SAdrian Chadd device_printf(dev, "sprom attach failed\n"); 196*e83ce340SAdrian Chadd return (error); 197*e83ce340SAdrian Chadd } 198*e83ce340SAdrian Chadd } 199*e83ce340SAdrian Chadd 2004ad7e9b0SAdrian Chadd return (0); 2014ad7e9b0SAdrian Chadd } 2024ad7e9b0SAdrian Chadd 203*e83ce340SAdrian Chadd static const struct bhndb_regwin * 204*e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc) 205*e83ce340SAdrian Chadd { 206*e83ce340SAdrian Chadd struct bhndb_resources *bres; 207*e83ce340SAdrian Chadd const struct bhndb_hwcfg *cfg; 208*e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 209*e83ce340SAdrian Chadd 210*e83ce340SAdrian Chadd bres = sc->bhndb.bus_res; 211*e83ce340SAdrian Chadd cfg = bres->cfg; 212*e83ce340SAdrian Chadd 213*e83ce340SAdrian Chadd sprom_win = bhndb_regwin_find_type(cfg->register_windows, 214*e83ce340SAdrian Chadd BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE); 215*e83ce340SAdrian Chadd 216*e83ce340SAdrian Chadd return (sprom_win); 217*e83ce340SAdrian Chadd } 218*e83ce340SAdrian Chadd 219*e83ce340SAdrian Chadd static bus_addr_t 220*e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc) 221*e83ce340SAdrian Chadd { 222*e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 223*e83ce340SAdrian Chadd struct resource *r; 224*e83ce340SAdrian Chadd 225*e83ce340SAdrian Chadd /* Fetch the SPROM register window */ 226*e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 227*e83ce340SAdrian Chadd KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+")); 228*e83ce340SAdrian Chadd 229*e83ce340SAdrian Chadd /* Fetch the associated resource */ 230*e83ce340SAdrian Chadd r = bhndb_find_regwin_resource(sc->bhndb.bus_res, sprom_win); 231*e83ce340SAdrian Chadd KASSERT(r != NULL, ("missing resource for sprom window\n")); 232*e83ce340SAdrian Chadd 233*e83ce340SAdrian Chadd return (rman_get_start(r) + sprom_win->win_offset); 234*e83ce340SAdrian Chadd } 235*e83ce340SAdrian Chadd 236*e83ce340SAdrian Chadd static bus_size_t 237*e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc) 238*e83ce340SAdrian Chadd { 239*e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 240*e83ce340SAdrian Chadd uint32_t sctl; 241*e83ce340SAdrian Chadd bus_size_t sprom_sz; 242*e83ce340SAdrian Chadd 243*e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 244*e83ce340SAdrian Chadd 245*e83ce340SAdrian Chadd /* PCI_V2 and later devices map SPROM/OTP via ChipCommon */ 246*e83ce340SAdrian Chadd if (sprom_win == NULL) 247*e83ce340SAdrian Chadd return (0); 248*e83ce340SAdrian Chadd 249*e83ce340SAdrian Chadd /* Determine SPROM size */ 250*e83ce340SAdrian Chadd sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4); 251*e83ce340SAdrian Chadd if (sctl & BHNDB_PCI_SPROM_BLANK) 252*e83ce340SAdrian Chadd return (0); 253*e83ce340SAdrian Chadd 254*e83ce340SAdrian Chadd switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) { 255*e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_1KB: 256*e83ce340SAdrian Chadd sprom_sz = (1 * 1024); 257*e83ce340SAdrian Chadd break; 258*e83ce340SAdrian Chadd 259*e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_4KB: 260*e83ce340SAdrian Chadd sprom_sz = (4 * 1024); 261*e83ce340SAdrian Chadd break; 262*e83ce340SAdrian Chadd 263*e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_16KB: 264*e83ce340SAdrian Chadd sprom_sz = (16 * 1024); 265*e83ce340SAdrian Chadd break; 266*e83ce340SAdrian Chadd 267*e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_RESERVED: 268*e83ce340SAdrian Chadd default: 269*e83ce340SAdrian Chadd device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl); 270*e83ce340SAdrian Chadd return (0); 271*e83ce340SAdrian Chadd } 272*e83ce340SAdrian Chadd 273*e83ce340SAdrian Chadd if (sprom_sz > sprom_win->win_size) { 274*e83ce340SAdrian Chadd device_printf(sc->dev, 275*e83ce340SAdrian Chadd "PCI sprom size (0x%x) overruns defined register window\n", 276*e83ce340SAdrian Chadd sctl); 277*e83ce340SAdrian Chadd return (0); 278*e83ce340SAdrian Chadd } 279*e83ce340SAdrian Chadd 280*e83ce340SAdrian Chadd return (sprom_sz); 281*e83ce340SAdrian Chadd } 282*e83ce340SAdrian Chadd 2834ad7e9b0SAdrian Chadd /* 2844ad7e9b0SAdrian Chadd * On devices without a SROM, the PCI(e) cores will be initialized with 285bb64eeccSAdrian Chadd * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows 286bb64eeccSAdrian Chadd * mapped to the wrong core. 2874ad7e9b0SAdrian Chadd * 288bb64eeccSAdrian Chadd * This function updates the SROM shadow to point the BAR0 windows at the 2894ad7e9b0SAdrian Chadd * current PCI core. 2904ad7e9b0SAdrian Chadd * 291bb64eeccSAdrian Chadd * Applies to all PCI/PCIe revisions. 2924ad7e9b0SAdrian Chadd */ 2934ad7e9b0SAdrian Chadd static void 2944ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc) 2954ad7e9b0SAdrian Chadd { 296bb64eeccSAdrian Chadd struct bhndb_resources *bres; 297bb64eeccSAdrian Chadd const struct bhndb_hwcfg *cfg; 298bb64eeccSAdrian Chadd const struct bhndb_regwin *win; 299bb64eeccSAdrian Chadd struct resource *core_regs; 300bb64eeccSAdrian Chadd bus_size_t srom_offset; 301bb64eeccSAdrian Chadd u_int pci_cidx, sprom_cidx; 3024ad7e9b0SAdrian Chadd uint16_t val; 3034ad7e9b0SAdrian Chadd 304bb64eeccSAdrian Chadd bres = sc->bhndb.bus_res; 305bb64eeccSAdrian Chadd cfg = bres->cfg; 306bb64eeccSAdrian Chadd 307bb64eeccSAdrian Chadd if (bhnd_get_vendor(sc->bhndb.hostb_dev) != BHND_MFGID_BCM) 308bb64eeccSAdrian Chadd return; 309bb64eeccSAdrian Chadd 310bb64eeccSAdrian Chadd switch (bhnd_get_device(sc->bhndb.hostb_dev)) { 311bb64eeccSAdrian Chadd case BHND_COREID_PCI: 312bb64eeccSAdrian Chadd srom_offset = BHND_PCI_SRSH_PI_OFFSET; 313bb64eeccSAdrian Chadd break; 314bb64eeccSAdrian Chadd case BHND_COREID_PCIE: 315bb64eeccSAdrian Chadd srom_offset = BHND_PCIE_SRSH_PI_OFFSET; 316bb64eeccSAdrian Chadd break; 317bb64eeccSAdrian Chadd default: 318bb64eeccSAdrian Chadd device_printf(sc->dev, "unsupported PCI host bridge device\n"); 319bb64eeccSAdrian Chadd return; 320bb64eeccSAdrian Chadd } 321bb64eeccSAdrian Chadd 322bb64eeccSAdrian Chadd /* Locate the static register window mapping the PCI core */ 323bb64eeccSAdrian Chadd win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass, 324bb64eeccSAdrian Chadd 0, BHND_PORT_DEVICE, 0, 0); 325bb64eeccSAdrian Chadd if (win == NULL) { 326bb64eeccSAdrian Chadd device_printf(sc->dev, "missing PCI core register window\n"); 327bb64eeccSAdrian Chadd return; 328bb64eeccSAdrian Chadd } 329bb64eeccSAdrian Chadd 330bb64eeccSAdrian Chadd /* Fetch the resource containing the register window */ 331bb64eeccSAdrian Chadd core_regs = bhndb_find_regwin_resource(bres, win); 332bb64eeccSAdrian Chadd if (core_regs == NULL) { 333bb64eeccSAdrian Chadd device_printf(sc->dev, "missing PCI core register resource\n"); 334bb64eeccSAdrian Chadd return; 335bb64eeccSAdrian Chadd } 336bb64eeccSAdrian Chadd 3374ad7e9b0SAdrian Chadd /* Fetch the SPROM's configured core index */ 338bb64eeccSAdrian Chadd val = bus_read_2(core_regs, win->win_offset + srom_offset); 339bb64eeccSAdrian Chadd sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; 3404ad7e9b0SAdrian Chadd 3414ad7e9b0SAdrian Chadd /* If it doesn't match host bridge's core index, update the index 3424ad7e9b0SAdrian Chadd * value */ 343bb64eeccSAdrian Chadd pci_cidx = bhnd_get_core_index(sc->bhndb.hostb_dev); 344bb64eeccSAdrian Chadd if (sprom_cidx != pci_cidx) { 345bb64eeccSAdrian Chadd val &= ~BHND_PCI_SRSH_PI_MASK; 346bb64eeccSAdrian Chadd val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT); 347bb64eeccSAdrian Chadd bus_write_2(core_regs, 348bb64eeccSAdrian Chadd win->win_offset + srom_offset, val); 3494ad7e9b0SAdrian Chadd } 3504ad7e9b0SAdrian Chadd } 3514ad7e9b0SAdrian Chadd 3524ad7e9b0SAdrian Chadd static int 3534ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev) 3544ad7e9b0SAdrian Chadd { 3554ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 3564ad7e9b0SAdrian Chadd int error; 3574ad7e9b0SAdrian Chadd 3584ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 3594ad7e9b0SAdrian Chadd 360bb64eeccSAdrian Chadd /* Enable clocks (if supported by this hardware) */ 361bb64eeccSAdrian Chadd if ((error = bhndb_enable_pci_clocks(sc))) 3624ad7e9b0SAdrian Chadd return (error); 3634ad7e9b0SAdrian Chadd 364bb64eeccSAdrian Chadd /* Perform resume */ 365bb64eeccSAdrian Chadd return (bhndb_generic_resume(dev)); 366bb64eeccSAdrian Chadd } 367bb64eeccSAdrian Chadd 368bb64eeccSAdrian Chadd static int 369bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev) 370bb64eeccSAdrian Chadd { 371bb64eeccSAdrian Chadd struct bhndb_pci_softc *sc; 372bb64eeccSAdrian Chadd int error; 373bb64eeccSAdrian Chadd 374bb64eeccSAdrian Chadd sc = device_get_softc(dev); 375bb64eeccSAdrian Chadd 376bb64eeccSAdrian Chadd /* Disable clocks (if supported by this hardware) */ 377bb64eeccSAdrian Chadd if ((error = bhndb_disable_pci_clocks(sc))) 3784ad7e9b0SAdrian Chadd return (error); 3794ad7e9b0SAdrian Chadd 380bb64eeccSAdrian Chadd /* Perform suspend */ 381bb64eeccSAdrian Chadd return (bhndb_generic_suspend(dev)); 382bb64eeccSAdrian Chadd } 383bb64eeccSAdrian Chadd 384bb64eeccSAdrian Chadd static int 385bb64eeccSAdrian Chadd bhndb_pci_detach(device_t dev) 386bb64eeccSAdrian Chadd { 387bb64eeccSAdrian Chadd struct bhndb_pci_softc *sc; 388bb64eeccSAdrian Chadd int error; 389bb64eeccSAdrian Chadd 390bb64eeccSAdrian Chadd sc = device_get_softc(dev); 391bb64eeccSAdrian Chadd 392bb64eeccSAdrian Chadd /* Disable clocks (if supported by this hardware) */ 393bb64eeccSAdrian Chadd if ((error = bhndb_disable_pci_clocks(sc))) 394bb64eeccSAdrian Chadd return (error); 395bb64eeccSAdrian Chadd 396bb64eeccSAdrian Chadd /* Perform detach */ 397bb64eeccSAdrian Chadd if ((error = bhndb_generic_detach(dev))) 398bb64eeccSAdrian Chadd return (error); 399bb64eeccSAdrian Chadd 400bb64eeccSAdrian Chadd /* Disable PCI bus mastering */ 401*e83ce340SAdrian Chadd pci_disable_busmaster(sc->parent); 402bb64eeccSAdrian Chadd 4034ad7e9b0SAdrian Chadd return (0); 4044ad7e9b0SAdrian Chadd } 4054ad7e9b0SAdrian Chadd 4064ad7e9b0SAdrian Chadd static int 4074ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw, 4084ad7e9b0SAdrian Chadd bhnd_addr_t addr) 4094ad7e9b0SAdrian Chadd { 4104ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc = device_get_softc(dev); 4114ad7e9b0SAdrian Chadd return (sc->set_regwin(sc, rw, addr)); 4124ad7e9b0SAdrian Chadd } 4134ad7e9b0SAdrian Chadd 4144ad7e9b0SAdrian Chadd /** 4154ad7e9b0SAdrian Chadd * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. 4164ad7e9b0SAdrian Chadd * 4174ad7e9b0SAdrian Chadd * On siba(4) devices, it's possible that writing a PCI window register may 4184ad7e9b0SAdrian Chadd * not succeed; it's necessary to immediately read the configuration register 4194ad7e9b0SAdrian Chadd * and retry if not set to the desired value. 4204ad7e9b0SAdrian Chadd * 4214ad7e9b0SAdrian Chadd * This is not necessary on bcma(4) devices, but other than the overhead of 4224ad7e9b0SAdrian Chadd * validating the register, there's no harm in performing the verification. 4234ad7e9b0SAdrian Chadd */ 4244ad7e9b0SAdrian Chadd static int 4254ad7e9b0SAdrian Chadd bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc, 4264ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 4274ad7e9b0SAdrian Chadd { 4284ad7e9b0SAdrian Chadd int error; 429*e83ce340SAdrian Chadd int reg; 4304ad7e9b0SAdrian Chadd 4314ad7e9b0SAdrian Chadd if (rw->win_type != BHNDB_REGWIN_T_DYN) 4324ad7e9b0SAdrian Chadd return (ENODEV); 4334ad7e9b0SAdrian Chadd 434*e83ce340SAdrian Chadd reg = rw->d.dyn.cfg_offset; 4354ad7e9b0SAdrian Chadd for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { 4364ad7e9b0SAdrian Chadd if ((error = bhndb_pci_fast_setregwin(sc, rw, addr))) 4374ad7e9b0SAdrian Chadd return (error); 4384ad7e9b0SAdrian Chadd 439*e83ce340SAdrian Chadd if (pci_read_config(sc->parent, reg, 4) == addr) 4404ad7e9b0SAdrian Chadd return (0); 4414ad7e9b0SAdrian Chadd 4424ad7e9b0SAdrian Chadd DELAY(10); 4434ad7e9b0SAdrian Chadd } 4444ad7e9b0SAdrian Chadd 4454ad7e9b0SAdrian Chadd /* Unable to set window */ 4464ad7e9b0SAdrian Chadd return (ENODEV); 4474ad7e9b0SAdrian Chadd } 4484ad7e9b0SAdrian Chadd 4494ad7e9b0SAdrian Chadd /** 4504ad7e9b0SAdrian Chadd * A bcma(4)-only bhndb_set_window_addr implementation. 4514ad7e9b0SAdrian Chadd */ 4524ad7e9b0SAdrian Chadd static int 4534ad7e9b0SAdrian Chadd bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc, 4544ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 4554ad7e9b0SAdrian Chadd { 4564ad7e9b0SAdrian Chadd /* The PCI bridge core only supports 32-bit addressing, regardless 4574ad7e9b0SAdrian Chadd * of the bus' support for 64-bit addressing */ 4584ad7e9b0SAdrian Chadd if (addr > UINT32_MAX) 4594ad7e9b0SAdrian Chadd return (ERANGE); 4604ad7e9b0SAdrian Chadd 4614ad7e9b0SAdrian Chadd switch (rw->win_type) { 4624ad7e9b0SAdrian Chadd case BHNDB_REGWIN_T_DYN: 4634ad7e9b0SAdrian Chadd /* Addresses must be page aligned */ 4644ad7e9b0SAdrian Chadd if (addr % rw->win_size != 0) 4654ad7e9b0SAdrian Chadd return (EINVAL); 4664ad7e9b0SAdrian Chadd 467*e83ce340SAdrian Chadd pci_write_config(sc->parent, rw->d.dyn.cfg_offset, addr, 4); 4684ad7e9b0SAdrian Chadd break; 4694ad7e9b0SAdrian Chadd default: 4704ad7e9b0SAdrian Chadd return (ENODEV); 4714ad7e9b0SAdrian Chadd } 4724ad7e9b0SAdrian Chadd 4734ad7e9b0SAdrian Chadd return (0); 4744ad7e9b0SAdrian Chadd } 4754ad7e9b0SAdrian Chadd 4764ad7e9b0SAdrian Chadd /** 477bb64eeccSAdrian Chadd * Enable externally managed clocks, if required. 4784ad7e9b0SAdrian Chadd * 479bb64eeccSAdrian Chadd * Some PCI chipsets (BCM4306, possibly others) chips do not support 480bb64eeccSAdrian Chadd * the idle low-power clock. Clocking must be bootstrapped at 481bb64eeccSAdrian Chadd * attach/resume by directly adjusting GPIO registers exposed in the 482bb64eeccSAdrian Chadd * PCI config space, and correspondingly, explicitly shutdown at 483bb64eeccSAdrian Chadd * detach/suspend. 4844ad7e9b0SAdrian Chadd * 4854ad7e9b0SAdrian Chadd * @param sc Bridge driver state. 4864ad7e9b0SAdrian Chadd */ 4874ad7e9b0SAdrian Chadd static int 4884ad7e9b0SAdrian Chadd bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc) 4894ad7e9b0SAdrian Chadd { 4904ad7e9b0SAdrian Chadd uint32_t gpio_in, gpio_out, gpio_en; 4914ad7e9b0SAdrian Chadd uint32_t gpio_flags; 4924ad7e9b0SAdrian Chadd uint16_t pci_status; 4934ad7e9b0SAdrian Chadd 494bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 495bb64eeccSAdrian Chadd if (sc->pci_devclass != BHND_DEVCLASS_PCI) 496bb64eeccSAdrian Chadd return (0); 4974ad7e9b0SAdrian Chadd 4984ad7e9b0SAdrian Chadd /* Read state of XTAL pin */ 499*e83ce340SAdrian Chadd gpio_in = pci_read_config(sc->parent, BHNDB_PCI_GPIO_IN, 4); 5004ad7e9b0SAdrian Chadd if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) 5014ad7e9b0SAdrian Chadd return (0); /* already enabled */ 5024ad7e9b0SAdrian Chadd 5034ad7e9b0SAdrian Chadd /* Fetch current config */ 504*e83ce340SAdrian Chadd gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); 505*e83ce340SAdrian Chadd gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4); 5064ad7e9b0SAdrian Chadd 5074ad7e9b0SAdrian Chadd /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ 5084ad7e9b0SAdrian Chadd gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 5094ad7e9b0SAdrian Chadd gpio_out |= gpio_flags; 5104ad7e9b0SAdrian Chadd gpio_en |= gpio_flags; 5114ad7e9b0SAdrian Chadd 512*e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 513*e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 5144ad7e9b0SAdrian Chadd DELAY(1000); 5154ad7e9b0SAdrian Chadd 5164ad7e9b0SAdrian Chadd /* Reset PLL_OFF */ 5174ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; 518*e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 5194ad7e9b0SAdrian Chadd DELAY(5000); 5204ad7e9b0SAdrian Chadd 5214ad7e9b0SAdrian Chadd /* Clear any PCI 'sent target-abort' flag. */ 522*e83ce340SAdrian Chadd pci_status = pci_read_config(sc->parent, PCIR_STATUS, 2); 5234ad7e9b0SAdrian Chadd pci_status &= ~PCIM_STATUS_STABORT; 524*e83ce340SAdrian Chadd pci_write_config(sc->parent, PCIR_STATUS, pci_status, 2); 5254ad7e9b0SAdrian Chadd 5264ad7e9b0SAdrian Chadd return (0); 5274ad7e9b0SAdrian Chadd } 5284ad7e9b0SAdrian Chadd 5294ad7e9b0SAdrian Chadd /** 530bb64eeccSAdrian Chadd * Disable externally managed clocks, if required. 5314ad7e9b0SAdrian Chadd * 5324ad7e9b0SAdrian Chadd * @param sc Bridge driver state. 5334ad7e9b0SAdrian Chadd */ 5344ad7e9b0SAdrian Chadd static int 5354ad7e9b0SAdrian Chadd bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc) 5364ad7e9b0SAdrian Chadd { 5374ad7e9b0SAdrian Chadd uint32_t gpio_out, gpio_en; 5384ad7e9b0SAdrian Chadd 539bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 540bb64eeccSAdrian Chadd if (sc->pci_devclass != BHND_DEVCLASS_PCI) 541bb64eeccSAdrian Chadd return (0); 5424ad7e9b0SAdrian Chadd 5434ad7e9b0SAdrian Chadd // TODO: Check board flags for BFL2_XTALBUFOUTEN? 5444ad7e9b0SAdrian Chadd // TODO: Check PCI core revision? 5454ad7e9b0SAdrian Chadd // TODO: Switch to 'slow' clock? 5464ad7e9b0SAdrian Chadd 5474ad7e9b0SAdrian Chadd /* Fetch current config */ 548*e83ce340SAdrian Chadd gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); 549*e83ce340SAdrian Chadd gpio_en = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, 4); 5504ad7e9b0SAdrian Chadd 5514ad7e9b0SAdrian Chadd /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ 5524ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; 5534ad7e9b0SAdrian Chadd gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; 554*e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 5554ad7e9b0SAdrian Chadd 5564ad7e9b0SAdrian Chadd /* Enable both output pins */ 5574ad7e9b0SAdrian Chadd gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 558*e83ce340SAdrian Chadd pci_write_config(sc->parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 5594ad7e9b0SAdrian Chadd 5604ad7e9b0SAdrian Chadd return (0); 5614ad7e9b0SAdrian Chadd } 5624ad7e9b0SAdrian Chadd 5634ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = { 5644ad7e9b0SAdrian Chadd /* Device interface */ 5654ad7e9b0SAdrian Chadd DEVMETHOD(device_probe, bhndb_pci_probe), 5664ad7e9b0SAdrian Chadd DEVMETHOD(device_attach, bhndb_pci_attach), 5674ad7e9b0SAdrian Chadd DEVMETHOD(device_resume, bhndb_pci_resume), 568bb64eeccSAdrian Chadd DEVMETHOD(device_suspend, bhndb_pci_suspend), 569bb64eeccSAdrian Chadd DEVMETHOD(device_detach, bhndb_pci_detach), 5704ad7e9b0SAdrian Chadd 5714ad7e9b0SAdrian Chadd /* BHNDB interface */ 5724ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_init_full_config, bhndb_pci_init_full_config), 5734ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), 5744ad7e9b0SAdrian Chadd 5754ad7e9b0SAdrian Chadd DEVMETHOD_END 5764ad7e9b0SAdrian Chadd }; 5774ad7e9b0SAdrian Chadd 5784ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods, 5794ad7e9b0SAdrian Chadd sizeof(struct bhndb_pci_softc), bhndb_driver); 5804ad7e9b0SAdrian Chadd 5814ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1); 582148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1); 5834ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); 5844ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); 585148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1); 586