Lines Matching +full:full +full:- +full:bridge

1 /*-
2 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
36 * PCI-specific implementation for the BHNDB bridge driver.
38 * Provides support for bridging from a PCI parent bus to a BHND-compatible
39 * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point
42 * This driver handles all initial generic host-level PCI interactions with a
43 * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4)
44 * bus has been enumerated, this driver works in tandem with a core-specific
161 device_t dev; /**< bridge device */
164 struct bhnd_core_info hostb_core; /**< PCI bridge core info */
169 struct bhnd_core_info *cores; /**< erom-owned core table */
183 /* Backplane interrupt flags must be routed via siba-specific
190 /* All PCI core revisions require the SRSH work-around */
196 /* All PCIe-G1 core revisions require the SRSH work-around */
214 if (bhnd_core_matches(ci, &entry->match)) in bhndb_pci_find_core()
238 if ((qtable = entry->quirks) == NULL) in bhndb_pci_get_core_quirks()
244 if (!bhnd_chip_matches(cid, &q->chip_desc)) in bhndb_pci_get_core_quirks()
247 if (!bhnd_core_matches(ci, &q->core_desc)) in bhndb_pci_get_core_quirks()
250 quirks |= q->quirks; in bhndb_pci_get_core_quirks()
302 if ((entry = bhndb_pci_find_core(&probe->hostb_core)) == NULL) { in bhndb_pci_probe()
307 device_set_desc(dev, "PCI-BHND bridge"); in bhndb_pci_probe()
309 /* fall-through */ in bhndb_pci_probe()
331 if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT) in bhndb_pci_alloc_msi()
336 if ((error = pci_alloc_msi(sc->parent, &count))) { in bhndb_pci_alloc_msi()
337 device_printf(sc->dev, "failed to allocate MSI interrupts: " in bhndb_pci_alloc_msi()
344 pci_release_msi(sc->parent); in bhndb_pci_alloc_msi()
365 sc->dev = dev; in bhndb_pci_attach()
366 sc->parent = device_get_parent(dev); in bhndb_pci_attach()
367 sc->pci_devclass = bhndb_expected_pci_devclass(dev); in bhndb_pci_attach()
368 sc->pci_quirks = 0; in bhndb_pci_attach()
369 sc->set_regwin = NULL; in bhndb_pci_attach()
377 pci_enable_busmaster(sc->parent); in bhndb_pci_attach()
380 if ((error = bhndb_enable_pci_clocks(sc->dev))) in bhndb_pci_attach()
384 error = bhndb_pci_probe_alloc(&probe, dev, sc->pci_devclass); in bhndb_pci_attach()
388 sc->pci_quirks = bhndb_pci_get_core_quirks(&probe->cid, in bhndb_pci_attach()
389 &probe->hostb_core); in bhndb_pci_attach()
392 if (probe->cid.chip_type == BHND_CHIPTYPE_SIBA) { in bhndb_pci_attach()
393 sc->set_regwin = bhndb_pci_compat_setregwin; in bhndb_pci_attach()
395 sc->set_regwin = bhndb_pci_fast_setregwin; in bhndb_pci_attach()
408 if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) { in bhndb_pci_attach()
413 device_get_nameunit(sc->parent)); in bhndb_pci_attach()
415 sc->msi_count = 0; in bhndb_pci_attach()
419 device_get_nameunit(sc->parent)); in bhndb_pci_attach()
422 sc->isrc = bhndb_alloc_intr_isrc(sc->parent, irq_rid, 0, RM_MAX_END, 1, in bhndb_pci_attach()
424 if (sc->isrc == NULL) { in bhndb_pci_attach()
425 device_printf(sc->dev, "failed to allocate interrupt " in bhndb_pci_attach()
433 * its exclusive ownership of host bridge resources. in bhndb_pci_attach()
435 * This must be done prior to full configuration of the bridge via in bhndb_pci_attach()
438 cid = probe->cid; in bhndb_pci_attach()
439 erom_class = probe->erom_class; in bhndb_pci_attach()
440 hostb_core = probe->hostb_core; in bhndb_pci_attach()
451 /* Perform bridge attach */ in bhndb_pci_attach()
470 if (sc->isrc != NULL) in bhndb_pci_attach()
471 bhndb_free_intr_isrc(sc->isrc); in bhndb_pci_attach()
473 if (sc->msi_count > 0) in bhndb_pci_attach()
474 pci_release_msi(sc->parent); in bhndb_pci_attach()
482 bhndb_disable_pci_clocks(sc->dev); in bhndb_pci_attach()
484 pci_disable_busmaster(sc->parent); in bhndb_pci_attach()
503 /* Perform generic bridge detach */ in bhndb_pci_detach()
508 if ((error = bhndb_disable_pci_clocks(sc->dev))) in bhndb_pci_detach()
512 bhndb_free_intr_isrc(sc->isrc); in bhndb_pci_detach()
515 if (sc->msi_count > 0) in bhndb_pci_detach()
516 pci_release_msi(sc->parent); in bhndb_pci_detach()
519 pci_disable_busmaster(sc->parent); in bhndb_pci_detach()
542 device_printf(sc->dev, "found SPROM (%ju bytes)\n", in bhndb_pci_add_children()
548 child = BUS_ADD_CHILD(sc->dev, in bhndb_pci_add_children()
549 BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1); in bhndb_pci_add_children()
551 device_printf(sc->dev, "failed to add sprom device\n"); in bhndb_pci_add_children()
558 dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; in bhndb_pci_add_children()
563 device_printf(sc->dev, in bhndb_pci_add_children()
579 bres = sc->bhndb.bus_res; in bhndb_pci_sprom_regwin()
580 cfg = bres->cfg; in bhndb_pci_sprom_regwin()
582 sprom_win = bhndb_regwin_find_type(cfg->register_windows, in bhndb_pci_sprom_regwin()
599 r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win); in bhndb_pci_sprom_addr()
602 return (rman_get_start(r) + sprom_win->win_offset); in bhndb_pci_sprom_addr()
619 sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4); in bhndb_pci_sprom_size()
638 device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl); in bhndb_pci_sprom_size()
645 sprom_sz = MIN(sprom_sz, sprom_win->win_size); in bhndb_pci_sprom_size()
674 win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows, in bhndb_pci_get_core_regs()
675 sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0, offset, size); in bhndb_pci_get_core_regs()
677 device_printf(sc->dev, "missing PCI core register window\n"); in bhndb_pci_get_core_regs()
682 r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, win); in bhndb_pci_get_core_regs()
684 device_printf(sc->dev, "missing PCI core register resource\n"); in bhndb_pci_get_core_regs()
688 KASSERT(offset >= win->d.core.offset, ("offset %#jx outside of " in bhndb_pci_get_core_regs()
692 *res_offset = win->win_offset + (offset - win->d.core.offset); in bhndb_pci_get_core_regs()
768 * Fix-up power on defaults for SPROM-less devices.
770 * On SPROM-less devices, the PCI(e) cores will be initialized with their their
771 * Power-on-Reset defaults; this can leave the BHND_PCI_SRSH_PI value pointing
776 * When translating accesses via these BAR0 regions, the PCI bridge determines
788 * 0x1000-0x17FF sprom shadow 0x800-0xFFF
789 * 0x1800-0x1DFF device registers 0x000-0x5FF
790 * 0x1E00+0x1FFF siba config registers 0xE00-0xFFF
792 * This function checks -- and if necessary, corrects -- the BHND_PCI_SRSH_PI
798 * Applies to all PCI and PCIe-G1 core revisions.
812 if ((sc->pci_quirks & BHNDB_PCI_QUIRK_SRSH_WAR) == 0) in bhndb_pci_srsh_pi_war()
817 md = bhnd_core_get_match_desc(&probe->hostb_core); in bhndb_pci_srsh_pi_war()
818 error = bhnd_erom_lookup_core_addr(probe->erom, &md, BHND_PORT_DEVICE, in bhndb_pci_srsh_pi_war()
821 device_printf(sc->dev, "no base address found for the PCI host " in bhndb_pci_srsh_pi_war()
822 "bridge core: %d\n", error); in bhndb_pci_srsh_pi_war()
854 if ((error = bhndb_enable_pci_clocks(sc->dev))) in bhndb_pci_resume()
870 if ((error = bhndb_disable_pci_clocks(sc->dev))) in bhndb_pci_suspend()
882 return (sc->set_regwin(sc->dev, sc->parent, rw, addr)); in bhndb_pci_set_window_addr()
886 * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation.
902 if (rw->win_type != BHNDB_REGWIN_T_DYN) in bhndb_pci_compat_setregwin()
905 reg = rw->d.dyn.cfg_offset; in bhndb_pci_compat_setregwin()
921 * A bcma(4)-only bhndb_set_window_addr implementation.
927 /* The PCI bridge core only supports 32-bit addressing, regardless in bhndb_pci_fast_setregwin()
928 * of the bus' support for 64-bit addressing */ in bhndb_pci_fast_setregwin()
932 switch (rw->win_type) { in bhndb_pci_fast_setregwin()
935 if (addr % rw->win_size != 0) in bhndb_pci_fast_setregwin()
938 pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4); in bhndb_pci_fast_setregwin()
957 * PCI subdevice to the SPROM-supplied boardtype. in bhndb_pci_populate_board_info()
963 * board-specific workarounds. in bhndb_pci_populate_board_info()
968 * explicit references to the SPROM-supplied boardtype(s) in our in bhndb_pci_populate_board_info()
971 if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) { in bhndb_pci_populate_board_info()
972 switch (info->board_type) { in bhndb_pci_populate_board_info()
977 info->board_type = 0; /* allow override below */ in bhndb_pci_populate_board_info()
986 if (info->board_vendor == 0) in bhndb_pci_populate_board_info()
987 info->board_vendor = pci_get_subvendor(sc->parent); in bhndb_pci_populate_board_info()
989 if (info->board_type == 0) in bhndb_pci_populate_board_info()
990 info->board_type = pci_get_subdevice(sc->parent); in bhndb_pci_populate_board_info()
992 if (info->board_devid == 0) in bhndb_pci_populate_board_info()
993 info->board_devid = pci_get_device(sc->parent); in bhndb_pci_populate_board_info()
999 * Examine the bridge device @p dev and return the expected host bridge
1002 * @param dev The bhndb bridge device
1014 * Return true if the bridge device @p dev is attached via PCIe,
1017 * @param dev The bhndb bridge device
1034 * the idle low-power clock. Clocking must be bootstrapped at
1042 * @param dev The bhndb bridge device
1081 /* Clear any PCI 'sent target-abort' flag. */ in bhndb_enable_pci_clocks()
1095 * @param dev The bhndb bridge device
1135 if (bhndb_is_pcie_attached(sc->dev)) in bhndb_pci_pwrctl_get_clksrc()
1142 gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); in bhndb_pci_pwrctl_get_clksrc()
1156 if (bhndb_is_pcie_attached(sc->dev)) in bhndb_pci_pwrctl_gate_clock()
1163 return (bhndb_disable_pci_clocks(sc->dev)); in bhndb_pci_pwrctl_gate_clock()
1173 if (bhndb_is_pcie_attached(sc->dev)) in bhndb_pci_pwrctl_ungate_clock()
1180 return (bhndb_enable_pci_clocks(sc->dev)); in bhndb_pci_pwrctl_ungate_clock()
1193 *isrc = sc->isrc; in bhndb_pci_map_intr_isrc()
1197 /* siba-specific implementation of BHNDB_ROUTE_INTERRUPTS() */
1205 KASSERT(sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC, in bhndb_pci_route_siba_interrupts()
1212 if (ivec > (sizeof(sbintvec)*8) - 1 /* aka '31' */) { in bhndb_pci_route_siba_interrupts()
1214 device_printf(sc->dev, "cannot route interrupts to high " in bhndb_pci_route_siba_interrupts()
1241 if (sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC) in bhndb_pci_route_interrupts()
1255 intmask = pci_read_config(sc->parent, BHNDB_PCI_INT_MASK, 4); in bhndb_pci_route_interrupts()
1257 pci_write_config(sc->parent, BHNDB_PCI_INT_MASK, intmask, 4); in bhndb_pci_route_interrupts()
1265 * Using the generic PCI bridge hardware configuration, allocate, initialize
1272 * @param dev The bhndb_pci bridge device.
1273 * @param hostb_devclass The expected device class of the bridge core.
1276 * @retval non-zero if allocating the probe state fails, a regular
1280 * configuring host bridge resources, and should only be called prior to
1281 * completion of device attach and full configuration of the bridge.
1298 p->dev = dev; in bhndb_pci_probe_alloc()
1299 p->pci_dev = parent_dev; in bhndb_pci_probe_alloc()
1304 p->m_win = NULL; in bhndb_pci_probe_alloc()
1305 p->m_res = NULL; in bhndb_pci_probe_alloc()
1306 p->m_valid = false; in bhndb_pci_probe_alloc()
1308 bhndb_pci_eio_init(&p->erom_io, p); in bhndb_pci_probe_alloc()
1309 eio = &p->erom_io.eio; in bhndb_pci_probe_alloc()
1316 error = bhndb_alloc_host_resources(&p->hr, dev, parent_dev, hwcfg); in bhndb_pci_probe_alloc()
1318 p->hr = NULL; in bhndb_pci_probe_alloc()
1329 p->erom_class = bhnd_erom_probe_driver_classes( in bhndb_pci_probe_alloc()
1330 device_get_devclass(dev), eio, hint, &p->cid); in bhndb_pci_probe_alloc()
1331 if (p->erom_class == NULL) { in bhndb_pci_probe_alloc()
1340 p->erom = bhnd_erom_alloc(p->erom_class, &p->cid, eio); in bhndb_pci_probe_alloc()
1341 if (p->erom == NULL) { in bhndb_pci_probe_alloc()
1351 /* Read the full core table */ in bhndb_pci_probe_alloc()
1352 error = bhnd_erom_get_core_table(p->erom, &p->cores, &p->ncores); in bhndb_pci_probe_alloc()
1354 device_printf(p->dev, "error fetching core table: %d\n", in bhndb_pci_probe_alloc()
1357 p->cores = NULL; in bhndb_pci_probe_alloc()
1361 /* Identify the host bridge core */ in bhndb_pci_probe_alloc()
1362 error = bhndb_find_hostb_core(p->cores, p->ncores, hostb_devclass, in bhndb_pci_probe_alloc()
1363 &p->hostb_core); in bhndb_pci_probe_alloc()
1365 device_printf(dev, "failed to identify the host bridge " in bhndb_pci_probe_alloc()
1376 KASSERT(p->erom == NULL, ("I/O instance will be freed by " in bhndb_pci_probe_alloc()
1382 if (p->erom != NULL) { in bhndb_pci_probe_alloc()
1383 if (p->cores != NULL) in bhndb_pci_probe_alloc()
1384 bhnd_erom_free_core_table(p->erom, p->cores); in bhndb_pci_probe_alloc()
1386 bhnd_erom_free(p->erom); in bhndb_pci_probe_alloc()
1388 KASSERT(p->cores == NULL, ("cannot free erom-owned core table " in bhndb_pci_probe_alloc()
1392 if (p->hr != NULL) in bhndb_pci_probe_alloc()
1393 bhndb_release_host_resources(p->hr); in bhndb_pci_probe_alloc()
1401 * Free the given @p probe instance and any associated host bridge resources.
1406 bhnd_erom_free_core_table(probe->erom, probe->cores); in bhndb_pci_probe_free()
1407 bhnd_erom_free(probe->erom); in bhndb_pci_probe_free()
1408 bhndb_release_host_resources(probe->hr); in bhndb_pci_probe_free()
1423 * @retval non-zero if enumerating the bridged bhnd(4) bus fails, a regular
1430 size_t len = sizeof(**cores) * probe->ncores; in bhndb_pci_probe_copy_core_table()
1433 memcpy(*cores, probe->cores, len); in bhndb_pci_probe_copy_core_table()
1435 *ncores = probe->ncores; in bhndb_pci_probe_copy_core_table()
1459 if (!probe->m_valid) in bhndb_pci_probe_has_mapping()
1462 KASSERT(probe->m_win != NULL, ("missing register window")); in bhndb_pci_probe_has_mapping()
1463 KASSERT(probe->m_res != NULL, ("missing regwin resource")); in bhndb_pci_probe_has_mapping()
1464 KASSERT(probe->m_win->win_type == BHNDB_REGWIN_T_DYN, in bhndb_pci_probe_has_mapping()
1465 ("unexpected window type %d", probe->m_win->win_type)); in bhndb_pci_probe_has_mapping()
1467 if (addr < probe->m_target) in bhndb_pci_probe_has_mapping()
1470 if (addr >= probe->m_target + probe->m_win->win_size) in bhndb_pci_probe_has_mapping()
1473 if ((probe->m_target + probe->m_win->win_size) - addr < size) in bhndb_pci_probe_has_mapping()
1492 * @retval non-zero if an error occurs adjusting the backing dynamic
1506 if (BHND_SIZE_MAX - offset < addr) { in bhndb_pci_probe_map()
1507 device_printf(probe->dev, "invalid offset %#jx+%#jx\n", addr, in bhndb_pci_probe_map()
1516 *res = probe->m_res; in bhndb_pci_probe_map()
1517 *res_offset = (addr - probe->m_target) + in bhndb_pci_probe_map()
1518 probe->m_win->win_offset; in bhndb_pci_probe_map()
1524 regwin_table = probe->hr->cfg->register_windows; in bhndb_pci_probe_map()
1528 device_printf(probe->dev, "unable to map %#jx+%#jx; no " in bhndb_pci_probe_map()
1535 regwin_res = bhndb_host_resource_for_regwin(probe->hr, regwin); in bhndb_pci_probe_map()
1537 device_printf(probe->dev, "unable to map %#jx+%#jx; no " in bhndb_pci_probe_map()
1542 /* Page-align the target address */ in bhndb_pci_probe_map()
1543 target = addr - (addr % regwin->win_size); in bhndb_pci_probe_map()
1546 error = bhndb_pci_compat_setregwin(probe->dev, probe->pci_dev, in bhndb_pci_probe_map()
1549 device_printf(probe->dev, "failed to configure dynamic " in bhndb_pci_probe_map()
1555 probe->m_win = regwin; in bhndb_pci_probe_map()
1556 probe->m_res = regwin_res; in bhndb_pci_probe_map()
1557 probe->m_addr = addr; in bhndb_pci_probe_map()
1558 probe->m_size = size; in bhndb_pci_probe_map()
1559 probe->m_target = target; in bhndb_pci_probe_map()
1560 probe->m_valid = true; in bhndb_pci_probe_map()
1563 *res_offset = (addr - target) + regwin->win_offset; in bhndb_pci_probe_map()
1594 device_printf(probe->dev, "error mapping %#jx+%#jx for " in bhndb_pci_probe_write()
1637 device_printf(probe->dev, "error mapping %#jx+%#jx for " in bhndb_pci_probe_read()
1656 * Initialize a new bhndb PCI bridge EROM I/O instance. All I/O will be
1668 pio->eio.map = bhndb_pci_eio_map; in bhndb_pci_eio_init()
1669 pio->eio.tell = bhndb_pci_eio_tell; in bhndb_pci_eio_init()
1670 pio->eio.read = bhndb_pci_eio_read; in bhndb_pci_eio_init()
1671 pio->eio.fini = NULL; in bhndb_pci_eio_init()
1673 pio->mapped = false; in bhndb_pci_eio_init()
1674 pio->addr = 0; in bhndb_pci_eio_init()
1675 pio->size = 0; in bhndb_pci_eio_init()
1676 pio->probe = probe; in bhndb_pci_eio_init()
1686 if (BHND_ADDR_MAX - addr < size) in bhndb_pci_eio_map()
1689 pio->addr = addr; in bhndb_pci_eio_map()
1690 pio->size = size; in bhndb_pci_eio_map()
1691 pio->mapped = true; in bhndb_pci_eio_map()
1703 if (!pio->mapped) in bhndb_pci_eio_tell()
1706 *addr = pio->addr; in bhndb_pci_eio_tell()
1707 *size = pio->size; in bhndb_pci_eio_tell()
1719 if (!pio->mapped) in bhndb_pci_eio_read()
1723 if (offset > pio->size || in bhndb_pci_eio_read()
1724 width > pio->size || in bhndb_pci_eio_read()
1725 pio->size - offset < width) in bhndb_pci_eio_read()
1730 return (bhndb_pci_probe_read(pio->probe, pio->addr, offset, width)); in bhndb_pci_eio_read()