14ad7e9b0SAdrian Chadd /*- 2bb64eeccSAdrian Chadd * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> 389294a78SLandon J. Fuller * Copyright (c) 2017 The FreeBSD Foundation 44ad7e9b0SAdrian Chadd * All rights reserved. 54ad7e9b0SAdrian Chadd * 689294a78SLandon J. Fuller * Portions of this software were developed by Landon Fuller 789294a78SLandon J. Fuller * under sponsorship from the FreeBSD Foundation. 889294a78SLandon J. Fuller * 94ad7e9b0SAdrian Chadd * Redistribution and use in source and binary forms, with or without 104ad7e9b0SAdrian Chadd * modification, are permitted provided that the following conditions 114ad7e9b0SAdrian Chadd * are met: 124ad7e9b0SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 134ad7e9b0SAdrian Chadd * notice, this list of conditions and the following disclaimer, 144ad7e9b0SAdrian Chadd * without modification. 154ad7e9b0SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 164ad7e9b0SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 174ad7e9b0SAdrian Chadd * redistribution must be conditioned upon including a substantially 184ad7e9b0SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 194ad7e9b0SAdrian Chadd * 204ad7e9b0SAdrian Chadd * NO WARRANTY 214ad7e9b0SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 224ad7e9b0SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 234ad7e9b0SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 244ad7e9b0SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 254ad7e9b0SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 264ad7e9b0SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 274ad7e9b0SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 284ad7e9b0SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 294ad7e9b0SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 304ad7e9b0SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 314ad7e9b0SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 324ad7e9b0SAdrian Chadd */ 334ad7e9b0SAdrian Chadd 344ad7e9b0SAdrian Chadd #include <sys/cdefs.h> 354ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$"); 364ad7e9b0SAdrian Chadd 374ad7e9b0SAdrian Chadd /* 384ad7e9b0SAdrian Chadd * PCI-specific implementation for the BHNDB bridge driver. 394ad7e9b0SAdrian Chadd * 404ad7e9b0SAdrian Chadd * Provides support for bridging from a PCI parent bus to a BHND-compatible 414ad7e9b0SAdrian Chadd * bus (e.g. bcma or siba) via a Broadcom PCI core configured in end-point 424ad7e9b0SAdrian Chadd * mode. 434ad7e9b0SAdrian Chadd * 448ef24a0dSAdrian Chadd * This driver handles all initial generic host-level PCI interactions with a 458ef24a0dSAdrian Chadd * PCI/PCIe bridge core operating in endpoint mode. Once the bridged bhnd(4) 468ef24a0dSAdrian Chadd * bus has been enumerated, this driver works in tandem with a core-specific 478ef24a0dSAdrian Chadd * bhnd_pci_hostb driver to manage the PCI core. 484ad7e9b0SAdrian Chadd */ 494ad7e9b0SAdrian Chadd 504ad7e9b0SAdrian Chadd #include <sys/param.h> 514ad7e9b0SAdrian Chadd #include <sys/kernel.h> 524ad7e9b0SAdrian Chadd #include <sys/bus.h> 534ad7e9b0SAdrian Chadd #include <sys/limits.h> 544ad7e9b0SAdrian Chadd #include <sys/malloc.h> 554ad7e9b0SAdrian Chadd #include <sys/module.h> 564ad7e9b0SAdrian Chadd #include <sys/systm.h> 574ad7e9b0SAdrian Chadd 584ad7e9b0SAdrian Chadd #include <dev/pci/pcireg.h> 594ad7e9b0SAdrian Chadd #include <dev/pci/pcivar.h> 604ad7e9b0SAdrian Chadd 614ad7e9b0SAdrian Chadd #include <dev/bhnd/bhnd.h> 6289294a78SLandon J. Fuller #include <dev/bhnd/bhndreg.h> 6389294a78SLandon J. Fuller 6489294a78SLandon J. Fuller #include <dev/bhnd/bhnd_erom.h> 6589294a78SLandon J. Fuller #include <dev/bhnd/bhnd_eromvar.h> 664ad7e9b0SAdrian Chadd 67*caeff9a3SLandon J. Fuller #include <dev/bhnd/siba/sibareg.h> 68*caeff9a3SLandon J. Fuller 694ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h> 704ad7e9b0SAdrian Chadd 714ad7e9b0SAdrian Chadd #include "bhndb_pcireg.h" 724ad7e9b0SAdrian Chadd #include "bhndb_pcivar.h" 734ad7e9b0SAdrian Chadd #include "bhndb_private.h" 744ad7e9b0SAdrian Chadd 7589294a78SLandon J. Fuller struct bhndb_pci_eio; 7689294a78SLandon J. Fuller 77*caeff9a3SLandon J. Fuller static int bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, 78*caeff9a3SLandon J. Fuller int *msi_count); 7989294a78SLandon J. Fuller static int bhndb_pci_read_core_table(device_t dev, 8089294a78SLandon J. Fuller struct bhnd_chipid *chipid, 8189294a78SLandon J. Fuller struct bhnd_core_info **cores, u_int *ncores, 8289294a78SLandon J. Fuller bhnd_erom_class_t **eromcls); 83111d7cb2SLandon J. Fuller static int bhndb_pci_add_children(struct bhndb_pci_softc *sc); 84111d7cb2SLandon J. Fuller 85*caeff9a3SLandon J. Fuller static bhnd_devclass_t bhndb_expected_pci_devclass(device_t dev); 8689294a78SLandon J. Fuller static bool bhndb_is_pcie_attached(device_t dev); 874ad7e9b0SAdrian Chadd 8889294a78SLandon J. Fuller static int bhndb_enable_pci_clocks(device_t dev); 8989294a78SLandon J. Fuller static int bhndb_disable_pci_clocks(device_t dev); 9089294a78SLandon J. Fuller 9189294a78SLandon J. Fuller static int bhndb_pci_compat_setregwin(device_t dev, 9289294a78SLandon J. Fuller device_t pci_dev, const struct bhndb_regwin *, 9389294a78SLandon J. Fuller bhnd_addr_t); 9489294a78SLandon J. Fuller static int bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev, 954ad7e9b0SAdrian Chadd const struct bhndb_regwin *, bhnd_addr_t); 964ad7e9b0SAdrian Chadd 97*caeff9a3SLandon J. Fuller static void bhndb_pci_write_core(struct bhndb_pci_softc *sc, 98*caeff9a3SLandon J. Fuller bus_size_t offset, uint32_t value, u_int width); 99*caeff9a3SLandon J. Fuller static uint32_t bhndb_pci_read_core(struct bhndb_pci_softc *sc, 100*caeff9a3SLandon J. Fuller bus_size_t offset, u_int width); 101*caeff9a3SLandon J. Fuller 102e83ce340SAdrian Chadd static void bhndb_init_sromless_pci_config( 103e83ce340SAdrian Chadd struct bhndb_pci_softc *sc); 104e83ce340SAdrian Chadd 105e83ce340SAdrian Chadd static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc); 106f9cf87a0SAdrian Chadd static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc); 1074ad7e9b0SAdrian Chadd 10889294a78SLandon J. Fuller static int bhndb_pci_eio_init(struct bhndb_pci_eio *pio, 10989294a78SLandon J. Fuller device_t dev, device_t pci_dev, 11089294a78SLandon J. Fuller struct bhndb_host_resources *hr); 11189294a78SLandon J. Fuller static int bhndb_pci_eio_map(struct bhnd_erom_io *eio, 11289294a78SLandon J. Fuller bhnd_addr_t addr, bhnd_size_t size); 11389294a78SLandon J. Fuller static uint32_t bhndb_pci_eio_read(struct bhnd_erom_io *eio, 11489294a78SLandon J. Fuller bhnd_size_t offset, u_int width); 11589294a78SLandon J. Fuller 116824b48efSLandon J. Fuller #define BHNDB_PCI_MSI_COUNT 1 117824b48efSLandon J. Fuller 118*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pci_quirks[]; 119*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie_quirks[]; 120*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie2_quirks[]; 121*caeff9a3SLandon J. Fuller 122*caeff9a3SLandon J. Fuller static struct bhndb_pci_core bhndb_pci_cores[] = { 123*caeff9a3SLandon J. Fuller BHNDB_PCI_CORE(PCI, BHND_PCI_SRSH_PI_OFFSET, bhndb_pci_quirks), 124*caeff9a3SLandon J. Fuller BHNDB_PCI_CORE(PCIE, BHND_PCIE_SRSH_PI_OFFSET, bhndb_pcie_quirks), 125*caeff9a3SLandon J. Fuller BHNDB_PCI_CORE(PCIE2, BHND_PCIE_SRSH_PI_OFFSET, bhndb_pcie2_quirks), 126*caeff9a3SLandon J. Fuller BHNDB_PCI_CORE_END 127*caeff9a3SLandon J. Fuller }; 128*caeff9a3SLandon J. Fuller 129*caeff9a3SLandon J. Fuller /* bhndb_pci erom I/O instance state */ 13089294a78SLandon J. Fuller struct bhndb_pci_eio { 13189294a78SLandon J. Fuller struct bhnd_erom_io eio; 13289294a78SLandon J. Fuller device_t dev; /**< bridge device */ 13389294a78SLandon J. Fuller device_t pci_dev; /**< parent PCI device */ 13489294a78SLandon J. Fuller struct bhndb_host_resources *hr; /**< borrowed reference to host resources */ 13589294a78SLandon J. Fuller const struct bhndb_regwin *win; /**< mapped register window, or NULL */ 13689294a78SLandon J. Fuller struct resource *res; /**< resource containing the register window, or NULL if no window mapped */ 13789294a78SLandon J. Fuller bhnd_addr_t res_target; /**< current target address (if mapped) */ 13889294a78SLandon J. Fuller bool mapped; /**< true if a valid mapping exists, false otherwise */ 13989294a78SLandon J. Fuller bhnd_addr_t addr; /**< mapped address */ 14089294a78SLandon J. Fuller bhnd_size_t size; /**< mapped size */ 14189294a78SLandon J. Fuller }; 14289294a78SLandon J. Fuller 143*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pci_quirks[] = { 144*caeff9a3SLandon J. Fuller /* Backplane interrupt flags must be routed via siba-specific 145*caeff9a3SLandon J. Fuller * SIBA_CFG0_INTVEC configuration register; the BHNDB_PCI_INT_MASK 146*caeff9a3SLandon J. Fuller * PCI configuration register is unsupported. */ 147*caeff9a3SLandon J. Fuller {{ BHND_MATCH_CHIP_TYPE (SIBA) }, 148*caeff9a3SLandon J. Fuller { BHND_MATCH_CORE_REV (HWREV_LTE(5)) }, 149*caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK_SIBA_INTVEC }, 150*caeff9a3SLandon J. Fuller 151*caeff9a3SLandon J. Fuller /* All PCI core revisions require the SRSH work-around */ 152*caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR), 153*caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK_END 154*caeff9a3SLandon J. Fuller }; 155*caeff9a3SLandon J. Fuller 156*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie_quirks[] = { 157*caeff9a3SLandon J. Fuller /* All PCIe-G1 core revisions require the SRSH work-around */ 158*caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR), 159*caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK_END 160*caeff9a3SLandon J. Fuller }; 161*caeff9a3SLandon J. Fuller 162*caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie2_quirks[] = { 163*caeff9a3SLandon J. Fuller /* All PCIe-G2 core revisions require the SRSH work-around */ 164*caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR), 165*caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK_END 166*caeff9a3SLandon J. Fuller }; 167*caeff9a3SLandon J. Fuller 168*caeff9a3SLandon J. Fuller 169*caeff9a3SLandon J. Fuller /** 170*caeff9a3SLandon J. Fuller * Return the device table entry for @p ci, or NULL if none. 171*caeff9a3SLandon J. Fuller */ 172*caeff9a3SLandon J. Fuller static struct bhndb_pci_core * 173*caeff9a3SLandon J. Fuller bhndb_pci_find_core(struct bhnd_core_info *ci) 174*caeff9a3SLandon J. Fuller { 175*caeff9a3SLandon J. Fuller for (size_t i = 0; !BHNDB_PCI_IS_CORE_END(&bhndb_pci_cores[i]); i++) { 176*caeff9a3SLandon J. Fuller struct bhndb_pci_core *entry = &bhndb_pci_cores[i]; 177*caeff9a3SLandon J. Fuller 178*caeff9a3SLandon J. Fuller if (bhnd_core_matches(ci, &entry->match)) 179*caeff9a3SLandon J. Fuller return (entry); 180*caeff9a3SLandon J. Fuller } 181*caeff9a3SLandon J. Fuller 182*caeff9a3SLandon J. Fuller return (NULL); 183*caeff9a3SLandon J. Fuller } 184*caeff9a3SLandon J. Fuller 185*caeff9a3SLandon J. Fuller /** 186*caeff9a3SLandon J. Fuller * Return all quirk flags for the given @p cid and @p ci. 187*caeff9a3SLandon J. Fuller */ 188*caeff9a3SLandon J. Fuller static uint32_t 189*caeff9a3SLandon J. Fuller bhndb_pci_get_core_quirks(struct bhnd_chipid *cid, struct bhnd_core_info *ci) 190*caeff9a3SLandon J. Fuller { 191*caeff9a3SLandon J. Fuller struct bhndb_pci_core *entry; 192*caeff9a3SLandon J. Fuller struct bhndb_pci_quirk *qtable; 193*caeff9a3SLandon J. Fuller uint32_t quirks; 194*caeff9a3SLandon J. Fuller 195*caeff9a3SLandon J. Fuller quirks = 0; 196*caeff9a3SLandon J. Fuller 197*caeff9a3SLandon J. Fuller /* No core entry? */ 198*caeff9a3SLandon J. Fuller if ((entry = bhndb_pci_find_core(ci)) == NULL) 199*caeff9a3SLandon J. Fuller return (quirks); 200*caeff9a3SLandon J. Fuller 201*caeff9a3SLandon J. Fuller /* No quirks? */ 202*caeff9a3SLandon J. Fuller if ((qtable = entry->quirks) == NULL) 203*caeff9a3SLandon J. Fuller return (quirks); 204*caeff9a3SLandon J. Fuller 205*caeff9a3SLandon J. Fuller for (size_t i = 0; !BHNDB_PCI_IS_QUIRK_END(&qtable[i]); i++) { 206*caeff9a3SLandon J. Fuller struct bhndb_pci_quirk *q = &qtable[i]; 207*caeff9a3SLandon J. Fuller 208*caeff9a3SLandon J. Fuller if (!bhnd_chip_matches(cid, &q->chip_desc)) 209*caeff9a3SLandon J. Fuller continue; 210*caeff9a3SLandon J. Fuller 211*caeff9a3SLandon J. Fuller if (!bhnd_core_matches(ci, &q->core_desc)) 212*caeff9a3SLandon J. Fuller continue; 213*caeff9a3SLandon J. Fuller 214*caeff9a3SLandon J. Fuller quirks |= q->quirks; 215*caeff9a3SLandon J. Fuller } 216*caeff9a3SLandon J. Fuller 217*caeff9a3SLandon J. Fuller return (quirks); 218*caeff9a3SLandon J. Fuller } 219*caeff9a3SLandon J. Fuller 2204ad7e9b0SAdrian Chadd /** 2214ad7e9b0SAdrian Chadd * Default bhndb_pci implementation of device_probe(). 2224ad7e9b0SAdrian Chadd * 2234ad7e9b0SAdrian Chadd * Verifies that the parent is a PCI/PCIe device. 2244ad7e9b0SAdrian Chadd */ 2254ad7e9b0SAdrian Chadd static int 2264ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev) 2274ad7e9b0SAdrian Chadd { 228*caeff9a3SLandon J. Fuller struct bhnd_chipid cid; 229*caeff9a3SLandon J. Fuller struct bhnd_core_info *cores, hostb_core; 230*caeff9a3SLandon J. Fuller struct bhndb_pci_core *entry; 231*caeff9a3SLandon J. Fuller bhnd_devclass_t hostb_devclass; 232*caeff9a3SLandon J. Fuller u_int ncores; 2334ad7e9b0SAdrian Chadd device_t parent; 234*caeff9a3SLandon J. Fuller devclass_t parent_bus, pci; 235*caeff9a3SLandon J. Fuller int error; 236*caeff9a3SLandon J. Fuller 237*caeff9a3SLandon J. Fuller cores = NULL; 2384ad7e9b0SAdrian Chadd 2394ad7e9b0SAdrian Chadd /* Our parent must be a PCI/PCIe device. */ 2404ad7e9b0SAdrian Chadd pci = devclass_find("pci"); 2414ad7e9b0SAdrian Chadd parent = device_get_parent(dev); 2424ad7e9b0SAdrian Chadd parent_bus = device_get_devclass(device_get_parent(parent)); 2434ad7e9b0SAdrian Chadd 2444ad7e9b0SAdrian Chadd if (parent_bus != pci) 2454ad7e9b0SAdrian Chadd return (ENXIO); 2464ad7e9b0SAdrian Chadd 247*caeff9a3SLandon J. Fuller /* Enable clocks */ 248*caeff9a3SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(dev))) 249*caeff9a3SLandon J. Fuller return (error); 2504ad7e9b0SAdrian Chadd 251*caeff9a3SLandon J. Fuller /* Identify the chip and enumerate the bridged cores */ 252*caeff9a3SLandon J. Fuller error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores, NULL); 253*caeff9a3SLandon J. Fuller if (error) 254*caeff9a3SLandon J. Fuller goto cleanup; 255*caeff9a3SLandon J. Fuller 256*caeff9a3SLandon J. Fuller /* Search our core table for the host bridge core */ 257*caeff9a3SLandon J. Fuller hostb_devclass = bhndb_expected_pci_devclass(dev); 258*caeff9a3SLandon J. Fuller error = bhndb_find_hostb_core(cores, ncores, hostb_devclass, 259*caeff9a3SLandon J. Fuller &hostb_core); 260*caeff9a3SLandon J. Fuller if (error) 261*caeff9a3SLandon J. Fuller goto cleanup; 262*caeff9a3SLandon J. Fuller 263*caeff9a3SLandon J. Fuller /* Look for a matching core table entry */ 264*caeff9a3SLandon J. Fuller if ((entry = bhndb_pci_find_core(&hostb_core)) == NULL) { 265*caeff9a3SLandon J. Fuller error = ENXIO; 266*caeff9a3SLandon J. Fuller goto cleanup; 2674ad7e9b0SAdrian Chadd } 2684ad7e9b0SAdrian Chadd 269*caeff9a3SLandon J. Fuller device_set_desc(dev, "PCI-BHND bridge"); 270*caeff9a3SLandon J. Fuller 271*caeff9a3SLandon J. Fuller /* fall-through */ 272*caeff9a3SLandon J. Fuller error = BUS_PROBE_DEFAULT; 273*caeff9a3SLandon J. Fuller 274*caeff9a3SLandon J. Fuller cleanup: 275*caeff9a3SLandon J. Fuller bhndb_disable_pci_clocks(dev); 276*caeff9a3SLandon J. Fuller if (cores != NULL) 277*caeff9a3SLandon J. Fuller free(cores, M_BHND); 278*caeff9a3SLandon J. Fuller 279*caeff9a3SLandon J. Fuller return (error); 280*caeff9a3SLandon J. Fuller } 281*caeff9a3SLandon J. Fuller 282*caeff9a3SLandon J. Fuller /** 283*caeff9a3SLandon J. Fuller * Attempt to allocate MSI interrupts, returning the count in @p msi_count 284*caeff9a3SLandon J. Fuller * on success. 285*caeff9a3SLandon J. Fuller */ 286824b48efSLandon J. Fuller static int 287*caeff9a3SLandon J. Fuller bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count) 288824b48efSLandon J. Fuller { 289*caeff9a3SLandon J. Fuller int error, count; 290824b48efSLandon J. Fuller 291824b48efSLandon J. Fuller /* Is MSI available? */ 292824b48efSLandon J. Fuller if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT) 293824b48efSLandon J. Fuller return (ENXIO); 294824b48efSLandon J. Fuller 295824b48efSLandon J. Fuller /* Allocate expected message count */ 296*caeff9a3SLandon J. Fuller count = BHNDB_PCI_MSI_COUNT; 297*caeff9a3SLandon J. Fuller if ((error = pci_alloc_msi(sc->parent, &count))) { 298824b48efSLandon J. Fuller device_printf(sc->dev, "failed to allocate MSI interrupts: " 299824b48efSLandon J. Fuller "%d\n", error); 300*caeff9a3SLandon J. Fuller 301824b48efSLandon J. Fuller return (error); 302824b48efSLandon J. Fuller } 303824b48efSLandon J. Fuller 304*caeff9a3SLandon J. Fuller if (count < BHNDB_PCI_MSI_COUNT) 305824b48efSLandon J. Fuller return (ENXIO); 306824b48efSLandon J. Fuller 307*caeff9a3SLandon J. Fuller *msi_count = count; 308824b48efSLandon J. Fuller return (0); 309824b48efSLandon J. Fuller } 310824b48efSLandon J. Fuller 3114ad7e9b0SAdrian Chadd static int 3124ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev) 3134ad7e9b0SAdrian Chadd { 3144ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 31589294a78SLandon J. Fuller struct bhnd_chipid cid; 31689294a78SLandon J. Fuller struct bhnd_core_info *cores, hostb_core; 31789294a78SLandon J. Fuller bhnd_erom_class_t *erom_class; 31889294a78SLandon J. Fuller u_int ncores; 319*caeff9a3SLandon J. Fuller int irq_rid; 320*caeff9a3SLandon J. Fuller int error; 3214ad7e9b0SAdrian Chadd 3224ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 3234ad7e9b0SAdrian Chadd sc->dev = dev; 324e83ce340SAdrian Chadd sc->parent = device_get_parent(dev); 325*caeff9a3SLandon J. Fuller sc->pci_devclass = bhndb_expected_pci_devclass(dev); 326*caeff9a3SLandon J. Fuller sc->pci_quirks = 0; 32789294a78SLandon J. Fuller sc->set_regwin = NULL; 32889294a78SLandon J. Fuller 329*caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK_INIT(sc); 330*caeff9a3SLandon J. Fuller 33189294a78SLandon J. Fuller cores = NULL; 3324ad7e9b0SAdrian Chadd 333824b48efSLandon J. Fuller /* Enable PCI bus mastering */ 334824b48efSLandon J. Fuller pci_enable_busmaster(sc->parent); 335824b48efSLandon J. Fuller 33689294a78SLandon J. Fuller /* Set up PCI interrupt handling */ 337*caeff9a3SLandon J. Fuller if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) { 338*caeff9a3SLandon J. Fuller /* MSI uses resource IDs starting at 1 */ 339*caeff9a3SLandon J. Fuller irq_rid = 1; 340*caeff9a3SLandon J. Fuller 341824b48efSLandon J. Fuller device_printf(dev, "Using MSI interrupts on %s\n", 342824b48efSLandon J. Fuller device_get_nameunit(sc->parent)); 343824b48efSLandon J. Fuller } else { 344*caeff9a3SLandon J. Fuller sc->msi_count = 0; 345*caeff9a3SLandon J. Fuller irq_rid = 0; 346*caeff9a3SLandon J. Fuller 347824b48efSLandon J. Fuller device_printf(dev, "Using INTx interrupts on %s\n", 348824b48efSLandon J. Fuller device_get_nameunit(sc->parent)); 349824b48efSLandon J. Fuller } 350824b48efSLandon J. Fuller 351*caeff9a3SLandon J. Fuller sc->isrc = bhndb_alloc_intr_isrc(sc->parent, irq_rid, 0, RM_MAX_END, 1, 352*caeff9a3SLandon J. Fuller RF_SHAREABLE | RF_ACTIVE); 353*caeff9a3SLandon J. Fuller if (sc->isrc == NULL) { 354*caeff9a3SLandon J. Fuller device_printf(sc->dev, "failed to allocate interrupt " 355*caeff9a3SLandon J. Fuller "resource\n"); 356*caeff9a3SLandon J. Fuller error = ENXIO; 357*caeff9a3SLandon J. Fuller goto cleanup; 358*caeff9a3SLandon J. Fuller } 3594ad7e9b0SAdrian Chadd 360111d7cb2SLandon J. Fuller /* Enable clocks (if required by this hardware) */ 36189294a78SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(sc->dev))) 362111d7cb2SLandon J. Fuller goto cleanup; 363111d7cb2SLandon J. Fuller 36489294a78SLandon J. Fuller /* Identify the chip and enumerate the bridged cores */ 36589294a78SLandon J. Fuller error = bhndb_pci_read_core_table(dev, &cid, &cores, &ncores, 36689294a78SLandon J. Fuller &erom_class); 36789294a78SLandon J. Fuller if (error) 368111d7cb2SLandon J. Fuller goto cleanup; 369111d7cb2SLandon J. Fuller 37089294a78SLandon J. Fuller /* Select the appropriate register window handler */ 37189294a78SLandon J. Fuller if (cid.chip_type == BHND_CHIPTYPE_SIBA) { 37289294a78SLandon J. Fuller sc->set_regwin = bhndb_pci_compat_setregwin; 37389294a78SLandon J. Fuller } else { 37489294a78SLandon J. Fuller sc->set_regwin = bhndb_pci_fast_setregwin; 3754ad7e9b0SAdrian Chadd } 3764ad7e9b0SAdrian Chadd 377*caeff9a3SLandon J. Fuller /* Determine our host bridge core and populate our quirk flags */ 37889294a78SLandon J. Fuller error = bhndb_find_hostb_core(cores, ncores, sc->pci_devclass, 37989294a78SLandon J. Fuller &hostb_core); 38089294a78SLandon J. Fuller if (error) 38189294a78SLandon J. Fuller goto cleanup; 38289294a78SLandon J. Fuller 383*caeff9a3SLandon J. Fuller sc->pci_quirks = bhndb_pci_get_core_quirks(&cid, &hostb_core); 384*caeff9a3SLandon J. Fuller 38589294a78SLandon J. Fuller /* Perform bridge attach */ 38689294a78SLandon J. Fuller error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class); 38789294a78SLandon J. Fuller if (error) 38889294a78SLandon J. Fuller goto cleanup; 3894ad7e9b0SAdrian Chadd 390bb64eeccSAdrian Chadd /* Fix-up power on defaults for SROM-less devices. */ 3914ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(sc); 3924ad7e9b0SAdrian Chadd 393111d7cb2SLandon J. Fuller /* Add any additional child devices */ 394111d7cb2SLandon J. Fuller if ((error = bhndb_pci_add_children(sc))) 395111d7cb2SLandon J. Fuller goto cleanup; 396111d7cb2SLandon J. Fuller 397111d7cb2SLandon J. Fuller /* Probe and attach our children */ 398111d7cb2SLandon J. Fuller if ((error = bus_generic_attach(dev))) 399111d7cb2SLandon J. Fuller goto cleanup; 400111d7cb2SLandon J. Fuller 40189294a78SLandon J. Fuller free(cores, M_BHND); 40289294a78SLandon J. Fuller 403111d7cb2SLandon J. Fuller return (0); 404111d7cb2SLandon J. Fuller 405111d7cb2SLandon J. Fuller cleanup: 406111d7cb2SLandon J. Fuller device_delete_children(dev); 40789294a78SLandon J. Fuller bhndb_disable_pci_clocks(sc->dev); 40889294a78SLandon J. Fuller 409*caeff9a3SLandon J. Fuller if (sc->isrc != NULL) 410*caeff9a3SLandon J. Fuller bhndb_free_intr_isrc(sc->isrc); 411*caeff9a3SLandon J. Fuller 412*caeff9a3SLandon J. Fuller if (sc->msi_count > 0) 413824b48efSLandon J. Fuller pci_release_msi(dev); 414824b48efSLandon J. Fuller 41589294a78SLandon J. Fuller if (cores != NULL) 41689294a78SLandon J. Fuller free(cores, M_BHND); 41789294a78SLandon J. Fuller 418111d7cb2SLandon J. Fuller pci_disable_busmaster(sc->parent); 419111d7cb2SLandon J. Fuller 420*caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK_DESTROY(sc); 421*caeff9a3SLandon J. Fuller 422111d7cb2SLandon J. Fuller return (error); 423111d7cb2SLandon J. Fuller } 424111d7cb2SLandon J. Fuller 425111d7cb2SLandon J. Fuller static int 426111d7cb2SLandon J. Fuller bhndb_pci_detach(device_t dev) 427111d7cb2SLandon J. Fuller { 428111d7cb2SLandon J. Fuller struct bhndb_pci_softc *sc; 429111d7cb2SLandon J. Fuller int error; 430111d7cb2SLandon J. Fuller 431111d7cb2SLandon J. Fuller sc = device_get_softc(dev); 432111d7cb2SLandon J. Fuller 433111d7cb2SLandon J. Fuller /* Attempt to detach our children */ 434111d7cb2SLandon J. Fuller if ((error = bus_generic_detach(dev))) 435111d7cb2SLandon J. Fuller return (error); 436111d7cb2SLandon J. Fuller 437111d7cb2SLandon J. Fuller /* Perform generic bridge detach */ 438111d7cb2SLandon J. Fuller if ((error = bhndb_generic_detach(dev))) 439111d7cb2SLandon J. Fuller return (error); 440111d7cb2SLandon J. Fuller 441111d7cb2SLandon J. Fuller /* Disable clocks (if required by this hardware) */ 44289294a78SLandon J. Fuller if ((error = bhndb_disable_pci_clocks(sc->dev))) 443111d7cb2SLandon J. Fuller return (error); 444111d7cb2SLandon J. Fuller 445*caeff9a3SLandon J. Fuller /* Free our interrupt resources */ 446*caeff9a3SLandon J. Fuller bhndb_free_intr_isrc(sc->isrc); 447*caeff9a3SLandon J. Fuller 448824b48efSLandon J. Fuller /* Release MSI interrupts */ 449*caeff9a3SLandon J. Fuller if (sc->msi_count > 0) 450824b48efSLandon J. Fuller pci_release_msi(dev); 451824b48efSLandon J. Fuller 452111d7cb2SLandon J. Fuller /* Disable PCI bus mastering */ 453111d7cb2SLandon J. Fuller pci_disable_busmaster(sc->parent); 454111d7cb2SLandon J. Fuller 455*caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK_DESTROY(sc); 456*caeff9a3SLandon J. Fuller 457111d7cb2SLandon J. Fuller return (0); 458111d7cb2SLandon J. Fuller } 459111d7cb2SLandon J. Fuller 46089294a78SLandon J. Fuller /** 46189294a78SLandon J. Fuller * Use the generic PCI bridge hardware configuration to enumerate the bridged 46289294a78SLandon J. Fuller * bhnd(4) bus' core table. 46389294a78SLandon J. Fuller * 46489294a78SLandon J. Fuller * @note This function may be safely called prior to device attach, (e.g. 46589294a78SLandon J. Fuller * from DEVICE_PROBE). 46689294a78SLandon J. Fuller * @note This function requires exclusive ownership over allocating and 46789294a78SLandon J. Fuller * configuring host bridge resources, and should only be called prior to 46889294a78SLandon J. Fuller * completion of device attach and full configuration of the bridge. 46989294a78SLandon J. Fuller * 47089294a78SLandon J. Fuller * @param dev The bhndb_pci bridge device. 47189294a78SLandon J. Fuller * @param[out] chipid On success, the parsed chip identification. 47289294a78SLandon J. Fuller * @param[out] cores On success, the enumerated core table. The 47389294a78SLandon J. Fuller * caller is responsible for freeing this table via 47489294a78SLandon J. Fuller * bhndb_pci_free_core_table(). 47589294a78SLandon J. Fuller * @param[out] ncores On success, the number of cores found in 47689294a78SLandon J. Fuller * @p cores. 47789294a78SLandon J. Fuller * @param[out] eromcls On success, a pointer to the erom class used to 47889294a78SLandon J. Fuller * parse the device enumeration table. This 47989294a78SLandon J. Fuller * argument may be NULL if the class is not 48089294a78SLandon J. Fuller * desired. 48189294a78SLandon J. Fuller * 48289294a78SLandon J. Fuller * @retval 0 success 48389294a78SLandon J. Fuller * @retval non-zero if enumerating the bridged bhnd(4) bus fails, a regular 48489294a78SLandon J. Fuller * unix error code will be returned. 48589294a78SLandon J. Fuller */ 48689294a78SLandon J. Fuller static int 48789294a78SLandon J. Fuller bhndb_pci_read_core_table(device_t dev, struct bhnd_chipid *chipid, 48889294a78SLandon J. Fuller struct bhnd_core_info **cores, u_int *ncores, 48989294a78SLandon J. Fuller bhnd_erom_class_t **eromcls) 49089294a78SLandon J. Fuller { 49189294a78SLandon J. Fuller const struct bhndb_hwcfg *cfg; 49289294a78SLandon J. Fuller struct bhndb_host_resources *hr; 49389294a78SLandon J. Fuller struct bhndb_pci_eio pio; 49489294a78SLandon J. Fuller struct bhnd_core_info *erom_cores; 49589294a78SLandon J. Fuller const struct bhnd_chipid *hint; 49689294a78SLandon J. Fuller struct bhnd_chipid cid; 49789294a78SLandon J. Fuller bhnd_erom_class_t *erom_class; 49889294a78SLandon J. Fuller bhnd_erom_t *erom; 49989294a78SLandon J. Fuller device_t parent_dev; 50089294a78SLandon J. Fuller u_int erom_ncores; 50189294a78SLandon J. Fuller int error; 50289294a78SLandon J. Fuller 50389294a78SLandon J. Fuller parent_dev = device_get_parent(dev); 50489294a78SLandon J. Fuller erom = NULL; 50589294a78SLandon J. Fuller erom_cores = NULL; 50689294a78SLandon J. Fuller 50789294a78SLandon J. Fuller /* Fetch our chipid hint (if any) and generic hardware configuration */ 50889294a78SLandon J. Fuller cfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev); 50989294a78SLandon J. Fuller hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev); 51089294a78SLandon J. Fuller 51189294a78SLandon J. Fuller /* Allocate our host resources */ 51289294a78SLandon J. Fuller if ((error = bhndb_alloc_host_resources(parent_dev, cfg, &hr))) 51389294a78SLandon J. Fuller return (error); 51489294a78SLandon J. Fuller 51589294a78SLandon J. Fuller /* Initialize our erom I/O state */ 51689294a78SLandon J. Fuller if ((error = bhndb_pci_eio_init(&pio, dev, parent_dev, hr))) 51789294a78SLandon J. Fuller goto failed; 51889294a78SLandon J. Fuller 51989294a78SLandon J. Fuller /* Map the first bus core from our bridged bhnd(4) bus */ 52089294a78SLandon J. Fuller error = bhndb_pci_eio_map(&pio.eio, BHND_DEFAULT_CHIPC_ADDR, 52189294a78SLandon J. Fuller BHND_DEFAULT_CORE_SIZE); 52289294a78SLandon J. Fuller if (error) 52389294a78SLandon J. Fuller goto failed; 52489294a78SLandon J. Fuller 52589294a78SLandon J. Fuller /* Probe for a usable EROM class, and read the chip identifier */ 52689294a78SLandon J. Fuller erom_class = bhnd_erom_probe_driver_classes(device_get_devclass(dev), 52789294a78SLandon J. Fuller &pio.eio, hint, &cid); 52889294a78SLandon J. Fuller if (erom_class == NULL) { 52989294a78SLandon J. Fuller device_printf(dev, "device enumeration unsupported; no " 53089294a78SLandon J. Fuller "compatible driver found\n"); 53189294a78SLandon J. Fuller 53289294a78SLandon J. Fuller error = ENXIO; 53389294a78SLandon J. Fuller goto failed; 53489294a78SLandon J. Fuller } 53589294a78SLandon J. Fuller 53689294a78SLandon J. Fuller /* Allocate EROM parser */ 53789294a78SLandon J. Fuller if ((erom = bhnd_erom_alloc(erom_class, &cid, &pio.eio)) == NULL) { 53889294a78SLandon J. Fuller device_printf(dev, "failed to allocate device enumeration " 53989294a78SLandon J. Fuller "table parser\n"); 54089294a78SLandon J. Fuller error = ENXIO; 54189294a78SLandon J. Fuller goto failed; 54289294a78SLandon J. Fuller } 54389294a78SLandon J. Fuller 54489294a78SLandon J. Fuller /* Read the full core table */ 54589294a78SLandon J. Fuller error = bhnd_erom_get_core_table(erom, &erom_cores, &erom_ncores); 54689294a78SLandon J. Fuller if (error) { 54789294a78SLandon J. Fuller device_printf(dev, "error fetching core table: %d\n", error); 54889294a78SLandon J. Fuller goto failed; 54989294a78SLandon J. Fuller } 55089294a78SLandon J. Fuller 55189294a78SLandon J. Fuller /* Provide the results to our caller */ 55289294a78SLandon J. Fuller *cores = malloc(sizeof(erom_cores[0]) * erom_ncores, M_BHND, M_WAITOK); 55389294a78SLandon J. Fuller memcpy(*cores, erom_cores, sizeof(erom_cores[0]) * erom_ncores); 55489294a78SLandon J. Fuller *ncores = erom_ncores; 55589294a78SLandon J. Fuller 55689294a78SLandon J. Fuller *chipid = cid; 55789294a78SLandon J. Fuller if (eromcls != NULL) 55889294a78SLandon J. Fuller *eromcls = erom_class; 55989294a78SLandon J. Fuller 56089294a78SLandon J. Fuller /* Clean up */ 56189294a78SLandon J. Fuller bhnd_erom_free_core_table(erom, erom_cores); 56289294a78SLandon J. Fuller bhnd_erom_free(erom); 56389294a78SLandon J. Fuller bhndb_release_host_resources(hr); 56489294a78SLandon J. Fuller 56589294a78SLandon J. Fuller return (0); 56689294a78SLandon J. Fuller 56789294a78SLandon J. Fuller failed: 56889294a78SLandon J. Fuller if (erom_cores != NULL) 56989294a78SLandon J. Fuller bhnd_erom_free_core_table(erom, erom_cores); 57089294a78SLandon J. Fuller 57189294a78SLandon J. Fuller if (erom != NULL) 57289294a78SLandon J. Fuller bhnd_erom_free(erom); 57389294a78SLandon J. Fuller 57489294a78SLandon J. Fuller bhndb_release_host_resources(hr); 57589294a78SLandon J. Fuller return (error); 57689294a78SLandon J. Fuller } 57789294a78SLandon J. Fuller 578111d7cb2SLandon J. Fuller static int 579111d7cb2SLandon J. Fuller bhndb_pci_add_children(struct bhndb_pci_softc *sc) 580111d7cb2SLandon J. Fuller { 581111d7cb2SLandon J. Fuller bus_size_t nv_sz; 582111d7cb2SLandon J. Fuller int error; 583111d7cb2SLandon J. Fuller 584111d7cb2SLandon J. Fuller /** 585111d7cb2SLandon J. Fuller * If SPROM is mapped directly into BAR0, add child NVRAM 586111d7cb2SLandon J. Fuller * device. 587111d7cb2SLandon J. Fuller */ 588e83ce340SAdrian Chadd nv_sz = bhndb_pci_sprom_size(sc); 589e83ce340SAdrian Chadd if (nv_sz > 0) { 590e83ce340SAdrian Chadd struct bhndb_devinfo *dinfo; 591111d7cb2SLandon J. Fuller device_t child; 592e83ce340SAdrian Chadd 593e83ce340SAdrian Chadd if (bootverbose) { 594111d7cb2SLandon J. Fuller device_printf(sc->dev, "found SPROM (%ju bytes)\n", 595111d7cb2SLandon J. Fuller (uintmax_t)nv_sz); 596e83ce340SAdrian Chadd } 597e83ce340SAdrian Chadd 598111d7cb2SLandon J. Fuller /* Add sprom device, ordered early enough to be available 599111d7cb2SLandon J. Fuller * before the bridged bhnd(4) bus is attached. */ 600111d7cb2SLandon J. Fuller child = BUS_ADD_CHILD(sc->dev, 601111d7cb2SLandon J. Fuller BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1); 602111d7cb2SLandon J. Fuller if (child == NULL) { 603111d7cb2SLandon J. Fuller device_printf(sc->dev, "failed to add sprom device\n"); 604e83ce340SAdrian Chadd return (ENXIO); 605e83ce340SAdrian Chadd } 606e83ce340SAdrian Chadd 607e83ce340SAdrian Chadd /* Initialize device address space and resource covering the 608e83ce340SAdrian Chadd * BAR0 SPROM shadow. */ 609111d7cb2SLandon J. Fuller dinfo = device_get_ivars(child); 610e83ce340SAdrian Chadd dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; 611111d7cb2SLandon J. Fuller 612111d7cb2SLandon J. Fuller error = bus_set_resource(child, SYS_RES_MEMORY, 0, 613e83ce340SAdrian Chadd bhndb_pci_sprom_addr(sc), nv_sz); 614e83ce340SAdrian Chadd if (error) { 615111d7cb2SLandon J. Fuller device_printf(sc->dev, 616e83ce340SAdrian Chadd "failed to register sprom resources\n"); 617e83ce340SAdrian Chadd return (error); 618e83ce340SAdrian Chadd } 619e83ce340SAdrian Chadd } 620e83ce340SAdrian Chadd 6214ad7e9b0SAdrian Chadd return (0); 6224ad7e9b0SAdrian Chadd } 6234ad7e9b0SAdrian Chadd 624e83ce340SAdrian Chadd static const struct bhndb_regwin * 625e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc) 626e83ce340SAdrian Chadd { 627e83ce340SAdrian Chadd struct bhndb_resources *bres; 628e83ce340SAdrian Chadd const struct bhndb_hwcfg *cfg; 629e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 630e83ce340SAdrian Chadd 631e83ce340SAdrian Chadd bres = sc->bhndb.bus_res; 632e83ce340SAdrian Chadd cfg = bres->cfg; 633e83ce340SAdrian Chadd 634e83ce340SAdrian Chadd sprom_win = bhndb_regwin_find_type(cfg->register_windows, 635e83ce340SAdrian Chadd BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE); 636e83ce340SAdrian Chadd 637e83ce340SAdrian Chadd return (sprom_win); 638e83ce340SAdrian Chadd } 639e83ce340SAdrian Chadd 640e83ce340SAdrian Chadd static bus_addr_t 641e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc) 642e83ce340SAdrian Chadd { 643e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 644e83ce340SAdrian Chadd struct resource *r; 645e83ce340SAdrian Chadd 646e83ce340SAdrian Chadd /* Fetch the SPROM register window */ 647e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 648e83ce340SAdrian Chadd KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+")); 649e83ce340SAdrian Chadd 650e83ce340SAdrian Chadd /* Fetch the associated resource */ 65189294a78SLandon J. Fuller r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win); 652e83ce340SAdrian Chadd KASSERT(r != NULL, ("missing resource for sprom window\n")); 653e83ce340SAdrian Chadd 654e83ce340SAdrian Chadd return (rman_get_start(r) + sprom_win->win_offset); 655e83ce340SAdrian Chadd } 656e83ce340SAdrian Chadd 657e83ce340SAdrian Chadd static bus_size_t 658e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc) 659e83ce340SAdrian Chadd { 660e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 661e83ce340SAdrian Chadd uint32_t sctl; 662e83ce340SAdrian Chadd bus_size_t sprom_sz; 663e83ce340SAdrian Chadd 664e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 665e83ce340SAdrian Chadd 666e83ce340SAdrian Chadd /* PCI_V2 and later devices map SPROM/OTP via ChipCommon */ 667e83ce340SAdrian Chadd if (sprom_win == NULL) 668e83ce340SAdrian Chadd return (0); 669e83ce340SAdrian Chadd 670e83ce340SAdrian Chadd /* Determine SPROM size */ 671e83ce340SAdrian Chadd sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4); 672e83ce340SAdrian Chadd if (sctl & BHNDB_PCI_SPROM_BLANK) 673e83ce340SAdrian Chadd return (0); 674e83ce340SAdrian Chadd 675e83ce340SAdrian Chadd switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) { 676e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_1KB: 677e83ce340SAdrian Chadd sprom_sz = (1 * 1024); 678e83ce340SAdrian Chadd break; 679e83ce340SAdrian Chadd 680e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_4KB: 681e83ce340SAdrian Chadd sprom_sz = (4 * 1024); 682e83ce340SAdrian Chadd break; 683e83ce340SAdrian Chadd 684e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_16KB: 685e83ce340SAdrian Chadd sprom_sz = (16 * 1024); 686e83ce340SAdrian Chadd break; 687e83ce340SAdrian Chadd 688e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_RESERVED: 689e83ce340SAdrian Chadd default: 690e83ce340SAdrian Chadd device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl); 691e83ce340SAdrian Chadd return (0); 692e83ce340SAdrian Chadd } 693e83ce340SAdrian Chadd 694e83ce340SAdrian Chadd if (sprom_sz > sprom_win->win_size) { 695e83ce340SAdrian Chadd device_printf(sc->dev, 696e83ce340SAdrian Chadd "PCI sprom size (0x%x) overruns defined register window\n", 697e83ce340SAdrian Chadd sctl); 698e83ce340SAdrian Chadd return (0); 699e83ce340SAdrian Chadd } 700e83ce340SAdrian Chadd 701e83ce340SAdrian Chadd return (sprom_sz); 702e83ce340SAdrian Chadd } 703e83ce340SAdrian Chadd 704*caeff9a3SLandon J. Fuller /** 705*caeff9a3SLandon J. Fuller * Return the host resource providing a static mapping of the PCI core's 706*caeff9a3SLandon J. Fuller * registers. 707*caeff9a3SLandon J. Fuller * 708*caeff9a3SLandon J. Fuller * @param sc bhndb PCI driver state. 709*caeff9a3SLandon J. Fuller * @param[out] res On success, the host resource containing our PCI 710*caeff9a3SLandon J. Fuller * core's register window. 711*caeff9a3SLandon J. Fuller * @param[out] offset On success, the offset of the PCI core registers within 712*caeff9a3SLandon J. Fuller * @p res. 713*caeff9a3SLandon J. Fuller * 714*caeff9a3SLandon J. Fuller * @retval 0 success 715*caeff9a3SLandon J. Fuller * @retval ENXIO if a valid static register window mapping the PCI core 716*caeff9a3SLandon J. Fuller * registers is not available. 717*caeff9a3SLandon J. Fuller */ 718*caeff9a3SLandon J. Fuller static int 719*caeff9a3SLandon J. Fuller bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, struct resource **res, 720*caeff9a3SLandon J. Fuller bus_size_t *offset) 721*caeff9a3SLandon J. Fuller { 722*caeff9a3SLandon J. Fuller const struct bhndb_regwin *win; 723*caeff9a3SLandon J. Fuller struct resource *r; 724*caeff9a3SLandon J. Fuller 725*caeff9a3SLandon J. Fuller /* Locate the static register window mapping the PCI core */ 726*caeff9a3SLandon J. Fuller win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows, 727*caeff9a3SLandon J. Fuller sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0); 728*caeff9a3SLandon J. Fuller if (win == NULL) { 729*caeff9a3SLandon J. Fuller device_printf(sc->dev, "missing PCI core register window\n"); 730*caeff9a3SLandon J. Fuller return (ENXIO); 731*caeff9a3SLandon J. Fuller } 732*caeff9a3SLandon J. Fuller 733*caeff9a3SLandon J. Fuller /* Fetch the resource containing the register window */ 734*caeff9a3SLandon J. Fuller r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, win); 735*caeff9a3SLandon J. Fuller if (r == NULL) { 736*caeff9a3SLandon J. Fuller device_printf(sc->dev, "missing PCI core register resource\n"); 737*caeff9a3SLandon J. Fuller return (ENXIO); 738*caeff9a3SLandon J. Fuller } 739*caeff9a3SLandon J. Fuller 740*caeff9a3SLandon J. Fuller *res = r; 741*caeff9a3SLandon J. Fuller *offset = win->win_offset; 742*caeff9a3SLandon J. Fuller 743*caeff9a3SLandon J. Fuller return (0); 744*caeff9a3SLandon J. Fuller } 745*caeff9a3SLandon J. Fuller 746*caeff9a3SLandon J. Fuller /** 747*caeff9a3SLandon J. Fuller * Write a 1, 2, or 4 byte data item to the PCI core's registers at @p offset. 748*caeff9a3SLandon J. Fuller * 749*caeff9a3SLandon J. Fuller * @param sc bhndb PCI driver state. 750*caeff9a3SLandon J. Fuller * @param offset register write offset. 751*caeff9a3SLandon J. Fuller * @param value value to be written. 752*caeff9a3SLandon J. Fuller * @param width item width (1, 2, or 4 bytes). 753*caeff9a3SLandon J. Fuller */ 754*caeff9a3SLandon J. Fuller static void 755*caeff9a3SLandon J. Fuller bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_size_t offset, 756*caeff9a3SLandon J. Fuller uint32_t value, u_int width) 757*caeff9a3SLandon J. Fuller { 758*caeff9a3SLandon J. Fuller struct resource *r; 759*caeff9a3SLandon J. Fuller bus_size_t r_offset; 760*caeff9a3SLandon J. Fuller int error; 761*caeff9a3SLandon J. Fuller 762*caeff9a3SLandon J. Fuller if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset))) 763*caeff9a3SLandon J. Fuller panic("no PCI core registers: %d", error); 764*caeff9a3SLandon J. Fuller 765*caeff9a3SLandon J. Fuller switch (width) { 766*caeff9a3SLandon J. Fuller case 1: 767*caeff9a3SLandon J. Fuller bus_write_1(r, r_offset + offset, value); 768*caeff9a3SLandon J. Fuller break; 769*caeff9a3SLandon J. Fuller case 2: 770*caeff9a3SLandon J. Fuller bus_write_2(r, r_offset + offset, value); 771*caeff9a3SLandon J. Fuller break; 772*caeff9a3SLandon J. Fuller case 4: 773*caeff9a3SLandon J. Fuller bus_write_4(r, r_offset + offset, value); 774*caeff9a3SLandon J. Fuller break; 775*caeff9a3SLandon J. Fuller default: 776*caeff9a3SLandon J. Fuller panic("invalid width: %u", width); 777*caeff9a3SLandon J. Fuller } 778*caeff9a3SLandon J. Fuller } 779*caeff9a3SLandon J. Fuller 780*caeff9a3SLandon J. Fuller /** 781*caeff9a3SLandon J. Fuller * Read a 1, 2, or 4 byte data item from the PCI core's registers 782*caeff9a3SLandon J. Fuller * at @p offset. 783*caeff9a3SLandon J. Fuller * 784*caeff9a3SLandon J. Fuller * @param sc bhndb PCI driver state. 785*caeff9a3SLandon J. Fuller * @param offset register read offset. 786*caeff9a3SLandon J. Fuller * @param width item width (1, 2, or 4 bytes). 787*caeff9a3SLandon J. Fuller */ 788*caeff9a3SLandon J. Fuller static uint32_t 789*caeff9a3SLandon J. Fuller bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_size_t offset, u_int width) 790*caeff9a3SLandon J. Fuller { 791*caeff9a3SLandon J. Fuller struct resource *r; 792*caeff9a3SLandon J. Fuller bus_size_t r_offset; 793*caeff9a3SLandon J. Fuller int error; 794*caeff9a3SLandon J. Fuller 795*caeff9a3SLandon J. Fuller if ((error = bhndb_pci_get_core_regs(sc, &r, &r_offset))) 796*caeff9a3SLandon J. Fuller panic("no PCI core registers: %d", error); 797*caeff9a3SLandon J. Fuller 798*caeff9a3SLandon J. Fuller switch (width) { 799*caeff9a3SLandon J. Fuller case 1: 800*caeff9a3SLandon J. Fuller return (bus_read_1(r, r_offset + offset)); 801*caeff9a3SLandon J. Fuller case 2: 802*caeff9a3SLandon J. Fuller return (bus_read_2(r, r_offset + offset)); 803*caeff9a3SLandon J. Fuller case 4: 804*caeff9a3SLandon J. Fuller return (bus_read_4(r, r_offset + offset)); 805*caeff9a3SLandon J. Fuller default: 806*caeff9a3SLandon J. Fuller panic("invalid width: %u", width); 807*caeff9a3SLandon J. Fuller } 808*caeff9a3SLandon J. Fuller } 809*caeff9a3SLandon J. Fuller 8104ad7e9b0SAdrian Chadd /* 8114ad7e9b0SAdrian Chadd * On devices without a SROM, the PCI(e) cores will be initialized with 812bb64eeccSAdrian Chadd * their Power-on-Reset defaults; this can leave two of the BAR0 PCI windows 813bb64eeccSAdrian Chadd * mapped to the wrong core. 8144ad7e9b0SAdrian Chadd * 815bb64eeccSAdrian Chadd * This function updates the SROM shadow to point the BAR0 windows at the 8164ad7e9b0SAdrian Chadd * current PCI core. 8174ad7e9b0SAdrian Chadd * 818bb64eeccSAdrian Chadd * Applies to all PCI/PCIe revisions. 8194ad7e9b0SAdrian Chadd */ 8204ad7e9b0SAdrian Chadd static void 8214ad7e9b0SAdrian Chadd bhndb_init_sromless_pci_config(struct bhndb_pci_softc *sc) 8224ad7e9b0SAdrian Chadd { 823*caeff9a3SLandon J. Fuller const struct bhndb_pci_core *pci_core; 824*caeff9a3SLandon J. Fuller bus_size_t srsh_offset; 825bb64eeccSAdrian Chadd u_int pci_cidx, sprom_cidx; 8264ad7e9b0SAdrian Chadd uint16_t val; 8274ad7e9b0SAdrian Chadd 828*caeff9a3SLandon J. Fuller if ((sc->pci_quirks & BHNDB_PCI_QUIRK_SRSH_WAR) == 0) 829bb64eeccSAdrian Chadd return; 830bb64eeccSAdrian Chadd 831*caeff9a3SLandon J. Fuller /* Determine the correct register offset for our PCI core */ 832*caeff9a3SLandon J. Fuller pci_core = bhndb_pci_find_core(&sc->bhndb.bridge_core); 833*caeff9a3SLandon J. Fuller KASSERT(pci_core != NULL, ("missing core table entry")); 834bb64eeccSAdrian Chadd 835*caeff9a3SLandon J. Fuller srsh_offset = pci_core->srsh_offset; 836bb64eeccSAdrian Chadd 8374ad7e9b0SAdrian Chadd /* Fetch the SPROM's configured core index */ 838*caeff9a3SLandon J. Fuller val = bhndb_pci_read_core(sc, srsh_offset, sizeof(val)); 839bb64eeccSAdrian Chadd sprom_cidx = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; 8404ad7e9b0SAdrian Chadd 8414ad7e9b0SAdrian Chadd /* If it doesn't match host bridge's core index, update the index 8424ad7e9b0SAdrian Chadd * value */ 843*caeff9a3SLandon J. Fuller pci_cidx = sc->bhndb.bridge_core.core_idx; 844bb64eeccSAdrian Chadd if (sprom_cidx != pci_cidx) { 845bb64eeccSAdrian Chadd val &= ~BHND_PCI_SRSH_PI_MASK; 846bb64eeccSAdrian Chadd val |= (pci_cidx << BHND_PCI_SRSH_PI_SHIFT); 847*caeff9a3SLandon J. Fuller bhndb_pci_write_core(sc, srsh_offset, val, sizeof(val)); 8484ad7e9b0SAdrian Chadd } 8494ad7e9b0SAdrian Chadd } 8504ad7e9b0SAdrian Chadd 8514ad7e9b0SAdrian Chadd static int 8524ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev) 8534ad7e9b0SAdrian Chadd { 8544ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 8554ad7e9b0SAdrian Chadd int error; 8564ad7e9b0SAdrian Chadd 8574ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 8584ad7e9b0SAdrian Chadd 859bb64eeccSAdrian Chadd /* Enable clocks (if supported by this hardware) */ 86089294a78SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(sc->dev))) 8614ad7e9b0SAdrian Chadd return (error); 8624ad7e9b0SAdrian Chadd 863bb64eeccSAdrian Chadd /* Perform resume */ 864bb64eeccSAdrian Chadd return (bhndb_generic_resume(dev)); 865bb64eeccSAdrian Chadd } 866bb64eeccSAdrian Chadd 867bb64eeccSAdrian Chadd static int 868bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev) 869bb64eeccSAdrian Chadd { 870bb64eeccSAdrian Chadd struct bhndb_pci_softc *sc; 871bb64eeccSAdrian Chadd int error; 872bb64eeccSAdrian Chadd 873bb64eeccSAdrian Chadd sc = device_get_softc(dev); 874bb64eeccSAdrian Chadd 875bb64eeccSAdrian Chadd /* Disable clocks (if supported by this hardware) */ 87689294a78SLandon J. Fuller if ((error = bhndb_disable_pci_clocks(sc->dev))) 8774ad7e9b0SAdrian Chadd return (error); 8784ad7e9b0SAdrian Chadd 879bb64eeccSAdrian Chadd /* Perform suspend */ 880bb64eeccSAdrian Chadd return (bhndb_generic_suspend(dev)); 881bb64eeccSAdrian Chadd } 882bb64eeccSAdrian Chadd 883bb64eeccSAdrian Chadd static int 8844ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw, 8854ad7e9b0SAdrian Chadd bhnd_addr_t addr) 8864ad7e9b0SAdrian Chadd { 8874ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc = device_get_softc(dev); 88889294a78SLandon J. Fuller return (sc->set_regwin(sc->dev, sc->parent, rw, addr)); 8894ad7e9b0SAdrian Chadd } 8904ad7e9b0SAdrian Chadd 8914ad7e9b0SAdrian Chadd /** 8924ad7e9b0SAdrian Chadd * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. 8934ad7e9b0SAdrian Chadd * 8944ad7e9b0SAdrian Chadd * On siba(4) devices, it's possible that writing a PCI window register may 8954ad7e9b0SAdrian Chadd * not succeed; it's necessary to immediately read the configuration register 8964ad7e9b0SAdrian Chadd * and retry if not set to the desired value. 8974ad7e9b0SAdrian Chadd * 8984ad7e9b0SAdrian Chadd * This is not necessary on bcma(4) devices, but other than the overhead of 8994ad7e9b0SAdrian Chadd * validating the register, there's no harm in performing the verification. 9004ad7e9b0SAdrian Chadd */ 9014ad7e9b0SAdrian Chadd static int 90289294a78SLandon J. Fuller bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev, 9034ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 9044ad7e9b0SAdrian Chadd { 9054ad7e9b0SAdrian Chadd int error; 906e83ce340SAdrian Chadd int reg; 9074ad7e9b0SAdrian Chadd 9084ad7e9b0SAdrian Chadd if (rw->win_type != BHNDB_REGWIN_T_DYN) 9094ad7e9b0SAdrian Chadd return (ENODEV); 9104ad7e9b0SAdrian Chadd 911e83ce340SAdrian Chadd reg = rw->d.dyn.cfg_offset; 9124ad7e9b0SAdrian Chadd for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { 91389294a78SLandon J. Fuller if ((error = bhndb_pci_fast_setregwin(dev, pci_dev, rw, addr))) 9144ad7e9b0SAdrian Chadd return (error); 9154ad7e9b0SAdrian Chadd 91689294a78SLandon J. Fuller if (pci_read_config(pci_dev, reg, 4) == addr) 9174ad7e9b0SAdrian Chadd return (0); 9184ad7e9b0SAdrian Chadd 9194ad7e9b0SAdrian Chadd DELAY(10); 9204ad7e9b0SAdrian Chadd } 9214ad7e9b0SAdrian Chadd 9224ad7e9b0SAdrian Chadd /* Unable to set window */ 9234ad7e9b0SAdrian Chadd return (ENODEV); 9244ad7e9b0SAdrian Chadd } 9254ad7e9b0SAdrian Chadd 9264ad7e9b0SAdrian Chadd /** 9274ad7e9b0SAdrian Chadd * A bcma(4)-only bhndb_set_window_addr implementation. 9284ad7e9b0SAdrian Chadd */ 9294ad7e9b0SAdrian Chadd static int 93089294a78SLandon J. Fuller bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev, 9314ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 9324ad7e9b0SAdrian Chadd { 9334ad7e9b0SAdrian Chadd /* The PCI bridge core only supports 32-bit addressing, regardless 9344ad7e9b0SAdrian Chadd * of the bus' support for 64-bit addressing */ 9354ad7e9b0SAdrian Chadd if (addr > UINT32_MAX) 9364ad7e9b0SAdrian Chadd return (ERANGE); 9374ad7e9b0SAdrian Chadd 9384ad7e9b0SAdrian Chadd switch (rw->win_type) { 9394ad7e9b0SAdrian Chadd case BHNDB_REGWIN_T_DYN: 9404ad7e9b0SAdrian Chadd /* Addresses must be page aligned */ 9414ad7e9b0SAdrian Chadd if (addr % rw->win_size != 0) 9424ad7e9b0SAdrian Chadd return (EINVAL); 9434ad7e9b0SAdrian Chadd 94489294a78SLandon J. Fuller pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4); 9454ad7e9b0SAdrian Chadd break; 9464ad7e9b0SAdrian Chadd default: 9474ad7e9b0SAdrian Chadd return (ENODEV); 9484ad7e9b0SAdrian Chadd } 9494ad7e9b0SAdrian Chadd 9504ad7e9b0SAdrian Chadd return (0); 9514ad7e9b0SAdrian Chadd } 9524ad7e9b0SAdrian Chadd 953d567592bSAdrian Chadd static int 954d567592bSAdrian Chadd bhndb_pci_populate_board_info(device_t dev, device_t child, 955d567592bSAdrian Chadd struct bhnd_board_info *info) 956d567592bSAdrian Chadd { 957d567592bSAdrian Chadd struct bhndb_pci_softc *sc; 958d567592bSAdrian Chadd 959d567592bSAdrian Chadd sc = device_get_softc(dev); 960d567592bSAdrian Chadd 9618ef24a0dSAdrian Chadd /* 9628ef24a0dSAdrian Chadd * On a subset of Apple BCM4360 modules, always prefer the 9638ef24a0dSAdrian Chadd * PCI subdevice to the SPROM-supplied boardtype. 9648ef24a0dSAdrian Chadd * 9658ef24a0dSAdrian Chadd * TODO: 9668ef24a0dSAdrian Chadd * 9678ef24a0dSAdrian Chadd * Broadcom's own drivers implement this override, and then later use 9688ef24a0dSAdrian Chadd * the remapped BCM4360 board type to determine the required 9698ef24a0dSAdrian Chadd * board-specific workarounds. 9708ef24a0dSAdrian Chadd * 9718ef24a0dSAdrian Chadd * Without access to this hardware, it's unclear why this mapping 9728ef24a0dSAdrian Chadd * is done, and we must do the same. If we can survey the hardware 9738ef24a0dSAdrian Chadd * in question, it may be possible to replace this behavior with 9748ef24a0dSAdrian Chadd * explicit references to the SPROM-supplied boardtype(s) in our 9758ef24a0dSAdrian Chadd * quirk definitions. 9768ef24a0dSAdrian Chadd */ 9778ef24a0dSAdrian Chadd if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) { 9788ef24a0dSAdrian Chadd switch (info->board_type) { 9798ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X29C: 9808ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X29CP2: 9818ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X51: 9828ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X51P2: 9838ef24a0dSAdrian Chadd info->board_type = 0; /* allow override below */ 9848ef24a0dSAdrian Chadd break; 9858ef24a0dSAdrian Chadd default: 9868ef24a0dSAdrian Chadd break; 9878ef24a0dSAdrian Chadd } 9888ef24a0dSAdrian Chadd } 9898ef24a0dSAdrian Chadd 990d567592bSAdrian Chadd /* If NVRAM did not supply vendor/type info, provide the PCI 991d567592bSAdrian Chadd * subvendor/subdevice values. */ 992d567592bSAdrian Chadd if (info->board_vendor == 0) 993d567592bSAdrian Chadd info->board_vendor = pci_get_subvendor(sc->parent); 994d567592bSAdrian Chadd 995d567592bSAdrian Chadd if (info->board_type == 0) 996d567592bSAdrian Chadd info->board_type = pci_get_subdevice(sc->parent); 997d567592bSAdrian Chadd 998d567592bSAdrian Chadd return (0); 999d567592bSAdrian Chadd } 1000d567592bSAdrian Chadd 10014ad7e9b0SAdrian Chadd /** 1002*caeff9a3SLandon J. Fuller * Examine the bridge device @p dev and return the expected host bridge 1003*caeff9a3SLandon J. Fuller * device class. 1004*caeff9a3SLandon J. Fuller * 1005*caeff9a3SLandon J. Fuller * @param dev The bhndb bridge device 1006*caeff9a3SLandon J. Fuller */ 1007*caeff9a3SLandon J. Fuller static bhnd_devclass_t 1008*caeff9a3SLandon J. Fuller bhndb_expected_pci_devclass(device_t dev) 1009*caeff9a3SLandon J. Fuller { 1010*caeff9a3SLandon J. Fuller if (bhndb_is_pcie_attached(dev)) 1011*caeff9a3SLandon J. Fuller return (BHND_DEVCLASS_PCIE); 1012*caeff9a3SLandon J. Fuller else 1013*caeff9a3SLandon J. Fuller return (BHND_DEVCLASS_PCI); 1014*caeff9a3SLandon J. Fuller } 1015*caeff9a3SLandon J. Fuller 1016*caeff9a3SLandon J. Fuller /** 1017*caeff9a3SLandon J. Fuller * Return true if the bridge device @p dev is attached via PCIe, 101889294a78SLandon J. Fuller * false otherwise. 101989294a78SLandon J. Fuller * 102089294a78SLandon J. Fuller * @param dev The bhndb bridge device 102189294a78SLandon J. Fuller */ 102289294a78SLandon J. Fuller static bool 102389294a78SLandon J. Fuller bhndb_is_pcie_attached(device_t dev) 102489294a78SLandon J. Fuller { 102589294a78SLandon J. Fuller int reg; 102689294a78SLandon J. Fuller 102789294a78SLandon J. Fuller if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, ®) == 0) 102889294a78SLandon J. Fuller return (true); 102989294a78SLandon J. Fuller 103089294a78SLandon J. Fuller return (false); 103189294a78SLandon J. Fuller } 103289294a78SLandon J. Fuller 103389294a78SLandon J. Fuller /** 1034bb64eeccSAdrian Chadd * Enable externally managed clocks, if required. 10354ad7e9b0SAdrian Chadd * 1036bb64eeccSAdrian Chadd * Some PCI chipsets (BCM4306, possibly others) chips do not support 1037bb64eeccSAdrian Chadd * the idle low-power clock. Clocking must be bootstrapped at 1038bb64eeccSAdrian Chadd * attach/resume by directly adjusting GPIO registers exposed in the 1039bb64eeccSAdrian Chadd * PCI config space, and correspondingly, explicitly shutdown at 1040bb64eeccSAdrian Chadd * detach/suspend. 10414ad7e9b0SAdrian Chadd * 104289294a78SLandon J. Fuller * @note This function may be safely called prior to device attach, (e.g. 104389294a78SLandon J. Fuller * from DEVICE_PROBE). 104489294a78SLandon J. Fuller * 104589294a78SLandon J. Fuller * @param dev The bhndb bridge device 10464ad7e9b0SAdrian Chadd */ 10474ad7e9b0SAdrian Chadd static int 104889294a78SLandon J. Fuller bhndb_enable_pci_clocks(device_t dev) 10494ad7e9b0SAdrian Chadd { 105089294a78SLandon J. Fuller device_t pci_dev; 10514ad7e9b0SAdrian Chadd uint32_t gpio_in, gpio_out, gpio_en; 10524ad7e9b0SAdrian Chadd uint32_t gpio_flags; 10534ad7e9b0SAdrian Chadd uint16_t pci_status; 10544ad7e9b0SAdrian Chadd 105589294a78SLandon J. Fuller pci_dev = device_get_parent(dev); 105689294a78SLandon J. Fuller 1057bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 105889294a78SLandon J. Fuller if (!bhndb_is_pcie_attached(dev)) 1059bb64eeccSAdrian Chadd return (0); 10604ad7e9b0SAdrian Chadd 10614ad7e9b0SAdrian Chadd /* Read state of XTAL pin */ 106289294a78SLandon J. Fuller gpio_in = pci_read_config(pci_dev, BHNDB_PCI_GPIO_IN, 4); 10634ad7e9b0SAdrian Chadd if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) 10644ad7e9b0SAdrian Chadd return (0); /* already enabled */ 10654ad7e9b0SAdrian Chadd 10664ad7e9b0SAdrian Chadd /* Fetch current config */ 106789294a78SLandon J. Fuller gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4); 106889294a78SLandon J. Fuller gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4); 10694ad7e9b0SAdrian Chadd 10704ad7e9b0SAdrian Chadd /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ 10714ad7e9b0SAdrian Chadd gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 10724ad7e9b0SAdrian Chadd gpio_out |= gpio_flags; 10734ad7e9b0SAdrian Chadd gpio_en |= gpio_flags; 10744ad7e9b0SAdrian Chadd 107589294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 107689294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 10774ad7e9b0SAdrian Chadd DELAY(1000); 10784ad7e9b0SAdrian Chadd 10794ad7e9b0SAdrian Chadd /* Reset PLL_OFF */ 10804ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; 108189294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 10824ad7e9b0SAdrian Chadd DELAY(5000); 10834ad7e9b0SAdrian Chadd 10844ad7e9b0SAdrian Chadd /* Clear any PCI 'sent target-abort' flag. */ 108589294a78SLandon J. Fuller pci_status = pci_read_config(pci_dev, PCIR_STATUS, 2); 10864ad7e9b0SAdrian Chadd pci_status &= ~PCIM_STATUS_STABORT; 108789294a78SLandon J. Fuller pci_write_config(pci_dev, PCIR_STATUS, pci_status, 2); 10884ad7e9b0SAdrian Chadd 10894ad7e9b0SAdrian Chadd return (0); 10904ad7e9b0SAdrian Chadd } 10914ad7e9b0SAdrian Chadd 10924ad7e9b0SAdrian Chadd /** 1093bb64eeccSAdrian Chadd * Disable externally managed clocks, if required. 10944ad7e9b0SAdrian Chadd * 109589294a78SLandon J. Fuller * This function may be safely called prior to device attach, (e.g. 109689294a78SLandon J. Fuller * from DEVICE_PROBE). 109789294a78SLandon J. Fuller * 109889294a78SLandon J. Fuller * @param dev The bhndb bridge device 10994ad7e9b0SAdrian Chadd */ 11004ad7e9b0SAdrian Chadd static int 110189294a78SLandon J. Fuller bhndb_disable_pci_clocks(device_t dev) 11024ad7e9b0SAdrian Chadd { 110389294a78SLandon J. Fuller device_t pci_dev; 11044ad7e9b0SAdrian Chadd uint32_t gpio_out, gpio_en; 11054ad7e9b0SAdrian Chadd 110689294a78SLandon J. Fuller pci_dev = device_get_parent(dev); 110789294a78SLandon J. Fuller 1108bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 110989294a78SLandon J. Fuller if (bhndb_is_pcie_attached(dev)) 1110bb64eeccSAdrian Chadd return (0); 11114ad7e9b0SAdrian Chadd 11124ad7e9b0SAdrian Chadd /* Fetch current config */ 111389294a78SLandon J. Fuller gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4); 111489294a78SLandon J. Fuller gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4); 11154ad7e9b0SAdrian Chadd 11164ad7e9b0SAdrian Chadd /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ 11174ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; 11184ad7e9b0SAdrian Chadd gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; 111989294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 11204ad7e9b0SAdrian Chadd 11214ad7e9b0SAdrian Chadd /* Enable both output pins */ 11224ad7e9b0SAdrian Chadd gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 112389294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 11244ad7e9b0SAdrian Chadd 11254ad7e9b0SAdrian Chadd return (0); 11264ad7e9b0SAdrian Chadd } 11274ad7e9b0SAdrian Chadd 1128f90f4b65SLandon J. Fuller static bhnd_clksrc 1129f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child, 1130f90f4b65SLandon J. Fuller bhnd_clock clock) 1131f90f4b65SLandon J. Fuller { 1132f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc; 1133f90f4b65SLandon J. Fuller uint32_t gpio_out; 1134f90f4b65SLandon J. Fuller 1135f90f4b65SLandon J. Fuller sc = device_get_softc(dev); 1136f90f4b65SLandon J. Fuller 1137f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 113889294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 1139f90f4b65SLandon J. Fuller return (ENODEV); 1140f90f4b65SLandon J. Fuller 1141f90f4b65SLandon J. Fuller /* Only ILP is supported */ 1142f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_ILP) 1143f90f4b65SLandon J. Fuller return (ENXIO); 1144f90f4b65SLandon J. Fuller 1145f90f4b65SLandon J. Fuller gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); 1146f90f4b65SLandon J. Fuller if (gpio_out & BHNDB_PCI_GPIO_SCS) 1147f90f4b65SLandon J. Fuller return (BHND_CLKSRC_PCI); 1148f90f4b65SLandon J. Fuller else 1149f90f4b65SLandon J. Fuller return (BHND_CLKSRC_XTAL); 1150f90f4b65SLandon J. Fuller } 1151f90f4b65SLandon J. Fuller 1152f90f4b65SLandon J. Fuller static int 1153f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child, 1154f90f4b65SLandon J. Fuller bhnd_clock clock) 1155f90f4b65SLandon J. Fuller { 1156f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 1157f90f4b65SLandon J. Fuller 1158f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 115989294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 1160f90f4b65SLandon J. Fuller return (ENODEV); 1161f90f4b65SLandon J. Fuller 1162f90f4b65SLandon J. Fuller /* Only HT is supported */ 1163f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_HT) 1164f90f4b65SLandon J. Fuller return (ENXIO); 1165f90f4b65SLandon J. Fuller 116689294a78SLandon J. Fuller return (bhndb_disable_pci_clocks(sc->dev)); 1167f90f4b65SLandon J. Fuller } 1168f90f4b65SLandon J. Fuller 1169f90f4b65SLandon J. Fuller static int 1170f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child, 1171f90f4b65SLandon J. Fuller bhnd_clock clock) 1172f90f4b65SLandon J. Fuller { 1173f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 1174f90f4b65SLandon J. Fuller 1175f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 117689294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 1177f90f4b65SLandon J. Fuller return (ENODEV); 1178f90f4b65SLandon J. Fuller 1179f90f4b65SLandon J. Fuller /* Only HT is supported */ 1180f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_HT) 1181f90f4b65SLandon J. Fuller return (ENXIO); 1182f90f4b65SLandon J. Fuller 118389294a78SLandon J. Fuller return (bhndb_enable_pci_clocks(sc->dev)); 1184f90f4b65SLandon J. Fuller } 1185f90f4b65SLandon J. Fuller 1186*caeff9a3SLandon J. Fuller /** 1187*caeff9a3SLandon J. Fuller * BHNDB_MAP_INTR_ISRC() 1188*caeff9a3SLandon J. Fuller */ 1189824b48efSLandon J. Fuller static int 1190*caeff9a3SLandon J. Fuller bhndb_pci_map_intr_isrc(device_t dev, struct resource *irq, 1191*caeff9a3SLandon J. Fuller struct bhndb_intr_isrc **isrc) 1192*caeff9a3SLandon J. Fuller { 1193*caeff9a3SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 1194*caeff9a3SLandon J. Fuller 1195*caeff9a3SLandon J. Fuller /* There's only one bridged interrupt to choose from */ 1196*caeff9a3SLandon J. Fuller *isrc = sc->isrc; 1197*caeff9a3SLandon J. Fuller return (0); 1198*caeff9a3SLandon J. Fuller } 1199*caeff9a3SLandon J. Fuller 1200*caeff9a3SLandon J. Fuller /* siba-specific implementation of BHNDB_ROUTE_INTERRUPTS() */ 1201*caeff9a3SLandon J. Fuller static int 1202*caeff9a3SLandon J. Fuller bhndb_pci_route_siba_interrupts(struct bhndb_pci_softc *sc, device_t child) 1203*caeff9a3SLandon J. Fuller { 1204*caeff9a3SLandon J. Fuller uint32_t sbintvec; 1205*caeff9a3SLandon J. Fuller u_int ivec; 1206*caeff9a3SLandon J. Fuller int error; 1207*caeff9a3SLandon J. Fuller 1208*caeff9a3SLandon J. Fuller KASSERT(sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC, 1209*caeff9a3SLandon J. Fuller ("route_siba_interrupts not supported by this hardware")); 1210*caeff9a3SLandon J. Fuller 1211*caeff9a3SLandon J. Fuller /* Fetch the sbflag# for the child */ 1212*caeff9a3SLandon J. Fuller if ((error = bhnd_get_intr_ivec(child, 0, &ivec))) 1213*caeff9a3SLandon J. Fuller return (error); 1214*caeff9a3SLandon J. Fuller 1215*caeff9a3SLandon J. Fuller if (ivec > (sizeof(sbintvec)*8) - 1 /* aka '31' */) { 1216*caeff9a3SLandon J. Fuller /* This should never be an issue in practice */ 1217*caeff9a3SLandon J. Fuller device_printf(sc->dev, "cannot route interrupts to high " 1218*caeff9a3SLandon J. Fuller "sbflag# %u\n", ivec); 1219*caeff9a3SLandon J. Fuller return (ENXIO); 1220*caeff9a3SLandon J. Fuller } 1221*caeff9a3SLandon J. Fuller 1222*caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK(sc); 1223*caeff9a3SLandon J. Fuller 1224*caeff9a3SLandon J. Fuller sbintvec = bhndb_pci_read_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), 4); 1225*caeff9a3SLandon J. Fuller sbintvec |= (1 << ivec); 1226*caeff9a3SLandon J. Fuller bhndb_pci_write_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), sbintvec, 4); 1227*caeff9a3SLandon J. Fuller 1228*caeff9a3SLandon J. Fuller BHNDB_PCI_UNLOCK(sc); 1229*caeff9a3SLandon J. Fuller 1230*caeff9a3SLandon J. Fuller return (0); 1231*caeff9a3SLandon J. Fuller } 1232*caeff9a3SLandon J. Fuller 1233*caeff9a3SLandon J. Fuller /* BHNDB_ROUTE_INTERRUPTS() */ 1234*caeff9a3SLandon J. Fuller static int 1235*caeff9a3SLandon J. Fuller bhndb_pci_route_interrupts(device_t dev, device_t child) 1236824b48efSLandon J. Fuller { 1237824b48efSLandon J. Fuller struct bhndb_pci_softc *sc; 1238*caeff9a3SLandon J. Fuller struct bhnd_core_info core; 1239*caeff9a3SLandon J. Fuller uint32_t core_bit; 1240*caeff9a3SLandon J. Fuller uint32_t intmask; 1241824b48efSLandon J. Fuller 1242824b48efSLandon J. Fuller sc = device_get_softc(dev); 1243824b48efSLandon J. Fuller 1244*caeff9a3SLandon J. Fuller if (sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC) 1245*caeff9a3SLandon J. Fuller return (bhndb_pci_route_siba_interrupts(sc, child)); 1246824b48efSLandon J. Fuller 1247*caeff9a3SLandon J. Fuller core = bhnd_get_core_info(child); 1248*caeff9a3SLandon J. Fuller if (core.core_idx > BHNDB_PCI_SBIM_COREIDX_MAX) { 1249*caeff9a3SLandon J. Fuller /* This should never be an issue in practice */ 1250*caeff9a3SLandon J. Fuller device_printf(dev, "cannot route interrupts to high core " 1251*caeff9a3SLandon J. Fuller "index %u\n", core.core_idx); 1252*caeff9a3SLandon J. Fuller return (ENXIO); 1253*caeff9a3SLandon J. Fuller } 1254824b48efSLandon J. Fuller 1255*caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK(sc); 1256*caeff9a3SLandon J. Fuller 1257*caeff9a3SLandon J. Fuller core_bit = (1<<core.core_idx) << BHNDB_PCI_SBIM_SHIFT; 1258*caeff9a3SLandon J. Fuller intmask = pci_read_config(sc->parent, BHNDB_PCI_INT_MASK, 4); 1259*caeff9a3SLandon J. Fuller intmask |= core_bit; 1260*caeff9a3SLandon J. Fuller pci_write_config(sc->parent, BHNDB_PCI_INT_MASK, intmask, 4); 1261*caeff9a3SLandon J. Fuller 1262*caeff9a3SLandon J. Fuller BHNDB_PCI_UNLOCK(sc); 1263*caeff9a3SLandon J. Fuller 1264*caeff9a3SLandon J. Fuller return (0); 1265824b48efSLandon J. Fuller } 1266824b48efSLandon J. Fuller 126789294a78SLandon J. Fuller /** 126889294a78SLandon J. Fuller * Initialize a new bhndb PCI bridge EROM I/O instance. This EROM I/O 126989294a78SLandon J. Fuller * implementation supports mapping of the device enumeration table via the 127089294a78SLandon J. Fuller * @p hr host resources. 127189294a78SLandon J. Fuller * 127289294a78SLandon J. Fuller * @param pio The instance to be initialized. 127389294a78SLandon J. Fuller * @param dev The bridge device. 127489294a78SLandon J. Fuller * @param pci_dev The bridge's parent PCI device. 127589294a78SLandon J. Fuller * @param hr The host resources to be used to map the device 127689294a78SLandon J. Fuller * enumeration table. 127789294a78SLandon J. Fuller */ 127889294a78SLandon J. Fuller static int 127989294a78SLandon J. Fuller bhndb_pci_eio_init(struct bhndb_pci_eio *pio, device_t dev, device_t pci_dev, 128089294a78SLandon J. Fuller struct bhndb_host_resources *hr) 128189294a78SLandon J. Fuller { 128289294a78SLandon J. Fuller memset(&pio->eio, sizeof(pio->eio), 0); 128389294a78SLandon J. Fuller pio->eio.map = bhndb_pci_eio_map; 128489294a78SLandon J. Fuller pio->eio.read = bhndb_pci_eio_read; 128589294a78SLandon J. Fuller pio->eio.fini = NULL; 128689294a78SLandon J. Fuller 128789294a78SLandon J. Fuller pio->dev = dev; 128889294a78SLandon J. Fuller pio->pci_dev = pci_dev; 128989294a78SLandon J. Fuller pio->hr = hr; 129089294a78SLandon J. Fuller pio->win = NULL; 129189294a78SLandon J. Fuller pio->res = NULL; 129289294a78SLandon J. Fuller 129389294a78SLandon J. Fuller return (0); 129489294a78SLandon J. Fuller } 129589294a78SLandon J. Fuller 129689294a78SLandon J. Fuller /** 129789294a78SLandon J. Fuller * Attempt to adjust the dynamic register window backing @p pio to permit 129889294a78SLandon J. Fuller * reading @p size bytes at @p addr. 129989294a78SLandon J. Fuller * 130089294a78SLandon J. Fuller * If @p addr or @p size fall outside the existing mapped range, or if 130189294a78SLandon J. Fuller * @p pio is not backed by a dynamic register window, ENXIO will be returned. 130289294a78SLandon J. Fuller * 130389294a78SLandon J. Fuller * @param pio The bhndb PCI erom I/O state to be modified. 130489294a78SLandon J. Fuller * @param addr The address to be include 130589294a78SLandon J. Fuller */ 130689294a78SLandon J. Fuller static int 130789294a78SLandon J. Fuller bhndb_pci_eio_adjust_mapping(struct bhndb_pci_eio *pio, bhnd_addr_t addr, 130889294a78SLandon J. Fuller bhnd_size_t size) 130989294a78SLandon J. Fuller { 131089294a78SLandon J. Fuller bhnd_addr_t target; 131189294a78SLandon J. Fuller bhnd_size_t offset; 131289294a78SLandon J. Fuller int error; 131389294a78SLandon J. Fuller 131489294a78SLandon J. Fuller 131589294a78SLandon J. Fuller KASSERT(pio->win != NULL, ("missing register window")); 131689294a78SLandon J. Fuller KASSERT(pio->res != NULL, ("missing regwin resource")); 131789294a78SLandon J. Fuller KASSERT(pio->win->win_type == BHNDB_REGWIN_T_DYN, 131889294a78SLandon J. Fuller ("unexpected window type %d", pio->win->win_type)); 131989294a78SLandon J. Fuller 132089294a78SLandon J. Fuller /* The requested subrange must fall within the total mapped range */ 132189294a78SLandon J. Fuller if (addr < pio->addr || (addr - pio->addr) > pio->size || 132289294a78SLandon J. Fuller size > pio->size || (addr - pio->addr) - pio->size < size) 132389294a78SLandon J. Fuller { 132489294a78SLandon J. Fuller return (ENXIO); 132589294a78SLandon J. Fuller } 132689294a78SLandon J. Fuller 132789294a78SLandon J. Fuller /* Do we already have a useable mapping? */ 132889294a78SLandon J. Fuller if (addr >= pio->res_target && 132989294a78SLandon J. Fuller addr <= pio->res_target + pio->win->win_size && 133089294a78SLandon J. Fuller (pio->res_target + pio->win->win_size) - addr >= size) 133189294a78SLandon J. Fuller { 133289294a78SLandon J. Fuller return (0); 133389294a78SLandon J. Fuller } 133489294a78SLandon J. Fuller 133589294a78SLandon J. Fuller /* Page-align the target address */ 133689294a78SLandon J. Fuller offset = addr % pio->win->win_size; 133789294a78SLandon J. Fuller target = addr - offset; 133889294a78SLandon J. Fuller 133989294a78SLandon J. Fuller /* Configure the register window */ 134089294a78SLandon J. Fuller error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, pio->win, 134189294a78SLandon J. Fuller target); 134289294a78SLandon J. Fuller if (error) { 134389294a78SLandon J. Fuller device_printf(pio->dev, "failed to configure dynamic register " 134489294a78SLandon J. Fuller "window: %d\n", error); 134589294a78SLandon J. Fuller return (error); 134689294a78SLandon J. Fuller } 134789294a78SLandon J. Fuller 134889294a78SLandon J. Fuller pio->res_target = target; 134989294a78SLandon J. Fuller return (0); 135089294a78SLandon J. Fuller } 135189294a78SLandon J. Fuller 135289294a78SLandon J. Fuller /* bhnd_erom_io_map() implementation */ 135389294a78SLandon J. Fuller static int 135489294a78SLandon J. Fuller bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 135589294a78SLandon J. Fuller bhnd_size_t size) 135689294a78SLandon J. Fuller { 135789294a78SLandon J. Fuller struct bhndb_pci_eio *pio; 135889294a78SLandon J. Fuller const struct bhndb_regwin *regwin; 135989294a78SLandon J. Fuller struct resource *r; 136089294a78SLandon J. Fuller bhnd_addr_t target; 136189294a78SLandon J. Fuller bhnd_size_t offset; 136289294a78SLandon J. Fuller int error; 136389294a78SLandon J. Fuller 136489294a78SLandon J. Fuller pio = (struct bhndb_pci_eio *)eio; 136589294a78SLandon J. Fuller 136689294a78SLandon J. Fuller /* Locate a useable dynamic register window */ 136789294a78SLandon J. Fuller regwin = bhndb_regwin_find_type(pio->hr->cfg->register_windows, 136889294a78SLandon J. Fuller BHNDB_REGWIN_T_DYN, MIN(size, BHND_DEFAULT_CORE_SIZE)); 136989294a78SLandon J. Fuller if (regwin == NULL) { 137089294a78SLandon J. Fuller device_printf(pio->dev, "unable to map %#jx+%#jx; no " 137189294a78SLandon J. Fuller "usable dynamic register window found\n", addr, size); 137289294a78SLandon J. Fuller return (ENXIO); 137389294a78SLandon J. Fuller } 137489294a78SLandon J. Fuller 137589294a78SLandon J. Fuller /* Locate the host resource mapping our register window */ 137689294a78SLandon J. Fuller if ((r = bhndb_host_resource_for_regwin(pio->hr, regwin)) == NULL) { 137789294a78SLandon J. Fuller device_printf(pio->dev, "unable to map %#jx+%#jx; no " 137889294a78SLandon J. Fuller "usable register resource found\n", addr, size); 137989294a78SLandon J. Fuller return (ENXIO); 138089294a78SLandon J. Fuller } 138189294a78SLandon J. Fuller 138289294a78SLandon J. Fuller /* Page-align the target address */ 138389294a78SLandon J. Fuller offset = addr % regwin->win_size; 138489294a78SLandon J. Fuller target = addr - offset; 138589294a78SLandon J. Fuller 138689294a78SLandon J. Fuller /* Configure the register window */ 138789294a78SLandon J. Fuller error = bhndb_pci_compat_setregwin(pio->dev, pio->pci_dev, regwin, 138889294a78SLandon J. Fuller target); 138989294a78SLandon J. Fuller if (error) { 139089294a78SLandon J. Fuller device_printf(pio->dev, "failed to configure dynamic register " 139189294a78SLandon J. Fuller "window: %d\n", error); 139289294a78SLandon J. Fuller return (error); 139389294a78SLandon J. Fuller } 139489294a78SLandon J. Fuller 139589294a78SLandon J. Fuller /* Update our mapping state */ 139689294a78SLandon J. Fuller pio->win = regwin; 139789294a78SLandon J. Fuller pio->res = r; 139889294a78SLandon J. Fuller pio->addr = addr; 139989294a78SLandon J. Fuller pio->size = size; 140089294a78SLandon J. Fuller pio->res_target = target; 140189294a78SLandon J. Fuller 140289294a78SLandon J. Fuller return (0); 140389294a78SLandon J. Fuller } 140489294a78SLandon J. Fuller 140589294a78SLandon J. Fuller /* bhnd_erom_io_read() implementation */ 140689294a78SLandon J. Fuller static uint32_t 140789294a78SLandon J. Fuller bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 140889294a78SLandon J. Fuller { 140989294a78SLandon J. Fuller struct bhndb_pci_eio *pio; 141089294a78SLandon J. Fuller bhnd_addr_t addr; 141189294a78SLandon J. Fuller bus_size_t res_offset; 141289294a78SLandon J. Fuller int error; 141389294a78SLandon J. Fuller 141489294a78SLandon J. Fuller pio = (struct bhndb_pci_eio *)eio; 141589294a78SLandon J. Fuller 141689294a78SLandon J. Fuller /* Calculate absolute address */ 141789294a78SLandon J. Fuller if (BHND_SIZE_MAX - offset < pio->addr) { 141889294a78SLandon J. Fuller device_printf(pio->dev, "invalid offset %#jx+%#jx\n", pio->addr, 141989294a78SLandon J. Fuller offset); 142089294a78SLandon J. Fuller return (UINT32_MAX); 142189294a78SLandon J. Fuller } 142289294a78SLandon J. Fuller 142389294a78SLandon J. Fuller addr = pio->addr + offset; 142489294a78SLandon J. Fuller 142589294a78SLandon J. Fuller /* Adjust the mapping for our read */ 142689294a78SLandon J. Fuller if ((error = bhndb_pci_eio_adjust_mapping(pio, addr, width))) { 142789294a78SLandon J. Fuller device_printf(pio->dev, "failed to adjust register mapping: " 142889294a78SLandon J. Fuller "%d\n", error); 142989294a78SLandon J. Fuller return (UINT32_MAX); 143089294a78SLandon J. Fuller } 143189294a78SLandon J. Fuller 143289294a78SLandon J. Fuller KASSERT(pio->res_target <= addr, ("invalid mapping (%#jx vs. %#jx)", 143389294a78SLandon J. Fuller pio->res_target, addr)); 143489294a78SLandon J. Fuller 143589294a78SLandon J. Fuller /* Determine the actual read offset within our register window 143689294a78SLandon J. Fuller * resource */ 143789294a78SLandon J. Fuller res_offset = (addr - pio->res_target) + pio->win->win_offset; 143889294a78SLandon J. Fuller 143989294a78SLandon J. Fuller /* Perform our read */ 144089294a78SLandon J. Fuller switch (width) { 144189294a78SLandon J. Fuller case 1: 144289294a78SLandon J. Fuller return (bus_read_1(pio->res, res_offset)); 144389294a78SLandon J. Fuller case 2: 144489294a78SLandon J. Fuller return (bus_read_2(pio->res, res_offset)); 144589294a78SLandon J. Fuller case 4: 144689294a78SLandon J. Fuller return (bus_read_4(pio->res, res_offset)); 144789294a78SLandon J. Fuller default: 144889294a78SLandon J. Fuller panic("unsupported width: %u", width); 144989294a78SLandon J. Fuller } 145089294a78SLandon J. Fuller } 145189294a78SLandon J. Fuller 14524ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = { 14534ad7e9b0SAdrian Chadd /* Device interface */ 14544ad7e9b0SAdrian Chadd DEVMETHOD(device_probe, bhndb_pci_probe), 14554ad7e9b0SAdrian Chadd DEVMETHOD(device_attach, bhndb_pci_attach), 14564ad7e9b0SAdrian Chadd DEVMETHOD(device_resume, bhndb_pci_resume), 1457bb64eeccSAdrian Chadd DEVMETHOD(device_suspend, bhndb_pci_suspend), 1458bb64eeccSAdrian Chadd DEVMETHOD(device_detach, bhndb_pci_detach), 14594ad7e9b0SAdrian Chadd 1460f90f4b65SLandon J. Fuller /* BHND interface */ 1461f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhndb_pci_pwrctl_get_clksrc), 1462f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhndb_pci_pwrctl_gate_clock), 1463f90f4b65SLandon J. Fuller DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhndb_pci_pwrctl_ungate_clock), 1464f90f4b65SLandon J. Fuller 14654ad7e9b0SAdrian Chadd /* BHNDB interface */ 14664ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), 1467d567592bSAdrian Chadd DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info), 1468*caeff9a3SLandon J. Fuller DEVMETHOD(bhndb_map_intr_isrc, bhndb_pci_map_intr_isrc), 1469*caeff9a3SLandon J. Fuller DEVMETHOD(bhndb_route_interrupts, bhndb_pci_route_interrupts), 14704ad7e9b0SAdrian Chadd 14714ad7e9b0SAdrian Chadd DEVMETHOD_END 14724ad7e9b0SAdrian Chadd }; 14734ad7e9b0SAdrian Chadd 14744ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods, 14754ad7e9b0SAdrian Chadd sizeof(struct bhndb_pci_softc), bhndb_driver); 14764ad7e9b0SAdrian Chadd 14774ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1); 1478148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1); 14794ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); 14804ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); 1481148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1); 1482