1*4ad7e9b0SAdrian Chadd /*- 2*4ad7e9b0SAdrian Chadd * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 3*4ad7e9b0SAdrian Chadd * All rights reserved. 4*4ad7e9b0SAdrian Chadd * 5*4ad7e9b0SAdrian Chadd * Redistribution and use in source and binary forms, with or without 6*4ad7e9b0SAdrian Chadd * modification, are permitted provided that the following conditions 7*4ad7e9b0SAdrian Chadd * are met: 8*4ad7e9b0SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 9*4ad7e9b0SAdrian Chadd * notice, this list of conditions and the following disclaimer, 10*4ad7e9b0SAdrian Chadd * without modification. 11*4ad7e9b0SAdrian Chadd * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12*4ad7e9b0SAdrian Chadd * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13*4ad7e9b0SAdrian Chadd * redistribution must be conditioned upon including a substantially 14*4ad7e9b0SAdrian Chadd * similar Disclaimer requirement for further binary redistribution. 15*4ad7e9b0SAdrian Chadd * 16*4ad7e9b0SAdrian Chadd * NO WARRANTY 17*4ad7e9b0SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18*4ad7e9b0SAdrian Chadd * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19*4ad7e9b0SAdrian Chadd * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20*4ad7e9b0SAdrian Chadd * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21*4ad7e9b0SAdrian Chadd * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22*4ad7e9b0SAdrian Chadd * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23*4ad7e9b0SAdrian Chadd * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24*4ad7e9b0SAdrian Chadd * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25*4ad7e9b0SAdrian Chadd * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26*4ad7e9b0SAdrian Chadd * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27*4ad7e9b0SAdrian Chadd * THE POSSIBILITY OF SUCH DAMAGES. 28*4ad7e9b0SAdrian Chadd */ 29*4ad7e9b0SAdrian Chadd 30*4ad7e9b0SAdrian Chadd #include <sys/cdefs.h> 31*4ad7e9b0SAdrian Chadd __FBSDID("$FreeBSD$"); 32*4ad7e9b0SAdrian Chadd 33*4ad7e9b0SAdrian Chadd /* 34*4ad7e9b0SAdrian Chadd * Abstract BHND Bridge Device Driver 35*4ad7e9b0SAdrian Chadd * 36*4ad7e9b0SAdrian Chadd * Provides generic support for bridging from a parent bus (such as PCI) to 37*4ad7e9b0SAdrian Chadd * a BHND-compatible bus (e.g. bcma or siba). 38*4ad7e9b0SAdrian Chadd */ 39*4ad7e9b0SAdrian Chadd 40*4ad7e9b0SAdrian Chadd #include <sys/param.h> 41*4ad7e9b0SAdrian Chadd #include <sys/kernel.h> 42*4ad7e9b0SAdrian Chadd #include <sys/bus.h> 43*4ad7e9b0SAdrian Chadd #include <sys/module.h> 44*4ad7e9b0SAdrian Chadd #include <sys/systm.h> 45*4ad7e9b0SAdrian Chadd 46*4ad7e9b0SAdrian Chadd #include <machine/bus.h> 47*4ad7e9b0SAdrian Chadd #include <sys/rman.h> 48*4ad7e9b0SAdrian Chadd #include <machine/resource.h> 49*4ad7e9b0SAdrian Chadd 50*4ad7e9b0SAdrian Chadd #include <dev/bhnd/bhndvar.h> 51*4ad7e9b0SAdrian Chadd #include <dev/bhnd/bhndreg.h> 52*4ad7e9b0SAdrian Chadd 53*4ad7e9b0SAdrian Chadd #include <dev/bhnd/cores/chipc/chipcreg.h> 54*4ad7e9b0SAdrian Chadd 55*4ad7e9b0SAdrian Chadd #include "bhndbvar.h" 56*4ad7e9b0SAdrian Chadd #include "bhndb_bus_if.h" 57*4ad7e9b0SAdrian Chadd #include "bhndb_hwdata.h" 58*4ad7e9b0SAdrian Chadd #include "bhndb_private.h" 59*4ad7e9b0SAdrian Chadd 60*4ad7e9b0SAdrian Chadd /* Debugging flags */ 61*4ad7e9b0SAdrian Chadd static u_long bhndb_debug = 0; 62*4ad7e9b0SAdrian Chadd TUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug); 63*4ad7e9b0SAdrian Chadd 64*4ad7e9b0SAdrian Chadd enum { 65*4ad7e9b0SAdrian Chadd BHNDB_DEBUG_PRIO = 1 << 0, 66*4ad7e9b0SAdrian Chadd }; 67*4ad7e9b0SAdrian Chadd 68*4ad7e9b0SAdrian Chadd #define BHNDB_DEBUG(_type) (BHNDB_DEBUG_ ## _type & bhndb_debug) 69*4ad7e9b0SAdrian Chadd 70*4ad7e9b0SAdrian Chadd static bool bhndb_hw_matches(device_t *devlist, 71*4ad7e9b0SAdrian Chadd int num_devs, 72*4ad7e9b0SAdrian Chadd const struct bhndb_hw *hw); 73*4ad7e9b0SAdrian Chadd 74*4ad7e9b0SAdrian Chadd static int bhndb_initialize_region_cfg( 75*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc, device_t *devs, 76*4ad7e9b0SAdrian Chadd int ndevs, 77*4ad7e9b0SAdrian Chadd const struct bhndb_hw_priority *table, 78*4ad7e9b0SAdrian Chadd struct bhndb_resources *r); 79*4ad7e9b0SAdrian Chadd 80*4ad7e9b0SAdrian Chadd static int bhndb_find_hwspec(struct bhndb_softc *sc, 81*4ad7e9b0SAdrian Chadd device_t *devs, int ndevs, 82*4ad7e9b0SAdrian Chadd const struct bhndb_hw **hw); 83*4ad7e9b0SAdrian Chadd 84*4ad7e9b0SAdrian Chadd static int bhndb_read_chipid(struct bhndb_softc *sc, 85*4ad7e9b0SAdrian Chadd const struct bhndb_hwcfg *cfg, 86*4ad7e9b0SAdrian Chadd struct bhnd_chipid *result); 87*4ad7e9b0SAdrian Chadd 88*4ad7e9b0SAdrian Chadd static struct rman *bhndb_get_rman(struct bhndb_softc *sc, 89*4ad7e9b0SAdrian Chadd int type); 90*4ad7e9b0SAdrian Chadd 91*4ad7e9b0SAdrian Chadd static int bhndb_init_child_resource(struct resource *r, 92*4ad7e9b0SAdrian Chadd struct resource *parent, 93*4ad7e9b0SAdrian Chadd bhnd_size_t offset, 94*4ad7e9b0SAdrian Chadd bhnd_size_t size); 95*4ad7e9b0SAdrian Chadd 96*4ad7e9b0SAdrian Chadd static int bhndb_activate_static_region( 97*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc, 98*4ad7e9b0SAdrian Chadd struct bhndb_region *region, 99*4ad7e9b0SAdrian Chadd device_t child, int type, int rid, 100*4ad7e9b0SAdrian Chadd struct resource *r); 101*4ad7e9b0SAdrian Chadd 102*4ad7e9b0SAdrian Chadd static int bhndb_try_activate_resource( 103*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc, device_t child, 104*4ad7e9b0SAdrian Chadd int type, int rid, struct resource *r, 105*4ad7e9b0SAdrian Chadd bool *indirect); 106*4ad7e9b0SAdrian Chadd 107*4ad7e9b0SAdrian Chadd 108*4ad7e9b0SAdrian Chadd /** 109*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of DEVICE_PROBE(). 110*4ad7e9b0SAdrian Chadd * 111*4ad7e9b0SAdrian Chadd * This function provides the default bhndb implementation of DEVICE_PROBE(), 112*4ad7e9b0SAdrian Chadd * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge(). 113*4ad7e9b0SAdrian Chadd */ 114*4ad7e9b0SAdrian Chadd int 115*4ad7e9b0SAdrian Chadd bhndb_generic_probe(device_t dev) 116*4ad7e9b0SAdrian Chadd { 117*4ad7e9b0SAdrian Chadd return (BUS_PROBE_NOWILDCARD); 118*4ad7e9b0SAdrian Chadd } 119*4ad7e9b0SAdrian Chadd 120*4ad7e9b0SAdrian Chadd static void 121*4ad7e9b0SAdrian Chadd bhndb_probe_nomatch(device_t dev, device_t child) 122*4ad7e9b0SAdrian Chadd { 123*4ad7e9b0SAdrian Chadd const char *name; 124*4ad7e9b0SAdrian Chadd 125*4ad7e9b0SAdrian Chadd name = device_get_name(child); 126*4ad7e9b0SAdrian Chadd if (name == NULL) 127*4ad7e9b0SAdrian Chadd name = "unknown device"; 128*4ad7e9b0SAdrian Chadd 129*4ad7e9b0SAdrian Chadd device_printf(dev, "<%s> (no driver attached)\n", name); 130*4ad7e9b0SAdrian Chadd } 131*4ad7e9b0SAdrian Chadd 132*4ad7e9b0SAdrian Chadd static int 133*4ad7e9b0SAdrian Chadd bhndb_print_child(device_t dev, device_t child) 134*4ad7e9b0SAdrian Chadd { 135*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 136*4ad7e9b0SAdrian Chadd struct resource_list *rl; 137*4ad7e9b0SAdrian Chadd int retval = 0; 138*4ad7e9b0SAdrian Chadd 139*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 140*4ad7e9b0SAdrian Chadd 141*4ad7e9b0SAdrian Chadd retval += bus_print_child_header(dev, child); 142*4ad7e9b0SAdrian Chadd 143*4ad7e9b0SAdrian Chadd rl = BUS_GET_RESOURCE_LIST(dev, child); 144*4ad7e9b0SAdrian Chadd if (rl != NULL) { 145*4ad7e9b0SAdrian Chadd retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, 146*4ad7e9b0SAdrian Chadd "%#lx"); 147*4ad7e9b0SAdrian Chadd retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, 148*4ad7e9b0SAdrian Chadd "%ld"); 149*4ad7e9b0SAdrian Chadd } 150*4ad7e9b0SAdrian Chadd 151*4ad7e9b0SAdrian Chadd retval += bus_print_child_domain(dev, child); 152*4ad7e9b0SAdrian Chadd retval += bus_print_child_footer(dev, child); 153*4ad7e9b0SAdrian Chadd 154*4ad7e9b0SAdrian Chadd return (retval); 155*4ad7e9b0SAdrian Chadd } 156*4ad7e9b0SAdrian Chadd 157*4ad7e9b0SAdrian Chadd static int 158*4ad7e9b0SAdrian Chadd bhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf, 159*4ad7e9b0SAdrian Chadd size_t buflen) 160*4ad7e9b0SAdrian Chadd { 161*4ad7e9b0SAdrian Chadd *buf = '\0'; 162*4ad7e9b0SAdrian Chadd return (0); 163*4ad7e9b0SAdrian Chadd } 164*4ad7e9b0SAdrian Chadd 165*4ad7e9b0SAdrian Chadd static int 166*4ad7e9b0SAdrian Chadd bhndb_child_location_str(device_t dev, device_t child, char *buf, 167*4ad7e9b0SAdrian Chadd size_t buflen) 168*4ad7e9b0SAdrian Chadd { 169*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 170*4ad7e9b0SAdrian Chadd 171*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 172*4ad7e9b0SAdrian Chadd 173*4ad7e9b0SAdrian Chadd snprintf(buf, buflen, "base=0x%llx", 174*4ad7e9b0SAdrian Chadd (unsigned long long) sc->chipid.enum_addr); 175*4ad7e9b0SAdrian Chadd return (0); 176*4ad7e9b0SAdrian Chadd } 177*4ad7e9b0SAdrian Chadd 178*4ad7e9b0SAdrian Chadd /** 179*4ad7e9b0SAdrian Chadd * Return true if @p devlist matches the @p hw specification. 180*4ad7e9b0SAdrian Chadd * 181*4ad7e9b0SAdrian Chadd * @param devlist A device table to match against. 182*4ad7e9b0SAdrian Chadd * @param num_devs The number of devices in @p devlist. 183*4ad7e9b0SAdrian Chadd * @param hw The hardware description to be matched against. 184*4ad7e9b0SAdrian Chadd */ 185*4ad7e9b0SAdrian Chadd static bool 186*4ad7e9b0SAdrian Chadd bhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw) 187*4ad7e9b0SAdrian Chadd { 188*4ad7e9b0SAdrian Chadd for (u_int i = 0; i < hw->num_hw_reqs; i++) { 189*4ad7e9b0SAdrian Chadd const struct bhnd_core_match *match; 190*4ad7e9b0SAdrian Chadd bool found; 191*4ad7e9b0SAdrian Chadd 192*4ad7e9b0SAdrian Chadd match = &hw->hw_reqs[i]; 193*4ad7e9b0SAdrian Chadd found = false; 194*4ad7e9b0SAdrian Chadd 195*4ad7e9b0SAdrian Chadd for (int d = 0; d < num_devs; d++) { 196*4ad7e9b0SAdrian Chadd if (!bhnd_device_matches(devlist[d], match)) 197*4ad7e9b0SAdrian Chadd continue; 198*4ad7e9b0SAdrian Chadd 199*4ad7e9b0SAdrian Chadd found = true; 200*4ad7e9b0SAdrian Chadd break; 201*4ad7e9b0SAdrian Chadd } 202*4ad7e9b0SAdrian Chadd 203*4ad7e9b0SAdrian Chadd if (!found) 204*4ad7e9b0SAdrian Chadd return (false); 205*4ad7e9b0SAdrian Chadd } 206*4ad7e9b0SAdrian Chadd 207*4ad7e9b0SAdrian Chadd return (true); 208*4ad7e9b0SAdrian Chadd } 209*4ad7e9b0SAdrian Chadd 210*4ad7e9b0SAdrian Chadd /** 211*4ad7e9b0SAdrian Chadd * Initialize the region maps and priority configuration in @p r using 212*4ad7e9b0SAdrian Chadd * the provided priority @p table and the set of devices attached to 213*4ad7e9b0SAdrian Chadd * the bridged @p bus_dev . 214*4ad7e9b0SAdrian Chadd * 215*4ad7e9b0SAdrian Chadd * @param sc The bhndb device state. 216*4ad7e9b0SAdrian Chadd * @param devs All devices enumerated on the bridged bhnd bus. 217*4ad7e9b0SAdrian Chadd * @param ndevs The length of @p devs. 218*4ad7e9b0SAdrian Chadd * @param table Hardware priority table to be used to determine the relative 219*4ad7e9b0SAdrian Chadd * priorities of per-core port resources. 220*4ad7e9b0SAdrian Chadd * @param r The resource state to be configured. 221*4ad7e9b0SAdrian Chadd */ 222*4ad7e9b0SAdrian Chadd static int 223*4ad7e9b0SAdrian Chadd bhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs, 224*4ad7e9b0SAdrian Chadd const struct bhndb_hw_priority *table, struct bhndb_resources *r) 225*4ad7e9b0SAdrian Chadd { 226*4ad7e9b0SAdrian Chadd const struct bhndb_hw_priority *hp; 227*4ad7e9b0SAdrian Chadd bhnd_addr_t addr; 228*4ad7e9b0SAdrian Chadd bhnd_size_t size; 229*4ad7e9b0SAdrian Chadd size_t prio_low, prio_default, prio_high; 230*4ad7e9b0SAdrian Chadd int error; 231*4ad7e9b0SAdrian Chadd 232*4ad7e9b0SAdrian Chadd /* The number of port regions per priority band that must be accessible 233*4ad7e9b0SAdrian Chadd * via dynamic register windows */ 234*4ad7e9b0SAdrian Chadd prio_low = 0; 235*4ad7e9b0SAdrian Chadd prio_default = 0; 236*4ad7e9b0SAdrian Chadd prio_high = 0; 237*4ad7e9b0SAdrian Chadd 238*4ad7e9b0SAdrian Chadd /* 239*4ad7e9b0SAdrian Chadd * Register bridge regions covering all statically mapped ports. 240*4ad7e9b0SAdrian Chadd */ 241*4ad7e9b0SAdrian Chadd for (int i = 0; i < ndevs; i++) { 242*4ad7e9b0SAdrian Chadd const struct bhndb_regwin *regw; 243*4ad7e9b0SAdrian Chadd device_t child; 244*4ad7e9b0SAdrian Chadd 245*4ad7e9b0SAdrian Chadd child = devs[i]; 246*4ad7e9b0SAdrian Chadd 247*4ad7e9b0SAdrian Chadd for (regw = r->cfg->register_windows; 248*4ad7e9b0SAdrian Chadd regw->win_type != BHNDB_REGWIN_T_INVALID; regw++) 249*4ad7e9b0SAdrian Chadd { 250*4ad7e9b0SAdrian Chadd /* Only core windows are supported */ 251*4ad7e9b0SAdrian Chadd if (regw->win_type != BHNDB_REGWIN_T_CORE) 252*4ad7e9b0SAdrian Chadd continue; 253*4ad7e9b0SAdrian Chadd 254*4ad7e9b0SAdrian Chadd /* Skip non-applicable register windows. */ 255*4ad7e9b0SAdrian Chadd if (!bhndb_regwin_matches_device(regw, child)) 256*4ad7e9b0SAdrian Chadd continue; 257*4ad7e9b0SAdrian Chadd 258*4ad7e9b0SAdrian Chadd /* Fetch the base address of the mapped port. */ 259*4ad7e9b0SAdrian Chadd error = bhnd_get_region_addr(child, 260*4ad7e9b0SAdrian Chadd regw->core.port_type, regw->core.port, 261*4ad7e9b0SAdrian Chadd regw->core.region, &addr, &size); 262*4ad7e9b0SAdrian Chadd if (error) 263*4ad7e9b0SAdrian Chadd return (error); 264*4ad7e9b0SAdrian Chadd 265*4ad7e9b0SAdrian Chadd /* 266*4ad7e9b0SAdrian Chadd * Always defer to the register window's size. 267*4ad7e9b0SAdrian Chadd * 268*4ad7e9b0SAdrian Chadd * If the port size is smaller than the window size, 269*4ad7e9b0SAdrian Chadd * this ensures that we fully utilize register windows 270*4ad7e9b0SAdrian Chadd * larger than the referenced port. 271*4ad7e9b0SAdrian Chadd * 272*4ad7e9b0SAdrian Chadd * If the port size is larger than the window size, this 273*4ad7e9b0SAdrian Chadd * ensures that we do not directly map the allocations 274*4ad7e9b0SAdrian Chadd * within the region to a too-small window. 275*4ad7e9b0SAdrian Chadd */ 276*4ad7e9b0SAdrian Chadd size = regw->win_size; 277*4ad7e9b0SAdrian Chadd 278*4ad7e9b0SAdrian Chadd /* 279*4ad7e9b0SAdrian Chadd * Add to the bus region list. 280*4ad7e9b0SAdrian Chadd * 281*4ad7e9b0SAdrian Chadd * The window priority for a statically mapped 282*4ad7e9b0SAdrian Chadd * region is always HIGH. 283*4ad7e9b0SAdrian Chadd */ 284*4ad7e9b0SAdrian Chadd error = bhndb_add_resource_region(r, addr, size, 285*4ad7e9b0SAdrian Chadd BHNDB_PRIORITY_HIGH, regw); 286*4ad7e9b0SAdrian Chadd if (error) 287*4ad7e9b0SAdrian Chadd return (error); 288*4ad7e9b0SAdrian Chadd } 289*4ad7e9b0SAdrian Chadd } 290*4ad7e9b0SAdrian Chadd 291*4ad7e9b0SAdrian Chadd /* 292*4ad7e9b0SAdrian Chadd * Perform priority accounting and register bridge regions for all 293*4ad7e9b0SAdrian Chadd * ports defined in the priority table 294*4ad7e9b0SAdrian Chadd */ 295*4ad7e9b0SAdrian Chadd for (int i = 0; i < ndevs; i++) { 296*4ad7e9b0SAdrian Chadd struct bhndb_region *region; 297*4ad7e9b0SAdrian Chadd device_t child; 298*4ad7e9b0SAdrian Chadd 299*4ad7e9b0SAdrian Chadd child = devs[i]; 300*4ad7e9b0SAdrian Chadd 301*4ad7e9b0SAdrian Chadd /* 302*4ad7e9b0SAdrian Chadd * Skip priority accounting for cores that ... 303*4ad7e9b0SAdrian Chadd */ 304*4ad7e9b0SAdrian Chadd 305*4ad7e9b0SAdrian Chadd /* ... do not require bridge resources */ 306*4ad7e9b0SAdrian Chadd if (bhnd_is_hw_disabled(child) || !device_is_enabled(child)) 307*4ad7e9b0SAdrian Chadd continue; 308*4ad7e9b0SAdrian Chadd 309*4ad7e9b0SAdrian Chadd /* ... do not have a priority table entry */ 310*4ad7e9b0SAdrian Chadd hp = bhndb_hw_priority_find_device(table, child); 311*4ad7e9b0SAdrian Chadd if (hp == NULL) 312*4ad7e9b0SAdrian Chadd continue; 313*4ad7e9b0SAdrian Chadd 314*4ad7e9b0SAdrian Chadd /* ... are explicitly disabled in the priority table. */ 315*4ad7e9b0SAdrian Chadd if (hp->priority == BHNDB_PRIORITY_NONE) 316*4ad7e9b0SAdrian Chadd continue; 317*4ad7e9b0SAdrian Chadd 318*4ad7e9b0SAdrian Chadd /* Determine the number of dynamic windows required and 319*4ad7e9b0SAdrian Chadd * register their bus_region entries. */ 320*4ad7e9b0SAdrian Chadd for (u_int i = 0; i < hp->num_ports; i++) { 321*4ad7e9b0SAdrian Chadd const struct bhndb_port_priority *pp; 322*4ad7e9b0SAdrian Chadd 323*4ad7e9b0SAdrian Chadd pp = &hp->ports[i]; 324*4ad7e9b0SAdrian Chadd 325*4ad7e9b0SAdrian Chadd /* Skip ports not defined on this device */ 326*4ad7e9b0SAdrian Chadd if (!bhnd_is_region_valid(child, pp->type, pp->port, 327*4ad7e9b0SAdrian Chadd pp->region)) 328*4ad7e9b0SAdrian Chadd { 329*4ad7e9b0SAdrian Chadd continue; 330*4ad7e9b0SAdrian Chadd } 331*4ad7e9b0SAdrian Chadd 332*4ad7e9b0SAdrian Chadd /* Fetch the address+size of the mapped port. */ 333*4ad7e9b0SAdrian Chadd error = bhnd_get_region_addr(child, pp->type, pp->port, 334*4ad7e9b0SAdrian Chadd pp->region, &addr, &size); 335*4ad7e9b0SAdrian Chadd if (error) 336*4ad7e9b0SAdrian Chadd return (error); 337*4ad7e9b0SAdrian Chadd 338*4ad7e9b0SAdrian Chadd /* Skip ports with an existing static mapping */ 339*4ad7e9b0SAdrian Chadd region = bhndb_find_resource_region(r, addr, size); 340*4ad7e9b0SAdrian Chadd if (region != NULL && region->static_regwin != NULL) 341*4ad7e9b0SAdrian Chadd continue; 342*4ad7e9b0SAdrian Chadd 343*4ad7e9b0SAdrian Chadd /* Define a dynamic region for this port */ 344*4ad7e9b0SAdrian Chadd error = bhndb_add_resource_region(r, addr, size, 345*4ad7e9b0SAdrian Chadd pp->priority, NULL); 346*4ad7e9b0SAdrian Chadd if (error) 347*4ad7e9b0SAdrian Chadd return (error); 348*4ad7e9b0SAdrian Chadd 349*4ad7e9b0SAdrian Chadd /* Update port mapping counts */ 350*4ad7e9b0SAdrian Chadd switch (pp->priority) { 351*4ad7e9b0SAdrian Chadd case BHNDB_PRIORITY_NONE: 352*4ad7e9b0SAdrian Chadd break; 353*4ad7e9b0SAdrian Chadd case BHNDB_PRIORITY_LOW: 354*4ad7e9b0SAdrian Chadd prio_low++; 355*4ad7e9b0SAdrian Chadd break; 356*4ad7e9b0SAdrian Chadd case BHNDB_PRIORITY_DEFAULT: 357*4ad7e9b0SAdrian Chadd prio_default++; 358*4ad7e9b0SAdrian Chadd break; 359*4ad7e9b0SAdrian Chadd case BHNDB_PRIORITY_HIGH: 360*4ad7e9b0SAdrian Chadd prio_high++; 361*4ad7e9b0SAdrian Chadd break; 362*4ad7e9b0SAdrian Chadd } 363*4ad7e9b0SAdrian Chadd } 364*4ad7e9b0SAdrian Chadd } 365*4ad7e9b0SAdrian Chadd 366*4ad7e9b0SAdrian Chadd /* Determine the minimum priority at which we'll allocate direct 367*4ad7e9b0SAdrian Chadd * register windows from our dynamic pool */ 368*4ad7e9b0SAdrian Chadd size_t prio_total = prio_low + prio_default + prio_high; 369*4ad7e9b0SAdrian Chadd if (prio_total <= r->dwa_count) { 370*4ad7e9b0SAdrian Chadd /* low+default+high priority regions get windows */ 371*4ad7e9b0SAdrian Chadd r->min_prio = BHNDB_PRIORITY_LOW; 372*4ad7e9b0SAdrian Chadd 373*4ad7e9b0SAdrian Chadd } else if (prio_default + prio_high <= r->dwa_count) { 374*4ad7e9b0SAdrian Chadd /* default+high priority regions get windows */ 375*4ad7e9b0SAdrian Chadd r->min_prio = BHNDB_PRIORITY_DEFAULT; 376*4ad7e9b0SAdrian Chadd 377*4ad7e9b0SAdrian Chadd } else { 378*4ad7e9b0SAdrian Chadd /* high priority regions get windows */ 379*4ad7e9b0SAdrian Chadd r->min_prio = BHNDB_PRIORITY_HIGH; 380*4ad7e9b0SAdrian Chadd } 381*4ad7e9b0SAdrian Chadd 382*4ad7e9b0SAdrian Chadd if (BHNDB_DEBUG(PRIO)) { 383*4ad7e9b0SAdrian Chadd struct bhndb_region *region; 384*4ad7e9b0SAdrian Chadd const char *direct_msg, *type_msg; 385*4ad7e9b0SAdrian Chadd bhndb_priority_t prio, prio_min; 386*4ad7e9b0SAdrian Chadd 387*4ad7e9b0SAdrian Chadd prio_min = r->min_prio; 388*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "min_prio: %d\n", prio_min); 389*4ad7e9b0SAdrian Chadd 390*4ad7e9b0SAdrian Chadd STAILQ_FOREACH(region, &r->bus_regions, link) { 391*4ad7e9b0SAdrian Chadd prio = region->priority; 392*4ad7e9b0SAdrian Chadd 393*4ad7e9b0SAdrian Chadd direct_msg = prio >= prio_min ? "direct" : "indirect"; 394*4ad7e9b0SAdrian Chadd type_msg = region->static_regwin ? "static" : "dynamic"; 395*4ad7e9b0SAdrian Chadd 396*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "region 0x%llx+0x%llx priority " 397*4ad7e9b0SAdrian Chadd "%u %s/%s\n", 398*4ad7e9b0SAdrian Chadd (unsigned long long) region->addr, 399*4ad7e9b0SAdrian Chadd (unsigned long long) region->size, 400*4ad7e9b0SAdrian Chadd region->priority, 401*4ad7e9b0SAdrian Chadd direct_msg, type_msg); 402*4ad7e9b0SAdrian Chadd } 403*4ad7e9b0SAdrian Chadd } 404*4ad7e9b0SAdrian Chadd 405*4ad7e9b0SAdrian Chadd return (0); 406*4ad7e9b0SAdrian Chadd } 407*4ad7e9b0SAdrian Chadd 408*4ad7e9b0SAdrian Chadd /** 409*4ad7e9b0SAdrian Chadd * Find a hardware specification for @p dev. 410*4ad7e9b0SAdrian Chadd * 411*4ad7e9b0SAdrian Chadd * @param sc The bhndb device state. 412*4ad7e9b0SAdrian Chadd * @param devs All devices enumerated on the bridged bhnd bus. 413*4ad7e9b0SAdrian Chadd * @param ndevs The length of @p devs. 414*4ad7e9b0SAdrian Chadd * @param[out] hw On success, the matched hardware specification. 415*4ad7e9b0SAdrian Chadd * with @p dev. 416*4ad7e9b0SAdrian Chadd * 417*4ad7e9b0SAdrian Chadd * @retval 0 success 418*4ad7e9b0SAdrian Chadd * @retval non-zero if an error occurs fetching device info for comparison. 419*4ad7e9b0SAdrian Chadd */ 420*4ad7e9b0SAdrian Chadd static int 421*4ad7e9b0SAdrian Chadd bhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs, 422*4ad7e9b0SAdrian Chadd const struct bhndb_hw **hw) 423*4ad7e9b0SAdrian Chadd { 424*4ad7e9b0SAdrian Chadd const struct bhndb_hw *next, *hw_table; 425*4ad7e9b0SAdrian Chadd 426*4ad7e9b0SAdrian Chadd /* Search for the first matching hardware config. */ 427*4ad7e9b0SAdrian Chadd hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev); 428*4ad7e9b0SAdrian Chadd for (next = hw_table; next->hw_reqs != NULL; next++) { 429*4ad7e9b0SAdrian Chadd if (!bhndb_hw_matches(devs, ndevs, next)) 430*4ad7e9b0SAdrian Chadd continue; 431*4ad7e9b0SAdrian Chadd 432*4ad7e9b0SAdrian Chadd /* Found */ 433*4ad7e9b0SAdrian Chadd *hw = next; 434*4ad7e9b0SAdrian Chadd return (0); 435*4ad7e9b0SAdrian Chadd } 436*4ad7e9b0SAdrian Chadd 437*4ad7e9b0SAdrian Chadd return (ENOENT); 438*4ad7e9b0SAdrian Chadd } 439*4ad7e9b0SAdrian Chadd 440*4ad7e9b0SAdrian Chadd /** 441*4ad7e9b0SAdrian Chadd * Read the ChipCommon identification data for this device. 442*4ad7e9b0SAdrian Chadd * 443*4ad7e9b0SAdrian Chadd * @param sc bhndb device state. 444*4ad7e9b0SAdrian Chadd * @param cfg The hardware configuration to use when mapping the ChipCommon 445*4ad7e9b0SAdrian Chadd * registers. 446*4ad7e9b0SAdrian Chadd * @param[out] result the chip identification data. 447*4ad7e9b0SAdrian Chadd * 448*4ad7e9b0SAdrian Chadd * @retval 0 success 449*4ad7e9b0SAdrian Chadd * @retval non-zero if the ChipCommon identification data could not be read. 450*4ad7e9b0SAdrian Chadd */ 451*4ad7e9b0SAdrian Chadd static int 452*4ad7e9b0SAdrian Chadd bhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg, 453*4ad7e9b0SAdrian Chadd struct bhnd_chipid *result) 454*4ad7e9b0SAdrian Chadd { 455*4ad7e9b0SAdrian Chadd const struct bhnd_chipid *parent_cid; 456*4ad7e9b0SAdrian Chadd const struct bhndb_regwin *cc_win; 457*4ad7e9b0SAdrian Chadd struct resource_spec rs; 458*4ad7e9b0SAdrian Chadd int error; 459*4ad7e9b0SAdrian Chadd 460*4ad7e9b0SAdrian Chadd /* Let our parent device override the discovery process */ 461*4ad7e9b0SAdrian Chadd parent_cid = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev); 462*4ad7e9b0SAdrian Chadd if (parent_cid != NULL) { 463*4ad7e9b0SAdrian Chadd *result = *parent_cid; 464*4ad7e9b0SAdrian Chadd return (0); 465*4ad7e9b0SAdrian Chadd } 466*4ad7e9b0SAdrian Chadd 467*4ad7e9b0SAdrian Chadd /* Find a register window we can use to map the first CHIPC_CHIPID_SIZE 468*4ad7e9b0SAdrian Chadd * of ChipCommon registers. */ 469*4ad7e9b0SAdrian Chadd cc_win = bhndb_regwin_find_best(cfg->register_windows, 470*4ad7e9b0SAdrian Chadd BHND_DEVCLASS_CC, 0, BHND_PORT_DEVICE, 0, 0, CHIPC_CHIPID_SIZE); 471*4ad7e9b0SAdrian Chadd if (cc_win == NULL) { 472*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "no chipcommon register window\n"); 473*4ad7e9b0SAdrian Chadd return (0); 474*4ad7e9b0SAdrian Chadd } 475*4ad7e9b0SAdrian Chadd 476*4ad7e9b0SAdrian Chadd /* We can assume a device without a static ChipCommon window uses the 477*4ad7e9b0SAdrian Chadd * default ChipCommon address. */ 478*4ad7e9b0SAdrian Chadd if (cc_win->win_type == BHNDB_REGWIN_T_DYN) { 479*4ad7e9b0SAdrian Chadd error = BHNDB_SET_WINDOW_ADDR(sc->dev, cc_win, 480*4ad7e9b0SAdrian Chadd BHND_DEFAULT_CHIPC_ADDR); 481*4ad7e9b0SAdrian Chadd 482*4ad7e9b0SAdrian Chadd if (error) { 483*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "failed to set chipcommon " 484*4ad7e9b0SAdrian Chadd "register window\n"); 485*4ad7e9b0SAdrian Chadd return (error); 486*4ad7e9b0SAdrian Chadd } 487*4ad7e9b0SAdrian Chadd } 488*4ad7e9b0SAdrian Chadd 489*4ad7e9b0SAdrian Chadd /* Let the default bhnd implemenation alloc/release the resource and 490*4ad7e9b0SAdrian Chadd * perform the read */ 491*4ad7e9b0SAdrian Chadd rs.type = cc_win->res.type; 492*4ad7e9b0SAdrian Chadd rs.rid = cc_win->res.rid; 493*4ad7e9b0SAdrian Chadd rs.flags = RF_ACTIVE; 494*4ad7e9b0SAdrian Chadd 495*4ad7e9b0SAdrian Chadd return (bhnd_read_chipid(sc->parent_dev, &rs, cc_win->win_offset, 496*4ad7e9b0SAdrian Chadd result)); 497*4ad7e9b0SAdrian Chadd } 498*4ad7e9b0SAdrian Chadd 499*4ad7e9b0SAdrian Chadd /** 500*4ad7e9b0SAdrian Chadd * Helper function that must be called by subclass bhndb(4) drivers 501*4ad7e9b0SAdrian Chadd * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4) 502*4ad7e9b0SAdrian Chadd * APIs on the bridge device. 503*4ad7e9b0SAdrian Chadd * 504*4ad7e9b0SAdrian Chadd * @param dev The bridge device to attach. 505*4ad7e9b0SAdrian Chadd * @param bridge_devclass The device class of the bridging core. This is used 506*4ad7e9b0SAdrian Chadd * to automatically detect the bridge core, and to disable additional bridge 507*4ad7e9b0SAdrian Chadd * cores (e.g. PCMCIA on a PCIe device). 508*4ad7e9b0SAdrian Chadd */ 509*4ad7e9b0SAdrian Chadd int 510*4ad7e9b0SAdrian Chadd bhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass) 511*4ad7e9b0SAdrian Chadd { 512*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 513*4ad7e9b0SAdrian Chadd const struct bhndb_hwcfg *cfg; 514*4ad7e9b0SAdrian Chadd int error; 515*4ad7e9b0SAdrian Chadd 516*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 517*4ad7e9b0SAdrian Chadd sc->dev = dev; 518*4ad7e9b0SAdrian Chadd sc->parent_dev = device_get_parent(dev); 519*4ad7e9b0SAdrian Chadd sc->bridge_class = bridge_devclass; 520*4ad7e9b0SAdrian Chadd 521*4ad7e9b0SAdrian Chadd BHNDB_LOCK_INIT(sc); 522*4ad7e9b0SAdrian Chadd 523*4ad7e9b0SAdrian Chadd /* Read our chip identification data */ 524*4ad7e9b0SAdrian Chadd cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev); 525*4ad7e9b0SAdrian Chadd if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid))) 526*4ad7e9b0SAdrian Chadd return (error); 527*4ad7e9b0SAdrian Chadd 528*4ad7e9b0SAdrian Chadd /* Set up a resource manager for the device's address space. */ 529*4ad7e9b0SAdrian Chadd sc->mem_rman.rm_start = 0; 530*4ad7e9b0SAdrian Chadd sc->mem_rman.rm_end = BUS_SPACE_MAXADDR_32BIT; 531*4ad7e9b0SAdrian Chadd sc->mem_rman.rm_type = RMAN_ARRAY; 532*4ad7e9b0SAdrian Chadd sc->mem_rman.rm_descr = "BHND I/O memory addresses"; 533*4ad7e9b0SAdrian Chadd 534*4ad7e9b0SAdrian Chadd if ((error = rman_init(&sc->mem_rman))) { 535*4ad7e9b0SAdrian Chadd device_printf(dev, "could not initialize mem_rman\n"); 536*4ad7e9b0SAdrian Chadd return (error); 537*4ad7e9b0SAdrian Chadd } 538*4ad7e9b0SAdrian Chadd 539*4ad7e9b0SAdrian Chadd error = rman_manage_region(&sc->mem_rman, 0, BUS_SPACE_MAXADDR_32BIT); 540*4ad7e9b0SAdrian Chadd if (error) { 541*4ad7e9b0SAdrian Chadd device_printf(dev, "could not configure mem_rman\n"); 542*4ad7e9b0SAdrian Chadd goto failed; 543*4ad7e9b0SAdrian Chadd } 544*4ad7e9b0SAdrian Chadd 545*4ad7e9b0SAdrian Chadd /* Initialize basic resource allocation state. */ 546*4ad7e9b0SAdrian Chadd sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg); 547*4ad7e9b0SAdrian Chadd if (sc->bus_res == NULL) { 548*4ad7e9b0SAdrian Chadd error = ENXIO; 549*4ad7e9b0SAdrian Chadd goto failed; 550*4ad7e9b0SAdrian Chadd } 551*4ad7e9b0SAdrian Chadd 552*4ad7e9b0SAdrian Chadd /* Attach our bridged bus device */ 553*4ad7e9b0SAdrian Chadd sc->bus_dev = device_add_child(dev, devclass_get_name(bhnd_devclass), 554*4ad7e9b0SAdrian Chadd -1); 555*4ad7e9b0SAdrian Chadd if (sc->bus_dev == NULL) { 556*4ad7e9b0SAdrian Chadd error = ENXIO; 557*4ad7e9b0SAdrian Chadd goto failed; 558*4ad7e9b0SAdrian Chadd } 559*4ad7e9b0SAdrian Chadd 560*4ad7e9b0SAdrian Chadd return (bus_generic_attach(dev)); 561*4ad7e9b0SAdrian Chadd 562*4ad7e9b0SAdrian Chadd failed: 563*4ad7e9b0SAdrian Chadd BHNDB_LOCK_DESTROY(sc); 564*4ad7e9b0SAdrian Chadd 565*4ad7e9b0SAdrian Chadd rman_fini(&sc->mem_rman); 566*4ad7e9b0SAdrian Chadd 567*4ad7e9b0SAdrian Chadd if (sc->bus_res != NULL) 568*4ad7e9b0SAdrian Chadd bhndb_free_resources(sc->bus_res); 569*4ad7e9b0SAdrian Chadd 570*4ad7e9b0SAdrian Chadd return (error); 571*4ad7e9b0SAdrian Chadd } 572*4ad7e9b0SAdrian Chadd 573*4ad7e9b0SAdrian Chadd /** 574*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BHNDB_INIT_FULL_CONFIG(). 575*4ad7e9b0SAdrian Chadd * 576*4ad7e9b0SAdrian Chadd * This function provides the default bhndb implementation of 577*4ad7e9b0SAdrian Chadd * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver 578*4ad7e9b0SAdrian Chadd * overriding BHNDB_INIT_FULL_CONFIG(). 579*4ad7e9b0SAdrian Chadd * 580*4ad7e9b0SAdrian Chadd * As documented by BHNDB_INIT_FULL_CONFIG, this function performs final 581*4ad7e9b0SAdrian Chadd * bridge configuration based on the hardware information enumerated by the 582*4ad7e9b0SAdrian Chadd * child bus, and will reset all resource allocation state on the bridge. 583*4ad7e9b0SAdrian Chadd * 584*4ad7e9b0SAdrian Chadd * When calling this method: 585*4ad7e9b0SAdrian Chadd * - Any bus resources previously allocated by @p child must be deallocated. 586*4ad7e9b0SAdrian Chadd * - The @p child bus must have performed initial enumeration -- but not 587*4ad7e9b0SAdrian Chadd * probe or attachment -- of its children. 588*4ad7e9b0SAdrian Chadd */ 589*4ad7e9b0SAdrian Chadd int 590*4ad7e9b0SAdrian Chadd bhndb_generic_init_full_config(device_t dev, device_t child, 591*4ad7e9b0SAdrian Chadd const struct bhndb_hw_priority *hw_prio_table) 592*4ad7e9b0SAdrian Chadd { 593*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 594*4ad7e9b0SAdrian Chadd const struct bhndb_hw *hw; 595*4ad7e9b0SAdrian Chadd struct bhndb_resources *r; 596*4ad7e9b0SAdrian Chadd device_t *devs; 597*4ad7e9b0SAdrian Chadd device_t hostb; 598*4ad7e9b0SAdrian Chadd int ndevs; 599*4ad7e9b0SAdrian Chadd int error; 600*4ad7e9b0SAdrian Chadd 601*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 602*4ad7e9b0SAdrian Chadd hostb = NULL; 603*4ad7e9b0SAdrian Chadd 604*4ad7e9b0SAdrian Chadd /* Fetch the full set of attached devices */ 605*4ad7e9b0SAdrian Chadd if ((error = device_get_children(sc->bus_dev, &devs, &ndevs))) 606*4ad7e9b0SAdrian Chadd return (error); 607*4ad7e9b0SAdrian Chadd 608*4ad7e9b0SAdrian Chadd /* Find our host bridge device */ 609*4ad7e9b0SAdrian Chadd for (int i = 0; i < ndevs; i++) { 610*4ad7e9b0SAdrian Chadd if (bhnd_is_hostb_device(devs[i])) { 611*4ad7e9b0SAdrian Chadd hostb = devs[i]; 612*4ad7e9b0SAdrian Chadd break; 613*4ad7e9b0SAdrian Chadd } 614*4ad7e9b0SAdrian Chadd } 615*4ad7e9b0SAdrian Chadd 616*4ad7e9b0SAdrian Chadd if (hostb == NULL) { 617*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "no host bridge core found\n"); 618*4ad7e9b0SAdrian Chadd error = ENODEV; 619*4ad7e9b0SAdrian Chadd goto cleanup; 620*4ad7e9b0SAdrian Chadd } 621*4ad7e9b0SAdrian Chadd 622*4ad7e9b0SAdrian Chadd /* Find our full register window configuration */ 623*4ad7e9b0SAdrian Chadd if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) { 624*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "unable to identify device, " 625*4ad7e9b0SAdrian Chadd " using generic bridge resource definitions\n"); 626*4ad7e9b0SAdrian Chadd error = 0; 627*4ad7e9b0SAdrian Chadd goto cleanup; 628*4ad7e9b0SAdrian Chadd } 629*4ad7e9b0SAdrian Chadd 630*4ad7e9b0SAdrian Chadd if (bootverbose) 631*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "%s resource configuration\n", hw->name); 632*4ad7e9b0SAdrian Chadd 633*4ad7e9b0SAdrian Chadd /* Release existing resource state */ 634*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); 635*4ad7e9b0SAdrian Chadd bhndb_free_resources(sc->bus_res); 636*4ad7e9b0SAdrian Chadd sc->bus_res = NULL; 637*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 638*4ad7e9b0SAdrian Chadd 639*4ad7e9b0SAdrian Chadd /* Allocate new resource state */ 640*4ad7e9b0SAdrian Chadd r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg); 641*4ad7e9b0SAdrian Chadd if (r == NULL) { 642*4ad7e9b0SAdrian Chadd error = ENXIO; 643*4ad7e9b0SAdrian Chadd goto cleanup; 644*4ad7e9b0SAdrian Chadd } 645*4ad7e9b0SAdrian Chadd 646*4ad7e9b0SAdrian Chadd /* Initialize our resource priority configuration */ 647*4ad7e9b0SAdrian Chadd error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r); 648*4ad7e9b0SAdrian Chadd if (error) { 649*4ad7e9b0SAdrian Chadd bhndb_free_resources(r); 650*4ad7e9b0SAdrian Chadd goto cleanup; 651*4ad7e9b0SAdrian Chadd } 652*4ad7e9b0SAdrian Chadd 653*4ad7e9b0SAdrian Chadd /* Update our bridge state */ 654*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); 655*4ad7e9b0SAdrian Chadd sc->bus_res = r; 656*4ad7e9b0SAdrian Chadd sc->hostb_dev = hostb; 657*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 658*4ad7e9b0SAdrian Chadd 659*4ad7e9b0SAdrian Chadd cleanup: 660*4ad7e9b0SAdrian Chadd free(devs, M_TEMP); 661*4ad7e9b0SAdrian Chadd return (error); 662*4ad7e9b0SAdrian Chadd } 663*4ad7e9b0SAdrian Chadd 664*4ad7e9b0SAdrian Chadd /** 665*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of DEVICE_DETACH(). 666*4ad7e9b0SAdrian Chadd * 667*4ad7e9b0SAdrian Chadd * This function detaches any child devices, and if successful, releases all 668*4ad7e9b0SAdrian Chadd * resources held by the bridge device. 669*4ad7e9b0SAdrian Chadd */ 670*4ad7e9b0SAdrian Chadd int 671*4ad7e9b0SAdrian Chadd bhndb_generic_detach(device_t dev) 672*4ad7e9b0SAdrian Chadd { 673*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 674*4ad7e9b0SAdrian Chadd int error; 675*4ad7e9b0SAdrian Chadd 676*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 677*4ad7e9b0SAdrian Chadd 678*4ad7e9b0SAdrian Chadd /* Detach children */ 679*4ad7e9b0SAdrian Chadd if ((error = bus_generic_detach(dev))) 680*4ad7e9b0SAdrian Chadd return (error); 681*4ad7e9b0SAdrian Chadd 682*4ad7e9b0SAdrian Chadd /* Clean up our driver state. */ 683*4ad7e9b0SAdrian Chadd rman_fini(&sc->mem_rman); 684*4ad7e9b0SAdrian Chadd bhndb_free_resources(sc->bus_res); 685*4ad7e9b0SAdrian Chadd 686*4ad7e9b0SAdrian Chadd BHNDB_LOCK_DESTROY(sc); 687*4ad7e9b0SAdrian Chadd 688*4ad7e9b0SAdrian Chadd return (0); 689*4ad7e9b0SAdrian Chadd } 690*4ad7e9b0SAdrian Chadd 691*4ad7e9b0SAdrian Chadd /** 692*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of DEVICE_SUSPEND(). 693*4ad7e9b0SAdrian Chadd * 694*4ad7e9b0SAdrian Chadd * This function calls bus_generic_suspend() (or implements equivalent 695*4ad7e9b0SAdrian Chadd * behavior). 696*4ad7e9b0SAdrian Chadd */ 697*4ad7e9b0SAdrian Chadd int 698*4ad7e9b0SAdrian Chadd bhndb_generic_suspend(device_t dev) 699*4ad7e9b0SAdrian Chadd { 700*4ad7e9b0SAdrian Chadd return (bus_generic_suspend(dev)); 701*4ad7e9b0SAdrian Chadd } 702*4ad7e9b0SAdrian Chadd 703*4ad7e9b0SAdrian Chadd /** 704*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of DEVICE_RESUME(). 705*4ad7e9b0SAdrian Chadd * 706*4ad7e9b0SAdrian Chadd * This function calls bus_generic_resume() (or implements equivalent 707*4ad7e9b0SAdrian Chadd * behavior). 708*4ad7e9b0SAdrian Chadd */ 709*4ad7e9b0SAdrian Chadd int 710*4ad7e9b0SAdrian Chadd bhndb_generic_resume(device_t dev) 711*4ad7e9b0SAdrian Chadd { 712*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 713*4ad7e9b0SAdrian Chadd struct bhndb_resources *bus_res; 714*4ad7e9b0SAdrian Chadd struct bhndb_dw_alloc *dwa; 715*4ad7e9b0SAdrian Chadd int error; 716*4ad7e9b0SAdrian Chadd 717*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 718*4ad7e9b0SAdrian Chadd bus_res = sc->bus_res; 719*4ad7e9b0SAdrian Chadd 720*4ad7e9b0SAdrian Chadd /* Guarantee that all in-use dynamic register windows are mapped to 721*4ad7e9b0SAdrian Chadd * their previously configured target address. */ 722*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); 723*4ad7e9b0SAdrian Chadd for (size_t i = 0; i < bus_res->dwa_count; i++) { 724*4ad7e9b0SAdrian Chadd dwa = &bus_res->dw_alloc[i]; 725*4ad7e9b0SAdrian Chadd 726*4ad7e9b0SAdrian Chadd /* Skip regions that were not previously used */ 727*4ad7e9b0SAdrian Chadd if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0) 728*4ad7e9b0SAdrian Chadd continue; 729*4ad7e9b0SAdrian Chadd 730*4ad7e9b0SAdrian Chadd /* Otherwise, ensure the register window is correct before 731*4ad7e9b0SAdrian Chadd * any children attempt MMIO */ 732*4ad7e9b0SAdrian Chadd error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); 733*4ad7e9b0SAdrian Chadd if (error) 734*4ad7e9b0SAdrian Chadd break; 735*4ad7e9b0SAdrian Chadd } 736*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 737*4ad7e9b0SAdrian Chadd 738*4ad7e9b0SAdrian Chadd /* Error restoring hardware state; children cannot be safely resumed */ 739*4ad7e9b0SAdrian Chadd if (error) { 740*4ad7e9b0SAdrian Chadd device_printf(dev, "Unable to restore hardware configuration; " 741*4ad7e9b0SAdrian Chadd "cannot resume: %d\n", error); 742*4ad7e9b0SAdrian Chadd return (error); 743*4ad7e9b0SAdrian Chadd } 744*4ad7e9b0SAdrian Chadd 745*4ad7e9b0SAdrian Chadd return (bus_generic_resume(dev)); 746*4ad7e9b0SAdrian Chadd } 747*4ad7e9b0SAdrian Chadd 748*4ad7e9b0SAdrian Chadd /** 749*4ad7e9b0SAdrian Chadd * Default implementation of BHNDB_SUSPEND_RESOURCE. 750*4ad7e9b0SAdrian Chadd */ 751*4ad7e9b0SAdrian Chadd static void 752*4ad7e9b0SAdrian Chadd bhndb_suspend_resource(device_t dev, device_t child, int type, 753*4ad7e9b0SAdrian Chadd struct resource *r) 754*4ad7e9b0SAdrian Chadd { 755*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 756*4ad7e9b0SAdrian Chadd struct bhndb_dw_alloc *dwa; 757*4ad7e9b0SAdrian Chadd 758*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 759*4ad7e9b0SAdrian Chadd 760*4ad7e9b0SAdrian Chadd // TODO: IRQs? 761*4ad7e9b0SAdrian Chadd if (type != SYS_RES_MEMORY) 762*4ad7e9b0SAdrian Chadd return; 763*4ad7e9b0SAdrian Chadd 764*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); 765*4ad7e9b0SAdrian Chadd dwa = bhndb_dw_find_resource(sc->bus_res, r); 766*4ad7e9b0SAdrian Chadd if (dwa == NULL) { 767*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 768*4ad7e9b0SAdrian Chadd return; 769*4ad7e9b0SAdrian Chadd } 770*4ad7e9b0SAdrian Chadd 771*4ad7e9b0SAdrian Chadd if (BHNDB_DEBUG(PRIO)) 772*4ad7e9b0SAdrian Chadd device_printf(child, "suspend resource type=%d 0x%lx+0x%lx\n", 773*4ad7e9b0SAdrian Chadd type, rman_get_start(r), rman_get_size(r)); 774*4ad7e9b0SAdrian Chadd 775*4ad7e9b0SAdrian Chadd /* Release the resource's window reference */ 776*4ad7e9b0SAdrian Chadd bhndb_dw_release(sc->bus_res, dwa, r); 777*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 778*4ad7e9b0SAdrian Chadd } 779*4ad7e9b0SAdrian Chadd 780*4ad7e9b0SAdrian Chadd /** 781*4ad7e9b0SAdrian Chadd * Default implementation of BHNDB_RESUME_RESOURCE. 782*4ad7e9b0SAdrian Chadd */ 783*4ad7e9b0SAdrian Chadd static int 784*4ad7e9b0SAdrian Chadd bhndb_resume_resource(device_t dev, device_t child, int type, 785*4ad7e9b0SAdrian Chadd struct resource *r) 786*4ad7e9b0SAdrian Chadd { 787*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 788*4ad7e9b0SAdrian Chadd 789*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 790*4ad7e9b0SAdrian Chadd 791*4ad7e9b0SAdrian Chadd // TODO: IRQs? 792*4ad7e9b0SAdrian Chadd if (type != SYS_RES_MEMORY) 793*4ad7e9b0SAdrian Chadd return (0); 794*4ad7e9b0SAdrian Chadd 795*4ad7e9b0SAdrian Chadd /* Inactive resources don't require reallocation of bridge resources */ 796*4ad7e9b0SAdrian Chadd if (!(rman_get_flags(r) & RF_ACTIVE)) 797*4ad7e9b0SAdrian Chadd return (0); 798*4ad7e9b0SAdrian Chadd 799*4ad7e9b0SAdrian Chadd if (BHNDB_DEBUG(PRIO)) 800*4ad7e9b0SAdrian Chadd device_printf(child, "resume resource type=%d 0x%lx+0x%lx\n", 801*4ad7e9b0SAdrian Chadd type, rman_get_start(r), rman_get_size(r)); 802*4ad7e9b0SAdrian Chadd 803*4ad7e9b0SAdrian Chadd return (bhndb_try_activate_resource(sc, rman_get_device(r), type, 804*4ad7e9b0SAdrian Chadd rman_get_rid(r), r, NULL)); 805*4ad7e9b0SAdrian Chadd } 806*4ad7e9b0SAdrian Chadd 807*4ad7e9b0SAdrian Chadd 808*4ad7e9b0SAdrian Chadd /** 809*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_READ_IVAR(). 810*4ad7e9b0SAdrian Chadd */ 811*4ad7e9b0SAdrian Chadd static int 812*4ad7e9b0SAdrian Chadd bhndb_read_ivar(device_t dev, device_t child, int index, 813*4ad7e9b0SAdrian Chadd uintptr_t *result) 814*4ad7e9b0SAdrian Chadd { 815*4ad7e9b0SAdrian Chadd return (ENOENT); 816*4ad7e9b0SAdrian Chadd } 817*4ad7e9b0SAdrian Chadd 818*4ad7e9b0SAdrian Chadd /** 819*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_WRITE_IVAR(). 820*4ad7e9b0SAdrian Chadd */ 821*4ad7e9b0SAdrian Chadd static int 822*4ad7e9b0SAdrian Chadd bhndb_write_ivar(device_t dev, device_t child, int index, 823*4ad7e9b0SAdrian Chadd uintptr_t value) 824*4ad7e9b0SAdrian Chadd { 825*4ad7e9b0SAdrian Chadd return (ENOENT); 826*4ad7e9b0SAdrian Chadd } 827*4ad7e9b0SAdrian Chadd 828*4ad7e9b0SAdrian Chadd /** 829*4ad7e9b0SAdrian Chadd * Return the rman instance for a given resource @p type, if any. 830*4ad7e9b0SAdrian Chadd * 831*4ad7e9b0SAdrian Chadd * @param sc The bhndb device state. 832*4ad7e9b0SAdrian Chadd * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...) 833*4ad7e9b0SAdrian Chadd */ 834*4ad7e9b0SAdrian Chadd static struct rman * 835*4ad7e9b0SAdrian Chadd bhndb_get_rman(struct bhndb_softc *sc, int type) 836*4ad7e9b0SAdrian Chadd { 837*4ad7e9b0SAdrian Chadd switch (type) { 838*4ad7e9b0SAdrian Chadd case SYS_RES_MEMORY: 839*4ad7e9b0SAdrian Chadd return &sc->mem_rman; 840*4ad7e9b0SAdrian Chadd case SYS_RES_IRQ: 841*4ad7e9b0SAdrian Chadd // TODO 842*4ad7e9b0SAdrian Chadd // return &sc->irq_rman; 843*4ad7e9b0SAdrian Chadd return (NULL); 844*4ad7e9b0SAdrian Chadd default: 845*4ad7e9b0SAdrian Chadd return (NULL); 846*4ad7e9b0SAdrian Chadd }; 847*4ad7e9b0SAdrian Chadd } 848*4ad7e9b0SAdrian Chadd 849*4ad7e9b0SAdrian Chadd /** 850*4ad7e9b0SAdrian Chadd * Default implementation of BUS_ADD_CHILD() 851*4ad7e9b0SAdrian Chadd */ 852*4ad7e9b0SAdrian Chadd static device_t 853*4ad7e9b0SAdrian Chadd bhndb_add_child(device_t dev, u_int order, const char *name, int unit) 854*4ad7e9b0SAdrian Chadd { 855*4ad7e9b0SAdrian Chadd struct bhndb_devinfo *dinfo; 856*4ad7e9b0SAdrian Chadd device_t child; 857*4ad7e9b0SAdrian Chadd 858*4ad7e9b0SAdrian Chadd child = device_add_child_ordered(dev, order, name, unit); 859*4ad7e9b0SAdrian Chadd if (child == NULL) 860*4ad7e9b0SAdrian Chadd return (NULL); 861*4ad7e9b0SAdrian Chadd 862*4ad7e9b0SAdrian Chadd dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT); 863*4ad7e9b0SAdrian Chadd if (dinfo == NULL) { 864*4ad7e9b0SAdrian Chadd device_delete_child(dev, child); 865*4ad7e9b0SAdrian Chadd return (NULL); 866*4ad7e9b0SAdrian Chadd } 867*4ad7e9b0SAdrian Chadd 868*4ad7e9b0SAdrian Chadd resource_list_init(&dinfo->resources); 869*4ad7e9b0SAdrian Chadd 870*4ad7e9b0SAdrian Chadd device_set_ivars(child, dinfo); 871*4ad7e9b0SAdrian Chadd 872*4ad7e9b0SAdrian Chadd return (child); 873*4ad7e9b0SAdrian Chadd } 874*4ad7e9b0SAdrian Chadd 875*4ad7e9b0SAdrian Chadd /** 876*4ad7e9b0SAdrian Chadd * Default implementation of BUS_CHILD_DELETED(). 877*4ad7e9b0SAdrian Chadd */ 878*4ad7e9b0SAdrian Chadd static void 879*4ad7e9b0SAdrian Chadd bhndb_child_deleted(device_t dev, device_t child) 880*4ad7e9b0SAdrian Chadd { 881*4ad7e9b0SAdrian Chadd struct bhndb_devinfo *dinfo = device_get_ivars(child); 882*4ad7e9b0SAdrian Chadd if (dinfo != NULL) { 883*4ad7e9b0SAdrian Chadd resource_list_free(&dinfo->resources); 884*4ad7e9b0SAdrian Chadd free(dinfo, M_BHND); 885*4ad7e9b0SAdrian Chadd } 886*4ad7e9b0SAdrian Chadd 887*4ad7e9b0SAdrian Chadd device_set_ivars(child, NULL); 888*4ad7e9b0SAdrian Chadd } 889*4ad7e9b0SAdrian Chadd 890*4ad7e9b0SAdrian Chadd /** 891*4ad7e9b0SAdrian Chadd * Default implementation of BHNDB_GET_CHIPID(). 892*4ad7e9b0SAdrian Chadd */ 893*4ad7e9b0SAdrian Chadd static const struct bhnd_chipid * 894*4ad7e9b0SAdrian Chadd bhndb_get_chipid(device_t dev, device_t child) 895*4ad7e9b0SAdrian Chadd { 896*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc = device_get_softc(dev); 897*4ad7e9b0SAdrian Chadd return (&sc->chipid); 898*4ad7e9b0SAdrian Chadd } 899*4ad7e9b0SAdrian Chadd 900*4ad7e9b0SAdrian Chadd 901*4ad7e9b0SAdrian Chadd /** 902*4ad7e9b0SAdrian Chadd * Default implementation of BHNDB_IS_HW_DISABLED(). 903*4ad7e9b0SAdrian Chadd */ 904*4ad7e9b0SAdrian Chadd static bool 905*4ad7e9b0SAdrian Chadd bhndb_is_hw_disabled(device_t dev, device_t child) { 906*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 907*4ad7e9b0SAdrian Chadd struct bhnd_core_info core; 908*4ad7e9b0SAdrian Chadd 909*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 910*4ad7e9b0SAdrian Chadd 911*4ad7e9b0SAdrian Chadd /* Requestor must be attached to the bhnd bus */ 912*4ad7e9b0SAdrian Chadd if (device_get_parent(child) != sc->bus_dev) { 913*4ad7e9b0SAdrian Chadd return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child)); 914*4ad7e9b0SAdrian Chadd } 915*4ad7e9b0SAdrian Chadd 916*4ad7e9b0SAdrian Chadd /* Fetch core info */ 917*4ad7e9b0SAdrian Chadd core = bhnd_get_core_info(child); 918*4ad7e9b0SAdrian Chadd 919*4ad7e9b0SAdrian Chadd /* Try to defer to the bhndb bus parent */ 920*4ad7e9b0SAdrian Chadd if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, &core)) 921*4ad7e9b0SAdrian Chadd return (true); 922*4ad7e9b0SAdrian Chadd 923*4ad7e9b0SAdrian Chadd /* Otherwise, we treat bridge-capable cores as unpopulated if they're 924*4ad7e9b0SAdrian Chadd * not the configured host bridge */ 925*4ad7e9b0SAdrian Chadd if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(&core))) 926*4ad7e9b0SAdrian Chadd return (!BHND_BUS_IS_HOSTB_DEVICE(dev, child)); 927*4ad7e9b0SAdrian Chadd 928*4ad7e9b0SAdrian Chadd /* Otherwise, assume the core is populated */ 929*4ad7e9b0SAdrian Chadd return (false); 930*4ad7e9b0SAdrian Chadd } 931*4ad7e9b0SAdrian Chadd 932*4ad7e9b0SAdrian Chadd /* ascending core index comparison used by bhndb_is_hostb_device() */ 933*4ad7e9b0SAdrian Chadd static int 934*4ad7e9b0SAdrian Chadd compare_core_index(const void *lhs, const void *rhs) 935*4ad7e9b0SAdrian Chadd { 936*4ad7e9b0SAdrian Chadd u_int left = bhnd_get_core_index(*(const device_t *) lhs); 937*4ad7e9b0SAdrian Chadd u_int right = bhnd_get_core_index(*(const device_t *) rhs); 938*4ad7e9b0SAdrian Chadd 939*4ad7e9b0SAdrian Chadd if (left < right) 940*4ad7e9b0SAdrian Chadd return (-1); 941*4ad7e9b0SAdrian Chadd else if (left > right) 942*4ad7e9b0SAdrian Chadd return (1); 943*4ad7e9b0SAdrian Chadd else 944*4ad7e9b0SAdrian Chadd return (0); 945*4ad7e9b0SAdrian Chadd } 946*4ad7e9b0SAdrian Chadd 947*4ad7e9b0SAdrian Chadd /** 948*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BHND_BUS_IS_HOSTB_DEVICE(). 949*4ad7e9b0SAdrian Chadd * 950*4ad7e9b0SAdrian Chadd * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged 951*4ad7e9b0SAdrian Chadd * bhnd(4) devices to determine the hostb core: 952*4ad7e9b0SAdrian Chadd * 953*4ad7e9b0SAdrian Chadd * - The core must have a Broadcom vendor ID. 954*4ad7e9b0SAdrian Chadd * - The core devclass must match the bridge type. 955*4ad7e9b0SAdrian Chadd * - The core must be the first device on the bus with the bridged device 956*4ad7e9b0SAdrian Chadd * class. 957*4ad7e9b0SAdrian Chadd * 958*4ad7e9b0SAdrian Chadd * @param sc The bridge device state. 959*4ad7e9b0SAdrian Chadd * @param cores The table of bridge-enumerated cores. 960*4ad7e9b0SAdrian Chadd * @param num_cores The length of @p cores. 961*4ad7e9b0SAdrian Chadd * @param core The core to check. 962*4ad7e9b0SAdrian Chadd */ 963*4ad7e9b0SAdrian Chadd static bool 964*4ad7e9b0SAdrian Chadd bhndb_is_hostb_device(device_t dev, device_t child) 965*4ad7e9b0SAdrian Chadd { 966*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 967*4ad7e9b0SAdrian Chadd struct bhnd_core_match md; 968*4ad7e9b0SAdrian Chadd device_t hostb_dev, *devlist; 969*4ad7e9b0SAdrian Chadd int devcnt, error; 970*4ad7e9b0SAdrian Chadd 971*4ad7e9b0SAdrian Chadd 972*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 973*4ad7e9b0SAdrian Chadd 974*4ad7e9b0SAdrian Chadd /* Requestor must be attached to the bhnd bus */ 975*4ad7e9b0SAdrian Chadd if (device_get_parent(child) != sc->bus_dev) 976*4ad7e9b0SAdrian Chadd return (BHND_BUS_IS_HOSTB_DEVICE(device_get_parent(dev), 977*4ad7e9b0SAdrian Chadd child)); 978*4ad7e9b0SAdrian Chadd 979*4ad7e9b0SAdrian Chadd /* Determine required device class and set up a match descriptor. */ 980*4ad7e9b0SAdrian Chadd md = (struct bhnd_core_match) { 981*4ad7e9b0SAdrian Chadd .vendor = BHND_MFGID_BCM, 982*4ad7e9b0SAdrian Chadd .device = BHND_COREID_INVALID, 983*4ad7e9b0SAdrian Chadd .hwrev = { BHND_HWREV_INVALID, BHND_HWREV_INVALID }, 984*4ad7e9b0SAdrian Chadd .class = sc->bridge_class, 985*4ad7e9b0SAdrian Chadd .unit = 0 986*4ad7e9b0SAdrian Chadd }; 987*4ad7e9b0SAdrian Chadd 988*4ad7e9b0SAdrian Chadd /* Pre-screen the device before searching over the full device list. */ 989*4ad7e9b0SAdrian Chadd if (!bhnd_device_matches(child, &md)) 990*4ad7e9b0SAdrian Chadd return (false); 991*4ad7e9b0SAdrian Chadd 992*4ad7e9b0SAdrian Chadd /* Must be the absolute first matching device on the bus. */ 993*4ad7e9b0SAdrian Chadd if ((error = device_get_children(sc->bus_dev, &devlist, &devcnt))) 994*4ad7e9b0SAdrian Chadd return (false); 995*4ad7e9b0SAdrian Chadd 996*4ad7e9b0SAdrian Chadd /* Sort by core index value, ascending */ 997*4ad7e9b0SAdrian Chadd qsort(devlist, devcnt, sizeof(*devlist), compare_core_index); 998*4ad7e9b0SAdrian Chadd 999*4ad7e9b0SAdrian Chadd /* Find the actual hostb device */ 1000*4ad7e9b0SAdrian Chadd hostb_dev = NULL; 1001*4ad7e9b0SAdrian Chadd for (int i = 0; i < devcnt; i++) { 1002*4ad7e9b0SAdrian Chadd if (bhnd_device_matches(devlist[i], &md)) { 1003*4ad7e9b0SAdrian Chadd hostb_dev = devlist[i]; 1004*4ad7e9b0SAdrian Chadd break; 1005*4ad7e9b0SAdrian Chadd } 1006*4ad7e9b0SAdrian Chadd } 1007*4ad7e9b0SAdrian Chadd 1008*4ad7e9b0SAdrian Chadd /* Clean up */ 1009*4ad7e9b0SAdrian Chadd free(devlist, M_TEMP); 1010*4ad7e9b0SAdrian Chadd 1011*4ad7e9b0SAdrian Chadd return (child == hostb_dev); 1012*4ad7e9b0SAdrian Chadd } 1013*4ad7e9b0SAdrian Chadd 1014*4ad7e9b0SAdrian Chadd /** 1015*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE(). 1016*4ad7e9b0SAdrian Chadd */ 1017*4ad7e9b0SAdrian Chadd static struct resource * 1018*4ad7e9b0SAdrian Chadd bhndb_alloc_resource(device_t dev, device_t child, int type, 1019*4ad7e9b0SAdrian Chadd int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 1020*4ad7e9b0SAdrian Chadd { 1021*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 1022*4ad7e9b0SAdrian Chadd struct resource_list_entry *rle; 1023*4ad7e9b0SAdrian Chadd struct resource *rv; 1024*4ad7e9b0SAdrian Chadd struct rman *rm; 1025*4ad7e9b0SAdrian Chadd int error; 1026*4ad7e9b0SAdrian Chadd bool immed_child, defaults; 1027*4ad7e9b0SAdrian Chadd 1028*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1029*4ad7e9b0SAdrian Chadd immed_child = (device_get_parent(child) == dev); 1030*4ad7e9b0SAdrian Chadd defaults = (start == 0UL && end == ~0UL); 1031*4ad7e9b0SAdrian Chadd rle = NULL; 1032*4ad7e9b0SAdrian Chadd 1033*4ad7e9b0SAdrian Chadd /* Populate defaults */ 1034*4ad7e9b0SAdrian Chadd if (immed_child && defaults) { 1035*4ad7e9b0SAdrian Chadd /* Fetch the resource list entry. */ 1036*4ad7e9b0SAdrian Chadd rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), 1037*4ad7e9b0SAdrian Chadd type, *rid); 1038*4ad7e9b0SAdrian Chadd if (rle == NULL) { 1039*4ad7e9b0SAdrian Chadd device_printf(dev, 1040*4ad7e9b0SAdrian Chadd "default resource %#x type %d for child %s " 1041*4ad7e9b0SAdrian Chadd "not found\n", *rid, type, 1042*4ad7e9b0SAdrian Chadd device_get_nameunit(child)); 1043*4ad7e9b0SAdrian Chadd 1044*4ad7e9b0SAdrian Chadd return (NULL); 1045*4ad7e9b0SAdrian Chadd } 1046*4ad7e9b0SAdrian Chadd 1047*4ad7e9b0SAdrian Chadd if (rle->res != NULL) { 1048*4ad7e9b0SAdrian Chadd device_printf(dev, 1049*4ad7e9b0SAdrian Chadd "resource entry %#x type %d for child %s is busy\n", 1050*4ad7e9b0SAdrian Chadd *rid, type, device_get_nameunit(child)); 1051*4ad7e9b0SAdrian Chadd 1052*4ad7e9b0SAdrian Chadd return (NULL); 1053*4ad7e9b0SAdrian Chadd } 1054*4ad7e9b0SAdrian Chadd 1055*4ad7e9b0SAdrian Chadd start = rle->start; 1056*4ad7e9b0SAdrian Chadd end = rle->end; 1057*4ad7e9b0SAdrian Chadd count = ulmax(count, rle->count); 1058*4ad7e9b0SAdrian Chadd } 1059*4ad7e9b0SAdrian Chadd 1060*4ad7e9b0SAdrian Chadd /* Validate resource addresses */ 1061*4ad7e9b0SAdrian Chadd if (start > end || end < start || count > ((end - start) + 1)) 1062*4ad7e9b0SAdrian Chadd return (NULL); 1063*4ad7e9b0SAdrian Chadd 1064*4ad7e9b0SAdrian Chadd /* Fetch the resource manager */ 1065*4ad7e9b0SAdrian Chadd rm = bhndb_get_rman(sc, type); 1066*4ad7e9b0SAdrian Chadd if (rm == NULL) 1067*4ad7e9b0SAdrian Chadd return (NULL); 1068*4ad7e9b0SAdrian Chadd 1069*4ad7e9b0SAdrian Chadd /* Make our reservation */ 1070*4ad7e9b0SAdrian Chadd rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, 1071*4ad7e9b0SAdrian Chadd child); 1072*4ad7e9b0SAdrian Chadd if (rv == NULL) 1073*4ad7e9b0SAdrian Chadd return (NULL); 1074*4ad7e9b0SAdrian Chadd 1075*4ad7e9b0SAdrian Chadd rman_set_rid(rv, *rid); 1076*4ad7e9b0SAdrian Chadd 1077*4ad7e9b0SAdrian Chadd /* Activate */ 1078*4ad7e9b0SAdrian Chadd if (flags & RF_ACTIVE) { 1079*4ad7e9b0SAdrian Chadd error = bus_activate_resource(child, type, *rid, rv); 1080*4ad7e9b0SAdrian Chadd if (error) { 1081*4ad7e9b0SAdrian Chadd device_printf(dev, 1082*4ad7e9b0SAdrian Chadd "failed to activate entry %#x type %d for " 1083*4ad7e9b0SAdrian Chadd "child %s\n", 1084*4ad7e9b0SAdrian Chadd *rid, type, device_get_nameunit(child)); 1085*4ad7e9b0SAdrian Chadd 1086*4ad7e9b0SAdrian Chadd rman_release_resource(rv); 1087*4ad7e9b0SAdrian Chadd 1088*4ad7e9b0SAdrian Chadd return (NULL); 1089*4ad7e9b0SAdrian Chadd } 1090*4ad7e9b0SAdrian Chadd } 1091*4ad7e9b0SAdrian Chadd 1092*4ad7e9b0SAdrian Chadd /* Update child's resource list entry */ 1093*4ad7e9b0SAdrian Chadd if (rle != NULL) { 1094*4ad7e9b0SAdrian Chadd rle->res = rv; 1095*4ad7e9b0SAdrian Chadd rle->start = rman_get_start(rv); 1096*4ad7e9b0SAdrian Chadd rle->end = rman_get_end(rv); 1097*4ad7e9b0SAdrian Chadd rle->count = rman_get_size(rv); 1098*4ad7e9b0SAdrian Chadd } 1099*4ad7e9b0SAdrian Chadd 1100*4ad7e9b0SAdrian Chadd return (rv); 1101*4ad7e9b0SAdrian Chadd } 1102*4ad7e9b0SAdrian Chadd 1103*4ad7e9b0SAdrian Chadd /** 1104*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE(). 1105*4ad7e9b0SAdrian Chadd */ 1106*4ad7e9b0SAdrian Chadd static int 1107*4ad7e9b0SAdrian Chadd bhndb_release_resource(device_t dev, device_t child, int type, int rid, 1108*4ad7e9b0SAdrian Chadd struct resource *r) 1109*4ad7e9b0SAdrian Chadd { 1110*4ad7e9b0SAdrian Chadd int error; 1111*4ad7e9b0SAdrian Chadd 1112*4ad7e9b0SAdrian Chadd /* Deactivate resources */ 1113*4ad7e9b0SAdrian Chadd if (rman_get_flags(r) & RF_ACTIVE) { 1114*4ad7e9b0SAdrian Chadd error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r); 1115*4ad7e9b0SAdrian Chadd if (error) 1116*4ad7e9b0SAdrian Chadd return (error); 1117*4ad7e9b0SAdrian Chadd } 1118*4ad7e9b0SAdrian Chadd 1119*4ad7e9b0SAdrian Chadd if ((error = rman_release_resource(r))) 1120*4ad7e9b0SAdrian Chadd return (error); 1121*4ad7e9b0SAdrian Chadd 1122*4ad7e9b0SAdrian Chadd return (0); 1123*4ad7e9b0SAdrian Chadd } 1124*4ad7e9b0SAdrian Chadd 1125*4ad7e9b0SAdrian Chadd /** 1126*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE(). 1127*4ad7e9b0SAdrian Chadd */ 1128*4ad7e9b0SAdrian Chadd static int 1129*4ad7e9b0SAdrian Chadd bhndb_adjust_resource(device_t dev, device_t child, int type, 1130*4ad7e9b0SAdrian Chadd struct resource *r, rman_res_t start, rman_res_t end) 1131*4ad7e9b0SAdrian Chadd { 1132*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 1133*4ad7e9b0SAdrian Chadd struct rman *rm; 1134*4ad7e9b0SAdrian Chadd int error; 1135*4ad7e9b0SAdrian Chadd 1136*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1137*4ad7e9b0SAdrian Chadd error = 0; 1138*4ad7e9b0SAdrian Chadd 1139*4ad7e9b0SAdrian Chadd /* Fetch resource manager */ 1140*4ad7e9b0SAdrian Chadd rm = bhndb_get_rman(sc, type); 1141*4ad7e9b0SAdrian Chadd if (rm == NULL) 1142*4ad7e9b0SAdrian Chadd return (ENXIO); 1143*4ad7e9b0SAdrian Chadd 1144*4ad7e9b0SAdrian Chadd if (!rman_is_region_manager(r, rm)) 1145*4ad7e9b0SAdrian Chadd return (ENXIO); 1146*4ad7e9b0SAdrian Chadd 1147*4ad7e9b0SAdrian Chadd /* If active, adjustment is limited by the assigned window. */ 1148*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); 1149*4ad7e9b0SAdrian Chadd 1150*4ad7e9b0SAdrian Chadd // TODO: Currently unsupported 1151*4ad7e9b0SAdrian Chadd error = ENODEV; 1152*4ad7e9b0SAdrian Chadd 1153*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 1154*4ad7e9b0SAdrian Chadd if (!error) 1155*4ad7e9b0SAdrian Chadd error = rman_adjust_resource(r, start, end); 1156*4ad7e9b0SAdrian Chadd 1157*4ad7e9b0SAdrian Chadd return (error); 1158*4ad7e9b0SAdrian Chadd } 1159*4ad7e9b0SAdrian Chadd 1160*4ad7e9b0SAdrian Chadd /** 1161*4ad7e9b0SAdrian Chadd * Initialize child resource @p r with a virtual address, tag, and handle 1162*4ad7e9b0SAdrian Chadd * copied from @p parent, adjusted to contain only the range defined by @p win. 1163*4ad7e9b0SAdrian Chadd * 1164*4ad7e9b0SAdrian Chadd * @param r The register to be initialized. 1165*4ad7e9b0SAdrian Chadd * @param parent The parent bus resource that fully contains the subregion. 1166*4ad7e9b0SAdrian Chadd * @param offset The subregion offset within @p parent. 1167*4ad7e9b0SAdrian Chadd * @param size The subregion size. 1168*4ad7e9b0SAdrian Chadd * @p r. 1169*4ad7e9b0SAdrian Chadd */ 1170*4ad7e9b0SAdrian Chadd static int 1171*4ad7e9b0SAdrian Chadd bhndb_init_child_resource(struct resource *r, 1172*4ad7e9b0SAdrian Chadd struct resource *parent, bhnd_size_t offset, bhnd_size_t size) 1173*4ad7e9b0SAdrian Chadd { 1174*4ad7e9b0SAdrian Chadd 1175*4ad7e9b0SAdrian Chadd bus_space_handle_t bh, child_bh; 1176*4ad7e9b0SAdrian Chadd bus_space_tag_t bt; 1177*4ad7e9b0SAdrian Chadd uintptr_t vaddr; 1178*4ad7e9b0SAdrian Chadd int error; 1179*4ad7e9b0SAdrian Chadd 1180*4ad7e9b0SAdrian Chadd /* Fetch the parent resource's real bus values */ 1181*4ad7e9b0SAdrian Chadd vaddr = (uintptr_t) rman_get_virtual(parent); 1182*4ad7e9b0SAdrian Chadd bt = rman_get_bustag(parent); 1183*4ad7e9b0SAdrian Chadd bh = rman_get_bushandle(parent); 1184*4ad7e9b0SAdrian Chadd 1185*4ad7e9b0SAdrian Chadd /* Configure child resource with window-adjusted real bus values */ 1186*4ad7e9b0SAdrian Chadd vaddr += offset; 1187*4ad7e9b0SAdrian Chadd error = bus_space_subregion(bt, bh, offset, size, &child_bh); 1188*4ad7e9b0SAdrian Chadd if (error) 1189*4ad7e9b0SAdrian Chadd return (error); 1190*4ad7e9b0SAdrian Chadd 1191*4ad7e9b0SAdrian Chadd rman_set_virtual(r, (void *) vaddr); 1192*4ad7e9b0SAdrian Chadd rman_set_bustag(r, bt); 1193*4ad7e9b0SAdrian Chadd rman_set_bushandle(r, child_bh); 1194*4ad7e9b0SAdrian Chadd 1195*4ad7e9b0SAdrian Chadd return (0); 1196*4ad7e9b0SAdrian Chadd } 1197*4ad7e9b0SAdrian Chadd 1198*4ad7e9b0SAdrian Chadd /** 1199*4ad7e9b0SAdrian Chadd * Attempt activation of a fixed register window mapping for @p child. 1200*4ad7e9b0SAdrian Chadd * 1201*4ad7e9b0SAdrian Chadd * @param sc BHNDB device state. 1202*4ad7e9b0SAdrian Chadd * @param region The static region definition capable of mapping @p r. 1203*4ad7e9b0SAdrian Chadd * @param child A child requesting resource activation. 1204*4ad7e9b0SAdrian Chadd * @param type Resource type. 1205*4ad7e9b0SAdrian Chadd * @param rid Resource identifier. 1206*4ad7e9b0SAdrian Chadd * @param r Resource to be activated. 1207*4ad7e9b0SAdrian Chadd * 1208*4ad7e9b0SAdrian Chadd * @retval 0 if @p r was activated successfully 1209*4ad7e9b0SAdrian Chadd * @retval ENOENT if no fixed register window was found. 1210*4ad7e9b0SAdrian Chadd * @retval non-zero if @p r could not be activated. 1211*4ad7e9b0SAdrian Chadd */ 1212*4ad7e9b0SAdrian Chadd static int 1213*4ad7e9b0SAdrian Chadd bhndb_activate_static_region(struct bhndb_softc *sc, 1214*4ad7e9b0SAdrian Chadd struct bhndb_region *region, device_t child, int type, int rid, 1215*4ad7e9b0SAdrian Chadd struct resource *r) 1216*4ad7e9b0SAdrian Chadd { 1217*4ad7e9b0SAdrian Chadd struct resource *bridge_res; 1218*4ad7e9b0SAdrian Chadd const struct bhndb_regwin *win; 1219*4ad7e9b0SAdrian Chadd bhnd_size_t parent_offset; 1220*4ad7e9b0SAdrian Chadd rman_res_t r_start, r_size; 1221*4ad7e9b0SAdrian Chadd int error; 1222*4ad7e9b0SAdrian Chadd 1223*4ad7e9b0SAdrian Chadd win = region->static_regwin; 1224*4ad7e9b0SAdrian Chadd 1225*4ad7e9b0SAdrian Chadd KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type), 1226*4ad7e9b0SAdrian Chadd ("can't activate non-static region")); 1227*4ad7e9b0SAdrian Chadd 1228*4ad7e9b0SAdrian Chadd r_start = rman_get_start(r); 1229*4ad7e9b0SAdrian Chadd r_size = rman_get_size(r); 1230*4ad7e9b0SAdrian Chadd 1231*4ad7e9b0SAdrian Chadd /* Find the corresponding bridge resource */ 1232*4ad7e9b0SAdrian Chadd bridge_res = bhndb_find_regwin_resource(sc->bus_res, win); 1233*4ad7e9b0SAdrian Chadd if (bridge_res == NULL) 1234*4ad7e9b0SAdrian Chadd return (ENXIO); 1235*4ad7e9b0SAdrian Chadd 1236*4ad7e9b0SAdrian Chadd /* Calculate subregion offset within the parent resource */ 1237*4ad7e9b0SAdrian Chadd parent_offset = r_start - region->addr; 1238*4ad7e9b0SAdrian Chadd parent_offset += win->win_offset; 1239*4ad7e9b0SAdrian Chadd 1240*4ad7e9b0SAdrian Chadd /* Configure resource with its real bus values. */ 1241*4ad7e9b0SAdrian Chadd error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size); 1242*4ad7e9b0SAdrian Chadd if (error) 1243*4ad7e9b0SAdrian Chadd return (error); 1244*4ad7e9b0SAdrian Chadd 1245*4ad7e9b0SAdrian Chadd /* Mark active */ 1246*4ad7e9b0SAdrian Chadd if ((error = rman_activate_resource(r))) 1247*4ad7e9b0SAdrian Chadd return (error); 1248*4ad7e9b0SAdrian Chadd 1249*4ad7e9b0SAdrian Chadd return (0); 1250*4ad7e9b0SAdrian Chadd } 1251*4ad7e9b0SAdrian Chadd 1252*4ad7e9b0SAdrian Chadd /** 1253*4ad7e9b0SAdrian Chadd * Attempt to allocate/retain a dynamic register window for @p r, returning 1254*4ad7e9b0SAdrian Chadd * the retained window. 1255*4ad7e9b0SAdrian Chadd * 1256*4ad7e9b0SAdrian Chadd * @param sc The bhndb driver state. 1257*4ad7e9b0SAdrian Chadd * @param r The resource for which a window will be retained. 1258*4ad7e9b0SAdrian Chadd */ 1259*4ad7e9b0SAdrian Chadd static struct bhndb_dw_alloc * 1260*4ad7e9b0SAdrian Chadd bhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r) 1261*4ad7e9b0SAdrian Chadd { 1262*4ad7e9b0SAdrian Chadd struct bhndb_dw_alloc *dwa; 1263*4ad7e9b0SAdrian Chadd rman_res_t r_start, r_size; 1264*4ad7e9b0SAdrian Chadd int error; 1265*4ad7e9b0SAdrian Chadd 1266*4ad7e9b0SAdrian Chadd BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1267*4ad7e9b0SAdrian Chadd 1268*4ad7e9b0SAdrian Chadd r_start = rman_get_start(r); 1269*4ad7e9b0SAdrian Chadd r_size = rman_get_size(r); 1270*4ad7e9b0SAdrian Chadd 1271*4ad7e9b0SAdrian Chadd /* Look for an existing dynamic window we can reference */ 1272*4ad7e9b0SAdrian Chadd dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size); 1273*4ad7e9b0SAdrian Chadd if (dwa != NULL) { 1274*4ad7e9b0SAdrian Chadd if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0) 1275*4ad7e9b0SAdrian Chadd return (dwa); 1276*4ad7e9b0SAdrian Chadd 1277*4ad7e9b0SAdrian Chadd return (NULL); 1278*4ad7e9b0SAdrian Chadd } 1279*4ad7e9b0SAdrian Chadd 1280*4ad7e9b0SAdrian Chadd /* Otherwise, try to reserve a free window */ 1281*4ad7e9b0SAdrian Chadd dwa = bhndb_dw_next_free(sc->bus_res); 1282*4ad7e9b0SAdrian Chadd if (dwa == NULL) { 1283*4ad7e9b0SAdrian Chadd /* No free windows */ 1284*4ad7e9b0SAdrian Chadd return (NULL); 1285*4ad7e9b0SAdrian Chadd } 1286*4ad7e9b0SAdrian Chadd 1287*4ad7e9b0SAdrian Chadd /* Set the window target */ 1288*4ad7e9b0SAdrian Chadd error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r), 1289*4ad7e9b0SAdrian Chadd rman_get_size(r)); 1290*4ad7e9b0SAdrian Chadd if (error) { 1291*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "dynamic window initialization " 1292*4ad7e9b0SAdrian Chadd "for 0x%llx-0x%llx failed\n", 1293*4ad7e9b0SAdrian Chadd (unsigned long long) r_start, 1294*4ad7e9b0SAdrian Chadd (unsigned long long) r_start + r_size - 1); 1295*4ad7e9b0SAdrian Chadd return (NULL); 1296*4ad7e9b0SAdrian Chadd } 1297*4ad7e9b0SAdrian Chadd 1298*4ad7e9b0SAdrian Chadd /* Add our reservation */ 1299*4ad7e9b0SAdrian Chadd if (bhndb_dw_retain(sc->bus_res, dwa, r)) 1300*4ad7e9b0SAdrian Chadd return (NULL); 1301*4ad7e9b0SAdrian Chadd 1302*4ad7e9b0SAdrian Chadd return (dwa); 1303*4ad7e9b0SAdrian Chadd } 1304*4ad7e9b0SAdrian Chadd 1305*4ad7e9b0SAdrian Chadd /** 1306*4ad7e9b0SAdrian Chadd * Activate a resource using any viable static or dynamic register window. 1307*4ad7e9b0SAdrian Chadd * 1308*4ad7e9b0SAdrian Chadd * @param sc The bhndb driver state. 1309*4ad7e9b0SAdrian Chadd * @param child The child holding ownership of @p r. 1310*4ad7e9b0SAdrian Chadd * @param type The type of the resource to be activated. 1311*4ad7e9b0SAdrian Chadd * @param rid The resource ID of @p r. 1312*4ad7e9b0SAdrian Chadd * @param r The resource to be activated 1313*4ad7e9b0SAdrian Chadd * @param[out] indirect On error and if not NULL, will be set to 'true' if 1314*4ad7e9b0SAdrian Chadd * the caller should instead use an indirect resource mapping. 1315*4ad7e9b0SAdrian Chadd * 1316*4ad7e9b0SAdrian Chadd * @retval 0 success 1317*4ad7e9b0SAdrian Chadd * @retval non-zero activation failed. 1318*4ad7e9b0SAdrian Chadd */ 1319*4ad7e9b0SAdrian Chadd static int 1320*4ad7e9b0SAdrian Chadd bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type, 1321*4ad7e9b0SAdrian Chadd int rid, struct resource *r, bool *indirect) 1322*4ad7e9b0SAdrian Chadd { 1323*4ad7e9b0SAdrian Chadd struct bhndb_region *region; 1324*4ad7e9b0SAdrian Chadd struct bhndb_dw_alloc *dwa; 1325*4ad7e9b0SAdrian Chadd bhndb_priority_t dw_priority; 1326*4ad7e9b0SAdrian Chadd rman_res_t r_start, r_size; 1327*4ad7e9b0SAdrian Chadd rman_res_t parent_offset; 1328*4ad7e9b0SAdrian Chadd int error; 1329*4ad7e9b0SAdrian Chadd 1330*4ad7e9b0SAdrian Chadd BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED); 1331*4ad7e9b0SAdrian Chadd 1332*4ad7e9b0SAdrian Chadd // TODO - IRQs 1333*4ad7e9b0SAdrian Chadd if (type != SYS_RES_MEMORY) 1334*4ad7e9b0SAdrian Chadd return (ENXIO); 1335*4ad7e9b0SAdrian Chadd 1336*4ad7e9b0SAdrian Chadd if (indirect) 1337*4ad7e9b0SAdrian Chadd *indirect = false; 1338*4ad7e9b0SAdrian Chadd 1339*4ad7e9b0SAdrian Chadd /* Default to low priority */ 1340*4ad7e9b0SAdrian Chadd dw_priority = BHNDB_PRIORITY_LOW; 1341*4ad7e9b0SAdrian Chadd 1342*4ad7e9b0SAdrian Chadd /* Look for a bus region matching the resource's address range */ 1343*4ad7e9b0SAdrian Chadd r_start = rman_get_start(r); 1344*4ad7e9b0SAdrian Chadd r_size = rman_get_size(r); 1345*4ad7e9b0SAdrian Chadd region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); 1346*4ad7e9b0SAdrian Chadd if (region != NULL) 1347*4ad7e9b0SAdrian Chadd dw_priority = region->priority; 1348*4ad7e9b0SAdrian Chadd 1349*4ad7e9b0SAdrian Chadd /* Prefer static mappings over consuming a dynamic windows. */ 1350*4ad7e9b0SAdrian Chadd if (region && region->static_regwin) { 1351*4ad7e9b0SAdrian Chadd error = bhndb_activate_static_region(sc, region, child, type, 1352*4ad7e9b0SAdrian Chadd rid, r); 1353*4ad7e9b0SAdrian Chadd if (error) 1354*4ad7e9b0SAdrian Chadd device_printf(sc->dev, "static window allocation " 1355*4ad7e9b0SAdrian Chadd "for 0x%llx-0x%llx failed\n", 1356*4ad7e9b0SAdrian Chadd (unsigned long long) r_start, 1357*4ad7e9b0SAdrian Chadd (unsigned long long) r_start + r_size - 1); 1358*4ad7e9b0SAdrian Chadd return (error); 1359*4ad7e9b0SAdrian Chadd } 1360*4ad7e9b0SAdrian Chadd 1361*4ad7e9b0SAdrian Chadd /* A dynamic window will be required; is this resource high enough 1362*4ad7e9b0SAdrian Chadd * priority to be reserved a dynamic window? */ 1363*4ad7e9b0SAdrian Chadd if (dw_priority < sc->bus_res->min_prio) { 1364*4ad7e9b0SAdrian Chadd if (indirect) 1365*4ad7e9b0SAdrian Chadd *indirect = true; 1366*4ad7e9b0SAdrian Chadd 1367*4ad7e9b0SAdrian Chadd return (ENOMEM); 1368*4ad7e9b0SAdrian Chadd } 1369*4ad7e9b0SAdrian Chadd 1370*4ad7e9b0SAdrian Chadd /* Find and retain a usable window */ 1371*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); { 1372*4ad7e9b0SAdrian Chadd dwa = bhndb_retain_dynamic_window(sc, r); 1373*4ad7e9b0SAdrian Chadd } BHNDB_UNLOCK(sc); 1374*4ad7e9b0SAdrian Chadd 1375*4ad7e9b0SAdrian Chadd if (dwa == NULL) { 1376*4ad7e9b0SAdrian Chadd if (indirect) 1377*4ad7e9b0SAdrian Chadd *indirect = true; 1378*4ad7e9b0SAdrian Chadd return (ENOMEM); 1379*4ad7e9b0SAdrian Chadd } 1380*4ad7e9b0SAdrian Chadd 1381*4ad7e9b0SAdrian Chadd /* Configure resource with its real bus values. */ 1382*4ad7e9b0SAdrian Chadd parent_offset = dwa->win->win_offset; 1383*4ad7e9b0SAdrian Chadd parent_offset += r_start - dwa->target; 1384*4ad7e9b0SAdrian Chadd 1385*4ad7e9b0SAdrian Chadd error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset, 1386*4ad7e9b0SAdrian Chadd dwa->win->win_size); 1387*4ad7e9b0SAdrian Chadd if (error) 1388*4ad7e9b0SAdrian Chadd goto failed; 1389*4ad7e9b0SAdrian Chadd 1390*4ad7e9b0SAdrian Chadd /* Mark active */ 1391*4ad7e9b0SAdrian Chadd if ((error = rman_activate_resource(r))) 1392*4ad7e9b0SAdrian Chadd goto failed; 1393*4ad7e9b0SAdrian Chadd 1394*4ad7e9b0SAdrian Chadd return (0); 1395*4ad7e9b0SAdrian Chadd 1396*4ad7e9b0SAdrian Chadd failed: 1397*4ad7e9b0SAdrian Chadd /* Release our region allocation. */ 1398*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); 1399*4ad7e9b0SAdrian Chadd bhndb_dw_release(sc->bus_res, dwa, r); 1400*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 1401*4ad7e9b0SAdrian Chadd 1402*4ad7e9b0SAdrian Chadd return (error); 1403*4ad7e9b0SAdrian Chadd } 1404*4ad7e9b0SAdrian Chadd 1405*4ad7e9b0SAdrian Chadd /** 1406*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE(). 1407*4ad7e9b0SAdrian Chadd * 1408*4ad7e9b0SAdrian Chadd * Maps resource activation requests to a viable static or dynamic 1409*4ad7e9b0SAdrian Chadd * register window, if any. 1410*4ad7e9b0SAdrian Chadd */ 1411*4ad7e9b0SAdrian Chadd static int 1412*4ad7e9b0SAdrian Chadd bhndb_activate_resource(device_t dev, device_t child, int type, int rid, 1413*4ad7e9b0SAdrian Chadd struct resource *r) 1414*4ad7e9b0SAdrian Chadd { 1415*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc = device_get_softc(dev); 1416*4ad7e9b0SAdrian Chadd 1417*4ad7e9b0SAdrian Chadd return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL)); 1418*4ad7e9b0SAdrian Chadd } 1419*4ad7e9b0SAdrian Chadd 1420*4ad7e9b0SAdrian Chadd /** 1421*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE(). 1422*4ad7e9b0SAdrian Chadd */ 1423*4ad7e9b0SAdrian Chadd static int 1424*4ad7e9b0SAdrian Chadd bhndb_deactivate_resource(device_t dev, device_t child, int type, 1425*4ad7e9b0SAdrian Chadd int rid, struct resource *r) 1426*4ad7e9b0SAdrian Chadd { 1427*4ad7e9b0SAdrian Chadd struct bhndb_dw_alloc *dwa; 1428*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 1429*4ad7e9b0SAdrian Chadd struct rman *rm; 1430*4ad7e9b0SAdrian Chadd int error; 1431*4ad7e9b0SAdrian Chadd 1432*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1433*4ad7e9b0SAdrian Chadd 1434*4ad7e9b0SAdrian Chadd if ((rm = bhndb_get_rman(sc, type)) == NULL) 1435*4ad7e9b0SAdrian Chadd return (EINVAL); 1436*4ad7e9b0SAdrian Chadd 1437*4ad7e9b0SAdrian Chadd /* Mark inactive */ 1438*4ad7e9b0SAdrian Chadd if ((error = rman_deactivate_resource(r))) 1439*4ad7e9b0SAdrian Chadd return (error); 1440*4ad7e9b0SAdrian Chadd 1441*4ad7e9b0SAdrian Chadd /* Free any dynamic window allocation. */ 1442*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); 1443*4ad7e9b0SAdrian Chadd dwa = bhndb_dw_find_resource(sc->bus_res, r); 1444*4ad7e9b0SAdrian Chadd if (dwa != NULL) 1445*4ad7e9b0SAdrian Chadd bhndb_dw_release(sc->bus_res, dwa, r); 1446*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 1447*4ad7e9b0SAdrian Chadd 1448*4ad7e9b0SAdrian Chadd return (0); 1449*4ad7e9b0SAdrian Chadd } 1450*4ad7e9b0SAdrian Chadd 1451*4ad7e9b0SAdrian Chadd /** 1452*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST(). 1453*4ad7e9b0SAdrian Chadd */ 1454*4ad7e9b0SAdrian Chadd static struct resource_list * 1455*4ad7e9b0SAdrian Chadd bhndb_get_resource_list(device_t dev, device_t child) 1456*4ad7e9b0SAdrian Chadd { 1457*4ad7e9b0SAdrian Chadd struct bhndb_devinfo *dinfo = device_get_ivars(child); 1458*4ad7e9b0SAdrian Chadd return (&dinfo->resources); 1459*4ad7e9b0SAdrian Chadd } 1460*4ad7e9b0SAdrian Chadd 1461*4ad7e9b0SAdrian Chadd /** 1462*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BHND_BUS_ALLOC_RESOURCE(). 1463*4ad7e9b0SAdrian Chadd */ 1464*4ad7e9b0SAdrian Chadd static struct bhnd_resource * 1465*4ad7e9b0SAdrian Chadd bhndb_alloc_bhnd_resource(device_t dev, device_t child, int type, 1466*4ad7e9b0SAdrian Chadd int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 1467*4ad7e9b0SAdrian Chadd { 1468*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 1469*4ad7e9b0SAdrian Chadd struct bhnd_resource *br; 1470*4ad7e9b0SAdrian Chadd 1471*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1472*4ad7e9b0SAdrian Chadd 1473*4ad7e9b0SAdrian Chadd /* Allocate resource wrapper */ 1474*4ad7e9b0SAdrian Chadd br = malloc(sizeof(struct bhnd_resource), M_BHND, M_NOWAIT|M_ZERO); 1475*4ad7e9b0SAdrian Chadd if (br == NULL) 1476*4ad7e9b0SAdrian Chadd return (NULL); 1477*4ad7e9b0SAdrian Chadd 1478*4ad7e9b0SAdrian Chadd /* Configure */ 1479*4ad7e9b0SAdrian Chadd br->direct = false; 1480*4ad7e9b0SAdrian Chadd br->res = bus_alloc_resource(child, type, rid, start, end, count, 1481*4ad7e9b0SAdrian Chadd flags & ~RF_ACTIVE); 1482*4ad7e9b0SAdrian Chadd if (br->res == NULL) 1483*4ad7e9b0SAdrian Chadd goto failed; 1484*4ad7e9b0SAdrian Chadd 1485*4ad7e9b0SAdrian Chadd 1486*4ad7e9b0SAdrian Chadd if (flags & RF_ACTIVE) { 1487*4ad7e9b0SAdrian Chadd if (bhnd_activate_resource(child, type, *rid, br)) 1488*4ad7e9b0SAdrian Chadd goto failed; 1489*4ad7e9b0SAdrian Chadd } 1490*4ad7e9b0SAdrian Chadd 1491*4ad7e9b0SAdrian Chadd return (br); 1492*4ad7e9b0SAdrian Chadd 1493*4ad7e9b0SAdrian Chadd failed: 1494*4ad7e9b0SAdrian Chadd if (br->res != NULL) 1495*4ad7e9b0SAdrian Chadd bus_release_resource(child, type, *rid, br->res); 1496*4ad7e9b0SAdrian Chadd 1497*4ad7e9b0SAdrian Chadd free(br, M_BHND); 1498*4ad7e9b0SAdrian Chadd return (NULL); 1499*4ad7e9b0SAdrian Chadd } 1500*4ad7e9b0SAdrian Chadd 1501*4ad7e9b0SAdrian Chadd /** 1502*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BHND_BUS_RELEASE_RESOURCE(). 1503*4ad7e9b0SAdrian Chadd */ 1504*4ad7e9b0SAdrian Chadd static int 1505*4ad7e9b0SAdrian Chadd bhndb_release_bhnd_resource(device_t dev, device_t child, 1506*4ad7e9b0SAdrian Chadd int type, int rid, struct bhnd_resource *r) 1507*4ad7e9b0SAdrian Chadd { 1508*4ad7e9b0SAdrian Chadd int error; 1509*4ad7e9b0SAdrian Chadd 1510*4ad7e9b0SAdrian Chadd if ((error = bus_release_resource(child, type, rid, r->res))) 1511*4ad7e9b0SAdrian Chadd return (error); 1512*4ad7e9b0SAdrian Chadd 1513*4ad7e9b0SAdrian Chadd free(r, M_BHND); 1514*4ad7e9b0SAdrian Chadd return (0); 1515*4ad7e9b0SAdrian Chadd } 1516*4ad7e9b0SAdrian Chadd 1517*4ad7e9b0SAdrian Chadd /** 1518*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE(). 1519*4ad7e9b0SAdrian Chadd * 1520*4ad7e9b0SAdrian Chadd * Attempts to activate a static register window, a dynamic register window, 1521*4ad7e9b0SAdrian Chadd * or configures @p r as an indirect resource -- in that order. 1522*4ad7e9b0SAdrian Chadd */ 1523*4ad7e9b0SAdrian Chadd static int 1524*4ad7e9b0SAdrian Chadd bhndb_activate_bhnd_resource(device_t dev, device_t child, 1525*4ad7e9b0SAdrian Chadd int type, int rid, struct bhnd_resource *r) 1526*4ad7e9b0SAdrian Chadd { 1527*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; 1528*4ad7e9b0SAdrian Chadd struct bhndb_region *region; 1529*4ad7e9b0SAdrian Chadd bhndb_priority_t r_prio; 1530*4ad7e9b0SAdrian Chadd rman_res_t r_start, r_size; 1531*4ad7e9b0SAdrian Chadd int error; 1532*4ad7e9b0SAdrian Chadd bool indirect; 1533*4ad7e9b0SAdrian Chadd 1534*4ad7e9b0SAdrian Chadd KASSERT(!r->direct, 1535*4ad7e9b0SAdrian Chadd ("direct flag set on inactive resource")); 1536*4ad7e9b0SAdrian Chadd 1537*4ad7e9b0SAdrian Chadd KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE), 1538*4ad7e9b0SAdrian Chadd ("RF_ACTIVE set on inactive resource")); 1539*4ad7e9b0SAdrian Chadd 1540*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); 1541*4ad7e9b0SAdrian Chadd 1542*4ad7e9b0SAdrian Chadd /* Fetch the address range's resource priority */ 1543*4ad7e9b0SAdrian Chadd r_start = rman_get_start(r->res); 1544*4ad7e9b0SAdrian Chadd r_size = rman_get_size(r->res); 1545*4ad7e9b0SAdrian Chadd r_prio = BHNDB_PRIORITY_NONE; 1546*4ad7e9b0SAdrian Chadd 1547*4ad7e9b0SAdrian Chadd region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); 1548*4ad7e9b0SAdrian Chadd if (region != NULL) 1549*4ad7e9b0SAdrian Chadd r_prio = region->priority; 1550*4ad7e9b0SAdrian Chadd 1551*4ad7e9b0SAdrian Chadd /* If less than the minimum dynamic window priority, this 1552*4ad7e9b0SAdrian Chadd * resource should always be indirect. */ 1553*4ad7e9b0SAdrian Chadd if (r_prio < sc->bus_res->min_prio) 1554*4ad7e9b0SAdrian Chadd return (0); 1555*4ad7e9b0SAdrian Chadd 1556*4ad7e9b0SAdrian Chadd /* Attempt direct activation */ 1557*4ad7e9b0SAdrian Chadd error = bhndb_try_activate_resource(sc, child, type, rid, r->res, 1558*4ad7e9b0SAdrian Chadd &indirect); 1559*4ad7e9b0SAdrian Chadd if (!error) { 1560*4ad7e9b0SAdrian Chadd r->direct = true; 1561*4ad7e9b0SAdrian Chadd } else if (indirect) { 1562*4ad7e9b0SAdrian Chadd /* The request was valid, but no viable register window is 1563*4ad7e9b0SAdrian Chadd * available; indirection must be employed. */ 1564*4ad7e9b0SAdrian Chadd error = 0; 1565*4ad7e9b0SAdrian Chadd r->direct = false; 1566*4ad7e9b0SAdrian Chadd } 1567*4ad7e9b0SAdrian Chadd 1568*4ad7e9b0SAdrian Chadd if (BHNDB_DEBUG(PRIO)) { 1569*4ad7e9b0SAdrian Chadd device_printf(child, "activated 0x%llx-0x%llx as %s " 1570*4ad7e9b0SAdrian Chadd "resource\n", 1571*4ad7e9b0SAdrian Chadd (unsigned long long) r_start, 1572*4ad7e9b0SAdrian Chadd (unsigned long long) r_start + r_size - 1, 1573*4ad7e9b0SAdrian Chadd r->direct ? "direct" : "indirect"); 1574*4ad7e9b0SAdrian Chadd } 1575*4ad7e9b0SAdrian Chadd 1576*4ad7e9b0SAdrian Chadd return (error); 1577*4ad7e9b0SAdrian Chadd }; 1578*4ad7e9b0SAdrian Chadd 1579*4ad7e9b0SAdrian Chadd /** 1580*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE(). 1581*4ad7e9b0SAdrian Chadd */ 1582*4ad7e9b0SAdrian Chadd static int 1583*4ad7e9b0SAdrian Chadd bhndb_deactivate_bhnd_resource(device_t dev, device_t child, 1584*4ad7e9b0SAdrian Chadd int type, int rid, struct bhnd_resource *r) 1585*4ad7e9b0SAdrian Chadd { 1586*4ad7e9b0SAdrian Chadd int error; 1587*4ad7e9b0SAdrian Chadd 1588*4ad7e9b0SAdrian Chadd /* Indirect resources don't require activation */ 1589*4ad7e9b0SAdrian Chadd if (!r->direct) 1590*4ad7e9b0SAdrian Chadd return (0); 1591*4ad7e9b0SAdrian Chadd 1592*4ad7e9b0SAdrian Chadd KASSERT(rman_get_flags(r->res) & RF_ACTIVE, 1593*4ad7e9b0SAdrian Chadd ("RF_ACTIVE not set on direct resource")); 1594*4ad7e9b0SAdrian Chadd 1595*4ad7e9b0SAdrian Chadd /* Perform deactivation */ 1596*4ad7e9b0SAdrian Chadd error = bus_deactivate_resource(child, type, rid, r->res); 1597*4ad7e9b0SAdrian Chadd if (!error) 1598*4ad7e9b0SAdrian Chadd r->direct = false; 1599*4ad7e9b0SAdrian Chadd 1600*4ad7e9b0SAdrian Chadd return (error); 1601*4ad7e9b0SAdrian Chadd }; 1602*4ad7e9b0SAdrian Chadd 1603*4ad7e9b0SAdrian Chadd /** 1604*4ad7e9b0SAdrian Chadd * Slow path for bhndb_io_resource(). 1605*4ad7e9b0SAdrian Chadd * 1606*4ad7e9b0SAdrian Chadd * Iterates over the existing allocated dynamic windows looking for a viable 1607*4ad7e9b0SAdrian Chadd * in-use region; the first matching region is returned. 1608*4ad7e9b0SAdrian Chadd */ 1609*4ad7e9b0SAdrian Chadd static struct bhndb_dw_alloc * 1610*4ad7e9b0SAdrian Chadd bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, 1611*4ad7e9b0SAdrian Chadd bus_size_t size, bus_size_t *offset) 1612*4ad7e9b0SAdrian Chadd { 1613*4ad7e9b0SAdrian Chadd struct bhndb_resources *br; 1614*4ad7e9b0SAdrian Chadd struct bhndb_dw_alloc *dwa; 1615*4ad7e9b0SAdrian Chadd 1616*4ad7e9b0SAdrian Chadd BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1617*4ad7e9b0SAdrian Chadd 1618*4ad7e9b0SAdrian Chadd br = sc->bus_res; 1619*4ad7e9b0SAdrian Chadd 1620*4ad7e9b0SAdrian Chadd /* Search for an existing dynamic mapping of this address range. 1621*4ad7e9b0SAdrian Chadd * Static regions are not searched, as a statically mapped 1622*4ad7e9b0SAdrian Chadd * region would never be allocated as an indirect resource. */ 1623*4ad7e9b0SAdrian Chadd for (size_t i = 0; i < br->dwa_count; i++) { 1624*4ad7e9b0SAdrian Chadd const struct bhndb_regwin *win; 1625*4ad7e9b0SAdrian Chadd 1626*4ad7e9b0SAdrian Chadd dwa = &br->dw_alloc[i]; 1627*4ad7e9b0SAdrian Chadd win = dwa->win; 1628*4ad7e9b0SAdrian Chadd 1629*4ad7e9b0SAdrian Chadd KASSERT(win->win_type == BHNDB_REGWIN_T_DYN, 1630*4ad7e9b0SAdrian Chadd ("invalid register window type")); 1631*4ad7e9b0SAdrian Chadd 1632*4ad7e9b0SAdrian Chadd /* Verify the range */ 1633*4ad7e9b0SAdrian Chadd if (addr < dwa->target) 1634*4ad7e9b0SAdrian Chadd continue; 1635*4ad7e9b0SAdrian Chadd 1636*4ad7e9b0SAdrian Chadd if (addr + size > dwa->target + win->win_size) 1637*4ad7e9b0SAdrian Chadd continue; 1638*4ad7e9b0SAdrian Chadd 1639*4ad7e9b0SAdrian Chadd /* Found */ 1640*4ad7e9b0SAdrian Chadd *offset = dwa->win->win_offset; 1641*4ad7e9b0SAdrian Chadd *offset += addr - dwa->target; 1642*4ad7e9b0SAdrian Chadd 1643*4ad7e9b0SAdrian Chadd return (dwa); 1644*4ad7e9b0SAdrian Chadd } 1645*4ad7e9b0SAdrian Chadd 1646*4ad7e9b0SAdrian Chadd /* not found */ 1647*4ad7e9b0SAdrian Chadd return (NULL); 1648*4ad7e9b0SAdrian Chadd } 1649*4ad7e9b0SAdrian Chadd 1650*4ad7e9b0SAdrian Chadd /** 1651*4ad7e9b0SAdrian Chadd * Find the bridge resource to be used for I/O requests. 1652*4ad7e9b0SAdrian Chadd * 1653*4ad7e9b0SAdrian Chadd * @param sc Bridge driver state. 1654*4ad7e9b0SAdrian Chadd * @param addr The I/O target address. 1655*4ad7e9b0SAdrian Chadd * @param size The size of the I/O operation to be performed at @p addr. 1656*4ad7e9b0SAdrian Chadd * @param[out] offset The offset within the returned resource at which 1657*4ad7e9b0SAdrian Chadd * to perform the I/O request. 1658*4ad7e9b0SAdrian Chadd */ 1659*4ad7e9b0SAdrian Chadd static inline struct bhndb_dw_alloc * 1660*4ad7e9b0SAdrian Chadd bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, 1661*4ad7e9b0SAdrian Chadd bus_size_t *offset) 1662*4ad7e9b0SAdrian Chadd { 1663*4ad7e9b0SAdrian Chadd struct bhndb_resources *br; 1664*4ad7e9b0SAdrian Chadd struct bhndb_dw_alloc *dwa; 1665*4ad7e9b0SAdrian Chadd int error; 1666*4ad7e9b0SAdrian Chadd 1667*4ad7e9b0SAdrian Chadd BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1668*4ad7e9b0SAdrian Chadd 1669*4ad7e9b0SAdrian Chadd br = sc->bus_res; 1670*4ad7e9b0SAdrian Chadd 1671*4ad7e9b0SAdrian Chadd /* Try to fetch a free window */ 1672*4ad7e9b0SAdrian Chadd dwa = bhndb_dw_next_free(br); 1673*4ad7e9b0SAdrian Chadd 1674*4ad7e9b0SAdrian Chadd /* 1675*4ad7e9b0SAdrian Chadd * If no dynamic windows are available, look for an existing 1676*4ad7e9b0SAdrian Chadd * region that maps the target range. 1677*4ad7e9b0SAdrian Chadd * 1678*4ad7e9b0SAdrian Chadd * If none are found, this is a child driver bug -- our window 1679*4ad7e9b0SAdrian Chadd * over-commit should only fail in the case where a child driver leaks 1680*4ad7e9b0SAdrian Chadd * resources, or perform operations out-of-order. 1681*4ad7e9b0SAdrian Chadd * 1682*4ad7e9b0SAdrian Chadd * Broadcom HND chipsets are designed to not require register window 1683*4ad7e9b0SAdrian Chadd * swapping during execution; as long as the child devices are 1684*4ad7e9b0SAdrian Chadd * attached/detached correctly, using the hardware's required order 1685*4ad7e9b0SAdrian Chadd * of operations, there should always be a window available for the 1686*4ad7e9b0SAdrian Chadd * current operation. 1687*4ad7e9b0SAdrian Chadd */ 1688*4ad7e9b0SAdrian Chadd if (dwa == NULL) { 1689*4ad7e9b0SAdrian Chadd dwa = bhndb_io_resource_slow(sc, addr, size, offset); 1690*4ad7e9b0SAdrian Chadd if (dwa == NULL) { 1691*4ad7e9b0SAdrian Chadd panic("register windows exhausted attempting to map " 1692*4ad7e9b0SAdrian Chadd "0x%llx-0x%llx\n", 1693*4ad7e9b0SAdrian Chadd (unsigned long long) addr, 1694*4ad7e9b0SAdrian Chadd (unsigned long long) addr+size-1); 1695*4ad7e9b0SAdrian Chadd } 1696*4ad7e9b0SAdrian Chadd 1697*4ad7e9b0SAdrian Chadd return (dwa); 1698*4ad7e9b0SAdrian Chadd } 1699*4ad7e9b0SAdrian Chadd 1700*4ad7e9b0SAdrian Chadd /* Adjust the window if the I/O request won't fit in the current 1701*4ad7e9b0SAdrian Chadd * target range. */ 1702*4ad7e9b0SAdrian Chadd if (addr < dwa->target || 1703*4ad7e9b0SAdrian Chadd (dwa->target + dwa->win->win_size) - addr < size) 1704*4ad7e9b0SAdrian Chadd { 1705*4ad7e9b0SAdrian Chadd error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr, 1706*4ad7e9b0SAdrian Chadd size); 1707*4ad7e9b0SAdrian Chadd if (error) { 1708*4ad7e9b0SAdrian Chadd panic("failed to set register window target mapping " 1709*4ad7e9b0SAdrian Chadd "0x%llx-0x%llx\n", 1710*4ad7e9b0SAdrian Chadd (unsigned long long) addr, 1711*4ad7e9b0SAdrian Chadd (unsigned long long) addr+size-1); 1712*4ad7e9b0SAdrian Chadd } 1713*4ad7e9b0SAdrian Chadd } 1714*4ad7e9b0SAdrian Chadd 1715*4ad7e9b0SAdrian Chadd /* Calculate the offset and return */ 1716*4ad7e9b0SAdrian Chadd *offset = (addr - dwa->target) + dwa->win->win_offset; 1717*4ad7e9b0SAdrian Chadd return (dwa); 1718*4ad7e9b0SAdrian Chadd } 1719*4ad7e9b0SAdrian Chadd 1720*4ad7e9b0SAdrian Chadd /* 1721*4ad7e9b0SAdrian Chadd * BHND_BUS_(READ|WRITE_* implementations 1722*4ad7e9b0SAdrian Chadd */ 1723*4ad7e9b0SAdrian Chadd 1724*4ad7e9b0SAdrian Chadd /* bhndb_bus_(read|write) common implementation */ 1725*4ad7e9b0SAdrian Chadd #define BHNDB_IO_COMMON_SETUP(_io_size) \ 1726*4ad7e9b0SAdrian Chadd struct bhndb_softc *sc; \ 1727*4ad7e9b0SAdrian Chadd struct bhndb_dw_alloc *dwa; \ 1728*4ad7e9b0SAdrian Chadd struct resource *io_res; \ 1729*4ad7e9b0SAdrian Chadd bus_size_t io_offset; \ 1730*4ad7e9b0SAdrian Chadd \ 1731*4ad7e9b0SAdrian Chadd sc = device_get_softc(dev); \ 1732*4ad7e9b0SAdrian Chadd \ 1733*4ad7e9b0SAdrian Chadd BHNDB_LOCK(sc); \ 1734*4ad7e9b0SAdrian Chadd dwa = bhndb_io_resource(sc, rman_get_start(r->res) + \ 1735*4ad7e9b0SAdrian Chadd offset, _io_size, &io_offset); \ 1736*4ad7e9b0SAdrian Chadd io_res = dwa->parent_res; \ 1737*4ad7e9b0SAdrian Chadd \ 1738*4ad7e9b0SAdrian Chadd KASSERT(!r->direct, \ 1739*4ad7e9b0SAdrian Chadd ("bhnd_bus slow path used for direct resource")); \ 1740*4ad7e9b0SAdrian Chadd \ 1741*4ad7e9b0SAdrian Chadd KASSERT(rman_get_flags(io_res) & RF_ACTIVE, \ 1742*4ad7e9b0SAdrian Chadd ("i/o resource is not active")); 1743*4ad7e9b0SAdrian Chadd 1744*4ad7e9b0SAdrian Chadd #define BHNDB_IO_COMMON_TEARDOWN() \ 1745*4ad7e9b0SAdrian Chadd BHNDB_UNLOCK(sc); 1746*4ad7e9b0SAdrian Chadd 1747*4ad7e9b0SAdrian Chadd /* Defines a bhndb_bus_read_* method implementation */ 1748*4ad7e9b0SAdrian Chadd #define BHNDB_IO_READ(_type, _size) \ 1749*4ad7e9b0SAdrian Chadd static _type \ 1750*4ad7e9b0SAdrian Chadd bhndb_bus_read_ ## _size (device_t dev, device_t child, \ 1751*4ad7e9b0SAdrian Chadd struct bhnd_resource *r, bus_size_t offset) \ 1752*4ad7e9b0SAdrian Chadd { \ 1753*4ad7e9b0SAdrian Chadd _type v; \ 1754*4ad7e9b0SAdrian Chadd BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ 1755*4ad7e9b0SAdrian Chadd v = bus_read_ ## _size (io_res, io_offset); \ 1756*4ad7e9b0SAdrian Chadd BHNDB_IO_COMMON_TEARDOWN(); \ 1757*4ad7e9b0SAdrian Chadd \ 1758*4ad7e9b0SAdrian Chadd return (v); \ 1759*4ad7e9b0SAdrian Chadd } 1760*4ad7e9b0SAdrian Chadd 1761*4ad7e9b0SAdrian Chadd /* Defines a bhndb_bus_write_* method implementation */ 1762*4ad7e9b0SAdrian Chadd #define BHNDB_IO_WRITE(_type, _size) \ 1763*4ad7e9b0SAdrian Chadd static void \ 1764*4ad7e9b0SAdrian Chadd bhndb_bus_write_ ## _size (device_t dev, device_t child, \ 1765*4ad7e9b0SAdrian Chadd struct bhnd_resource *r, bus_size_t offset, _type value) \ 1766*4ad7e9b0SAdrian Chadd { \ 1767*4ad7e9b0SAdrian Chadd BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ 1768*4ad7e9b0SAdrian Chadd bus_write_ ## _size (io_res, io_offset, value); \ 1769*4ad7e9b0SAdrian Chadd BHNDB_IO_COMMON_TEARDOWN(); \ 1770*4ad7e9b0SAdrian Chadd } 1771*4ad7e9b0SAdrian Chadd 1772*4ad7e9b0SAdrian Chadd BHNDB_IO_READ(uint8_t, 1); 1773*4ad7e9b0SAdrian Chadd BHNDB_IO_READ(uint16_t, 2); 1774*4ad7e9b0SAdrian Chadd BHNDB_IO_READ(uint32_t, 4); 1775*4ad7e9b0SAdrian Chadd 1776*4ad7e9b0SAdrian Chadd BHNDB_IO_WRITE(uint8_t, 1); 1777*4ad7e9b0SAdrian Chadd BHNDB_IO_WRITE(uint16_t, 2); 1778*4ad7e9b0SAdrian Chadd BHNDB_IO_WRITE(uint32_t, 4); 1779*4ad7e9b0SAdrian Chadd 1780*4ad7e9b0SAdrian Chadd /** 1781*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BHND_BUS_BARRIER(). 1782*4ad7e9b0SAdrian Chadd */ 1783*4ad7e9b0SAdrian Chadd static void 1784*4ad7e9b0SAdrian Chadd bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r, 1785*4ad7e9b0SAdrian Chadd bus_size_t offset, bus_size_t length, int flags) 1786*4ad7e9b0SAdrian Chadd { 1787*4ad7e9b0SAdrian Chadd bus_size_t remain; 1788*4ad7e9b0SAdrian Chadd 1789*4ad7e9b0SAdrian Chadd BHNDB_IO_COMMON_SETUP(length); 1790*4ad7e9b0SAdrian Chadd 1791*4ad7e9b0SAdrian Chadd /* TODO: It's unclear whether we need a barrier implementation, 1792*4ad7e9b0SAdrian Chadd * and if we do, what it needs to actually do. This may need 1793*4ad7e9b0SAdrian Chadd * revisiting once we have a better idea of requirements after 1794*4ad7e9b0SAdrian Chadd * porting the core drivers. */ 1795*4ad7e9b0SAdrian Chadd panic("implementation incorrect"); 1796*4ad7e9b0SAdrian Chadd 1797*4ad7e9b0SAdrian Chadd /* Use 4-byte reads where possible */ 1798*4ad7e9b0SAdrian Chadd remain = length % sizeof(uint32_t); 1799*4ad7e9b0SAdrian Chadd for (bus_size_t i = 0; i < (length - remain); i += 4) 1800*4ad7e9b0SAdrian Chadd bus_read_4(io_res, io_offset + offset + i); 1801*4ad7e9b0SAdrian Chadd 1802*4ad7e9b0SAdrian Chadd /* Use 1 byte reads for the remainder */ 1803*4ad7e9b0SAdrian Chadd for (bus_size_t i = 0; i < remain; i++) 1804*4ad7e9b0SAdrian Chadd bus_read_1(io_res, io_offset + offset + length + i); 1805*4ad7e9b0SAdrian Chadd 1806*4ad7e9b0SAdrian Chadd BHNDB_IO_COMMON_TEARDOWN(); 1807*4ad7e9b0SAdrian Chadd } 1808*4ad7e9b0SAdrian Chadd 1809*4ad7e9b0SAdrian Chadd /** 1810*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_SETUP_INTR(). 1811*4ad7e9b0SAdrian Chadd */ 1812*4ad7e9b0SAdrian Chadd static int 1813*4ad7e9b0SAdrian Chadd bhndb_setup_intr(device_t dev, device_t child, struct resource *r, 1814*4ad7e9b0SAdrian Chadd int flags, driver_filter_t filter, driver_intr_t handler, void *arg, 1815*4ad7e9b0SAdrian Chadd void **cookiep) 1816*4ad7e9b0SAdrian Chadd { 1817*4ad7e9b0SAdrian Chadd // TODO 1818*4ad7e9b0SAdrian Chadd return (EOPNOTSUPP); 1819*4ad7e9b0SAdrian Chadd } 1820*4ad7e9b0SAdrian Chadd 1821*4ad7e9b0SAdrian Chadd /** 1822*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_TEARDOWN_INTR(). 1823*4ad7e9b0SAdrian Chadd */ 1824*4ad7e9b0SAdrian Chadd static int 1825*4ad7e9b0SAdrian Chadd bhndb_teardown_intr(device_t dev, device_t child, struct resource *r, 1826*4ad7e9b0SAdrian Chadd void *cookie) 1827*4ad7e9b0SAdrian Chadd { 1828*4ad7e9b0SAdrian Chadd // TODO 1829*4ad7e9b0SAdrian Chadd return (EOPNOTSUPP); 1830*4ad7e9b0SAdrian Chadd } 1831*4ad7e9b0SAdrian Chadd 1832*4ad7e9b0SAdrian Chadd /** 1833*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_CONFIG_INTR(). 1834*4ad7e9b0SAdrian Chadd */ 1835*4ad7e9b0SAdrian Chadd static int 1836*4ad7e9b0SAdrian Chadd bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig, 1837*4ad7e9b0SAdrian Chadd enum intr_polarity pol) 1838*4ad7e9b0SAdrian Chadd { 1839*4ad7e9b0SAdrian Chadd // TODO 1840*4ad7e9b0SAdrian Chadd return (EOPNOTSUPP); 1841*4ad7e9b0SAdrian Chadd } 1842*4ad7e9b0SAdrian Chadd 1843*4ad7e9b0SAdrian Chadd /** 1844*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_BIND_INTR(). 1845*4ad7e9b0SAdrian Chadd */ 1846*4ad7e9b0SAdrian Chadd static int 1847*4ad7e9b0SAdrian Chadd bhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu) 1848*4ad7e9b0SAdrian Chadd { 1849*4ad7e9b0SAdrian Chadd // TODO 1850*4ad7e9b0SAdrian Chadd return (EOPNOTSUPP); 1851*4ad7e9b0SAdrian Chadd } 1852*4ad7e9b0SAdrian Chadd 1853*4ad7e9b0SAdrian Chadd /** 1854*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_DESCRIBE_INTR(). 1855*4ad7e9b0SAdrian Chadd */ 1856*4ad7e9b0SAdrian Chadd static int 1857*4ad7e9b0SAdrian Chadd bhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, 1858*4ad7e9b0SAdrian Chadd const char *descr) 1859*4ad7e9b0SAdrian Chadd { 1860*4ad7e9b0SAdrian Chadd // TODO 1861*4ad7e9b0SAdrian Chadd return (EOPNOTSUPP); 1862*4ad7e9b0SAdrian Chadd } 1863*4ad7e9b0SAdrian Chadd 1864*4ad7e9b0SAdrian Chadd /** 1865*4ad7e9b0SAdrian Chadd * Default bhndb(4) implementation of BUS_GET_DMA_TAG(). 1866*4ad7e9b0SAdrian Chadd */ 1867*4ad7e9b0SAdrian Chadd static bus_dma_tag_t 1868*4ad7e9b0SAdrian Chadd bhndb_get_dma_tag(device_t dev, device_t child) 1869*4ad7e9b0SAdrian Chadd { 1870*4ad7e9b0SAdrian Chadd // TODO 1871*4ad7e9b0SAdrian Chadd return (NULL); 1872*4ad7e9b0SAdrian Chadd } 1873*4ad7e9b0SAdrian Chadd 1874*4ad7e9b0SAdrian Chadd static device_method_t bhndb_methods[] = { 1875*4ad7e9b0SAdrian Chadd /* Device interface */ \ 1876*4ad7e9b0SAdrian Chadd DEVMETHOD(device_probe, bhndb_generic_probe), 1877*4ad7e9b0SAdrian Chadd DEVMETHOD(device_detach, bhndb_generic_detach), 1878*4ad7e9b0SAdrian Chadd DEVMETHOD(device_shutdown, bus_generic_shutdown), 1879*4ad7e9b0SAdrian Chadd DEVMETHOD(device_suspend, bhndb_generic_suspend), 1880*4ad7e9b0SAdrian Chadd DEVMETHOD(device_resume, bhndb_generic_resume), 1881*4ad7e9b0SAdrian Chadd 1882*4ad7e9b0SAdrian Chadd /* Bus interface */ 1883*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_probe_nomatch, bhndb_probe_nomatch), 1884*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_print_child, bhndb_print_child), 1885*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_child_pnpinfo_str, bhndb_child_pnpinfo_str), 1886*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_child_location_str, bhndb_child_location_str), 1887*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_add_child, bhndb_add_child), 1888*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_child_deleted, bhndb_child_deleted), 1889*4ad7e9b0SAdrian Chadd 1890*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_alloc_resource, bhndb_alloc_resource), 1891*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_release_resource, bhndb_release_resource), 1892*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_activate_resource, bhndb_activate_resource), 1893*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_deactivate_resource, bhndb_deactivate_resource), 1894*4ad7e9b0SAdrian Chadd 1895*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_setup_intr, bhndb_setup_intr), 1896*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_teardown_intr, bhndb_teardown_intr), 1897*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_config_intr, bhndb_config_intr), 1898*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_bind_intr, bhndb_bind_intr), 1899*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_describe_intr, bhndb_describe_intr), 1900*4ad7e9b0SAdrian Chadd 1901*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_get_dma_tag, bhndb_get_dma_tag), 1902*4ad7e9b0SAdrian Chadd 1903*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_adjust_resource, bhndb_adjust_resource), 1904*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 1905*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 1906*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 1907*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_get_resource_list, bhndb_get_resource_list), 1908*4ad7e9b0SAdrian Chadd 1909*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_read_ivar, bhndb_read_ivar), 1910*4ad7e9b0SAdrian Chadd DEVMETHOD(bus_write_ivar, bhndb_write_ivar), 1911*4ad7e9b0SAdrian Chadd 1912*4ad7e9b0SAdrian Chadd /* BHNDB interface */ 1913*4ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_get_chipid, bhndb_get_chipid), 1914*4ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_init_full_config, bhndb_generic_init_full_config), 1915*4ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_suspend_resource, bhndb_suspend_resource), 1916*4ad7e9b0SAdrian Chadd DEVMETHOD(bhndb_resume_resource, bhndb_resume_resource), 1917*4ad7e9b0SAdrian Chadd 1918*4ad7e9b0SAdrian Chadd /* BHND interface */ 1919*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_is_hw_disabled, bhndb_is_hw_disabled), 1920*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_is_hostb_device, bhndb_is_hostb_device), 1921*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_get_chipid, bhndb_get_chipid), 1922*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_alloc_resource, bhndb_alloc_bhnd_resource), 1923*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_release_resource, bhndb_release_bhnd_resource), 1924*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_activate_resource, bhndb_activate_bhnd_resource), 1925*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_activate_resource, bhndb_deactivate_bhnd_resource), 1926*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_read_1, bhndb_bus_read_1), 1927*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_read_2, bhndb_bus_read_2), 1928*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_read_4, bhndb_bus_read_4), 1929*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_write_1, bhndb_bus_write_1), 1930*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_write_2, bhndb_bus_write_2), 1931*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_write_4, bhndb_bus_write_4), 1932*4ad7e9b0SAdrian Chadd DEVMETHOD(bhnd_bus_barrier, bhndb_bus_barrier), 1933*4ad7e9b0SAdrian Chadd 1934*4ad7e9b0SAdrian Chadd DEVMETHOD_END 1935*4ad7e9b0SAdrian Chadd }; 1936*4ad7e9b0SAdrian Chadd 1937*4ad7e9b0SAdrian Chadd devclass_t bhndb_devclass; 1938*4ad7e9b0SAdrian Chadd 1939*4ad7e9b0SAdrian Chadd DEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc)); 1940*4ad7e9b0SAdrian Chadd 1941*4ad7e9b0SAdrian Chadd MODULE_VERSION(bhndb, 1); 1942*4ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb, bhnd, 1, 1, 1); 1943*4ad7e9b0SAdrian Chadd MODULE_DEPEND(bhndb, bhnd_chipc, 1, 1, 1); 1944