1 /*- 2 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 /* 34 * PCI-specific implementation for the BHNDB bridge driver. 35 * 36 * Provides support for bridging from a PCI parent bus to a BHND-compatible 37 * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point 38 * mode. 39 * 40 * This driver handles all host-level PCI interactions with a PCI/PCIe bridge 41 * core operating in endpoint mode. On the bridged bhnd bus, the PCI core 42 * device will be managed by a bhnd_pci_hostb driver. 43 */ 44 45 #include <sys/param.h> 46 #include <sys/kernel.h> 47 #include <sys/bus.h> 48 #include <sys/limits.h> 49 #include <sys/malloc.h> 50 #include <sys/module.h> 51 #include <sys/systm.h> 52 53 #include <dev/pci/pcireg.h> 54 #include <dev/pci/pcivar.h> 55 56 #include <dev/bhnd/bhnd.h> 57 58 #include <dev/bhnd/cores/pci/bhnd_pcireg.h> 59 60 #include "bhndb_pcireg.h" 61 #include "bhndb_pcivar.h" 62 #include "bhndb_private.h" 63 64 static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc); 65 static int bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc); 66 67 static int bhndb_pci_compat_setregwin(struct bhndb_pci_softc *, 68 const struct bhndb_regwin *, bhnd_addr_t); 69 static int bhndb_pci_fast_setregwin(struct bhndb_pci_softc *, 70 const struct bhndb_regwin *, bhnd_addr_t); 71 72 static void bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc); 73 74 /** 75 * Default bhndb_pci implementation of device_probe(). 76 * 77 * Verifies that the parent is a PCI/PCIe device. 78 */ 79 static int 80 bhndb_pci_probe(device_t dev) 81 { 82 device_t parent; 83 devclass_t parent_bus; 84 devclass_t pci; 85 86 /* Our parent must be a PCI/PCIe device. */ 87 pci = devclass_find("pci"); 88 parent = device_get_parent(dev); 89 parent_bus = device_get_devclass(device_get_parent(parent)); 90 91 if (parent_bus != pci) 92 return (ENXIO); 93 94 device_set_desc(dev, "PCI-BHND bridge"); 95 96 return (BUS_PROBE_DEFAULT); 97 } 98 99 static int 100 bhndb_pci_attach(device_t dev) 101 { 102 struct bhndb_pci_softc *sc; 103 int error, reg; 104 105 sc = device_get_softc(dev); 106 sc->dev = dev; 107 108 /* Enable PCI bus mastering */ 109 pci_enable_busmaster(device_get_parent(dev)); 110 111 /* Determine our bridge device class */ 112 sc->pci_devclass = BHND_DEVCLASS_PCI; 113 if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, ®) == 0) 114 sc->pci_devclass = BHND_DEVCLASS_PCIE; 115 116 /* Enable clocks (if supported by this hardware) */ 117 if ((error = bhndb_enable_pci_clocks(sc))) 118 return (error); 119 120 /* Use siba(4)-compatible regwin handling until we know 121 * what kind of bus is attached */ 122 sc->set_regwin = bhndb_pci_compat_setregwin; 123 124 /* Perform full bridge attach. This should call back into our 125 * bhndb_pci_init_full_config() implementation once the bridged 126 * bhnd(4) bus has been enumerated, but before any devices have been 127 * probed or attached. */ 128 if ((error = bhndb_attach(dev, sc->pci_devclass))) 129 return (error); 130 131 /* If supported, switch to the faster regwin handling */ 132 if (sc->bhndb.chipid.chip_type != BHND_CHIPTYPE_SIBA) { 133 atomic_store_rel_ptr((volatile void *) &sc->set_regwin, 134 (uintptr_t) &bhndb_pci_fast_setregwin); 135 } 136 137 return (0); 138 } 139 140 static int 141 bhndb_pci_init_full_config(device_t dev, device_t child, 142 const struct bhndb_hw_priority *hw_prio_table) 143 { 144 struct bhndb_pci_softc *sc; 145 int error; 146 147 sc = device_get_softc(dev); 148 149 /* Let our parent perform standard initialization first */ 150 if ((error = bhndb_generic_init_full_config(dev, child, hw_prio_table))) 151 return (error); 152 153 /* Fix-up power on defaults for SROM-less devices. */ 154 bhndb_init_sromless_pci_config(sc); 155 156 return (0); 157 } 158 159 /* 160 * On devices without a SROM, the PCI(e) cores will be initialized with 161 * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows 162 * mapped to the wrong core. 163 * 164 * This function updates the SROM shadow to point the BAR0 windows at the 165 * current PCI core. 166 * 167 * Applies to all PCI/PCIe revisions. 168 */ 169 static void 170 bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc) 171 { 172 struct bhndb_resources *bres; 173 const struct bhndb_hwcfg *cfg; 174 const struct bhndb_regwin *win; 175 struct resource *core_regs; 176 bus_size_t srom_offset; 177 u_int pci_cidx, sprom_cidx; 178 uint16_t val; 179 180 bres = sc->bhndb.bus_res; 181 cfg = bres->cfg; 182 183 if (bhnd_get_vendor(sc->bhndb.hostb_dev) != BHND_MFGID_BCM) 184 return; 185 186 switch (bhnd_get_device(sc->bhndb.hostb_dev)) { 187 case BHND_COREID_PCI: 188 srom_offset = BHND_PCI_SRSH_PI_OFFSET; 189 break; 190 case BHND_COREID_PCIE: 191 srom_offset = BHND_PCIE_SRSH_PI_OFFSET; 192 break; 193 default: 194 device_printf(sc->dev, "unsupported PCI host bridge device\n"); 195 return; 196 } 197 198 /* Locate the static register window mapping the PCI core */ 199 win = bhndb_regwin_find_core(cfg->register_windows, sc->pci_devclass, 200 0, BHND_PORT_DEVICE, 0, 0); 201 if (win == NULL) { 202 device_printf(sc->dev, "missing PCI core register window\n"); 203 return; 204 } 205 206 /* Fetch the resource containing the register window */ 207 core_regs = bhndb_find_regwin_resource(bres, win); 208 if (core_regs == NULL) { 209 device_printf(sc->dev, "missing PCI core register resource\n"); 210 return; 211 } 212 213 /* Fetch the SPROM's configured core index */ 214 val = bus_read_2(core_regs, win->win_offset + srom_offset); 215 sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; 216 217 /* If it doesn't match host bridge's core index, update the index 218 * value */ 219 pci_cidx = bhnd_get_core_index(sc->bhndb.hostb_dev); 220 if (sprom_cidx != pci_cidx) { 221 val &= ~BHND_PCI_SRSH_PI_MASK; 222 val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT); 223 bus_write_2(core_regs, 224 win->win_offset + srom_offset, val); 225 } 226 } 227 228 static int 229 bhndb_pci_resume(device_t dev) 230 { 231 struct bhndb_pci_softc *sc; 232 int error; 233 234 sc = device_get_softc(dev); 235 236 /* Enable clocks (if supported by this hardware) */ 237 if ((error = bhndb_enable_pci_clocks(sc))) 238 return (error); 239 240 /* Perform resume */ 241 return (bhndb_generic_resume(dev)); 242 } 243 244 static int 245 bhndb_pci_suspend(device_t dev) 246 { 247 struct bhndb_pci_softc *sc; 248 int error; 249 250 sc = device_get_softc(dev); 251 252 /* Disable clocks (if supported by this hardware) */ 253 if ((error = bhndb_disable_pci_clocks(sc))) 254 return (error); 255 256 /* Perform suspend */ 257 return (bhndb_generic_suspend(dev)); 258 } 259 260 static int 261 bhndb_pci_detach(device_t dev) 262 { 263 struct bhndb_pci_softc *sc; 264 int error; 265 266 sc = device_get_softc(dev); 267 268 /* Disable clocks (if supported by this hardware) */ 269 if ((error = bhndb_disable_pci_clocks(sc))) 270 return (error); 271 272 /* Perform detach */ 273 if ((error = bhndb_generic_detach(dev))) 274 return (error); 275 276 /* Disable PCI bus mastering */ 277 pci_disable_busmaster(device_get_parent(dev)); 278 279 return (0); 280 } 281 282 static int 283 bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw, 284 bhnd_addr_t addr) 285 { 286 struct bhndb_pci_softc *sc = device_get_softc(dev); 287 return (sc->set_regwin(sc, rw, addr)); 288 } 289 290 /** 291 * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. 292 * 293 * On siba(4) devices, it's possible that writing a PCI window register may 294 * not succeed; it's necessary to immediately read the configuration register 295 * and retry if not set to the desired value. 296 * 297 * This is not necessary on bcma(4) devices, but other than the overhead of 298 * validating the register, there's no harm in performing the verification. 299 */ 300 static int 301 bhndb_pci_compat_setregwin(struct bhndb_pci_softc *sc, 302 const struct bhndb_regwin *rw, bhnd_addr_t addr) 303 { 304 device_t parent; 305 int error; 306 307 parent = sc->bhndb.parent_dev; 308 309 if (rw->win_type != BHNDB_REGWIN_T_DYN) 310 return (ENODEV); 311 312 for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { 313 if ((error = bhndb_pci_fast_setregwin(sc, rw, addr))) 314 return (error); 315 316 if (pci_read_config(parent, rw->dyn.cfg_offset, 4) == addr) 317 return (0); 318 319 DELAY(10); 320 } 321 322 /* Unable to set window */ 323 return (ENODEV); 324 } 325 326 /** 327 * A bcma(4)-only bhndb_set_window_addr implementation. 328 */ 329 static int 330 bhndb_pci_fast_setregwin(struct bhndb_pci_softc *sc, 331 const struct bhndb_regwin *rw, bhnd_addr_t addr) 332 { 333 device_t parent = sc->bhndb.parent_dev; 334 335 /* The PCI bridge core only supports 32-bit addressing, regardless 336 * of the bus' support for 64-bit addressing */ 337 if (addr > UINT32_MAX) 338 return (ERANGE); 339 340 switch (rw->win_type) { 341 case BHNDB_REGWIN_T_DYN: 342 /* Addresses must be page aligned */ 343 if (addr % rw->win_size != 0) 344 return (EINVAL); 345 346 pci_write_config(parent, rw->dyn.cfg_offset, addr, 4); 347 break; 348 default: 349 return (ENODEV); 350 } 351 352 return (0); 353 } 354 355 /** 356 * Enable externally managed clocks, if required. 357 * 358 * Some PCI chipsets (BCM4306, possibly others) chips do not support 359 * the idle low-power clock. Clocking must be bootstrapped at 360 * attach/resume by directly adjusting GPIO registers exposed in the 361 * PCI config space, and correspondingly, explicitly shutdown at 362 * detach/suspend. 363 * 364 * @param sc Bridge driver state. 365 */ 366 static int 367 bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc) 368 { 369 device_t pci_parent; 370 uint32_t gpio_in, gpio_out, gpio_en; 371 uint32_t gpio_flags; 372 uint16_t pci_status; 373 374 /* Only supported and required on PCI devices */ 375 if (sc->pci_devclass != BHND_DEVCLASS_PCI) 376 return (0); 377 378 pci_parent = device_get_parent(sc->dev); 379 380 /* Read state of XTAL pin */ 381 gpio_in = pci_read_config(pci_parent, BHNDB_PCI_GPIO_IN, 4); 382 if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) 383 return (0); /* already enabled */ 384 385 /* Fetch current config */ 386 gpio_out = pci_read_config(pci_parent, BHNDB_PCI_GPIO_OUT, 4); 387 gpio_en = pci_read_config(pci_parent, BHNDB_PCI_GPIO_OUTEN, 4); 388 389 /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ 390 gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 391 gpio_out |= gpio_flags; 392 gpio_en |= gpio_flags; 393 394 pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 395 pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 396 DELAY(1000); 397 398 /* Reset PLL_OFF */ 399 gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; 400 pci_write_config(pci_parent, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 401 DELAY(5000); 402 403 /* Clear any PCI 'sent target-abort' flag. */ 404 pci_status = pci_read_config(pci_parent, PCIR_STATUS, 2); 405 pci_status &= ~PCIM_STATUS_STABORT; 406 pci_write_config(pci_parent, PCIR_STATUS, pci_status, 2); 407 408 return (0); 409 } 410 411 /** 412 * Disable externally managed clocks, if required. 413 * 414 * @param sc Bridge driver state. 415 */ 416 static int 417 bhndb_disable_pci_clocks(struct bhndb_pci_softc *sc) 418 { 419 device_t parent_dev; 420 uint32_t gpio_out, gpio_en; 421 422 /* Only supported and required on PCI devices */ 423 if (sc->pci_devclass != BHND_DEVCLASS_PCI) 424 return (0); 425 426 parent_dev = device_get_parent(sc->dev); 427 428 // TODO: Check board flags for BFL2_XTALBUFOUTEN? 429 // TODO: Check PCI core revision? 430 // TODO: Switch to 'slow' clock? 431 432 /* Fetch current config */ 433 gpio_out = pci_read_config(parent_dev, BHNDB_PCI_GPIO_OUT, 4); 434 gpio_en = pci_read_config(parent_dev, BHNDB_PCI_GPIO_OUTEN, 4); 435 436 /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ 437 gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; 438 gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; 439 pci_write_config(parent_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 440 441 /* Enable both output pins */ 442 gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 443 pci_write_config(parent_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 444 445 return (0); 446 } 447 448 static device_method_t bhndb_pci_methods[] = { 449 /* Device interface */ 450 DEVMETHOD(device_probe, bhndb_pci_probe), 451 DEVMETHOD(device_attach, bhndb_pci_attach), 452 DEVMETHOD(device_resume, bhndb_pci_resume), 453 DEVMETHOD(device_suspend, bhndb_pci_suspend), 454 DEVMETHOD(device_detach, bhndb_pci_detach), 455 456 /* BHNDB interface */ 457 DEVMETHOD(bhndb_init_full_config, bhndb_pci_init_full_config), 458 DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), 459 460 DEVMETHOD_END 461 }; 462 463 DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods, 464 sizeof(struct bhndb_pci_softc), bhndb_driver); 465 466 MODULE_VERSION(bhndb_pci, 1); 467 MODULE_DEPEND(bhndb_pci, bhnd_pci, 1, 1, 1); 468 MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); 469 MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); 470