1 /*- 2 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> 3 * Copyright (c) 2017 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * Portions of this software were developed by Landon Fuller 7 * under sponsorship from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer, 14 * without modification. 15 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 16 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 17 * redistribution must be conditioned upon including a substantially 18 * similar Disclaimer requirement for further binary redistribution. 19 * 20 * NO WARRANTY 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 24 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 25 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 26 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 29 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGES. 32 */ 33 34 #include <sys/cdefs.h> 35 #include <sys/param.h> 36 #include <sys/kernel.h> 37 #include <sys/bus.h> 38 #include <sys/module.h> 39 #include <sys/systm.h> 40 41 #include <dev/bhnd/bhnd_ids.h> 42 #include <dev/bhnd/bhndb/bhndbvar.h> 43 #include <dev/bhnd/bhndb/bhndb_hwdata.h> 44 45 #include "sibareg.h" 46 #include "sibavar.h" 47 48 /* 49 * Supports attachment of siba(4) bus devices via a bhndb bridge. 50 */ 51 52 struct siba_bhndb_softc; 53 54 static int siba_bhndb_wars_hwup(struct siba_bhndb_softc *sc); 55 56 /* siba_bhndb per-instance state */ 57 struct siba_bhndb_softc { 58 struct siba_softc siba; /**< common siba per-instance state */ 59 uint32_t quirks; /**< bus-level quirks */ 60 }; 61 62 /* siba_bhndb quirks */ 63 enum { 64 /** When PCIe-bridged, the D11 core's initiator request 65 * timeout must be disabled to prevent D11 from entering a 66 * RESP_TIMEOUT error state. */ 67 SIBA_QUIRK_PCIE_D11_SB_TIMEOUT = (1<<0) 68 }; 69 70 /* Bus-level quirks when bridged via a PCI host bridge core */ 71 static struct bhnd_device_quirk pci_bridge_quirks[] = { 72 BHND_DEVICE_QUIRK_END 73 }; 74 75 /* Bus-level quirks when bridged via a PCIe host bridge core */ 76 static struct bhnd_device_quirk pcie_bridge_quirks[] = { 77 BHND_CHIP_QUIRK (4311, HWREV_EQ(2), SIBA_QUIRK_PCIE_D11_SB_TIMEOUT), 78 BHND_CHIP_QUIRK (4312, HWREV_ANY, SIBA_QUIRK_PCIE_D11_SB_TIMEOUT), 79 BHND_DEVICE_QUIRK_END 80 }; 81 82 /* Bus-level quirks specific to a particular host bridge core */ 83 static struct bhnd_device bridge_devs[] = { 84 BHND_DEVICE(BCM, PCI, NULL, pci_bridge_quirks), 85 BHND_DEVICE(BCM, PCIE, NULL, pcie_bridge_quirks), 86 BHND_DEVICE_END 87 }; 88 89 static int 90 siba_bhndb_probe(device_t dev) 91 { 92 const struct bhnd_chipid *cid; 93 int error; 94 95 /* Defer to default probe implementation */ 96 if ((error = siba_probe(dev)) > 0) 97 return (error); 98 99 /* Check bus type */ 100 cid = BHNDB_GET_CHIPID(device_get_parent(dev), dev); 101 if (cid->chip_type != BHND_CHIPTYPE_SIBA) 102 return (ENXIO); 103 104 /* Set device description */ 105 bhnd_set_default_bus_desc(dev, cid); 106 107 return (error); 108 } 109 110 static int 111 siba_bhndb_attach(device_t dev) 112 { 113 struct siba_bhndb_softc *sc; 114 device_t hostb; 115 int error; 116 117 sc = device_get_softc(dev); 118 sc->quirks = 0; 119 120 /* Perform initial attach and enumerate our children. */ 121 if ((error = siba_attach(dev))) 122 return (error); 123 124 /* Fetch bus-level quirks required by the host bridge core */ 125 if ((hostb = bhnd_bus_find_hostb_device(dev)) != NULL) { 126 sc->quirks |= bhnd_device_quirks(hostb, bridge_devs, 127 sizeof(bridge_devs[0])); 128 } 129 130 /* Apply attach/resume workarounds before any child drivers attach */ 131 if ((error = siba_bhndb_wars_hwup(sc))) 132 goto failed; 133 134 /* Delegate remainder to standard bhnd method implementation */ 135 if ((error = bhnd_generic_attach(dev))) 136 goto failed; 137 138 return (0); 139 140 failed: 141 siba_detach(dev); 142 return (error); 143 } 144 145 static int 146 siba_bhndb_resume(device_t dev) 147 { 148 struct siba_bhndb_softc *sc; 149 int error; 150 151 sc = device_get_softc(dev); 152 153 /* Apply attach/resume work-arounds */ 154 if ((error = siba_bhndb_wars_hwup(sc))) 155 return (error); 156 157 /* Call our superclass' implementation */ 158 return (siba_resume(dev)); 159 } 160 161 /* Suspend all references to the device's cfg register blocks */ 162 static void 163 siba_bhndb_suspend_cfgblocks(device_t dev, struct siba_devinfo *dinfo) { 164 for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { 165 if (dinfo->cfg_res[i] == NULL) 166 continue; 167 168 BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, 169 SYS_RES_MEMORY, dinfo->cfg_res[i]->res); 170 } 171 } 172 173 static int 174 siba_bhndb_suspend_child(device_t dev, device_t child) 175 { 176 struct siba_devinfo *dinfo; 177 int error; 178 179 if (device_get_parent(child) != dev) 180 BUS_SUSPEND_CHILD(device_get_parent(dev), child); 181 182 dinfo = device_get_ivars(child); 183 184 /* Suspend the child */ 185 if ((error = bhnd_generic_br_suspend_child(dev, child))) 186 return (error); 187 188 /* Suspend resource references to the child's config registers */ 189 siba_bhndb_suspend_cfgblocks(dev, dinfo); 190 191 return (0); 192 } 193 194 static int 195 siba_bhndb_resume_child(device_t dev, device_t child) 196 { 197 struct siba_devinfo *dinfo; 198 int error; 199 200 if (device_get_parent(child) != dev) 201 BUS_SUSPEND_CHILD(device_get_parent(dev), child); 202 203 if (!device_is_suspended(child)) 204 return (EBUSY); 205 206 dinfo = device_get_ivars(child); 207 208 /* Resume all resource references to the child's config registers */ 209 for (u_int i = 0; i < dinfo->core_id.num_cfg_blocks; i++) { 210 if (dinfo->cfg_res[i] == NULL) 211 continue; 212 213 error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev, 214 SYS_RES_MEMORY, dinfo->cfg_res[i]->res); 215 if (error) { 216 siba_bhndb_suspend_cfgblocks(dev, dinfo); 217 return (error); 218 } 219 } 220 221 /* Resume the child */ 222 if ((error = bhnd_generic_br_resume_child(dev, child))) { 223 siba_bhndb_suspend_cfgblocks(dev, dinfo); 224 return (error); 225 } 226 227 return (0); 228 } 229 230 /* Work-around implementation for SIBA_QUIRK_PCIE_D11_SB_TIMEOUT */ 231 static int 232 siba_bhndb_wars_pcie_clear_d11_timeout(struct siba_bhndb_softc *sc) 233 { 234 struct siba_devinfo *dinfo; 235 device_t d11; 236 uint32_t imcfg; 237 238 /* Only applicable if there's a D11 core */ 239 d11 = bhnd_bus_match_child(sc->siba.dev, &(struct bhnd_core_match) { 240 BHND_MATCH_CORE(BHND_MFGID_BCM, BHND_COREID_D11), 241 BHND_MATCH_CORE_UNIT(0) 242 }); 243 if (d11 == NULL) 244 return (0); 245 246 /* Clear initiator timeout in D11's CFG0 block */ 247 dinfo = device_get_ivars(d11); 248 KASSERT(dinfo->cfg_res[0] != NULL, ("missing core config mapping")); 249 250 imcfg = bhnd_bus_read_4(dinfo->cfg_res[0], SIBA_CFG0_IMCONFIGLOW); 251 imcfg &= ~(SIBA_IMCL_RTO_MASK|SIBA_IMCL_STO_MASK); 252 253 bhnd_bus_write_4(dinfo->cfg_res[0], SIBA_CFG0_IMCONFIGLOW, imcfg); 254 255 return (0); 256 } 257 258 /** 259 * Apply any hardware workarounds that are required upon attach or resume 260 * of the bus. 261 */ 262 static int 263 siba_bhndb_wars_hwup(struct siba_bhndb_softc *sc) 264 { 265 int error; 266 267 if (sc->quirks & SIBA_QUIRK_PCIE_D11_SB_TIMEOUT) { 268 if ((error = siba_bhndb_wars_pcie_clear_d11_timeout(sc))) 269 return (error); 270 } 271 272 return (0); 273 } 274 275 static device_method_t siba_bhndb_methods[] = { 276 /* Device interface */ 277 DEVMETHOD(device_probe, siba_bhndb_probe), 278 DEVMETHOD(device_attach, siba_bhndb_attach), 279 DEVMETHOD(device_resume, siba_bhndb_resume), 280 281 /* Bus interface */ 282 DEVMETHOD(bus_suspend_child, siba_bhndb_suspend_child), 283 DEVMETHOD(bus_resume_child, siba_bhndb_resume_child), 284 285 DEVMETHOD_END 286 }; 287 288 DEFINE_CLASS_2(bhnd, siba_bhndb_driver, siba_bhndb_methods, 289 sizeof(struct siba_softc), bhnd_bhndb_driver, siba_driver); 290 291 DRIVER_MODULE(siba_bhndb, bhndb, siba_bhndb_driver, NULL, NULL); 292 293 MODULE_VERSION(siba_bhndb, 1); 294 MODULE_DEPEND(siba_bhndb, siba, 1, 1, 1); 295 MODULE_DEPEND(siba_bhndb, bhnd, 1, 1, 1); 296 MODULE_DEPEND(siba_bhndb, bhndb, 1, 1, 1); 297