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 67caeff9a3SLandon J. Fuller #include <dev/bhnd/siba/sibareg.h> 68caeff9a3SLandon J. Fuller 694ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/pci/bhnd_pcireg.h> 704ad7e9b0SAdrian Chadd 714e96bf3aSLandon J. Fuller #include "bhnd_pwrctl_hostb_if.h" 724e96bf3aSLandon J. Fuller 734ad7e9b0SAdrian Chadd #include "bhndb_pcireg.h" 744ad7e9b0SAdrian Chadd #include "bhndb_pcivar.h" 754ad7e9b0SAdrian Chadd #include "bhndb_private.h" 764ad7e9b0SAdrian Chadd 7789294a78SLandon J. Fuller struct bhndb_pci_eio; 7884d6a5d4SLandon J. Fuller struct bhndb_pci_probe; 7989294a78SLandon J. Fuller 80caeff9a3SLandon J. Fuller static int bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, 81caeff9a3SLandon J. Fuller int *msi_count); 8284d6a5d4SLandon J. Fuller 83111d7cb2SLandon J. Fuller static int bhndb_pci_add_children(struct bhndb_pci_softc *sc); 84111d7cb2SLandon J. Fuller 85caeff9a3SLandon 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 97caeff9a3SLandon J. Fuller static void bhndb_pci_write_core(struct bhndb_pci_softc *sc, 98caeff9a3SLandon J. Fuller bus_size_t offset, uint32_t value, u_int width); 99caeff9a3SLandon J. Fuller static uint32_t bhndb_pci_read_core(struct bhndb_pci_softc *sc, 100caeff9a3SLandon J. Fuller bus_size_t offset, u_int width); 101caeff9a3SLandon J. Fuller 10284d6a5d4SLandon J. Fuller static int bhndb_pci_srsh_pi_war(struct bhndb_pci_softc *sc, 10384d6a5d4SLandon J. Fuller struct bhndb_pci_probe *probe); 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 10884d6a5d4SLandon J. Fuller static int bhndb_pci_probe_alloc(struct bhndb_pci_probe **probe, 10984d6a5d4SLandon J. Fuller device_t dev, bhnd_devclass_t pci_devclass); 11084d6a5d4SLandon J. Fuller static void bhndb_pci_probe_free(struct bhndb_pci_probe *probe); 11184d6a5d4SLandon J. Fuller 11284d6a5d4SLandon J. Fuller static int bhndb_pci_probe_copy_core_table( 11384d6a5d4SLandon J. Fuller struct bhndb_pci_probe *probe, 11484d6a5d4SLandon J. Fuller struct bhnd_core_info **cores, u_int *ncores); 11584d6a5d4SLandon J. Fuller static void bhndb_pci_probe_free_core_table( 11684d6a5d4SLandon J. Fuller struct bhnd_core_info *cores); 11784d6a5d4SLandon J. Fuller 11884d6a5d4SLandon J. Fuller static void bhndb_pci_probe_write(struct bhndb_pci_probe *sc, 11984d6a5d4SLandon J. Fuller bhnd_addr_t addr, bhnd_size_t offset, 12084d6a5d4SLandon J. Fuller uint32_t value, u_int width); 12184d6a5d4SLandon J. Fuller static uint32_t bhndb_pci_probe_read(struct bhndb_pci_probe *sc, 12284d6a5d4SLandon J. Fuller bhnd_addr_t addr, bhnd_size_t offset, u_int width); 12384d6a5d4SLandon J. Fuller 12484d6a5d4SLandon J. Fuller static void bhndb_pci_eio_init(struct bhndb_pci_eio *eio, 12584d6a5d4SLandon J. Fuller struct bhndb_pci_probe *probe); 12689294a78SLandon J. Fuller static int bhndb_pci_eio_map(struct bhnd_erom_io *eio, 12789294a78SLandon J. Fuller bhnd_addr_t addr, bhnd_size_t size); 12889294a78SLandon J. Fuller static uint32_t bhndb_pci_eio_read(struct bhnd_erom_io *eio, 12989294a78SLandon J. Fuller bhnd_size_t offset, u_int width); 13089294a78SLandon J. Fuller 131824b48efSLandon J. Fuller #define BHNDB_PCI_MSI_COUNT 1 132824b48efSLandon J. Fuller 133caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pci_quirks[]; 134caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie_quirks[]; 135caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie2_quirks[]; 136caeff9a3SLandon J. Fuller 137caeff9a3SLandon J. Fuller static struct bhndb_pci_core bhndb_pci_cores[] = { 13884d6a5d4SLandon J. Fuller BHNDB_PCI_CORE(PCI, bhndb_pci_quirks), 13984d6a5d4SLandon J. Fuller BHNDB_PCI_CORE(PCIE, bhndb_pcie_quirks), 14084d6a5d4SLandon J. Fuller BHNDB_PCI_CORE(PCIE2, bhndb_pcie2_quirks), 141caeff9a3SLandon J. Fuller BHNDB_PCI_CORE_END 142caeff9a3SLandon J. Fuller }; 143caeff9a3SLandon J. Fuller 144caeff9a3SLandon J. Fuller /* bhndb_pci erom I/O instance state */ 14589294a78SLandon J. Fuller struct bhndb_pci_eio { 14689294a78SLandon J. Fuller struct bhnd_erom_io eio; 14789294a78SLandon J. Fuller bhnd_addr_t addr; /**< mapped address */ 14889294a78SLandon J. Fuller bhnd_size_t size; /**< mapped size */ 14984d6a5d4SLandon J. Fuller struct bhndb_pci_probe *probe; /**< borrowed probe reference */ 15089294a78SLandon J. Fuller }; 15189294a78SLandon J. Fuller 15284d6a5d4SLandon J. Fuller /** 15384d6a5d4SLandon J. Fuller * Provides early bus access to the bridged device's cores and core enumeration 15484d6a5d4SLandon J. Fuller * table. 15584d6a5d4SLandon J. Fuller * 15684d6a5d4SLandon J. Fuller * May be safely used during probe or early device attach, prior to calling 15784d6a5d4SLandon J. Fuller * bhndb_attach(). 15884d6a5d4SLandon J. Fuller */ 15984d6a5d4SLandon J. Fuller struct bhndb_pci_probe { 16084d6a5d4SLandon J. Fuller device_t dev; /**< bridge device */ 16184d6a5d4SLandon J. Fuller device_t pci_dev; /**< parent PCI device */ 16284d6a5d4SLandon J. Fuller struct bhnd_chipid cid; /**< chip identification */ 16384d6a5d4SLandon J. Fuller struct bhnd_core_info hostb_core; /**< PCI bridge core info */ 16484d6a5d4SLandon J. Fuller 16584d6a5d4SLandon J. Fuller struct bhndb_pci_eio erom_io; /**< erom I/O instance */ 16684d6a5d4SLandon J. Fuller bhnd_erom_class_t *erom_class; /**< probed erom class */ 16784d6a5d4SLandon J. Fuller bhnd_erom_t *erom; /**< erom parser */ 16884d6a5d4SLandon J. Fuller struct bhnd_core_info *cores; /**< erom-owned core table */ 16984d6a5d4SLandon J. Fuller u_int ncores; /**< number of cores */ 17084d6a5d4SLandon J. Fuller 17184d6a5d4SLandon J. Fuller const struct bhndb_regwin *m_win; /**< mapped register window, or NULL if no mapping */ 17284d6a5d4SLandon J. Fuller struct resource *m_res; /**< resource containing the register window, or NULL if no window mapped */ 17384d6a5d4SLandon J. Fuller bhnd_addr_t m_target; /**< base address mapped by m_win */ 17484d6a5d4SLandon J. Fuller bhnd_addr_t m_addr; /**< mapped address */ 17584d6a5d4SLandon J. Fuller bhnd_size_t m_size; /**< mapped size */ 17684d6a5d4SLandon J. Fuller bool m_valid; /**< true if a valid mapping exists, false otherwise */ 17784d6a5d4SLandon J. Fuller 17884d6a5d4SLandon J. Fuller struct bhndb_host_resources *hr; /**< backing host resources */ 17984d6a5d4SLandon J. Fuller }; 18084d6a5d4SLandon J. Fuller 18184d6a5d4SLandon J. Fuller 182caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pci_quirks[] = { 183caeff9a3SLandon J. Fuller /* Backplane interrupt flags must be routed via siba-specific 184caeff9a3SLandon J. Fuller * SIBA_CFG0_INTVEC configuration register; the BHNDB_PCI_INT_MASK 185caeff9a3SLandon J. Fuller * PCI configuration register is unsupported. */ 186caeff9a3SLandon J. Fuller {{ BHND_MATCH_CHIP_TYPE (SIBA) }, 187caeff9a3SLandon J. Fuller { BHND_MATCH_CORE_REV (HWREV_LTE(5)) }, 188caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK_SIBA_INTVEC }, 189caeff9a3SLandon J. Fuller 190caeff9a3SLandon J. Fuller /* All PCI core revisions require the SRSH work-around */ 191caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR), 192caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK_END 193caeff9a3SLandon J. Fuller }; 194caeff9a3SLandon J. Fuller 195caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie_quirks[] = { 196caeff9a3SLandon J. Fuller /* All PCIe-G1 core revisions require the SRSH work-around */ 197caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK(HWREV_ANY, BHNDB_PCI_QUIRK_SRSH_WAR), 198caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK_END 199caeff9a3SLandon J. Fuller }; 200caeff9a3SLandon J. Fuller 201caeff9a3SLandon J. Fuller static struct bhndb_pci_quirk bhndb_pcie2_quirks[] = { 202caeff9a3SLandon J. Fuller BHNDB_PCI_QUIRK_END 203caeff9a3SLandon J. Fuller }; 204caeff9a3SLandon J. Fuller 205caeff9a3SLandon J. Fuller /** 206caeff9a3SLandon J. Fuller * Return the device table entry for @p ci, or NULL if none. 207caeff9a3SLandon J. Fuller */ 208caeff9a3SLandon J. Fuller static struct bhndb_pci_core * 209caeff9a3SLandon J. Fuller bhndb_pci_find_core(struct bhnd_core_info *ci) 210caeff9a3SLandon J. Fuller { 211caeff9a3SLandon J. Fuller for (size_t i = 0; !BHNDB_PCI_IS_CORE_END(&bhndb_pci_cores[i]); i++) { 212caeff9a3SLandon J. Fuller struct bhndb_pci_core *entry = &bhndb_pci_cores[i]; 213caeff9a3SLandon J. Fuller 214caeff9a3SLandon J. Fuller if (bhnd_core_matches(ci, &entry->match)) 215caeff9a3SLandon J. Fuller return (entry); 216caeff9a3SLandon J. Fuller } 217caeff9a3SLandon J. Fuller 218caeff9a3SLandon J. Fuller return (NULL); 219caeff9a3SLandon J. Fuller } 220caeff9a3SLandon J. Fuller 221caeff9a3SLandon J. Fuller /** 222caeff9a3SLandon J. Fuller * Return all quirk flags for the given @p cid and @p ci. 223caeff9a3SLandon J. Fuller */ 224caeff9a3SLandon J. Fuller static uint32_t 225caeff9a3SLandon J. Fuller bhndb_pci_get_core_quirks(struct bhnd_chipid *cid, struct bhnd_core_info *ci) 226caeff9a3SLandon J. Fuller { 227caeff9a3SLandon J. Fuller struct bhndb_pci_core *entry; 228caeff9a3SLandon J. Fuller struct bhndb_pci_quirk *qtable; 229caeff9a3SLandon J. Fuller uint32_t quirks; 230caeff9a3SLandon J. Fuller 231caeff9a3SLandon J. Fuller quirks = 0; 232caeff9a3SLandon J. Fuller 233caeff9a3SLandon J. Fuller /* No core entry? */ 234caeff9a3SLandon J. Fuller if ((entry = bhndb_pci_find_core(ci)) == NULL) 235caeff9a3SLandon J. Fuller return (quirks); 236caeff9a3SLandon J. Fuller 237caeff9a3SLandon J. Fuller /* No quirks? */ 238caeff9a3SLandon J. Fuller if ((qtable = entry->quirks) == NULL) 239caeff9a3SLandon J. Fuller return (quirks); 240caeff9a3SLandon J. Fuller 241caeff9a3SLandon J. Fuller for (size_t i = 0; !BHNDB_PCI_IS_QUIRK_END(&qtable[i]); i++) { 242caeff9a3SLandon J. Fuller struct bhndb_pci_quirk *q = &qtable[i]; 243caeff9a3SLandon J. Fuller 244caeff9a3SLandon J. Fuller if (!bhnd_chip_matches(cid, &q->chip_desc)) 245caeff9a3SLandon J. Fuller continue; 246caeff9a3SLandon J. Fuller 247caeff9a3SLandon J. Fuller if (!bhnd_core_matches(ci, &q->core_desc)) 248caeff9a3SLandon J. Fuller continue; 249caeff9a3SLandon J. Fuller 250caeff9a3SLandon J. Fuller quirks |= q->quirks; 251caeff9a3SLandon J. Fuller } 252caeff9a3SLandon J. Fuller 253caeff9a3SLandon J. Fuller return (quirks); 254caeff9a3SLandon J. Fuller } 255caeff9a3SLandon J. Fuller 2564ad7e9b0SAdrian Chadd /** 2574ad7e9b0SAdrian Chadd * Default bhndb_pci implementation of device_probe(). 2584ad7e9b0SAdrian Chadd * 2594ad7e9b0SAdrian Chadd * Verifies that the parent is a PCI/PCIe device. 2604ad7e9b0SAdrian Chadd */ 2614ad7e9b0SAdrian Chadd static int 2624ad7e9b0SAdrian Chadd bhndb_pci_probe(device_t dev) 2634ad7e9b0SAdrian Chadd { 26484d6a5d4SLandon J. Fuller struct bhndb_pci_probe *probe; 265caeff9a3SLandon J. Fuller struct bhndb_pci_core *entry; 266caeff9a3SLandon J. Fuller bhnd_devclass_t hostb_devclass; 267*711221efSLandon J. Fuller device_t parent, parent_bus; 268*711221efSLandon J. Fuller devclass_t pci, bus_devclass; 269caeff9a3SLandon J. Fuller int error; 270caeff9a3SLandon J. Fuller 27184d6a5d4SLandon J. Fuller probe = NULL; 2724ad7e9b0SAdrian Chadd 2734ad7e9b0SAdrian Chadd /* Our parent must be a PCI/PCIe device. */ 2744ad7e9b0SAdrian Chadd pci = devclass_find("pci"); 2754ad7e9b0SAdrian Chadd parent = device_get_parent(dev); 276*711221efSLandon J. Fuller parent_bus = device_get_parent(parent); 277*711221efSLandon J. Fuller if (parent_bus == NULL) 278*711221efSLandon J. Fuller return (ENXIO); 2794ad7e9b0SAdrian Chadd 280*711221efSLandon J. Fuller /* The bus device class may inherit from 'pci' */ 281*711221efSLandon J. Fuller for (bus_devclass = device_get_devclass(parent_bus); 282*711221efSLandon J. Fuller bus_devclass != NULL; 283*711221efSLandon J. Fuller bus_devclass = devclass_get_parent(bus_devclass)) 284*711221efSLandon J. Fuller { 285*711221efSLandon J. Fuller if (bus_devclass == pci) 286*711221efSLandon J. Fuller break; 287*711221efSLandon J. Fuller } 288*711221efSLandon J. Fuller 289*711221efSLandon J. Fuller if (bus_devclass != pci) 2904ad7e9b0SAdrian Chadd return (ENXIO); 2914ad7e9b0SAdrian Chadd 292caeff9a3SLandon J. Fuller /* Enable clocks */ 293caeff9a3SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(dev))) 294caeff9a3SLandon J. Fuller return (error); 2954ad7e9b0SAdrian Chadd 296caeff9a3SLandon J. Fuller /* Identify the chip and enumerate the bridged cores */ 297caeff9a3SLandon J. Fuller hostb_devclass = bhndb_expected_pci_devclass(dev); 29884d6a5d4SLandon J. Fuller if ((error = bhndb_pci_probe_alloc(&probe, dev, hostb_devclass))) 299caeff9a3SLandon J. Fuller goto cleanup; 300caeff9a3SLandon J. Fuller 301caeff9a3SLandon J. Fuller /* Look for a matching core table entry */ 30284d6a5d4SLandon J. Fuller if ((entry = bhndb_pci_find_core(&probe->hostb_core)) == NULL) { 303caeff9a3SLandon J. Fuller error = ENXIO; 304caeff9a3SLandon J. Fuller goto cleanup; 3054ad7e9b0SAdrian Chadd } 3064ad7e9b0SAdrian Chadd 307caeff9a3SLandon J. Fuller device_set_desc(dev, "PCI-BHND bridge"); 308caeff9a3SLandon J. Fuller 309caeff9a3SLandon J. Fuller /* fall-through */ 310caeff9a3SLandon J. Fuller error = BUS_PROBE_DEFAULT; 311caeff9a3SLandon J. Fuller 312caeff9a3SLandon J. Fuller cleanup: 31384d6a5d4SLandon J. Fuller if (probe != NULL) 31484d6a5d4SLandon J. Fuller bhndb_pci_probe_free(probe); 31584d6a5d4SLandon J. Fuller 316caeff9a3SLandon J. Fuller bhndb_disable_pci_clocks(dev); 317caeff9a3SLandon J. Fuller 318caeff9a3SLandon J. Fuller return (error); 319caeff9a3SLandon J. Fuller } 320caeff9a3SLandon J. Fuller 321caeff9a3SLandon J. Fuller /** 322caeff9a3SLandon J. Fuller * Attempt to allocate MSI interrupts, returning the count in @p msi_count 323caeff9a3SLandon J. Fuller * on success. 324caeff9a3SLandon J. Fuller */ 325824b48efSLandon J. Fuller static int 326caeff9a3SLandon J. Fuller bhndb_pci_alloc_msi(struct bhndb_pci_softc *sc, int *msi_count) 327824b48efSLandon J. Fuller { 328caeff9a3SLandon J. Fuller int error, count; 329824b48efSLandon J. Fuller 330824b48efSLandon J. Fuller /* Is MSI available? */ 331824b48efSLandon J. Fuller if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT) 332824b48efSLandon J. Fuller return (ENXIO); 333824b48efSLandon J. Fuller 334824b48efSLandon J. Fuller /* Allocate expected message count */ 335caeff9a3SLandon J. Fuller count = BHNDB_PCI_MSI_COUNT; 336caeff9a3SLandon J. Fuller if ((error = pci_alloc_msi(sc->parent, &count))) { 337824b48efSLandon J. Fuller device_printf(sc->dev, "failed to allocate MSI interrupts: " 338824b48efSLandon J. Fuller "%d\n", error); 339caeff9a3SLandon J. Fuller 340824b48efSLandon J. Fuller return (error); 341824b48efSLandon J. Fuller } 342824b48efSLandon J. Fuller 343d16875a8SLandon J. Fuller if (count < BHNDB_PCI_MSI_COUNT) { 344d16875a8SLandon J. Fuller pci_release_msi(sc->parent); 345824b48efSLandon J. Fuller return (ENXIO); 346d16875a8SLandon J. Fuller } 347824b48efSLandon J. Fuller 348caeff9a3SLandon J. Fuller *msi_count = count; 349824b48efSLandon J. Fuller return (0); 350824b48efSLandon J. Fuller } 351824b48efSLandon J. Fuller 3524ad7e9b0SAdrian Chadd static int 3534ad7e9b0SAdrian Chadd bhndb_pci_attach(device_t dev) 3544ad7e9b0SAdrian Chadd { 3554ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 35689294a78SLandon J. Fuller struct bhnd_chipid cid; 35789294a78SLandon J. Fuller struct bhnd_core_info *cores, hostb_core; 35889294a78SLandon J. Fuller bhnd_erom_class_t *erom_class; 35984d6a5d4SLandon J. Fuller struct bhndb_pci_probe *probe; 36089294a78SLandon J. Fuller u_int ncores; 361caeff9a3SLandon J. Fuller int irq_rid; 362caeff9a3SLandon J. Fuller int error; 3634ad7e9b0SAdrian Chadd 3644ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 3654ad7e9b0SAdrian Chadd sc->dev = dev; 366e83ce340SAdrian Chadd sc->parent = device_get_parent(dev); 367caeff9a3SLandon J. Fuller sc->pci_devclass = bhndb_expected_pci_devclass(dev); 368caeff9a3SLandon J. Fuller sc->pci_quirks = 0; 36989294a78SLandon J. Fuller sc->set_regwin = NULL; 37089294a78SLandon J. Fuller 371caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK_INIT(sc); 372caeff9a3SLandon J. Fuller 37384d6a5d4SLandon J. Fuller probe = NULL; 37489294a78SLandon J. Fuller cores = NULL; 3754ad7e9b0SAdrian Chadd 376824b48efSLandon J. Fuller /* Enable PCI bus mastering */ 377824b48efSLandon J. Fuller pci_enable_busmaster(sc->parent); 378824b48efSLandon J. Fuller 37984d6a5d4SLandon J. Fuller /* Enable clocks (if required by this hardware) */ 38084d6a5d4SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(sc->dev))) 38184d6a5d4SLandon J. Fuller goto cleanup; 38284d6a5d4SLandon J. Fuller 38384d6a5d4SLandon J. Fuller /* Identify the chip and enumerate the bridged cores */ 38484d6a5d4SLandon J. Fuller error = bhndb_pci_probe_alloc(&probe, dev, sc->pci_devclass); 38584d6a5d4SLandon J. Fuller if (error) 38684d6a5d4SLandon J. Fuller goto cleanup; 38784d6a5d4SLandon J. Fuller 38884d6a5d4SLandon J. Fuller sc->pci_quirks = bhndb_pci_get_core_quirks(&probe->cid, 38984d6a5d4SLandon J. Fuller &probe->hostb_core); 39084d6a5d4SLandon J. Fuller 39184d6a5d4SLandon J. Fuller /* Select the appropriate register window handler */ 39284d6a5d4SLandon J. Fuller if (probe->cid.chip_type == BHND_CHIPTYPE_SIBA) { 39384d6a5d4SLandon J. Fuller sc->set_regwin = bhndb_pci_compat_setregwin; 39484d6a5d4SLandon J. Fuller } else { 39584d6a5d4SLandon J. Fuller sc->set_regwin = bhndb_pci_fast_setregwin; 39684d6a5d4SLandon J. Fuller } 39784d6a5d4SLandon J. Fuller 39884d6a5d4SLandon J. Fuller /* 39984d6a5d4SLandon J. Fuller * Fix up our PCI base address in the SPROM shadow, if necessary. 40084d6a5d4SLandon J. Fuller * 40184d6a5d4SLandon J. Fuller * This must be done prior to accessing any static register windows 40284d6a5d4SLandon J. Fuller * that map the PCI core. 40384d6a5d4SLandon J. Fuller */ 40484d6a5d4SLandon J. Fuller if ((error = bhndb_pci_srsh_pi_war(sc, probe))) 40584d6a5d4SLandon J. Fuller goto cleanup; 40684d6a5d4SLandon J. Fuller 40789294a78SLandon J. Fuller /* Set up PCI interrupt handling */ 408caeff9a3SLandon J. Fuller if (bhndb_pci_alloc_msi(sc, &sc->msi_count) == 0) { 409caeff9a3SLandon J. Fuller /* MSI uses resource IDs starting at 1 */ 410caeff9a3SLandon J. Fuller irq_rid = 1; 411caeff9a3SLandon J. Fuller 412824b48efSLandon J. Fuller device_printf(dev, "Using MSI interrupts on %s\n", 413824b48efSLandon J. Fuller device_get_nameunit(sc->parent)); 414824b48efSLandon J. Fuller } else { 415caeff9a3SLandon J. Fuller sc->msi_count = 0; 416caeff9a3SLandon J. Fuller irq_rid = 0; 417caeff9a3SLandon J. Fuller 418824b48efSLandon J. Fuller device_printf(dev, "Using INTx interrupts on %s\n", 419824b48efSLandon J. Fuller device_get_nameunit(sc->parent)); 420824b48efSLandon J. Fuller } 421824b48efSLandon J. Fuller 422caeff9a3SLandon J. Fuller sc->isrc = bhndb_alloc_intr_isrc(sc->parent, irq_rid, 0, RM_MAX_END, 1, 423caeff9a3SLandon J. Fuller RF_SHAREABLE | RF_ACTIVE); 424caeff9a3SLandon J. Fuller if (sc->isrc == NULL) { 425caeff9a3SLandon J. Fuller device_printf(sc->dev, "failed to allocate interrupt " 426caeff9a3SLandon J. Fuller "resource\n"); 427caeff9a3SLandon J. Fuller error = ENXIO; 428caeff9a3SLandon J. Fuller goto cleanup; 429caeff9a3SLandon J. Fuller } 4304ad7e9b0SAdrian Chadd 43184d6a5d4SLandon J. Fuller /* 43284d6a5d4SLandon J. Fuller * Copy out the probe results and then free our probe state, releasing 43384d6a5d4SLandon J. Fuller * its exclusive ownership of host bridge resources. 43484d6a5d4SLandon J. Fuller * 43584d6a5d4SLandon J. Fuller * This must be done prior to full configuration of the bridge via 43684d6a5d4SLandon J. Fuller * bhndb_attach(). 43784d6a5d4SLandon J. Fuller */ 43884d6a5d4SLandon J. Fuller cid = probe->cid; 43984d6a5d4SLandon J. Fuller erom_class = probe->erom_class; 44084d6a5d4SLandon J. Fuller hostb_core = probe->hostb_core; 441111d7cb2SLandon J. Fuller 44284d6a5d4SLandon J. Fuller error = bhndb_pci_probe_copy_core_table(probe, &cores, &ncores); 44384d6a5d4SLandon J. Fuller if (error) { 44484d6a5d4SLandon J. Fuller cores = NULL; 445111d7cb2SLandon J. Fuller goto cleanup; 4464ad7e9b0SAdrian Chadd } 4474ad7e9b0SAdrian Chadd 44884d6a5d4SLandon J. Fuller bhndb_pci_probe_free(probe); 44984d6a5d4SLandon J. Fuller probe = NULL; 450caeff9a3SLandon J. Fuller 45189294a78SLandon J. Fuller /* Perform bridge attach */ 45289294a78SLandon J. Fuller error = bhndb_attach(dev, &cid, cores, ncores, &hostb_core, erom_class); 45389294a78SLandon J. Fuller if (error) 45489294a78SLandon J. Fuller goto cleanup; 4554ad7e9b0SAdrian Chadd 456111d7cb2SLandon J. Fuller /* Add any additional child devices */ 457111d7cb2SLandon J. Fuller if ((error = bhndb_pci_add_children(sc))) 458111d7cb2SLandon J. Fuller goto cleanup; 459111d7cb2SLandon J. Fuller 460111d7cb2SLandon J. Fuller /* Probe and attach our children */ 461111d7cb2SLandon J. Fuller if ((error = bus_generic_attach(dev))) 462111d7cb2SLandon J. Fuller goto cleanup; 463111d7cb2SLandon J. Fuller 46484d6a5d4SLandon J. Fuller bhndb_pci_probe_free_core_table(cores); 46589294a78SLandon J. Fuller 466111d7cb2SLandon J. Fuller return (0); 467111d7cb2SLandon J. Fuller 468111d7cb2SLandon J. Fuller cleanup: 469111d7cb2SLandon J. Fuller device_delete_children(dev); 47089294a78SLandon J. Fuller 471caeff9a3SLandon J. Fuller if (sc->isrc != NULL) 472caeff9a3SLandon J. Fuller bhndb_free_intr_isrc(sc->isrc); 473caeff9a3SLandon J. Fuller 474caeff9a3SLandon J. Fuller if (sc->msi_count > 0) 475d16875a8SLandon J. Fuller pci_release_msi(sc->parent); 476824b48efSLandon J. Fuller 47789294a78SLandon J. Fuller if (cores != NULL) 47884d6a5d4SLandon J. Fuller bhndb_pci_probe_free_core_table(cores); 47984d6a5d4SLandon J. Fuller 48084d6a5d4SLandon J. Fuller if (probe != NULL) 48184d6a5d4SLandon J. Fuller bhndb_pci_probe_free(probe); 48284d6a5d4SLandon J. Fuller 48384d6a5d4SLandon J. Fuller bhndb_disable_pci_clocks(sc->dev); 48489294a78SLandon J. Fuller 485111d7cb2SLandon J. Fuller pci_disable_busmaster(sc->parent); 486111d7cb2SLandon J. Fuller 487caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK_DESTROY(sc); 488caeff9a3SLandon J. Fuller 489111d7cb2SLandon J. Fuller return (error); 490111d7cb2SLandon J. Fuller } 491111d7cb2SLandon J. Fuller 492111d7cb2SLandon J. Fuller static int 493111d7cb2SLandon J. Fuller bhndb_pci_detach(device_t dev) 494111d7cb2SLandon J. Fuller { 495111d7cb2SLandon J. Fuller struct bhndb_pci_softc *sc; 496111d7cb2SLandon J. Fuller int error; 497111d7cb2SLandon J. Fuller 498111d7cb2SLandon J. Fuller sc = device_get_softc(dev); 499111d7cb2SLandon J. Fuller 500111d7cb2SLandon J. Fuller /* Attempt to detach our children */ 501111d7cb2SLandon J. Fuller if ((error = bus_generic_detach(dev))) 502111d7cb2SLandon J. Fuller return (error); 503111d7cb2SLandon J. Fuller 504111d7cb2SLandon J. Fuller /* Perform generic bridge detach */ 505111d7cb2SLandon J. Fuller if ((error = bhndb_generic_detach(dev))) 506111d7cb2SLandon J. Fuller return (error); 507111d7cb2SLandon J. Fuller 508111d7cb2SLandon J. Fuller /* Disable clocks (if required by this hardware) */ 50989294a78SLandon J. Fuller if ((error = bhndb_disable_pci_clocks(sc->dev))) 510111d7cb2SLandon J. Fuller return (error); 511111d7cb2SLandon J. Fuller 512caeff9a3SLandon J. Fuller /* Free our interrupt resources */ 513caeff9a3SLandon J. Fuller bhndb_free_intr_isrc(sc->isrc); 514caeff9a3SLandon J. Fuller 515824b48efSLandon J. Fuller /* Release MSI interrupts */ 516caeff9a3SLandon J. Fuller if (sc->msi_count > 0) 517d16875a8SLandon J. Fuller pci_release_msi(sc->parent); 518824b48efSLandon J. Fuller 519111d7cb2SLandon J. Fuller /* Disable PCI bus mastering */ 520111d7cb2SLandon J. Fuller pci_disable_busmaster(sc->parent); 521111d7cb2SLandon J. Fuller 522caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK_DESTROY(sc); 523caeff9a3SLandon J. Fuller 524111d7cb2SLandon J. Fuller return (0); 525111d7cb2SLandon J. Fuller } 526111d7cb2SLandon J. Fuller 527111d7cb2SLandon J. Fuller static int 528111d7cb2SLandon J. Fuller bhndb_pci_add_children(struct bhndb_pci_softc *sc) 529111d7cb2SLandon J. Fuller { 530111d7cb2SLandon J. Fuller bus_size_t nv_sz; 531111d7cb2SLandon J. Fuller int error; 532111d7cb2SLandon J. Fuller 533111d7cb2SLandon J. Fuller /** 534111d7cb2SLandon J. Fuller * If SPROM is mapped directly into BAR0, add child NVRAM 535111d7cb2SLandon J. Fuller * device. 536111d7cb2SLandon J. Fuller */ 537e83ce340SAdrian Chadd nv_sz = bhndb_pci_sprom_size(sc); 538e83ce340SAdrian Chadd if (nv_sz > 0) { 539e83ce340SAdrian Chadd struct bhndb_devinfo *dinfo; 540111d7cb2SLandon J. Fuller device_t child; 541e83ce340SAdrian Chadd 542e83ce340SAdrian Chadd if (bootverbose) { 543111d7cb2SLandon J. Fuller device_printf(sc->dev, "found SPROM (%ju bytes)\n", 544111d7cb2SLandon J. Fuller (uintmax_t)nv_sz); 545e83ce340SAdrian Chadd } 546e83ce340SAdrian Chadd 547111d7cb2SLandon J. Fuller /* Add sprom device, ordered early enough to be available 548111d7cb2SLandon J. Fuller * before the bridged bhnd(4) bus is attached. */ 549111d7cb2SLandon J. Fuller child = BUS_ADD_CHILD(sc->dev, 550111d7cb2SLandon J. Fuller BHND_PROBE_ROOT + BHND_PROBE_ORDER_EARLY, "bhnd_nvram", -1); 551111d7cb2SLandon J. Fuller if (child == NULL) { 552111d7cb2SLandon J. Fuller device_printf(sc->dev, "failed to add sprom device\n"); 553e83ce340SAdrian Chadd return (ENXIO); 554e83ce340SAdrian Chadd } 555e83ce340SAdrian Chadd 556e83ce340SAdrian Chadd /* Initialize device address space and resource covering the 557e83ce340SAdrian Chadd * BAR0 SPROM shadow. */ 558111d7cb2SLandon J. Fuller dinfo = device_get_ivars(child); 559e83ce340SAdrian Chadd dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; 560111d7cb2SLandon J. Fuller 561111d7cb2SLandon J. Fuller error = bus_set_resource(child, SYS_RES_MEMORY, 0, 562e83ce340SAdrian Chadd bhndb_pci_sprom_addr(sc), nv_sz); 563e83ce340SAdrian Chadd if (error) { 564111d7cb2SLandon J. Fuller device_printf(sc->dev, 565e83ce340SAdrian Chadd "failed to register sprom resources\n"); 566e83ce340SAdrian Chadd return (error); 567e83ce340SAdrian Chadd } 568e83ce340SAdrian Chadd } 569e83ce340SAdrian Chadd 5704ad7e9b0SAdrian Chadd return (0); 5714ad7e9b0SAdrian Chadd } 5724ad7e9b0SAdrian Chadd 573e83ce340SAdrian Chadd static const struct bhndb_regwin * 574e83ce340SAdrian Chadd bhndb_pci_sprom_regwin(struct bhndb_pci_softc *sc) 575e83ce340SAdrian Chadd { 576e83ce340SAdrian Chadd struct bhndb_resources *bres; 577e83ce340SAdrian Chadd const struct bhndb_hwcfg *cfg; 578e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 579e83ce340SAdrian Chadd 580e83ce340SAdrian Chadd bres = sc->bhndb.bus_res; 581e83ce340SAdrian Chadd cfg = bres->cfg; 582e83ce340SAdrian Chadd 583e83ce340SAdrian Chadd sprom_win = bhndb_regwin_find_type(cfg->register_windows, 584e83ce340SAdrian Chadd BHNDB_REGWIN_T_SPROM, BHNDB_PCI_V0_BAR0_SPROM_SIZE); 585e83ce340SAdrian Chadd 586e83ce340SAdrian Chadd return (sprom_win); 587e83ce340SAdrian Chadd } 588e83ce340SAdrian Chadd 589e83ce340SAdrian Chadd static bus_addr_t 590e83ce340SAdrian Chadd bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc) 591e83ce340SAdrian Chadd { 592e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 593e83ce340SAdrian Chadd struct resource *r; 594e83ce340SAdrian Chadd 595e83ce340SAdrian Chadd /* Fetch the SPROM register window */ 596e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 597e83ce340SAdrian Chadd KASSERT(sprom_win != NULL, ("requested sprom address on PCI_V2+")); 598e83ce340SAdrian Chadd 599e83ce340SAdrian Chadd /* Fetch the associated resource */ 60089294a78SLandon J. Fuller r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, sprom_win); 601e83ce340SAdrian Chadd KASSERT(r != NULL, ("missing resource for sprom window\n")); 602e83ce340SAdrian Chadd 603e83ce340SAdrian Chadd return (rman_get_start(r) + sprom_win->win_offset); 604e83ce340SAdrian Chadd } 605e83ce340SAdrian Chadd 606e83ce340SAdrian Chadd static bus_size_t 607e83ce340SAdrian Chadd bhndb_pci_sprom_size(struct bhndb_pci_softc *sc) 608e83ce340SAdrian Chadd { 609e83ce340SAdrian Chadd const struct bhndb_regwin *sprom_win; 610e83ce340SAdrian Chadd uint32_t sctl; 611e83ce340SAdrian Chadd bus_size_t sprom_sz; 612e83ce340SAdrian Chadd 613e83ce340SAdrian Chadd sprom_win = bhndb_pci_sprom_regwin(sc); 614e83ce340SAdrian Chadd 615e83ce340SAdrian Chadd /* PCI_V2 and later devices map SPROM/OTP via ChipCommon */ 616e83ce340SAdrian Chadd if (sprom_win == NULL) 617e83ce340SAdrian Chadd return (0); 618e83ce340SAdrian Chadd 619e83ce340SAdrian Chadd /* Determine SPROM size */ 620e83ce340SAdrian Chadd sctl = pci_read_config(sc->parent, BHNDB_PCI_SPROM_CONTROL, 4); 621e83ce340SAdrian Chadd if (sctl & BHNDB_PCI_SPROM_BLANK) 622e83ce340SAdrian Chadd return (0); 623e83ce340SAdrian Chadd 624e83ce340SAdrian Chadd switch (sctl & BHNDB_PCI_SPROM_SZ_MASK) { 625e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_1KB: 626e83ce340SAdrian Chadd sprom_sz = (1 * 1024); 627e83ce340SAdrian Chadd break; 628e83ce340SAdrian Chadd 629e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_4KB: 630e83ce340SAdrian Chadd sprom_sz = (4 * 1024); 631e83ce340SAdrian Chadd break; 632e83ce340SAdrian Chadd 633e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_16KB: 634e83ce340SAdrian Chadd sprom_sz = (16 * 1024); 635e83ce340SAdrian Chadd break; 636e83ce340SAdrian Chadd 637e83ce340SAdrian Chadd case BHNDB_PCI_SPROM_SZ_RESERVED: 638e83ce340SAdrian Chadd default: 639e83ce340SAdrian Chadd device_printf(sc->dev, "invalid PCI sprom size 0x%x\n", sctl); 640e83ce340SAdrian Chadd return (0); 641e83ce340SAdrian Chadd } 642e83ce340SAdrian Chadd 643*711221efSLandon J. Fuller /* If the device has a larger SPROM than can be addressed via our SPROM 644*711221efSLandon J. Fuller * register window, the SPROM image data will still be located within 645*711221efSLandon J. Fuller * the window's addressable range */ 646*711221efSLandon J. Fuller sprom_sz = MIN(sprom_sz, sprom_win->win_size); 647e83ce340SAdrian Chadd 648e83ce340SAdrian Chadd return (sprom_sz); 649e83ce340SAdrian Chadd } 650e83ce340SAdrian Chadd 651caeff9a3SLandon J. Fuller /** 652caeff9a3SLandon J. Fuller * Return the host resource providing a static mapping of the PCI core's 653caeff9a3SLandon J. Fuller * registers. 654caeff9a3SLandon J. Fuller * 655caeff9a3SLandon J. Fuller * @param sc bhndb PCI driver state. 656eaa5fb4bSLandon J. Fuller * @param offset The required readable offset within the PCI core 657eaa5fb4bSLandon J. Fuller * register block. 658eaa5fb4bSLandon J. Fuller * @param size The required readable size at @p offset. 659caeff9a3SLandon J. Fuller * @param[out] res On success, the host resource containing our PCI 660caeff9a3SLandon J. Fuller * core's register window. 661eaa5fb4bSLandon J. Fuller * @param[out] res_offset On success, the @p offset relative to @p res. 662caeff9a3SLandon J. Fuller * 663caeff9a3SLandon J. Fuller * @retval 0 success 664caeff9a3SLandon J. Fuller * @retval ENXIO if a valid static register window mapping the PCI core 665caeff9a3SLandon J. Fuller * registers is not available. 666caeff9a3SLandon J. Fuller */ 667caeff9a3SLandon J. Fuller static int 668eaa5fb4bSLandon J. Fuller bhndb_pci_get_core_regs(struct bhndb_pci_softc *sc, bus_size_t offset, 669eaa5fb4bSLandon J. Fuller bus_size_t size, struct resource **res, bus_size_t *res_offset) 670caeff9a3SLandon J. Fuller { 671caeff9a3SLandon J. Fuller const struct bhndb_regwin *win; 672caeff9a3SLandon J. Fuller struct resource *r; 673caeff9a3SLandon J. Fuller 674eaa5fb4bSLandon J. Fuller /* Locate the static register window mapping the requested offset */ 675caeff9a3SLandon J. Fuller win = bhndb_regwin_find_core(sc->bhndb.bus_res->cfg->register_windows, 676eaa5fb4bSLandon J. Fuller sc->pci_devclass, 0, BHND_PORT_DEVICE, 0, 0, offset, size); 677caeff9a3SLandon J. Fuller if (win == NULL) { 678caeff9a3SLandon J. Fuller device_printf(sc->dev, "missing PCI core register window\n"); 679caeff9a3SLandon J. Fuller return (ENXIO); 680caeff9a3SLandon J. Fuller } 681caeff9a3SLandon J. Fuller 682caeff9a3SLandon J. Fuller /* Fetch the resource containing the register window */ 683caeff9a3SLandon J. Fuller r = bhndb_host_resource_for_regwin(sc->bhndb.bus_res->res, win); 684caeff9a3SLandon J. Fuller if (r == NULL) { 685caeff9a3SLandon J. Fuller device_printf(sc->dev, "missing PCI core register resource\n"); 686caeff9a3SLandon J. Fuller return (ENXIO); 687caeff9a3SLandon J. Fuller } 688caeff9a3SLandon J. Fuller 689eaa5fb4bSLandon J. Fuller KASSERT(offset >= win->d.core.offset, ("offset %#jx outside of " 690eaa5fb4bSLandon J. Fuller "register window", (uintmax_t)offset)); 691eaa5fb4bSLandon J. Fuller 692caeff9a3SLandon J. Fuller *res = r; 693eaa5fb4bSLandon J. Fuller *res_offset = win->win_offset + (offset - win->d.core.offset); 694caeff9a3SLandon J. Fuller 695caeff9a3SLandon J. Fuller return (0); 696caeff9a3SLandon J. Fuller } 697caeff9a3SLandon J. Fuller 698caeff9a3SLandon J. Fuller /** 699caeff9a3SLandon J. Fuller * Write a 1, 2, or 4 byte data item to the PCI core's registers at @p offset. 700caeff9a3SLandon J. Fuller * 701caeff9a3SLandon J. Fuller * @param sc bhndb PCI driver state. 702caeff9a3SLandon J. Fuller * @param offset register write offset. 703caeff9a3SLandon J. Fuller * @param value value to be written. 704caeff9a3SLandon J. Fuller * @param width item width (1, 2, or 4 bytes). 705caeff9a3SLandon J. Fuller */ 706caeff9a3SLandon J. Fuller static void 707caeff9a3SLandon J. Fuller bhndb_pci_write_core(struct bhndb_pci_softc *sc, bus_size_t offset, 708caeff9a3SLandon J. Fuller uint32_t value, u_int width) 709caeff9a3SLandon J. Fuller { 710caeff9a3SLandon J. Fuller struct resource *r; 711caeff9a3SLandon J. Fuller bus_size_t r_offset; 712caeff9a3SLandon J. Fuller int error; 713caeff9a3SLandon J. Fuller 714eaa5fb4bSLandon J. Fuller error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset); 715eaa5fb4bSLandon J. Fuller if (error) { 716eaa5fb4bSLandon J. Fuller panic("no PCI register window mapping %#jx+%#x: %d", 717eaa5fb4bSLandon J. Fuller (uintmax_t)offset, width, error); 718eaa5fb4bSLandon J. Fuller } 719caeff9a3SLandon J. Fuller 720caeff9a3SLandon J. Fuller switch (width) { 721caeff9a3SLandon J. Fuller case 1: 722eaa5fb4bSLandon J. Fuller bus_write_1(r, r_offset, value); 723caeff9a3SLandon J. Fuller break; 724caeff9a3SLandon J. Fuller case 2: 725eaa5fb4bSLandon J. Fuller bus_write_2(r, r_offset, value); 726caeff9a3SLandon J. Fuller break; 727caeff9a3SLandon J. Fuller case 4: 728eaa5fb4bSLandon J. Fuller bus_write_4(r, r_offset, value); 729caeff9a3SLandon J. Fuller break; 730caeff9a3SLandon J. Fuller default: 731caeff9a3SLandon J. Fuller panic("invalid width: %u", width); 732caeff9a3SLandon J. Fuller } 733caeff9a3SLandon J. Fuller } 734caeff9a3SLandon J. Fuller 735caeff9a3SLandon J. Fuller /** 736caeff9a3SLandon J. Fuller * Read a 1, 2, or 4 byte data item from the PCI core's registers 737caeff9a3SLandon J. Fuller * at @p offset. 738caeff9a3SLandon J. Fuller * 739caeff9a3SLandon J. Fuller * @param sc bhndb PCI driver state. 740caeff9a3SLandon J. Fuller * @param offset register read offset. 741caeff9a3SLandon J. Fuller * @param width item width (1, 2, or 4 bytes). 742caeff9a3SLandon J. Fuller */ 743caeff9a3SLandon J. Fuller static uint32_t 744caeff9a3SLandon J. Fuller bhndb_pci_read_core(struct bhndb_pci_softc *sc, bus_size_t offset, u_int width) 745caeff9a3SLandon J. Fuller { 746caeff9a3SLandon J. Fuller struct resource *r; 747caeff9a3SLandon J. Fuller bus_size_t r_offset; 748caeff9a3SLandon J. Fuller int error; 749caeff9a3SLandon J. Fuller 750eaa5fb4bSLandon J. Fuller error = bhndb_pci_get_core_regs(sc, offset, width, &r, &r_offset); 751eaa5fb4bSLandon J. Fuller if (error) { 752eaa5fb4bSLandon J. Fuller panic("no PCI register window mapping %#jx+%#x: %d", 753eaa5fb4bSLandon J. Fuller (uintmax_t)offset, width, error); 754eaa5fb4bSLandon J. Fuller } 755caeff9a3SLandon J. Fuller 756caeff9a3SLandon J. Fuller switch (width) { 757caeff9a3SLandon J. Fuller case 1: 758eaa5fb4bSLandon J. Fuller return (bus_read_1(r, r_offset)); 759caeff9a3SLandon J. Fuller case 2: 760eaa5fb4bSLandon J. Fuller return (bus_read_2(r, r_offset)); 761caeff9a3SLandon J. Fuller case 4: 762eaa5fb4bSLandon J. Fuller return (bus_read_4(r, r_offset)); 763caeff9a3SLandon J. Fuller default: 764caeff9a3SLandon J. Fuller panic("invalid width: %u", width); 765caeff9a3SLandon J. Fuller } 766caeff9a3SLandon J. Fuller } 767caeff9a3SLandon J. Fuller 76884d6a5d4SLandon J. Fuller /** 76984d6a5d4SLandon J. Fuller * Fix-up power on defaults for SPROM-less devices. 7704ad7e9b0SAdrian Chadd * 77184d6a5d4SLandon J. Fuller * On SPROM-less devices, the PCI(e) cores will be initialized with their their 77284d6a5d4SLandon J. Fuller * Power-on-Reset defaults; this can leave the BHND_PCI_SRSH_PI value pointing 77384d6a5d4SLandon J. Fuller * to the wrong backplane address. This value is used by the PCI core when 77484d6a5d4SLandon J. Fuller * performing address translation between static register windows in BAR0 that 77584d6a5d4SLandon J. Fuller * map the PCI core's register block, and backplane address space. 7764ad7e9b0SAdrian Chadd * 77784d6a5d4SLandon J. Fuller * When translating accesses via these BAR0 regions, the PCI bridge determines 77884d6a5d4SLandon J. Fuller * the base address of the PCI core by concatenating: 77984d6a5d4SLandon J. Fuller * 78084d6a5d4SLandon J. Fuller * [bits] [source] 78184d6a5d4SLandon J. Fuller * 31:16 bits [31:16] of the enumeration space address (e.g. 0x18000000) 78284d6a5d4SLandon J. Fuller * 15:12 value of BHND_PCI_SRSH_PI from the PCI core's SPROM shadow 78384d6a5d4SLandon J. Fuller * 11:0 bits [11:0] of the PCI bus address 78484d6a5d4SLandon J. Fuller * 78584d6a5d4SLandon J. Fuller * For example, on a PCI_V0 device, the following PCI core register offsets are 78684d6a5d4SLandon J. Fuller * mapped into BAR0: 78784d6a5d4SLandon J. Fuller * 78884d6a5d4SLandon J. Fuller * [BAR0 offset] [description] [PCI core offset] 78984d6a5d4SLandon J. Fuller * 0x1000-0x17FF sprom shadow 0x800-0xFFF 79084d6a5d4SLandon J. Fuller * 0x1800-0x1DFF device registers 0x000-0x5FF 79184d6a5d4SLandon J. Fuller * 0x1E00+0x1FFF siba config registers 0xE00-0xFFF 79284d6a5d4SLandon J. Fuller * 79384d6a5d4SLandon J. Fuller * This function checks -- and if necessary, corrects -- the BHND_PCI_SRSH_PI 79484d6a5d4SLandon J. Fuller * value in the SPROM shadow. 79584d6a5d4SLandon J. Fuller * 79684d6a5d4SLandon J. Fuller * This workaround must applied prior to accessing any static register windows 79784d6a5d4SLandon J. Fuller * that map the PCI core. 79884d6a5d4SLandon J. Fuller * 79984d6a5d4SLandon J. Fuller * Applies to all PCI and PCIe-G1 core revisions. 8004ad7e9b0SAdrian Chadd */ 80184d6a5d4SLandon J. Fuller static int 80284d6a5d4SLandon J. Fuller bhndb_pci_srsh_pi_war(struct bhndb_pci_softc *sc, 80384d6a5d4SLandon J. Fuller struct bhndb_pci_probe *probe) 8044ad7e9b0SAdrian Chadd { 80584d6a5d4SLandon J. Fuller struct bhnd_core_match md; 80684d6a5d4SLandon J. Fuller bhnd_addr_t pci_addr; 80784d6a5d4SLandon J. Fuller bhnd_size_t pci_size; 808caeff9a3SLandon J. Fuller bus_size_t srsh_offset; 80984d6a5d4SLandon J. Fuller uint16_t srsh_val, pci_val; 8104ad7e9b0SAdrian Chadd uint16_t val; 81184d6a5d4SLandon J. Fuller int error; 8124ad7e9b0SAdrian Chadd 813caeff9a3SLandon J. Fuller if ((sc->pci_quirks & BHNDB_PCI_QUIRK_SRSH_WAR) == 0) 81484d6a5d4SLandon J. Fuller return (0); 815bb64eeccSAdrian Chadd 81684d6a5d4SLandon J. Fuller /* Use an equality match descriptor to look up our PCI core's base 81784d6a5d4SLandon J. Fuller * address in the EROM */ 81884d6a5d4SLandon J. Fuller md = bhnd_core_get_match_desc(&probe->hostb_core); 81984d6a5d4SLandon J. Fuller error = bhnd_erom_lookup_core_addr(probe->erom, &md, BHND_PORT_DEVICE, 82084d6a5d4SLandon J. Fuller 0, 0, NULL, &pci_addr, &pci_size); 82184d6a5d4SLandon J. Fuller if (error) { 82284d6a5d4SLandon J. Fuller device_printf(sc->dev, "no base address found for the PCI host " 82384d6a5d4SLandon J. Fuller "bridge core: %d\n", error); 82484d6a5d4SLandon J. Fuller return (error); 8254ad7e9b0SAdrian Chadd } 82684d6a5d4SLandon J. Fuller 82784d6a5d4SLandon J. Fuller /* Fetch the SPROM SRSH_PI value */ 82884d6a5d4SLandon J. Fuller srsh_offset = BHND_PCI_SPROM_SHADOW + BHND_PCI_SRSH_PI_OFFSET; 82984d6a5d4SLandon J. Fuller val = bhndb_pci_probe_read(probe, pci_addr, srsh_offset, sizeof(val)); 83084d6a5d4SLandon J. Fuller srsh_val = (val & BHND_PCI_SRSH_PI_MASK) >> BHND_PCI_SRSH_PI_SHIFT; 83184d6a5d4SLandon J. Fuller 83284d6a5d4SLandon J. Fuller /* If it doesn't match PCI core's base address, update the SPROM 83384d6a5d4SLandon J. Fuller * shadow */ 83484d6a5d4SLandon J. Fuller pci_val = (pci_addr & BHND_PCI_SRSH_PI_ADDR_MASK) >> 83584d6a5d4SLandon J. Fuller BHND_PCI_SRSH_PI_ADDR_SHIFT; 83684d6a5d4SLandon J. Fuller if (srsh_val != pci_val) { 83784d6a5d4SLandon J. Fuller val &= ~BHND_PCI_SRSH_PI_MASK; 83884d6a5d4SLandon J. Fuller val |= (pci_val << BHND_PCI_SRSH_PI_SHIFT); 83984d6a5d4SLandon J. Fuller bhndb_pci_probe_write(probe, pci_addr, srsh_offset, val, 84084d6a5d4SLandon J. Fuller sizeof(val)); 84184d6a5d4SLandon J. Fuller } 84284d6a5d4SLandon J. Fuller 84384d6a5d4SLandon J. Fuller return (0); 8444ad7e9b0SAdrian Chadd } 8454ad7e9b0SAdrian Chadd 8464ad7e9b0SAdrian Chadd static int 8474ad7e9b0SAdrian Chadd bhndb_pci_resume(device_t dev) 8484ad7e9b0SAdrian Chadd { 8494ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc; 8504ad7e9b0SAdrian Chadd int error; 8514ad7e9b0SAdrian Chadd 8524ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 8534ad7e9b0SAdrian Chadd 854bb64eeccSAdrian Chadd /* Enable clocks (if supported by this hardware) */ 85589294a78SLandon J. Fuller if ((error = bhndb_enable_pci_clocks(sc->dev))) 8564ad7e9b0SAdrian Chadd return (error); 8574ad7e9b0SAdrian Chadd 858bb64eeccSAdrian Chadd /* Perform resume */ 859bb64eeccSAdrian Chadd return (bhndb_generic_resume(dev)); 860bb64eeccSAdrian Chadd } 861bb64eeccSAdrian Chadd 862bb64eeccSAdrian Chadd static int 863bb64eeccSAdrian Chadd bhndb_pci_suspend(device_t dev) 864bb64eeccSAdrian Chadd { 865bb64eeccSAdrian Chadd struct bhndb_pci_softc *sc; 866bb64eeccSAdrian Chadd int error; 867bb64eeccSAdrian Chadd 868bb64eeccSAdrian Chadd sc = device_get_softc(dev); 869bb64eeccSAdrian Chadd 870bb64eeccSAdrian Chadd /* Disable clocks (if supported by this hardware) */ 87189294a78SLandon J. Fuller if ((error = bhndb_disable_pci_clocks(sc->dev))) 8724ad7e9b0SAdrian Chadd return (error); 8734ad7e9b0SAdrian Chadd 874bb64eeccSAdrian Chadd /* Perform suspend */ 875bb64eeccSAdrian Chadd return (bhndb_generic_suspend(dev)); 876bb64eeccSAdrian Chadd } 877bb64eeccSAdrian Chadd 878bb64eeccSAdrian Chadd static int 8794ad7e9b0SAdrian Chadd bhndb_pci_set_window_addr(device_t dev, const struct bhndb_regwin *rw, 8804ad7e9b0SAdrian Chadd bhnd_addr_t addr) 8814ad7e9b0SAdrian Chadd { 8824ad7e9b0SAdrian Chadd struct bhndb_pci_softc *sc = device_get_softc(dev); 88389294a78SLandon J. Fuller return (sc->set_regwin(sc->dev, sc->parent, rw, addr)); 8844ad7e9b0SAdrian Chadd } 8854ad7e9b0SAdrian Chadd 8864ad7e9b0SAdrian Chadd /** 8874ad7e9b0SAdrian Chadd * A siba(4) and bcma(4)-compatible bhndb_set_window_addr implementation. 8884ad7e9b0SAdrian Chadd * 8894ad7e9b0SAdrian Chadd * On siba(4) devices, it's possible that writing a PCI window register may 8904ad7e9b0SAdrian Chadd * not succeed; it's necessary to immediately read the configuration register 8914ad7e9b0SAdrian Chadd * and retry if not set to the desired value. 8924ad7e9b0SAdrian Chadd * 8934ad7e9b0SAdrian Chadd * This is not necessary on bcma(4) devices, but other than the overhead of 8944ad7e9b0SAdrian Chadd * validating the register, there's no harm in performing the verification. 8954ad7e9b0SAdrian Chadd */ 8964ad7e9b0SAdrian Chadd static int 89789294a78SLandon J. Fuller bhndb_pci_compat_setregwin(device_t dev, device_t pci_dev, 8984ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 8994ad7e9b0SAdrian Chadd { 9004ad7e9b0SAdrian Chadd int error; 901e83ce340SAdrian Chadd int reg; 9024ad7e9b0SAdrian Chadd 9034ad7e9b0SAdrian Chadd if (rw->win_type != BHNDB_REGWIN_T_DYN) 9044ad7e9b0SAdrian Chadd return (ENODEV); 9054ad7e9b0SAdrian Chadd 906e83ce340SAdrian Chadd reg = rw->d.dyn.cfg_offset; 9074ad7e9b0SAdrian Chadd for (u_int i = 0; i < BHNDB_PCI_BARCTRL_WRITE_RETRY; i++) { 90889294a78SLandon J. Fuller if ((error = bhndb_pci_fast_setregwin(dev, pci_dev, rw, addr))) 9094ad7e9b0SAdrian Chadd return (error); 9104ad7e9b0SAdrian Chadd 91189294a78SLandon J. Fuller if (pci_read_config(pci_dev, reg, 4) == addr) 9124ad7e9b0SAdrian Chadd return (0); 9134ad7e9b0SAdrian Chadd 9144ad7e9b0SAdrian Chadd DELAY(10); 9154ad7e9b0SAdrian Chadd } 9164ad7e9b0SAdrian Chadd 9174ad7e9b0SAdrian Chadd /* Unable to set window */ 9184ad7e9b0SAdrian Chadd return (ENODEV); 9194ad7e9b0SAdrian Chadd } 9204ad7e9b0SAdrian Chadd 9214ad7e9b0SAdrian Chadd /** 9224ad7e9b0SAdrian Chadd * A bcma(4)-only bhndb_set_window_addr implementation. 9234ad7e9b0SAdrian Chadd */ 9244ad7e9b0SAdrian Chadd static int 92589294a78SLandon J. Fuller bhndb_pci_fast_setregwin(device_t dev, device_t pci_dev, 9264ad7e9b0SAdrian Chadd const struct bhndb_regwin *rw, bhnd_addr_t addr) 9274ad7e9b0SAdrian Chadd { 9284ad7e9b0SAdrian Chadd /* The PCI bridge core only supports 32-bit addressing, regardless 9294ad7e9b0SAdrian Chadd * of the bus' support for 64-bit addressing */ 9304ad7e9b0SAdrian Chadd if (addr > UINT32_MAX) 9314ad7e9b0SAdrian Chadd return (ERANGE); 9324ad7e9b0SAdrian Chadd 9334ad7e9b0SAdrian Chadd switch (rw->win_type) { 9344ad7e9b0SAdrian Chadd case BHNDB_REGWIN_T_DYN: 9354ad7e9b0SAdrian Chadd /* Addresses must be page aligned */ 9364ad7e9b0SAdrian Chadd if (addr % rw->win_size != 0) 9374ad7e9b0SAdrian Chadd return (EINVAL); 9384ad7e9b0SAdrian Chadd 93989294a78SLandon J. Fuller pci_write_config(pci_dev, rw->d.dyn.cfg_offset, addr, 4); 9404ad7e9b0SAdrian Chadd break; 9414ad7e9b0SAdrian Chadd default: 9424ad7e9b0SAdrian Chadd return (ENODEV); 9434ad7e9b0SAdrian Chadd } 9444ad7e9b0SAdrian Chadd 9454ad7e9b0SAdrian Chadd return (0); 9464ad7e9b0SAdrian Chadd } 9474ad7e9b0SAdrian Chadd 948d567592bSAdrian Chadd static int 949d567592bSAdrian Chadd bhndb_pci_populate_board_info(device_t dev, device_t child, 950d567592bSAdrian Chadd struct bhnd_board_info *info) 951d567592bSAdrian Chadd { 952d567592bSAdrian Chadd struct bhndb_pci_softc *sc; 953d567592bSAdrian Chadd 954d567592bSAdrian Chadd sc = device_get_softc(dev); 955d567592bSAdrian Chadd 9568ef24a0dSAdrian Chadd /* 9578ef24a0dSAdrian Chadd * On a subset of Apple BCM4360 modules, always prefer the 9588ef24a0dSAdrian Chadd * PCI subdevice to the SPROM-supplied boardtype. 9598ef24a0dSAdrian Chadd * 9608ef24a0dSAdrian Chadd * TODO: 9618ef24a0dSAdrian Chadd * 9628ef24a0dSAdrian Chadd * Broadcom's own drivers implement this override, and then later use 9638ef24a0dSAdrian Chadd * the remapped BCM4360 board type to determine the required 9648ef24a0dSAdrian Chadd * board-specific workarounds. 9658ef24a0dSAdrian Chadd * 9668ef24a0dSAdrian Chadd * Without access to this hardware, it's unclear why this mapping 9678ef24a0dSAdrian Chadd * is done, and we must do the same. If we can survey the hardware 9688ef24a0dSAdrian Chadd * in question, it may be possible to replace this behavior with 9698ef24a0dSAdrian Chadd * explicit references to the SPROM-supplied boardtype(s) in our 9708ef24a0dSAdrian Chadd * quirk definitions. 9718ef24a0dSAdrian Chadd */ 9728ef24a0dSAdrian Chadd if (pci_get_subvendor(sc->parent) == PCI_VENDOR_APPLE) { 9738ef24a0dSAdrian Chadd switch (info->board_type) { 9748ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X29C: 9758ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X29CP2: 9768ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X51: 9778ef24a0dSAdrian Chadd case BHND_BOARD_BCM94360X51P2: 9788ef24a0dSAdrian Chadd info->board_type = 0; /* allow override below */ 9798ef24a0dSAdrian Chadd break; 9808ef24a0dSAdrian Chadd default: 9818ef24a0dSAdrian Chadd break; 9828ef24a0dSAdrian Chadd } 9838ef24a0dSAdrian Chadd } 9848ef24a0dSAdrian Chadd 985566ca880SLandon J. Fuller /* If NVRAM did not supply vendor/type/devid info, provide the PCI 986566ca880SLandon J. Fuller * subvendor/subdevice/device values. */ 987d567592bSAdrian Chadd if (info->board_vendor == 0) 988d567592bSAdrian Chadd info->board_vendor = pci_get_subvendor(sc->parent); 989d567592bSAdrian Chadd 990d567592bSAdrian Chadd if (info->board_type == 0) 991d567592bSAdrian Chadd info->board_type = pci_get_subdevice(sc->parent); 992d567592bSAdrian Chadd 993566ca880SLandon J. Fuller if (info->board_devid == 0) 994566ca880SLandon J. Fuller info->board_devid = pci_get_device(sc->parent); 995566ca880SLandon J. Fuller 996d567592bSAdrian Chadd return (0); 997d567592bSAdrian Chadd } 998d567592bSAdrian Chadd 9994ad7e9b0SAdrian Chadd /** 1000caeff9a3SLandon J. Fuller * Examine the bridge device @p dev and return the expected host bridge 1001caeff9a3SLandon J. Fuller * device class. 1002caeff9a3SLandon J. Fuller * 1003caeff9a3SLandon J. Fuller * @param dev The bhndb bridge device 1004caeff9a3SLandon J. Fuller */ 1005caeff9a3SLandon J. Fuller static bhnd_devclass_t 1006caeff9a3SLandon J. Fuller bhndb_expected_pci_devclass(device_t dev) 1007caeff9a3SLandon J. Fuller { 1008caeff9a3SLandon J. Fuller if (bhndb_is_pcie_attached(dev)) 1009caeff9a3SLandon J. Fuller return (BHND_DEVCLASS_PCIE); 1010caeff9a3SLandon J. Fuller else 1011caeff9a3SLandon J. Fuller return (BHND_DEVCLASS_PCI); 1012caeff9a3SLandon J. Fuller } 1013caeff9a3SLandon J. Fuller 1014caeff9a3SLandon J. Fuller /** 1015caeff9a3SLandon J. Fuller * Return true if the bridge device @p dev is attached via PCIe, 101689294a78SLandon J. Fuller * false otherwise. 101789294a78SLandon J. Fuller * 101889294a78SLandon J. Fuller * @param dev The bhndb bridge device 101989294a78SLandon J. Fuller */ 102089294a78SLandon J. Fuller static bool 102189294a78SLandon J. Fuller bhndb_is_pcie_attached(device_t dev) 102289294a78SLandon J. Fuller { 102389294a78SLandon J. Fuller int reg; 102489294a78SLandon J. Fuller 102589294a78SLandon J. Fuller if (pci_find_cap(device_get_parent(dev), PCIY_EXPRESS, ®) == 0) 102689294a78SLandon J. Fuller return (true); 102789294a78SLandon J. Fuller 102889294a78SLandon J. Fuller return (false); 102989294a78SLandon J. Fuller } 103089294a78SLandon J. Fuller 103189294a78SLandon J. Fuller /** 1032bb64eeccSAdrian Chadd * Enable externally managed clocks, if required. 10334ad7e9b0SAdrian Chadd * 1034bb64eeccSAdrian Chadd * Some PCI chipsets (BCM4306, possibly others) chips do not support 1035bb64eeccSAdrian Chadd * the idle low-power clock. Clocking must be bootstrapped at 1036bb64eeccSAdrian Chadd * attach/resume by directly adjusting GPIO registers exposed in the 1037bb64eeccSAdrian Chadd * PCI config space, and correspondingly, explicitly shutdown at 1038bb64eeccSAdrian Chadd * detach/suspend. 10394ad7e9b0SAdrian Chadd * 104089294a78SLandon J. Fuller * @note This function may be safely called prior to device attach, (e.g. 104189294a78SLandon J. Fuller * from DEVICE_PROBE). 104289294a78SLandon J. Fuller * 104389294a78SLandon J. Fuller * @param dev The bhndb bridge device 10444ad7e9b0SAdrian Chadd */ 10454ad7e9b0SAdrian Chadd static int 104689294a78SLandon J. Fuller bhndb_enable_pci_clocks(device_t dev) 10474ad7e9b0SAdrian Chadd { 104889294a78SLandon J. Fuller device_t pci_dev; 10494ad7e9b0SAdrian Chadd uint32_t gpio_in, gpio_out, gpio_en; 10504ad7e9b0SAdrian Chadd uint32_t gpio_flags; 10514ad7e9b0SAdrian Chadd uint16_t pci_status; 10524ad7e9b0SAdrian Chadd 105389294a78SLandon J. Fuller pci_dev = device_get_parent(dev); 105489294a78SLandon J. Fuller 1055bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 1056eaa5fb4bSLandon J. Fuller if (bhndb_is_pcie_attached(dev)) 1057bb64eeccSAdrian Chadd return (0); 10584ad7e9b0SAdrian Chadd 10594ad7e9b0SAdrian Chadd /* Read state of XTAL pin */ 106089294a78SLandon J. Fuller gpio_in = pci_read_config(pci_dev, BHNDB_PCI_GPIO_IN, 4); 10614ad7e9b0SAdrian Chadd if (gpio_in & BHNDB_PCI_GPIO_XTAL_ON) 10624ad7e9b0SAdrian Chadd return (0); /* already enabled */ 10634ad7e9b0SAdrian Chadd 10644ad7e9b0SAdrian Chadd /* Fetch current config */ 106589294a78SLandon J. Fuller gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4); 106689294a78SLandon J. Fuller gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4); 10674ad7e9b0SAdrian Chadd 10684ad7e9b0SAdrian Chadd /* Set PLL_OFF/XTAL_ON pins to HIGH and enable both pins */ 10694ad7e9b0SAdrian Chadd gpio_flags = (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 10704ad7e9b0SAdrian Chadd gpio_out |= gpio_flags; 10714ad7e9b0SAdrian Chadd gpio_en |= gpio_flags; 10724ad7e9b0SAdrian Chadd 107389294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 107489294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 10754ad7e9b0SAdrian Chadd DELAY(1000); 10764ad7e9b0SAdrian Chadd 10774ad7e9b0SAdrian Chadd /* Reset PLL_OFF */ 10784ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_PLL_OFF; 107989294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 10804ad7e9b0SAdrian Chadd DELAY(5000); 10814ad7e9b0SAdrian Chadd 10824ad7e9b0SAdrian Chadd /* Clear any PCI 'sent target-abort' flag. */ 108389294a78SLandon J. Fuller pci_status = pci_read_config(pci_dev, PCIR_STATUS, 2); 10844ad7e9b0SAdrian Chadd pci_status &= ~PCIM_STATUS_STABORT; 108589294a78SLandon J. Fuller pci_write_config(pci_dev, PCIR_STATUS, pci_status, 2); 10864ad7e9b0SAdrian Chadd 10874ad7e9b0SAdrian Chadd return (0); 10884ad7e9b0SAdrian Chadd } 10894ad7e9b0SAdrian Chadd 10904ad7e9b0SAdrian Chadd /** 1091bb64eeccSAdrian Chadd * Disable externally managed clocks, if required. 10924ad7e9b0SAdrian Chadd * 109389294a78SLandon J. Fuller * This function may be safely called prior to device attach, (e.g. 109489294a78SLandon J. Fuller * from DEVICE_PROBE). 109589294a78SLandon J. Fuller * 109689294a78SLandon J. Fuller * @param dev The bhndb bridge device 10974ad7e9b0SAdrian Chadd */ 10984ad7e9b0SAdrian Chadd static int 109989294a78SLandon J. Fuller bhndb_disable_pci_clocks(device_t dev) 11004ad7e9b0SAdrian Chadd { 110189294a78SLandon J. Fuller device_t pci_dev; 11024ad7e9b0SAdrian Chadd uint32_t gpio_out, gpio_en; 11034ad7e9b0SAdrian Chadd 110489294a78SLandon J. Fuller pci_dev = device_get_parent(dev); 110589294a78SLandon J. Fuller 1106bb64eeccSAdrian Chadd /* Only supported and required on PCI devices */ 110789294a78SLandon J. Fuller if (bhndb_is_pcie_attached(dev)) 1108bb64eeccSAdrian Chadd return (0); 11094ad7e9b0SAdrian Chadd 11104ad7e9b0SAdrian Chadd /* Fetch current config */ 111189294a78SLandon J. Fuller gpio_out = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUT, 4); 111289294a78SLandon J. Fuller gpio_en = pci_read_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, 4); 11134ad7e9b0SAdrian Chadd 11144ad7e9b0SAdrian Chadd /* Set PLL_OFF to HIGH, XTAL_ON to LOW. */ 11154ad7e9b0SAdrian Chadd gpio_out &= ~BHNDB_PCI_GPIO_XTAL_ON; 11164ad7e9b0SAdrian Chadd gpio_out |= BHNDB_PCI_GPIO_PLL_OFF; 111789294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUT, gpio_out, 4); 11184ad7e9b0SAdrian Chadd 11194ad7e9b0SAdrian Chadd /* Enable both output pins */ 11204ad7e9b0SAdrian Chadd gpio_en |= (BHNDB_PCI_GPIO_PLL_OFF|BHNDB_PCI_GPIO_XTAL_ON); 112189294a78SLandon J. Fuller pci_write_config(pci_dev, BHNDB_PCI_GPIO_OUTEN, gpio_en, 4); 11224ad7e9b0SAdrian Chadd 11234ad7e9b0SAdrian Chadd return (0); 11244ad7e9b0SAdrian Chadd } 11254ad7e9b0SAdrian Chadd 1126f90f4b65SLandon J. Fuller static bhnd_clksrc 1127f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_get_clksrc(device_t dev, device_t child, 1128f90f4b65SLandon J. Fuller bhnd_clock clock) 1129f90f4b65SLandon J. Fuller { 1130f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc; 1131f90f4b65SLandon J. Fuller uint32_t gpio_out; 1132f90f4b65SLandon J. Fuller 1133f90f4b65SLandon J. Fuller sc = device_get_softc(dev); 1134f90f4b65SLandon J. Fuller 1135f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 113689294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 11374e96bf3aSLandon J. Fuller return (BHND_CLKSRC_UNKNOWN); 1138f90f4b65SLandon J. Fuller 1139f90f4b65SLandon J. Fuller /* Only ILP is supported */ 1140f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_ILP) 11414e96bf3aSLandon J. Fuller return (BHND_CLKSRC_UNKNOWN); 1142f90f4b65SLandon J. Fuller 1143f90f4b65SLandon J. Fuller gpio_out = pci_read_config(sc->parent, BHNDB_PCI_GPIO_OUT, 4); 1144f90f4b65SLandon J. Fuller if (gpio_out & BHNDB_PCI_GPIO_SCS) 1145f90f4b65SLandon J. Fuller return (BHND_CLKSRC_PCI); 1146f90f4b65SLandon J. Fuller else 1147f90f4b65SLandon J. Fuller return (BHND_CLKSRC_XTAL); 1148f90f4b65SLandon J. Fuller } 1149f90f4b65SLandon J. Fuller 1150f90f4b65SLandon J. Fuller static int 1151f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_gate_clock(device_t dev, device_t child, 1152f90f4b65SLandon J. Fuller bhnd_clock clock) 1153f90f4b65SLandon J. Fuller { 1154f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 1155f90f4b65SLandon J. Fuller 1156f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 115789294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 1158f90f4b65SLandon J. Fuller return (ENODEV); 1159f90f4b65SLandon J. Fuller 1160f90f4b65SLandon J. Fuller /* Only HT is supported */ 1161f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_HT) 1162f90f4b65SLandon J. Fuller return (ENXIO); 1163f90f4b65SLandon J. Fuller 116489294a78SLandon J. Fuller return (bhndb_disable_pci_clocks(sc->dev)); 1165f90f4b65SLandon J. Fuller } 1166f90f4b65SLandon J. Fuller 1167f90f4b65SLandon J. Fuller static int 1168f90f4b65SLandon J. Fuller bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child, 1169f90f4b65SLandon J. Fuller bhnd_clock clock) 1170f90f4b65SLandon J. Fuller { 1171f90f4b65SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 1172f90f4b65SLandon J. Fuller 1173f90f4b65SLandon J. Fuller /* Only supported on PCI devices */ 117489294a78SLandon J. Fuller if (bhndb_is_pcie_attached(sc->dev)) 1175f90f4b65SLandon J. Fuller return (ENODEV); 1176f90f4b65SLandon J. Fuller 1177f90f4b65SLandon J. Fuller /* Only HT is supported */ 1178f90f4b65SLandon J. Fuller if (clock != BHND_CLOCK_HT) 1179f90f4b65SLandon J. Fuller return (ENXIO); 1180f90f4b65SLandon J. Fuller 118189294a78SLandon J. Fuller return (bhndb_enable_pci_clocks(sc->dev)); 1182f90f4b65SLandon J. Fuller } 1183f90f4b65SLandon J. Fuller 1184caeff9a3SLandon J. Fuller /** 1185caeff9a3SLandon J. Fuller * BHNDB_MAP_INTR_ISRC() 1186caeff9a3SLandon J. Fuller */ 1187824b48efSLandon J. Fuller static int 1188caeff9a3SLandon J. Fuller bhndb_pci_map_intr_isrc(device_t dev, struct resource *irq, 1189caeff9a3SLandon J. Fuller struct bhndb_intr_isrc **isrc) 1190caeff9a3SLandon J. Fuller { 1191caeff9a3SLandon J. Fuller struct bhndb_pci_softc *sc = device_get_softc(dev); 1192caeff9a3SLandon J. Fuller 1193caeff9a3SLandon J. Fuller /* There's only one bridged interrupt to choose from */ 1194caeff9a3SLandon J. Fuller *isrc = sc->isrc; 1195caeff9a3SLandon J. Fuller return (0); 1196caeff9a3SLandon J. Fuller } 1197caeff9a3SLandon J. Fuller 1198caeff9a3SLandon J. Fuller /* siba-specific implementation of BHNDB_ROUTE_INTERRUPTS() */ 1199caeff9a3SLandon J. Fuller static int 1200caeff9a3SLandon J. Fuller bhndb_pci_route_siba_interrupts(struct bhndb_pci_softc *sc, device_t child) 1201caeff9a3SLandon J. Fuller { 1202caeff9a3SLandon J. Fuller uint32_t sbintvec; 1203caeff9a3SLandon J. Fuller u_int ivec; 1204caeff9a3SLandon J. Fuller int error; 1205caeff9a3SLandon J. Fuller 1206caeff9a3SLandon J. Fuller KASSERT(sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC, 1207caeff9a3SLandon J. Fuller ("route_siba_interrupts not supported by this hardware")); 1208caeff9a3SLandon J. Fuller 1209caeff9a3SLandon J. Fuller /* Fetch the sbflag# for the child */ 1210caeff9a3SLandon J. Fuller if ((error = bhnd_get_intr_ivec(child, 0, &ivec))) 1211caeff9a3SLandon J. Fuller return (error); 1212caeff9a3SLandon J. Fuller 1213caeff9a3SLandon J. Fuller if (ivec > (sizeof(sbintvec)*8) - 1 /* aka '31' */) { 1214caeff9a3SLandon J. Fuller /* This should never be an issue in practice */ 1215caeff9a3SLandon J. Fuller device_printf(sc->dev, "cannot route interrupts to high " 1216caeff9a3SLandon J. Fuller "sbflag# %u\n", ivec); 1217caeff9a3SLandon J. Fuller return (ENXIO); 1218caeff9a3SLandon J. Fuller } 1219caeff9a3SLandon J. Fuller 1220caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK(sc); 1221caeff9a3SLandon J. Fuller 1222caeff9a3SLandon J. Fuller sbintvec = bhndb_pci_read_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), 4); 1223caeff9a3SLandon J. Fuller sbintvec |= (1 << ivec); 1224caeff9a3SLandon J. Fuller bhndb_pci_write_core(sc, SB0_REG_ABS(SIBA_CFG0_INTVEC), sbintvec, 4); 1225caeff9a3SLandon J. Fuller 1226caeff9a3SLandon J. Fuller BHNDB_PCI_UNLOCK(sc); 1227caeff9a3SLandon J. Fuller 1228caeff9a3SLandon J. Fuller return (0); 1229caeff9a3SLandon J. Fuller } 1230caeff9a3SLandon J. Fuller 1231caeff9a3SLandon J. Fuller /* BHNDB_ROUTE_INTERRUPTS() */ 1232caeff9a3SLandon J. Fuller static int 1233caeff9a3SLandon J. Fuller bhndb_pci_route_interrupts(device_t dev, device_t child) 1234824b48efSLandon J. Fuller { 1235824b48efSLandon J. Fuller struct bhndb_pci_softc *sc; 1236caeff9a3SLandon J. Fuller struct bhnd_core_info core; 1237caeff9a3SLandon J. Fuller uint32_t core_bit; 1238caeff9a3SLandon J. Fuller uint32_t intmask; 1239824b48efSLandon J. Fuller 1240824b48efSLandon J. Fuller sc = device_get_softc(dev); 1241824b48efSLandon J. Fuller 1242caeff9a3SLandon J. Fuller if (sc->pci_quirks & BHNDB_PCI_QUIRK_SIBA_INTVEC) 1243caeff9a3SLandon J. Fuller return (bhndb_pci_route_siba_interrupts(sc, child)); 1244824b48efSLandon J. Fuller 1245caeff9a3SLandon J. Fuller core = bhnd_get_core_info(child); 1246caeff9a3SLandon J. Fuller if (core.core_idx > BHNDB_PCI_SBIM_COREIDX_MAX) { 1247caeff9a3SLandon J. Fuller /* This should never be an issue in practice */ 1248caeff9a3SLandon J. Fuller device_printf(dev, "cannot route interrupts to high core " 1249caeff9a3SLandon J. Fuller "index %u\n", core.core_idx); 1250caeff9a3SLandon J. Fuller return (ENXIO); 1251caeff9a3SLandon J. Fuller } 1252824b48efSLandon J. Fuller 1253caeff9a3SLandon J. Fuller BHNDB_PCI_LOCK(sc); 1254caeff9a3SLandon J. Fuller 1255caeff9a3SLandon J. Fuller core_bit = (1<<core.core_idx) << BHNDB_PCI_SBIM_SHIFT; 1256caeff9a3SLandon J. Fuller intmask = pci_read_config(sc->parent, BHNDB_PCI_INT_MASK, 4); 1257caeff9a3SLandon J. Fuller intmask |= core_bit; 1258caeff9a3SLandon J. Fuller pci_write_config(sc->parent, BHNDB_PCI_INT_MASK, intmask, 4); 1259caeff9a3SLandon J. Fuller 1260caeff9a3SLandon J. Fuller BHNDB_PCI_UNLOCK(sc); 1261caeff9a3SLandon J. Fuller 1262caeff9a3SLandon J. Fuller return (0); 1263824b48efSLandon J. Fuller } 1264824b48efSLandon J. Fuller 126589294a78SLandon J. Fuller /** 126684d6a5d4SLandon J. Fuller * Using the generic PCI bridge hardware configuration, allocate, initialize 126784d6a5d4SLandon J. Fuller * and return a new bhndb_pci probe state instance. 126889294a78SLandon J. Fuller * 126984d6a5d4SLandon J. Fuller * On success, the caller assumes ownership of the returned probe instance, and 127084d6a5d4SLandon J. Fuller * is responsible for releasing this reference using bhndb_pci_probe_free(). 127184d6a5d4SLandon J. Fuller * 127284d6a5d4SLandon J. Fuller * @param[out] probe On success, the newly allocated probe instance. 127384d6a5d4SLandon J. Fuller * @param dev The bhndb_pci bridge device. 127484d6a5d4SLandon J. Fuller * @param hostb_devclass The expected device class of the bridge core. 127584d6a5d4SLandon J. Fuller * 127684d6a5d4SLandon J. Fuller * @retval 0 success 127784d6a5d4SLandon J. Fuller * @retval non-zero if allocating the probe state fails, a regular 127884d6a5d4SLandon J. Fuller * unix error code will be returned. 127984d6a5d4SLandon J. Fuller * 128084d6a5d4SLandon J. Fuller * @note This function requires exclusive ownership over allocating and 128184d6a5d4SLandon J. Fuller * configuring host bridge resources, and should only be called prior to 128284d6a5d4SLandon J. Fuller * completion of device attach and full configuration of the bridge. 128389294a78SLandon J. Fuller */ 128489294a78SLandon J. Fuller static int 128584d6a5d4SLandon J. Fuller bhndb_pci_probe_alloc(struct bhndb_pci_probe **probe, device_t dev, 128684d6a5d4SLandon J. Fuller bhnd_devclass_t hostb_devclass) 128789294a78SLandon J. Fuller { 128884d6a5d4SLandon J. Fuller struct bhndb_pci_probe *p; 128984d6a5d4SLandon J. Fuller struct bhnd_erom_io *eio; 129084d6a5d4SLandon J. Fuller const struct bhndb_hwcfg *hwcfg; 129184d6a5d4SLandon J. Fuller const struct bhnd_chipid *hint; 129284d6a5d4SLandon J. Fuller device_t parent_dev; 129384d6a5d4SLandon J. Fuller int error; 129489294a78SLandon J. Fuller 129584d6a5d4SLandon J. Fuller parent_dev = device_get_parent(dev); 129684d6a5d4SLandon J. Fuller eio = NULL; 129784d6a5d4SLandon J. Fuller 129884d6a5d4SLandon J. Fuller p = malloc(sizeof(*p), M_BHND, M_ZERO|M_WAITOK); 129984d6a5d4SLandon J. Fuller p->dev = dev; 130084d6a5d4SLandon J. Fuller p->pci_dev = parent_dev; 130184d6a5d4SLandon J. Fuller 130284d6a5d4SLandon J. Fuller /* Our register window mapping state must be initialized at this point, 130384d6a5d4SLandon J. Fuller * as bhndb_pci_eio will begin making calls into 130484d6a5d4SLandon J. Fuller * bhndb_pci_probe_(read|write|get_mapping) */ 130584d6a5d4SLandon J. Fuller p->m_win = NULL; 130684d6a5d4SLandon J. Fuller p->m_res = NULL; 130784d6a5d4SLandon J. Fuller p->m_valid = false; 130884d6a5d4SLandon J. Fuller 130984d6a5d4SLandon J. Fuller bhndb_pci_eio_init(&p->erom_io, p); 131084d6a5d4SLandon J. Fuller eio = &p->erom_io.eio; 131184d6a5d4SLandon J. Fuller 131284d6a5d4SLandon J. Fuller /* Fetch our chipid hint (if any) and generic hardware configuration */ 131384d6a5d4SLandon J. Fuller hwcfg = BHNDB_BUS_GET_GENERIC_HWCFG(parent_dev, dev); 131484d6a5d4SLandon J. Fuller hint = BHNDB_BUS_GET_CHIPID(parent_dev, dev); 131584d6a5d4SLandon J. Fuller 131684d6a5d4SLandon J. Fuller /* Allocate our host resources */ 131784d6a5d4SLandon J. Fuller error = bhndb_alloc_host_resources(&p->hr, dev, parent_dev, hwcfg); 131884d6a5d4SLandon J. Fuller if (error) { 131984d6a5d4SLandon J. Fuller p->hr = NULL; 132084d6a5d4SLandon J. Fuller goto failed; 132184d6a5d4SLandon J. Fuller } 132284d6a5d4SLandon J. Fuller 132384d6a5d4SLandon J. Fuller /* Map the first bus core from our bridged bhnd(4) bus */ 132484d6a5d4SLandon J. Fuller error = bhnd_erom_io_map(eio, BHND_DEFAULT_CHIPC_ADDR, 132584d6a5d4SLandon J. Fuller BHND_DEFAULT_CORE_SIZE); 132684d6a5d4SLandon J. Fuller if (error) 132784d6a5d4SLandon J. Fuller goto failed; 132884d6a5d4SLandon J. Fuller 132984d6a5d4SLandon J. Fuller /* Probe for a usable EROM class, and read the chip identifier */ 133084d6a5d4SLandon J. Fuller p->erom_class = bhnd_erom_probe_driver_classes( 133184d6a5d4SLandon J. Fuller device_get_devclass(dev), eio, hint, &p->cid); 133284d6a5d4SLandon J. Fuller if (p->erom_class == NULL) { 133384d6a5d4SLandon J. Fuller device_printf(dev, "device enumeration unsupported; no " 133484d6a5d4SLandon J. Fuller "compatible driver found\n"); 133584d6a5d4SLandon J. Fuller 133684d6a5d4SLandon J. Fuller error = ENXIO; 133784d6a5d4SLandon J. Fuller goto failed; 133884d6a5d4SLandon J. Fuller } 133984d6a5d4SLandon J. Fuller 134084d6a5d4SLandon J. Fuller /* Allocate EROM parser */ 134184d6a5d4SLandon J. Fuller p->erom = bhnd_erom_alloc(p->erom_class, &p->cid, eio); 134284d6a5d4SLandon J. Fuller if (p->erom == NULL) { 134384d6a5d4SLandon J. Fuller device_printf(dev, "failed to allocate device enumeration " 134484d6a5d4SLandon J. Fuller "table parser\n"); 134584d6a5d4SLandon J. Fuller error = ENXIO; 134684d6a5d4SLandon J. Fuller goto failed; 134784d6a5d4SLandon J. Fuller } 134884d6a5d4SLandon J. Fuller 134984d6a5d4SLandon J. Fuller /* The EROM I/O instance is now owned by our EROM parser */ 135084d6a5d4SLandon J. Fuller eio = NULL; 135184d6a5d4SLandon J. Fuller 135284d6a5d4SLandon J. Fuller /* Read the full core table */ 135384d6a5d4SLandon J. Fuller error = bhnd_erom_get_core_table(p->erom, &p->cores, &p->ncores); 135484d6a5d4SLandon J. Fuller if (error) { 135584d6a5d4SLandon J. Fuller device_printf(p->dev, "error fetching core table: %d\n", 135684d6a5d4SLandon J. Fuller error); 135784d6a5d4SLandon J. Fuller 135884d6a5d4SLandon J. Fuller p->cores = NULL; 135984d6a5d4SLandon J. Fuller goto failed; 136084d6a5d4SLandon J. Fuller } 136184d6a5d4SLandon J. Fuller 136284d6a5d4SLandon J. Fuller /* Identify the host bridge core */ 136384d6a5d4SLandon J. Fuller error = bhndb_find_hostb_core(p->cores, p->ncores, hostb_devclass, 136484d6a5d4SLandon J. Fuller &p->hostb_core); 136584d6a5d4SLandon J. Fuller if (error) { 136684d6a5d4SLandon J. Fuller device_printf(dev, "failed to identify the host bridge " 136784d6a5d4SLandon J. Fuller "core: %d\n", error); 136884d6a5d4SLandon J. Fuller 136984d6a5d4SLandon J. Fuller goto failed; 137084d6a5d4SLandon J. Fuller } 137184d6a5d4SLandon J. Fuller 137284d6a5d4SLandon J. Fuller *probe = p; 137384d6a5d4SLandon J. Fuller return (0); 137484d6a5d4SLandon J. Fuller 137584d6a5d4SLandon J. Fuller failed: 137684d6a5d4SLandon J. Fuller if (eio != NULL) { 137784d6a5d4SLandon J. Fuller KASSERT(p->erom == NULL, ("I/O instance will be freed by " 137884d6a5d4SLandon J. Fuller "its owning parser")); 137984d6a5d4SLandon J. Fuller 138084d6a5d4SLandon J. Fuller bhnd_erom_io_fini(eio); 138184d6a5d4SLandon J. Fuller } 138284d6a5d4SLandon J. Fuller 138384d6a5d4SLandon J. Fuller if (p->erom != NULL) { 138484d6a5d4SLandon J. Fuller if (p->cores != NULL) 138584d6a5d4SLandon J. Fuller bhnd_erom_free_core_table(p->erom, p->cores); 138684d6a5d4SLandon J. Fuller 138784d6a5d4SLandon J. Fuller bhnd_erom_free(p->erom); 138884d6a5d4SLandon J. Fuller } else { 138984d6a5d4SLandon J. Fuller KASSERT(p->cores == NULL, ("cannot free erom-owned core table " 139084d6a5d4SLandon J. Fuller "without erom reference")); 139184d6a5d4SLandon J. Fuller } 139284d6a5d4SLandon J. Fuller 139384d6a5d4SLandon J. Fuller if (p->hr != NULL) 139484d6a5d4SLandon J. Fuller bhndb_release_host_resources(p->hr); 139584d6a5d4SLandon J. Fuller 139684d6a5d4SLandon J. Fuller free(p, M_BHND); 139784d6a5d4SLandon J. Fuller 139884d6a5d4SLandon J. Fuller return (error); 139984d6a5d4SLandon J. Fuller } 140084d6a5d4SLandon J. Fuller 140184d6a5d4SLandon J. Fuller /** 140284d6a5d4SLandon J. Fuller * Free the given @p probe instance and any associated host bridge resources. 140384d6a5d4SLandon J. Fuller */ 140484d6a5d4SLandon J. Fuller static void 140584d6a5d4SLandon J. Fuller bhndb_pci_probe_free(struct bhndb_pci_probe *probe) 140684d6a5d4SLandon J. Fuller { 140784d6a5d4SLandon J. Fuller bhnd_erom_free_core_table(probe->erom, probe->cores); 140884d6a5d4SLandon J. Fuller bhnd_erom_free(probe->erom); 140984d6a5d4SLandon J. Fuller bhndb_release_host_resources(probe->hr); 141084d6a5d4SLandon J. Fuller free(probe, M_BHND); 141184d6a5d4SLandon J. Fuller } 141284d6a5d4SLandon J. Fuller 141384d6a5d4SLandon J. Fuller /** 141484d6a5d4SLandon J. Fuller * Return a copy of probed core table from @p probe. 141584d6a5d4SLandon J. Fuller * 141684d6a5d4SLandon J. Fuller * @param probe The probe instance. 141784d6a5d4SLandon J. Fuller * @param[out] cores On success, a copy of the probed core table. The 141884d6a5d4SLandon J. Fuller * caller is responsible for freeing this table 141984d6a5d4SLandon J. Fuller * bhndb_pci_probe_free_core_table(). 142084d6a5d4SLandon J. Fuller * @param[out] ncores On success, the number of cores found in 142184d6a5d4SLandon J. Fuller * @p cores. 142284d6a5d4SLandon J. Fuller * 142384d6a5d4SLandon J. Fuller * @retval 0 success 142484d6a5d4SLandon J. Fuller * @retval non-zero if enumerating the bridged bhnd(4) bus fails, a regular 142584d6a5d4SLandon J. Fuller * unix error code will be returned. 142684d6a5d4SLandon J. Fuller */ 142784d6a5d4SLandon J. Fuller static int 142884d6a5d4SLandon J. Fuller bhndb_pci_probe_copy_core_table(struct bhndb_pci_probe *probe, 142984d6a5d4SLandon J. Fuller struct bhnd_core_info **cores, u_int *ncores) 143084d6a5d4SLandon J. Fuller { 143184d6a5d4SLandon J. Fuller size_t len = sizeof(**cores) * probe->ncores; 143284d6a5d4SLandon J. Fuller 143384d6a5d4SLandon J. Fuller *cores = malloc(len, M_BHND, M_WAITOK); 143484d6a5d4SLandon J. Fuller memcpy(*cores, probe->cores, len); 143584d6a5d4SLandon J. Fuller 143684d6a5d4SLandon J. Fuller *ncores = probe->ncores; 143789294a78SLandon J. Fuller 143889294a78SLandon J. Fuller return (0); 143989294a78SLandon J. Fuller } 144089294a78SLandon J. Fuller 144189294a78SLandon J. Fuller /** 144284d6a5d4SLandon J. Fuller * Free a core table previously returned by bhndb_pci_probe_copy_core_table(). 144389294a78SLandon J. Fuller * 144484d6a5d4SLandon J. Fuller * @param cores The core table to be freed. 144589294a78SLandon J. Fuller */ 144684d6a5d4SLandon J. Fuller static void 144784d6a5d4SLandon J. Fuller bhndb_pci_probe_free_core_table(struct bhnd_core_info *cores) 144884d6a5d4SLandon J. Fuller { 144984d6a5d4SLandon J. Fuller free(cores, M_BHND); 145084d6a5d4SLandon J. Fuller } 145184d6a5d4SLandon J. Fuller 145284d6a5d4SLandon J. Fuller /** 145384d6a5d4SLandon J. Fuller * Return true if @p addr and @p size are mapped by the dynamic register window 145484d6a5d4SLandon J. Fuller * backing @p probe. 145584d6a5d4SLandon J. Fuller */ 145684d6a5d4SLandon J. Fuller static bool 145784d6a5d4SLandon J. Fuller bhndb_pci_probe_has_mapping(struct bhndb_pci_probe *probe, bhnd_addr_t addr, 145889294a78SLandon J. Fuller bhnd_size_t size) 145989294a78SLandon J. Fuller { 146084d6a5d4SLandon J. Fuller if (!probe->m_valid) 146184d6a5d4SLandon J. Fuller return (false); 146284d6a5d4SLandon J. Fuller 146384d6a5d4SLandon J. Fuller KASSERT(probe->m_win != NULL, ("missing register window")); 146484d6a5d4SLandon J. Fuller KASSERT(probe->m_res != NULL, ("missing regwin resource")); 146584d6a5d4SLandon J. Fuller KASSERT(probe->m_win->win_type == BHNDB_REGWIN_T_DYN, 146684d6a5d4SLandon J. Fuller ("unexpected window type %d", probe->m_win->win_type)); 146784d6a5d4SLandon J. Fuller 146884d6a5d4SLandon J. Fuller if (addr < probe->m_target) 146984d6a5d4SLandon J. Fuller return (false); 147084d6a5d4SLandon J. Fuller 147184d6a5d4SLandon J. Fuller if (addr >= probe->m_target + probe->m_win->win_size) 147284d6a5d4SLandon J. Fuller return (false); 147384d6a5d4SLandon J. Fuller 147484d6a5d4SLandon J. Fuller if ((probe->m_target + probe->m_win->win_size) - addr < size) 147584d6a5d4SLandon J. Fuller return (false); 147684d6a5d4SLandon J. Fuller 147784d6a5d4SLandon J. Fuller return (true); 147884d6a5d4SLandon J. Fuller } 147984d6a5d4SLandon J. Fuller 148084d6a5d4SLandon J. Fuller /** 148184d6a5d4SLandon J. Fuller * Attempt to adjust the dynamic register window backing @p probe to permit 148284d6a5d4SLandon J. Fuller * accessing @p size bytes at @p addr. 148384d6a5d4SLandon J. Fuller * 148484d6a5d4SLandon J. Fuller * @param probe The bhndb_pci probe state to be modified. 148584d6a5d4SLandon J. Fuller * @param addr The address at which @p size bytes will mapped. 148684d6a5d4SLandon J. Fuller * @param size The number of bytes to be mapped. 148784d6a5d4SLandon J. Fuller * @param[out] res On success, will be set to the host resource 148884d6a5d4SLandon J. Fuller * mapping @p size bytes at @p addr. 148984d6a5d4SLandon J. Fuller * @param[out] res_offset On success, will be set to the offset of @addr 149084d6a5d4SLandon J. Fuller * within @p res. 149184d6a5d4SLandon J. Fuller * 149284d6a5d4SLandon J. Fuller * @retval 0 success 149384d6a5d4SLandon J. Fuller * @retval non-zero if an error occurs adjusting the backing dynamic 149484d6a5d4SLandon J. Fuller * register window. 149584d6a5d4SLandon J. Fuller */ 149684d6a5d4SLandon J. Fuller static int 149784d6a5d4SLandon J. Fuller bhndb_pci_probe_map(struct bhndb_pci_probe *probe, bhnd_addr_t addr, 149884d6a5d4SLandon J. Fuller bhnd_size_t offset, bhnd_size_t size, struct resource **res, 149984d6a5d4SLandon J. Fuller bus_size_t *res_offset) 150084d6a5d4SLandon J. Fuller { 150184d6a5d4SLandon J. Fuller const struct bhndb_regwin *regwin, *regwin_table; 150284d6a5d4SLandon J. Fuller struct resource *regwin_res; 150389294a78SLandon J. Fuller bhnd_addr_t target; 150489294a78SLandon J. Fuller int error; 150589294a78SLandon J. Fuller 150684d6a5d4SLandon J. Fuller /* Determine the absolute address */ 150784d6a5d4SLandon J. Fuller if (BHND_SIZE_MAX - offset < addr) { 150884d6a5d4SLandon J. Fuller device_printf(probe->dev, "invalid offset %#jx+%#jx\n", addr, 150984d6a5d4SLandon J. Fuller offset); 151089294a78SLandon J. Fuller return (ENXIO); 151189294a78SLandon J. Fuller } 151289294a78SLandon J. Fuller 151384d6a5d4SLandon J. Fuller addr += offset; 151484d6a5d4SLandon J. Fuller 151584d6a5d4SLandon J. Fuller /* Can we use the existing mapping? */ 151684d6a5d4SLandon J. Fuller if (bhndb_pci_probe_has_mapping(probe, addr, size)) { 151784d6a5d4SLandon J. Fuller *res = probe->m_res; 151884d6a5d4SLandon J. Fuller *res_offset = (addr - probe->m_target) + 151984d6a5d4SLandon J. Fuller probe->m_win->win_offset; 152084d6a5d4SLandon J. Fuller 152189294a78SLandon J. Fuller return (0); 152289294a78SLandon J. Fuller } 152389294a78SLandon J. Fuller 152484d6a5d4SLandon J. Fuller /* Locate a useable dynamic register window */ 152584d6a5d4SLandon J. Fuller regwin_table = probe->hr->cfg->register_windows; 152684d6a5d4SLandon J. Fuller regwin = bhndb_regwin_find_type(regwin_table, 152784d6a5d4SLandon J. Fuller BHNDB_REGWIN_T_DYN, size); 152884d6a5d4SLandon J. Fuller if (regwin == NULL) { 152984d6a5d4SLandon J. Fuller device_printf(probe->dev, "unable to map %#jx+%#jx; no " 153084d6a5d4SLandon J. Fuller "usable dynamic register window found\n", addr, 153184d6a5d4SLandon J. Fuller size); 153284d6a5d4SLandon J. Fuller return (ENXIO); 153384d6a5d4SLandon J. Fuller } 153484d6a5d4SLandon J. Fuller 153584d6a5d4SLandon J. Fuller /* Locate the host resource mapping our register window */ 153684d6a5d4SLandon J. Fuller regwin_res = bhndb_host_resource_for_regwin(probe->hr, regwin); 153784d6a5d4SLandon J. Fuller if (regwin_res == NULL) { 153884d6a5d4SLandon J. Fuller device_printf(probe->dev, "unable to map %#jx+%#jx; no " 153984d6a5d4SLandon J. Fuller "usable register resource found\n", addr, size); 154084d6a5d4SLandon J. Fuller return (ENXIO); 154184d6a5d4SLandon J. Fuller } 154284d6a5d4SLandon J. Fuller 154389294a78SLandon J. Fuller /* Page-align the target address */ 154484d6a5d4SLandon J. Fuller target = addr - (addr % regwin->win_size); 154589294a78SLandon J. Fuller 154689294a78SLandon J. Fuller /* Configure the register window */ 154784d6a5d4SLandon J. Fuller error = bhndb_pci_compat_setregwin(probe->dev, probe->pci_dev, 154884d6a5d4SLandon J. Fuller regwin, target); 154989294a78SLandon J. Fuller if (error) { 155084d6a5d4SLandon J. Fuller device_printf(probe->dev, "failed to configure dynamic " 155184d6a5d4SLandon J. Fuller "register window: %d\n", error); 155289294a78SLandon J. Fuller return (error); 155389294a78SLandon J. Fuller } 155489294a78SLandon J. Fuller 155584d6a5d4SLandon J. Fuller /* Update our mapping state */ 155684d6a5d4SLandon J. Fuller probe->m_win = regwin; 155784d6a5d4SLandon J. Fuller probe->m_res = regwin_res; 155884d6a5d4SLandon J. Fuller probe->m_addr = addr; 155984d6a5d4SLandon J. Fuller probe->m_size = size; 156084d6a5d4SLandon J. Fuller probe->m_target = target; 156184d6a5d4SLandon J. Fuller probe->m_valid = true; 156284d6a5d4SLandon J. Fuller 156384d6a5d4SLandon J. Fuller *res = regwin_res; 156484d6a5d4SLandon J. Fuller *res_offset = (addr - target) + regwin->win_offset; 156584d6a5d4SLandon J. Fuller 156689294a78SLandon J. Fuller return (0); 156789294a78SLandon J. Fuller } 156889294a78SLandon J. Fuller 156984d6a5d4SLandon J. Fuller /** 157084d6a5d4SLandon J. Fuller * Write a data item to the bridged address space at the given @p offset from 157184d6a5d4SLandon J. Fuller * @p addr. 157284d6a5d4SLandon J. Fuller * 157384d6a5d4SLandon J. Fuller * A dynamic register window will be used to map @p addr. 157484d6a5d4SLandon J. Fuller * 157584d6a5d4SLandon J. Fuller * @param probe The bhndb_pci probe state to be used to perform the 157684d6a5d4SLandon J. Fuller * write. 157784d6a5d4SLandon J. Fuller * @param addr The base address. 157884d6a5d4SLandon J. Fuller * @param offset The offset from @p addr at which @p value will be 157984d6a5d4SLandon J. Fuller * written. 158084d6a5d4SLandon J. Fuller * @param value The data item to be written. 158184d6a5d4SLandon J. Fuller * @param width The data item width (1, 2, or 4 bytes). 158284d6a5d4SLandon J. Fuller */ 158384d6a5d4SLandon J. Fuller static void 158484d6a5d4SLandon J. Fuller bhndb_pci_probe_write(struct bhndb_pci_probe *probe, bhnd_addr_t addr, 158584d6a5d4SLandon J. Fuller bhnd_size_t offset, uint32_t value, u_int width) 158684d6a5d4SLandon J. Fuller { 158784d6a5d4SLandon J. Fuller struct resource *r; 158884d6a5d4SLandon J. Fuller bus_size_t res_offset; 158984d6a5d4SLandon J. Fuller int error; 159084d6a5d4SLandon J. Fuller 159184d6a5d4SLandon J. Fuller /* Map the target address */ 159284d6a5d4SLandon J. Fuller error = bhndb_pci_probe_map(probe, addr, offset, width, &r, 159384d6a5d4SLandon J. Fuller &res_offset); 159484d6a5d4SLandon J. Fuller if (error) { 159584d6a5d4SLandon J. Fuller device_printf(probe->dev, "error mapping %#jx+%#jx for " 159684d6a5d4SLandon J. Fuller "writing: %d\n", addr, offset, error); 159784d6a5d4SLandon J. Fuller return; 159884d6a5d4SLandon J. Fuller } 159984d6a5d4SLandon J. Fuller 160084d6a5d4SLandon J. Fuller /* Perform write */ 160184d6a5d4SLandon J. Fuller switch (width) { 160284d6a5d4SLandon J. Fuller case 1: 160384d6a5d4SLandon J. Fuller return (bus_write_1(r, res_offset, value)); 160484d6a5d4SLandon J. Fuller case 2: 160584d6a5d4SLandon J. Fuller return (bus_write_2(r, res_offset, value)); 160684d6a5d4SLandon J. Fuller case 4: 160784d6a5d4SLandon J. Fuller return (bus_write_4(r, res_offset, value)); 160884d6a5d4SLandon J. Fuller default: 160984d6a5d4SLandon J. Fuller panic("unsupported width: %u", width); 161084d6a5d4SLandon J. Fuller } 161184d6a5d4SLandon J. Fuller } 161284d6a5d4SLandon J. Fuller 161384d6a5d4SLandon J. Fuller /** 161484d6a5d4SLandon J. Fuller * Read a data item from the bridged address space at the given @p offset 161584d6a5d4SLandon J. Fuller * from @p addr. 161684d6a5d4SLandon J. Fuller * 161784d6a5d4SLandon J. Fuller * A dynamic register window will be used to map @p addr. 161884d6a5d4SLandon J. Fuller * 161984d6a5d4SLandon J. Fuller * @param probe The bhndb_pci probe state to be used to perform the 162084d6a5d4SLandon J. Fuller * read. 162184d6a5d4SLandon J. Fuller * @param addr The base address. 162284d6a5d4SLandon J. Fuller * @param offset The offset from @p addr at which to read a data item of 162384d6a5d4SLandon J. Fuller * @p width bytes. 162484d6a5d4SLandon J. Fuller * @param width Item width (1, 2, or 4 bytes). 162584d6a5d4SLandon J. Fuller */ 162684d6a5d4SLandon J. Fuller static uint32_t 162784d6a5d4SLandon J. Fuller bhndb_pci_probe_read(struct bhndb_pci_probe *probe, bhnd_addr_t addr, 162884d6a5d4SLandon J. Fuller bhnd_size_t offset, u_int width) 162984d6a5d4SLandon J. Fuller { 163084d6a5d4SLandon J. Fuller struct resource *r; 163184d6a5d4SLandon J. Fuller bus_size_t res_offset; 163284d6a5d4SLandon J. Fuller int error; 163384d6a5d4SLandon J. Fuller 163484d6a5d4SLandon J. Fuller /* Map the target address */ 163584d6a5d4SLandon J. Fuller error = bhndb_pci_probe_map(probe, addr, offset, width, &r, 163684d6a5d4SLandon J. Fuller &res_offset); 163784d6a5d4SLandon J. Fuller if (error) { 163884d6a5d4SLandon J. Fuller device_printf(probe->dev, "error mapping %#jx+%#jx for " 163984d6a5d4SLandon J. Fuller "reading: %d\n", addr, offset, error); 164084d6a5d4SLandon J. Fuller return (UINT32_MAX); 164184d6a5d4SLandon J. Fuller } 164284d6a5d4SLandon J. Fuller 164384d6a5d4SLandon J. Fuller /* Perform read */ 164484d6a5d4SLandon J. Fuller switch (width) { 164584d6a5d4SLandon J. Fuller case 1: 164684d6a5d4SLandon J. Fuller return (bus_read_1(r, res_offset)); 164784d6a5d4SLandon J. Fuller case 2: 164884d6a5d4SLandon J. Fuller return (bus_read_2(r, res_offset)); 164984d6a5d4SLandon J. Fuller case 4: 165084d6a5d4SLandon J. Fuller return (bus_read_4(r, res_offset)); 165184d6a5d4SLandon J. Fuller default: 165284d6a5d4SLandon J. Fuller panic("unsupported width: %u", width); 165384d6a5d4SLandon J. Fuller } 165484d6a5d4SLandon J. Fuller } 165584d6a5d4SLandon J. Fuller 165684d6a5d4SLandon J. Fuller /** 165784d6a5d4SLandon J. Fuller * Initialize a new bhndb PCI bridge EROM I/O instance. All I/O will be 165884d6a5d4SLandon J. Fuller * performed using @p probe. 165984d6a5d4SLandon J. Fuller * 166084d6a5d4SLandon J. Fuller * @param pio The instance to be initialized. 166184d6a5d4SLandon J. Fuller * @param probe The bhndb_pci probe state to be used to perform all 166284d6a5d4SLandon J. Fuller * I/O. 166384d6a5d4SLandon J. Fuller */ 166484d6a5d4SLandon J. Fuller static void 166584d6a5d4SLandon J. Fuller bhndb_pci_eio_init(struct bhndb_pci_eio *pio, struct bhndb_pci_probe *probe) 166684d6a5d4SLandon J. Fuller { 166784d6a5d4SLandon J. Fuller memset(pio, 0, sizeof(*pio)); 166884d6a5d4SLandon J. Fuller 166984d6a5d4SLandon J. Fuller pio->eio.map = bhndb_pci_eio_map; 167084d6a5d4SLandon J. Fuller pio->eio.read = bhndb_pci_eio_read; 167184d6a5d4SLandon J. Fuller pio->eio.fini = NULL; 167284d6a5d4SLandon J. Fuller 167384d6a5d4SLandon J. Fuller pio->addr = 0; 167484d6a5d4SLandon J. Fuller pio->size = 0; 167584d6a5d4SLandon J. Fuller pio->probe = probe; 167684d6a5d4SLandon J. Fuller } 167784d6a5d4SLandon J. Fuller 167889294a78SLandon J. Fuller /* bhnd_erom_io_map() implementation */ 167989294a78SLandon J. Fuller static int 168089294a78SLandon J. Fuller bhndb_pci_eio_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 168189294a78SLandon J. Fuller bhnd_size_t size) 168289294a78SLandon J. Fuller { 168384d6a5d4SLandon J. Fuller struct bhndb_pci_eio *pio = (struct bhndb_pci_eio *)eio; 168489294a78SLandon J. Fuller 168584d6a5d4SLandon J. Fuller if (BHND_ADDR_MAX - addr < size) 168684d6a5d4SLandon J. Fuller return (EINVAL); /* addr+size would overflow */ 168789294a78SLandon J. Fuller 168889294a78SLandon J. Fuller pio->addr = addr; 168989294a78SLandon J. Fuller pio->size = size; 169089294a78SLandon J. Fuller 169189294a78SLandon J. Fuller return (0); 169289294a78SLandon J. Fuller } 169389294a78SLandon J. Fuller 169489294a78SLandon J. Fuller /* bhnd_erom_io_read() implementation */ 169589294a78SLandon J. Fuller static uint32_t 169689294a78SLandon J. Fuller bhndb_pci_eio_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 169789294a78SLandon J. Fuller { 169884d6a5d4SLandon J. Fuller struct bhndb_pci_eio *pio = (struct bhndb_pci_eio *)eio; 169989294a78SLandon J. Fuller 170084d6a5d4SLandon J. Fuller /* The requested subrange must fall within the existing mapped range */ 170184d6a5d4SLandon J. Fuller if (offset > pio->size || 170284d6a5d4SLandon J. Fuller width > pio->size || 170384d6a5d4SLandon J. Fuller pio->size - offset < width) 170484d6a5d4SLandon J. Fuller { 170584d6a5d4SLandon J. Fuller return (ENXIO); 170689294a78SLandon J. Fuller } 170789294a78SLandon J. Fuller 170884d6a5d4SLandon J. Fuller return (bhndb_pci_probe_read(pio->probe, pio->addr, offset, width)); 170989294a78SLandon J. Fuller } 171089294a78SLandon J. Fuller 17114ad7e9b0SAdrian Chadd static device_method_t bhndb_pci_methods[] = { 17124ad7e9b0SAdrian Chadd /* Device interface */ 17134ad7e9b0SAdrian Chadd DEVMETHOD(device_probe, bhndb_pci_probe), 17144ad7e9b0SAdrian Chadd DEVMETHOD(device_attach, bhndb_pci_attach), 17154ad7e9b0SAdrian Chadd DEVMETHOD(device_resume, bhndb_pci_resume), 1716bb64eeccSAdrian Chadd DEVMETHOD(device_suspend, bhndb_pci_suspend), 1717bb64eeccSAdrian Chadd DEVMETHOD(device_detach, bhndb_pci_detach), 17184ad7e9b0SAdrian Chadd 17194ad7e9b0SAdrian Chadd /* BHNDB interface */ 17204ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_set_window_addr, bhndb_pci_set_window_addr), 1721d567592bSAdrian Chadd DEVMETHOD(bhndb_populate_board_info, bhndb_pci_populate_board_info), 1722caeff9a3SLandon J. Fuller DEVMETHOD(bhndb_map_intr_isrc, bhndb_pci_map_intr_isrc), 1723caeff9a3SLandon J. Fuller DEVMETHOD(bhndb_route_interrupts, bhndb_pci_route_interrupts), 17244ad7e9b0SAdrian Chadd 17254e96bf3aSLandon J. Fuller /* BHND PWRCTL hostb interface */ 17264e96bf3aSLandon J. Fuller DEVMETHOD(bhnd_pwrctl_hostb_get_clksrc, bhndb_pci_pwrctl_get_clksrc), 17274e96bf3aSLandon J. Fuller DEVMETHOD(bhnd_pwrctl_hostb_gate_clock, bhndb_pci_pwrctl_gate_clock), 17284e96bf3aSLandon J. Fuller DEVMETHOD(bhnd_pwrctl_hostb_ungate_clock, bhndb_pci_pwrctl_ungate_clock), 17294e96bf3aSLandon J. Fuller 17304ad7e9b0SAdrian Chadd DEVMETHOD_END 17314ad7e9b0SAdrian Chadd }; 17324ad7e9b0SAdrian Chadd 17334ad7e9b0SAdrian Chadd DEFINE_CLASS_1(bhndb, bhndb_pci_driver, bhndb_pci_methods, 17344ad7e9b0SAdrian Chadd sizeof(struct bhndb_pci_softc), bhndb_driver); 17354ad7e9b0SAdrian Chadd 17364ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb_pci, 1); 1737148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd_pci_hostb, 1, 1, 1); 17384ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, pci, 1, 1, 1); 17394ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhndb, 1, 1, 1); 1740148ed571SAdrian Chadd MODULE_DEPEND(bhndb_pci, bhnd, 1, 1, 1); 1741