bhndb.c revision 300250
1184138Smav/*- 2184138Smav * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 3184138Smav * All rights reserved. 4184138Smav * 5184138Smav * Redistribution and use in source and binary forms, with or without 6184138Smav * modification, are permitted provided that the following conditions 7184138Smav * are met: 8184138Smav * 1. Redistributions of source code must retain the above copyright 9184138Smav * notice, this list of conditions and the following disclaimer, 10184138Smav * without modification. 11184138Smav * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12184138Smav * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13184138Smav * redistribution must be conditioned upon including a substantially 14184138Smav * similar Disclaimer requirement for further binary redistribution. 15184138Smav * 16184138Smav * NO WARRANTY 17184138Smav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18184138Smav * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19184138Smav * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20184138Smav * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21184138Smav * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22184138Smav * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23184138Smav * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24184138Smav * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25184138Smav * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26184138Smav * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27184138Smav * THE POSSIBILITY OF SUCH DAMAGES. 28184138Smav */ 29184138Smav 30184138Smav#include <sys/cdefs.h> 31184138Smav__FBSDID("$FreeBSD: head/sys/dev/bhnd/bhndb/bhndb.c 300250 2016-05-20 00:45:16Z adrian $"); 32266200Sian 33184138Smav/* 34184138Smav * Abstract BHND Bridge Device Driver 35184138Smav * 36184138Smav * Provides generic support for bridging from a parent bus (such as PCI) to 37184138Smav * a BHND-compatible bus (e.g. bcma or siba). 38184138Smav */ 39184138Smav 40187876Smav#include <sys/param.h> 41184138Smav#include <sys/kernel.h> 42184138Smav#include <sys/bus.h> 43184138Smav#include <sys/module.h> 44184138Smav#include <sys/systm.h> 45184138Smav 46184138Smav#include <machine/bus.h> 47184138Smav#include <sys/rman.h> 48184138Smav#include <machine/resource.h> 49184138Smav 50184138Smav#include <dev/bhnd/bhndvar.h> 51184138Smav#include <dev/bhnd/bhndreg.h> 52184138Smav 53241600Sgonzo#include <dev/bhnd/cores/chipc/chipcreg.h> 54184138Smav#include <dev/bhnd/nvram/bhnd_nvram.h> 55271051Smarius 56184138Smav#include "bhnd_chipc_if.h" 57271051Smarius#include "bhnd_nvram_if.h" 58187876Smav 59271051Smarius#include "bhndbvar.h" 60187876Smav#include "bhndb_bus_if.h" 61241600Sgonzo#include "bhndb_hwdata.h" 62241600Sgonzo#include "bhndb_private.h" 63241600Sgonzo 64241600Sgonzo/* Debugging flags */ 65241600Sgonzostatic u_long bhndb_debug = 0; 66184138SmavTUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug); 67241600Sgonzo 68241600Sgonzoenum { 69241600Sgonzo BHNDB_DEBUG_PRIO = 1 << 0, 70241600Sgonzo}; 71241600Sgonzo 72184138Smav#define BHNDB_DEBUG(_type) (BHNDB_DEBUG_ ## _type & bhndb_debug) 73184138Smav 74184138Smavstatic bool bhndb_hw_matches(device_t *devlist, 75184138Smav int num_devs, 76184138Smav const struct bhndb_hw *hw); 77184138Smav 78184138Smavstatic int bhndb_initialize_region_cfg( 79184138Smav struct bhndb_softc *sc, device_t *devs, 80184138Smav int ndevs, 81184138Smav const struct bhndb_hw_priority *table, 82184138Smav struct bhndb_resources *r); 83184138Smav 84184138Smavstatic int bhndb_find_hwspec(struct bhndb_softc *sc, 85184138Smav device_t *devs, int ndevs, 86184138Smav const struct bhndb_hw **hw); 87184138Smav 88243689Sgonzostatic int bhndb_read_chipid(struct bhndb_softc *sc, 89243689Sgonzo const struct bhndb_hwcfg *cfg, 90246886Sgonzo struct bhnd_chipid *result); 91246886Sgonzo 92246886Sgonzobhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc, 93241600Sgonzo device_t child); 94241600Sgonzo 95241600Sgonzostatic struct rman *bhndb_get_rman(struct bhndb_softc *sc, 96241600Sgonzo device_t child, int type); 97241600Sgonzo 98241600Sgonzostatic int bhndb_init_child_resource(struct resource *r, 99241600Sgonzo struct resource *parent, 100241600Sgonzo bhnd_size_t offset, 101241600Sgonzo bhnd_size_t size); 102241600Sgonzo 103184138Smavstatic int bhndb_activate_static_region( 104184138Smav struct bhndb_softc *sc, 105184138Smav struct bhndb_region *region, 106184138Smav device_t child, int type, int rid, 107184138Smav struct resource *r); 108184138Smav 109184138Smavstatic int bhndb_try_activate_resource( 110241600Sgonzo struct bhndb_softc *sc, device_t child, 111184138Smav int type, int rid, struct resource *r, 112184138Smav bool *indirect); 113184138Smav 114184138Smav 115184138Smav/** 116184138Smav * Default bhndb(4) implementation of DEVICE_PROBE(). 117184138Smav * 118184138Smav * This function provides the default bhndb implementation of DEVICE_PROBE(), 119184138Smav * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge(). 120184138Smav */ 121184138Smavint 122184138Smavbhndb_generic_probe(device_t dev) 123184138Smav{ 124184138Smav return (BUS_PROBE_NOWILDCARD); 125184138Smav} 126184138Smav 127184138Smavstatic void 128184138Smavbhndb_probe_nomatch(device_t dev, device_t child) 129184138Smav{ 130184138Smav const char *name; 131184138Smav 132184138Smav name = device_get_name(child); 133184138Smav if (name == NULL) 134184138Smav name = "unknown device"; 135184138Smav 136184138Smav device_printf(dev, "<%s> (no driver attached)\n", name); 137184138Smav} 138184138Smav 139184138Smavstatic int 140184138Smavbhndb_print_child(device_t dev, device_t child) 141184138Smav{ 142184138Smav struct bhndb_softc *sc; 143184138Smav struct resource_list *rl; 144184138Smav int retval = 0; 145184138Smav 146184138Smav sc = device_get_softc(dev); 147184138Smav 148184138Smav retval += bus_print_child_header(dev, child); 149184138Smav 150184138Smav rl = BUS_GET_RESOURCE_LIST(dev, child); 151184138Smav if (rl != NULL) { 152184138Smav retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, 153184138Smav "%#jx"); 154241600Sgonzo retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, 155184138Smav "%jd"); 156184138Smav } 157184138Smav 158184138Smav retval += bus_print_child_domain(dev, child); 159184138Smav retval += bus_print_child_footer(dev, child); 160184138Smav 161184138Smav return (retval); 162241600Sgonzo} 163184138Smav 164184138Smavstatic int 165184138Smavbhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf, 166184138Smav size_t buflen) 167184138Smav{ 168184138Smav *buf = '\0'; 169184138Smav return (0); 170184138Smav} 171185661Smav 172184138Smavstatic int 173185661Smavbhndb_child_location_str(device_t dev, device_t child, char *buf, 174185661Smav size_t buflen) 175184138Smav{ 176276287Sian struct bhndb_softc *sc; 177276287Sian 178276287Sian sc = device_get_softc(dev); 179276287Sian 180276287Sian snprintf(buf, buflen, "base=0x%llx", 181276287Sian (unsigned long long) sc->chipid.enum_addr); 182276287Sian return (0); 183276287Sian} 184276287Sian 185276287Sian/** 186276287Sian * Return true if @p devlist matches the @p hw specification. 187276287Sian * 188276287Sian * @param devlist A device table to match against. 189276287Sian * @param num_devs The number of devices in @p devlist. 190276287Sian * @param hw The hardware description to be matched against. 191276287Sian */ 192276287Sianstatic bool 193276287Sianbhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw) 194276287Sian{ 195276287Sian for (u_int i = 0; i < hw->num_hw_reqs; i++) { 196184138Smav const struct bhnd_core_match *match; 197276287Sian bool found; 198184138Smav 199276287Sian match = &hw->hw_reqs[i]; 200276287Sian found = false; 201276287Sian 202276287Sian for (int d = 0; d < num_devs; d++) { 203184138Smav if (!bhnd_device_matches(devlist[d], match)) 204184138Smav continue; 205184138Smav 206184138Smav found = true; 207276287Sian break; 208184138Smav } 209184138Smav 210184138Smav if (!found) 211184138Smav return (false); 212184138Smav } 213184138Smav 214184138Smav return (true); 215184138Smav} 216184138Smav 217184138Smav/** 218184138Smav * Initialize the region maps and priority configuration in @p r using 219184138Smav * the provided priority @p table and the set of devices attached to 220184138Smav * the bridged @p bus_dev . 221184138Smav * 222184138Smav * @param sc The bhndb device state. 223184138Smav * @param devs All devices enumerated on the bridged bhnd bus. 224184138Smav * @param ndevs The length of @p devs. 225184138Smav * @param table Hardware priority table to be used to determine the relative 226184138Smav * priorities of per-core port resources. 227184138Smav * @param r The resource state to be configured. 228184138Smav */ 229184138Smavstatic int 230184138Smavbhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs, 231184138Smav const struct bhndb_hw_priority *table, struct bhndb_resources *r) 232184138Smav{ 233184138Smav const struct bhndb_hw_priority *hp; 234242320Sgonzo bhnd_addr_t addr; 235184138Smav bhnd_size_t size; 236184138Smav size_t prio_low, prio_default, prio_high; 237184138Smav int error; 238184138Smav 239184138Smav /* The number of port regions per priority band that must be accessible 240184138Smav * via dynamic register windows */ 241184138Smav prio_low = 0; 242266751Sian prio_default = 0; 243266751Sian prio_high = 0; 244184138Smav 245184138Smav /* 246184138Smav * Register bridge regions covering all statically mapped ports. 247254423Sian */ 248254423Sian for (int i = 0; i < ndevs; i++) { 249254423Sian const struct bhndb_regwin *regw; 250254423Sian device_t child; 251254423Sian 252242320Sgonzo child = devs[i]; 253242320Sgonzo 254242320Sgonzo for (regw = r->cfg->register_windows; 255246886Sgonzo regw->win_type != BHNDB_REGWIN_T_INVALID; regw++) 256242320Sgonzo { 257242320Sgonzo /* Only core windows are supported */ 258242320Sgonzo if (regw->win_type != BHNDB_REGWIN_T_CORE) 259242320Sgonzo continue; 260242320Sgonzo 261242320Sgonzo /* Skip non-applicable register windows. */ 262184138Smav if (!bhndb_regwin_matches_device(regw, child)) 263242320Sgonzo continue; 264242320Sgonzo 265246886Sgonzo /* Fetch the base address of the mapped port. */ 266246886Sgonzo error = bhnd_get_region_addr(child, 267242320Sgonzo regw->d.core.port_type, regw->d.core.port, 268246886Sgonzo regw->d.core.region, &addr, &size); 269242320Sgonzo if (error) 270242320Sgonzo return (error); 271242320Sgonzo 272242320Sgonzo /* 273242320Sgonzo * Always defer to the register window's size. 274242320Sgonzo * 275242320Sgonzo * If the port size is smaller than the window size, 276242320Sgonzo * this ensures that we fully utilize register windows 277242320Sgonzo * larger than the referenced port. 278242320Sgonzo * 279242320Sgonzo * If the port size is larger than the window size, this 280184138Smav * ensures that we do not directly map the allocations 281242320Sgonzo * within the region to a too-small window. 282242320Sgonzo */ 283242320Sgonzo size = regw->win_size; 284242320Sgonzo 285184138Smav /* 286184138Smav * Add to the bus region list. 287184138Smav * 288184138Smav * The window priority for a statically mapped 289184138Smav * region is always HIGH. 290184138Smav */ 291184138Smav error = bhndb_add_resource_region(r, addr, size, 292184138Smav BHNDB_PRIORITY_HIGH, regw); 293184138Smav if (error) 294184138Smav return (error); 295184138Smav } 296184138Smav } 297184138Smav 298184138Smav /* 299184138Smav * Perform priority accounting and register bridge regions for all 300184138Smav * ports defined in the priority table 301184138Smav */ 302184138Smav for (int i = 0; i < ndevs; i++) { 303184138Smav struct bhndb_region *region; 304184138Smav device_t child; 305184138Smav 306184138Smav child = devs[i]; 307184138Smav 308184138Smav /* 309184138Smav * Skip priority accounting for cores that ... 310184138Smav */ 311184138Smav 312184138Smav /* ... do not require bridge resources */ 313184138Smav if (bhnd_is_hw_disabled(child) || !device_is_enabled(child)) 314241600Sgonzo continue; 315184138Smav 316184138Smav /* ... do not have a priority table entry */ 317184138Smav hp = bhndb_hw_priority_find_device(table, child); 318184138Smav if (hp == NULL) 319184138Smav continue; 320184138Smav 321184138Smav /* ... are explicitly disabled in the priority table. */ 322184138Smav if (hp->priority == BHNDB_PRIORITY_NONE) 323184138Smav continue; 324184138Smav 325184138Smav /* Determine the number of dynamic windows required and 326184138Smav * register their bus_region entries. */ 327184138Smav for (u_int i = 0; i < hp->num_ports; i++) { 328184138Smav const struct bhndb_port_priority *pp; 329184138Smav 330184138Smav pp = &hp->ports[i]; 331184138Smav 332184138Smav /* Skip ports not defined on this device */ 333184138Smav if (!bhnd_is_region_valid(child, pp->type, pp->port, 334184138Smav pp->region)) 335184138Smav { 336184138Smav continue; 337184138Smav } 338184138Smav 339184138Smav /* Fetch the address+size of the mapped port. */ 340184138Smav error = bhnd_get_region_addr(child, pp->type, pp->port, 341184138Smav pp->region, &addr, &size); 342184138Smav if (error) 343184138Smav return (error); 344184138Smav 345184138Smav /* Skip ports with an existing static mapping */ 346184138Smav region = bhndb_find_resource_region(r, addr, size); 347184138Smav if (region != NULL && region->static_regwin != NULL) 348184138Smav continue; 349184138Smav 350184138Smav /* Define a dynamic region for this port */ 351184138Smav error = bhndb_add_resource_region(r, addr, size, 352184138Smav pp->priority, NULL); 353184138Smav if (error) 354184138Smav return (error); 355184138Smav 356184138Smav /* Update port mapping counts */ 357241600Sgonzo switch (pp->priority) { 358184138Smav case BHNDB_PRIORITY_NONE: 359254512Srpaulo break; 360184138Smav case BHNDB_PRIORITY_LOW: 361184138Smav prio_low++; 362184138Smav break; 363184138Smav case BHNDB_PRIORITY_DEFAULT: 364184138Smav prio_default++; 365184138Smav break; 366184138Smav case BHNDB_PRIORITY_HIGH: 367184138Smav prio_high++; 368184138Smav break; 369184138Smav } 370184138Smav } 371241600Sgonzo } 372184138Smav 373184138Smav /* Determine the minimum priority at which we'll allocate direct 374184138Smav * register windows from our dynamic pool */ 375184138Smav size_t prio_total = prio_low + prio_default + prio_high; 376184138Smav if (prio_total <= r->dwa_count) { 377184138Smav /* low+default+high priority regions get windows */ 378184138Smav r->min_prio = BHNDB_PRIORITY_LOW; 379184138Smav 380184138Smav } else if (prio_default + prio_high <= r->dwa_count) { 381184138Smav /* default+high priority regions get windows */ 382184138Smav r->min_prio = BHNDB_PRIORITY_DEFAULT; 383184138Smav 384184138Smav } else { 385184138Smav /* high priority regions get windows */ 386184138Smav r->min_prio = BHNDB_PRIORITY_HIGH; 387184138Smav } 388184138Smav 389184138Smav if (BHNDB_DEBUG(PRIO)) { 390184138Smav struct bhndb_region *region; 391184138Smav const char *direct_msg, *type_msg; 392184138Smav bhndb_priority_t prio, prio_min; 393184138Smav 394184138Smav prio_min = r->min_prio; 395184138Smav device_printf(sc->dev, "min_prio: %d\n", prio_min); 396184138Smav 397184138Smav STAILQ_FOREACH(region, &r->bus_regions, link) { 398184138Smav prio = region->priority; 399254512Srpaulo 400184138Smav direct_msg = prio >= prio_min ? "direct" : "indirect"; 401184138Smav type_msg = region->static_regwin ? "static" : "dynamic"; 402184138Smav 403184138Smav device_printf(sc->dev, "region 0x%llx+0x%llx priority " 404184138Smav "%u %s/%s\n", 405184138Smav (unsigned long long) region->addr, 406184138Smav (unsigned long long) region->size, 407184138Smav region->priority, 408184138Smav direct_msg, type_msg); 409184138Smav } 410184138Smav } 411241600Sgonzo 412184138Smav return (0); 413184138Smav} 414184138Smav 415184138Smav/** 416184138Smav * Find a hardware specification for @p dev. 417184138Smav * 418184138Smav * @param sc The bhndb device state. 419184138Smav * @param devs All devices enumerated on the bridged bhnd bus. 420184138Smav * @param ndevs The length of @p devs. 421184138Smav * @param[out] hw On success, the matched hardware specification. 422184138Smav * with @p dev. 423184138Smav * 424184138Smav * @retval 0 success 425184138Smav * @retval non-zero if an error occurs fetching device info for comparison. 426184138Smav */ 427184138Smavstatic int 428184138Smavbhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs, 429184138Smav const struct bhndb_hw **hw) 430184138Smav{ 431184138Smav const struct bhndb_hw *next, *hw_table; 432184138Smav 433184138Smav /* Search for the first matching hardware config. */ 434184138Smav hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev); 435184138Smav for (next = hw_table; next->hw_reqs != NULL; next++) { 436184138Smav if (!bhndb_hw_matches(devs, ndevs, next)) 437184138Smav continue; 438184138Smav 439184138Smav /* Found */ 440184138Smav *hw = next; 441184138Smav return (0); 442184138Smav } 443184138Smav 444184138Smav return (ENOENT); 445184138Smav} 446184138Smav 447184138Smav/** 448184138Smav * Read the ChipCommon identification data for this device. 449184138Smav * 450184138Smav * @param sc bhndb device state. 451184138Smav * @param cfg The hardware configuration to use when mapping the ChipCommon 452184138Smav * registers. 453184138Smav * @param[out] result the chip identification data. 454184138Smav * 455184138Smav * @retval 0 success 456184138Smav * @retval non-zero if the ChipCommon identification data could not be read. 457184138Smav */ 458184138Smavstatic int 459184138Smavbhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg, 460184138Smav struct bhnd_chipid *result) 461184138Smav{ 462184138Smav const struct bhnd_chipid *parent_cid; 463184138Smav const struct bhndb_regwin *cc_win; 464184138Smav struct resource_spec rs; 465241600Sgonzo int error; 466184138Smav 467184138Smav /* Let our parent device override the discovery process */ 468184138Smav parent_cid = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev); 469184138Smav if (parent_cid != NULL) { 470184138Smav *result = *parent_cid; 471184138Smav return (0); 472184138Smav } 473184138Smav 474184138Smav /* Find a register window we can use to map the first CHIPC_CHIPID_SIZE 475184138Smav * of ChipCommon registers. */ 476184138Smav cc_win = bhndb_regwin_find_best(cfg->register_windows, 477241600Sgonzo BHND_DEVCLASS_CC, 0, BHND_PORT_DEVICE, 0, 0, CHIPC_CHIPID_SIZE); 478184138Smav if (cc_win == NULL) { 479184138Smav device_printf(sc->dev, "no chipcommon register window\n"); 480184138Smav return (0); 481184138Smav } 482184138Smav 483241600Sgonzo /* We can assume a device without a static ChipCommon window uses the 484241600Sgonzo * default ChipCommon address. */ 485184138Smav if (cc_win->win_type == BHNDB_REGWIN_T_DYN) { 486254507Sian error = BHNDB_SET_WINDOW_ADDR(sc->dev, cc_win, 487241600Sgonzo BHND_DEFAULT_CHIPC_ADDR); 488184138Smav 489241600Sgonzo if (error) { 490241600Sgonzo device_printf(sc->dev, "failed to set chipcommon " 491241600Sgonzo "register window\n"); 492184138Smav return (error); 493241600Sgonzo } 494241600Sgonzo } 495241600Sgonzo 496241600Sgonzo /* Let the default bhnd implemenation alloc/release the resource and 497241600Sgonzo * perform the read */ 498241600Sgonzo rs.type = cc_win->res.type; 499241600Sgonzo rs.rid = cc_win->res.rid; 500241600Sgonzo rs.flags = RF_ACTIVE; 501241600Sgonzo 502241600Sgonzo return (bhnd_read_chipid(sc->parent_dev, &rs, cc_win->win_offset, 503241600Sgonzo result)); 504184138Smav} 505241600Sgonzo 506241600Sgonzo/** 507241600Sgonzo * Helper function that must be called by subclass bhndb(4) drivers 508241600Sgonzo * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4) 509241600Sgonzo * APIs on the bridge device. 510241600Sgonzo * 511241600Sgonzo * @param dev The bridge device to attach. 512184138Smav * @param bridge_devclass The device class of the bridging core. This is used 513241600Sgonzo * to automatically detect the bridge core, and to disable additional bridge 514241600Sgonzo * cores (e.g. PCMCIA on a PCIe device). 515241600Sgonzo */ 516241600Sgonzoint 517241600Sgonzobhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass) 518241600Sgonzo{ 519241600Sgonzo struct bhndb_devinfo *dinfo; 520241600Sgonzo struct bhndb_softc *sc; 521241600Sgonzo const struct bhndb_hwcfg *cfg; 522241600Sgonzo int error; 523241600Sgonzo 524184138Smav sc = device_get_softc(dev); 525184138Smav sc->dev = dev; 526241600Sgonzo sc->parent_dev = device_get_parent(dev); 527241600Sgonzo sc->bridge_class = bridge_devclass; 528241600Sgonzo 529241600Sgonzo BHNDB_LOCK_INIT(sc); 530242320Sgonzo 531242320Sgonzo /* Read our chip identification data */ 532242320Sgonzo cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev); 533242320Sgonzo if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid))) 534241600Sgonzo return (error); 535243689Sgonzo 536254507Sian /* Populate generic resource allocation state. */ 537254507Sian sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg); 538243689Sgonzo if (sc->bus_res == NULL) { 539254507Sian return (ENXIO); 540254507Sian } 541254507Sian 542254507Sian /* Attach our bridged bus device */ 543254507Sian sc->bus_dev = BUS_ADD_CHILD(dev, 0, "bhnd", -1); 544254507Sian if (sc->bus_dev == NULL) { 545254507Sian error = ENXIO; 546254507Sian goto failed; 547254507Sian } 548241600Sgonzo 549254507Sian /* Configure address space */ 550241600Sgonzo dinfo = device_get_ivars(sc->bus_dev); 551243689Sgonzo dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED; 552241600Sgonzo 553241600Sgonzo /* Finish attach */ 554242320Sgonzo return (bus_generic_attach(dev)); 555242320Sgonzo 556242320Sgonzofailed: 557242320Sgonzo BHNDB_LOCK_DESTROY(sc); 558242320Sgonzo 559242320Sgonzo if (sc->bus_res != NULL) 560242320Sgonzo bhndb_free_resources(sc->bus_res); 561242320Sgonzo 562254507Sian return (error); 563254507Sian} 564254507Sian 565254507Sian/** 566254507Sian * Default bhndb(4) implementation of BHNDB_INIT_FULL_CONFIG(). 567241600Sgonzo * 568241600Sgonzo * This function provides the default bhndb implementation of 569254423Sian * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver 570254423Sian * overriding BHNDB_INIT_FULL_CONFIG(). 571241600Sgonzo * 572184138Smav * As documented by BHNDB_INIT_FULL_CONFIG, this function performs final 573246886Sgonzo * bridge configuration based on the hardware information enumerated by the 574241600Sgonzo * child bus, and will reset all resource allocation state on the bridge. 575241600Sgonzo * 576241600Sgonzo * When calling this method: 577241600Sgonzo * - Any bus resources previously allocated by @p child must be deallocated. 578241600Sgonzo * - The @p child bus must have performed initial enumeration -- but not 579241600Sgonzo * probe or attachment -- of its children. 580241600Sgonzo */ 581241600Sgonzoint 582241600Sgonzobhndb_generic_init_full_config(device_t dev, device_t child, 583241600Sgonzo const struct bhndb_hw_priority *hw_prio_table) 584241600Sgonzo{ 585184138Smav struct bhndb_softc *sc; 586241600Sgonzo const struct bhndb_hw *hw; 587283318Sian struct bhndb_resources *r; 588283318Sian device_t *devs; 589241600Sgonzo device_t hostb; 590241600Sgonzo int ndevs; 591241600Sgonzo int error; 592241600Sgonzo 593241600Sgonzo sc = device_get_softc(dev); 594184138Smav hostb = NULL; 595241600Sgonzo 596241600Sgonzo /* Fetch the full set of bhnd-attached cores */ 597241600Sgonzo if ((error = device_get_children(sc->bus_dev, &devs, &ndevs))) 598241600Sgonzo return (error); 599241600Sgonzo 600247495Sgonzo /* Find our host bridge device */ 601247495Sgonzo hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child); 602247495Sgonzo if (hostb == NULL) { 603247495Sgonzo device_printf(sc->dev, "no host bridge core found\n"); 604247495Sgonzo error = ENODEV; 605247495Sgonzo goto cleanup; 606247495Sgonzo } 607241600Sgonzo 608283318Sian /* Find our full register window configuration */ 609241600Sgonzo if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) { 610241600Sgonzo device_printf(sc->dev, "unable to identify device, " 611283318Sian " using generic bridge resource definitions\n"); 612283318Sian error = 0; 613241600Sgonzo goto cleanup; 614241600Sgonzo } 615241600Sgonzo 616241600Sgonzo if (bootverbose || BHNDB_DEBUG(PRIO)) 617241600Sgonzo device_printf(sc->dev, "%s resource configuration\n", hw->name); 618184138Smav 619241600Sgonzo /* Release existing resource state */ 620241600Sgonzo BHNDB_LOCK(sc); 621241600Sgonzo bhndb_free_resources(sc->bus_res); 622266200Sian sc->bus_res = NULL; 623184138Smav BHNDB_UNLOCK(sc); 624184138Smav 625184138Smav /* Allocate new resource state */ 626241600Sgonzo r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg); 627241600Sgonzo if (r == NULL) { 628184138Smav error = ENXIO; 629241600Sgonzo goto cleanup; 630241600Sgonzo } 631184138Smav 632241600Sgonzo /* Initialize our resource priority configuration */ 633241600Sgonzo error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r); 634241600Sgonzo if (error) { 635241600Sgonzo bhndb_free_resources(r); 636184138Smav goto cleanup; 637266200Sian } 638241600Sgonzo 639241600Sgonzo /* Update our bridge state */ 640184138Smav BHNDB_LOCK(sc); 641241600Sgonzo sc->bus_res = r; 642241600Sgonzo sc->hostb_dev = hostb; 643241600Sgonzo BHNDB_UNLOCK(sc); 644241600Sgonzo 645241600Sgonzocleanup: 646241600Sgonzo free(devs, M_TEMP); 647184138Smav return (error); 648241600Sgonzo} 649241600Sgonzo 650241600Sgonzo/** 651241600Sgonzo * Default bhndb(4) implementation of DEVICE_DETACH(). 652241600Sgonzo * 653241600Sgonzo * This function detaches any child devices, and if successful, releases all 654184138Smav * resources held by the bridge device. 655241600Sgonzo */ 656241600Sgonzoint 657184138Smavbhndb_generic_detach(device_t dev) 658184138Smav{ 659184138Smav struct bhndb_softc *sc; 660241600Sgonzo int error; 661241600Sgonzo 662185527Smav sc = device_get_softc(dev); 663241600Sgonzo 664185527Smav /* Detach children */ 665185527Smav if ((error = bus_generic_detach(dev))) 666185527Smav return (error); 667185527Smav 668241600Sgonzo /* Clean up our driver state. */ 669241600Sgonzo bhndb_free_resources(sc->bus_res); 670185527Smav 671241600Sgonzo BHNDB_LOCK_DESTROY(sc); 672185527Smav 673241600Sgonzo return (0); 674185527Smav} 675185527Smav 676246886Sgonzo/** 677246886Sgonzo * Default bhndb(4) implementation of DEVICE_SUSPEND(). 678246886Sgonzo * 679246886Sgonzo * This function calls bus_generic_suspend() (or implements equivalent 680246886Sgonzo * behavior). 681246886Sgonzo */ 682246886Sgonzoint 683246886Sgonzobhndb_generic_suspend(device_t dev) 684246886Sgonzo{ 685241600Sgonzo return (bus_generic_suspend(dev)); 686241600Sgonzo} 687184138Smav 688184138Smav/** 689184138Smav * Default bhndb(4) implementation of DEVICE_RESUME(). 690184138Smav * 691184138Smav * This function calls bus_generic_resume() (or implements equivalent 692184138Smav * behavior). 693184138Smav */ 694184138Smavint 695184138Smavbhndb_generic_resume(device_t dev) 696184138Smav{ 697184138Smav struct bhndb_softc *sc; 698184138Smav struct bhndb_resources *bus_res; 699283318Sian struct bhndb_dw_alloc *dwa; 700283318Sian int error; 701283318Sian 702283318Sian sc = device_get_softc(dev); 703283318Sian bus_res = sc->bus_res; 704283318Sian 705184138Smav /* Guarantee that all in-use dynamic register windows are mapped to 706283318Sian * their previously configured target address. */ 707283318Sian BHNDB_LOCK(sc); 708184138Smav for (size_t i = 0; i < bus_res->dwa_count; i++) { 709283318Sian dwa = &bus_res->dw_alloc[i]; 710283318Sian 711283318Sian /* Skip regions that were not previously used */ 712278703Sian if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0) 713278703Sian continue; 714184138Smav 715184138Smav /* Otherwise, ensure the register window is correct before 716184138Smav * any children attempt MMIO */ 717184138Smav error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); 718184138Smav if (error) 719241600Sgonzo break; 720184138Smav } 721184138Smav BHNDB_UNLOCK(sc); 722184138Smav 723184138Smav /* Error restoring hardware state; children cannot be safely resumed */ 724184138Smav if (error) { 725184138Smav device_printf(dev, "Unable to restore hardware configuration; " 726266200Sian "cannot resume: %d\n", error); 727266200Sian return (error); 728266200Sian } 729266200Sian 730266200Sian return (bus_generic_resume(dev)); 731266200Sian} 732266200Sian 733266200Sian/** 734266200Sian * Default implementation of BHNDB_SUSPEND_RESOURCE. 735266200Sian */ 736266200Sianstatic void 737266200Sianbhndb_suspend_resource(device_t dev, device_t child, int type, 738266200Sian struct resource *r) 739266200Sian{ 740266200Sian struct bhndb_softc *sc; 741266200Sian struct bhndb_dw_alloc *dwa; 742266200Sian 743266200Sian sc = device_get_softc(dev); 744266200Sian 745266200Sian // TODO: IRQs? 746276287Sian if (type != SYS_RES_MEMORY) 747276287Sian return; 748266200Sian 749266200Sian BHNDB_LOCK(sc); 750266200Sian dwa = bhndb_dw_find_resource(sc->bus_res, r); 751276287Sian if (dwa == NULL) { 752276287Sian BHNDB_UNLOCK(sc); 753266200Sian return; 754266200Sian } 755266200Sian 756184138Smav if (BHNDB_DEBUG(PRIO)) 757184138Smav device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n", 758184138Smav type, rman_get_start(r), rman_get_size(r)); 759184138Smav 760184138Smav /* Release the resource's window reference */ 761184138Smav bhndb_dw_release(sc->bus_res, dwa, r); 762184138Smav BHNDB_UNLOCK(sc); 763184138Smav} 764184138Smav 765184138Smav/** 766184138Smav * Default implementation of BHNDB_RESUME_RESOURCE. 767184138Smav */ 768184138Smavstatic int 769184138Smavbhndb_resume_resource(device_t dev, device_t child, int type, 770184138Smav struct resource *r) 771184138Smav{ 772184138Smav struct bhndb_softc *sc; 773184138Smav 774184138Smav sc = device_get_softc(dev); 775184138Smav 776184138Smav // TODO: IRQs? 777184138Smav if (type != SYS_RES_MEMORY) 778184138Smav return (0); 779184138Smav 780184138Smav /* Inactive resources don't require reallocation of bridge resources */ 781184138Smav if (!(rman_get_flags(r) & RF_ACTIVE)) 782184138Smav return (0); 783184138Smav 784184138Smav if (BHNDB_DEBUG(PRIO)) 785184138Smav device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n", 786184138Smav type, rman_get_start(r), rman_get_size(r)); 787184138Smav 788184138Smav return (bhndb_try_activate_resource(sc, rman_get_device(r), type, 789184138Smav rman_get_rid(r), r, NULL)); 790184138Smav} 791184138Smav 792184138Smav 793266200Sian/** 794184138Smav * Default bhndb(4) implementation of BUS_READ_IVAR(). 795184138Smav */ 796184138Smavstatic int 797184138Smavbhndb_read_ivar(device_t dev, device_t child, int index, 798184138Smav uintptr_t *result) 799185661Smav{ 800185661Smav return (ENOENT); 801185661Smav} 802185661Smav 803185661Smav/** 804184138Smav * Default bhndb(4) implementation of BUS_WRITE_IVAR(). 805266200Sian */ 806184138Smavstatic int 807184138Smavbhndb_write_ivar(device_t dev, device_t child, int index, 808184138Smav uintptr_t value) 809184138Smav{ 810184138Smav return (ENOENT); 811184138Smav} 812184138Smav 813184138Smav/** 814184138Smav * Return the address space for the given @p child device. 815184138Smav */ 816266200Sianbhndb_addrspace 817266200Sianbhndb_get_addrspace(struct bhndb_softc *sc, device_t child) 818266200Sian{ 819266200Sian struct bhndb_devinfo *dinfo; 820266200Sian device_t imd_dev; 821266200Sian 822266200Sian /* Find the directly attached parent of the requesting device */ 823266200Sian imd_dev = child; 824266200Sian while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev) 825266200Sian imd_dev = device_get_parent(imd_dev); 826266200Sian 827184138Smav if (imd_dev == NULL) 828184138Smav panic("bhndb address space request for non-child device %s\n", 829184138Smav device_get_nameunit(child)); 830184138Smav 831184138Smav dinfo = device_get_ivars(imd_dev); 832184138Smav return (dinfo->addrspace); 833266200Sian} 834184138Smav 835184138Smav/** 836184138Smav * Return the rman instance for a given resource @p type, if any. 837184138Smav * 838184138Smav * @param sc The bhndb device state. 839184138Smav * @param child The requesting child. 840184138Smav * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...) 841184138Smav */ 842184138Smavstatic struct rman * 843184138Smavbhndb_get_rman(struct bhndb_softc *sc, device_t child, int type) 844184138Smav{ 845184138Smav switch (bhndb_get_addrspace(sc, child)) { 846184138Smav case BHNDB_ADDRSPACE_NATIVE: 847184138Smav switch (type) { 848184138Smav case SYS_RES_MEMORY: 849184138Smav return (&sc->bus_res->ht_mem_rman); 850184138Smav case SYS_RES_IRQ: 851184138Smav return (NULL); 852184138Smav default: 853184138Smav return (NULL); 854184138Smav }; 855184138Smav 856184138Smav case BHNDB_ADDRSPACE_BRIDGED: 857184138Smav switch (type) { 858184138Smav case SYS_RES_MEMORY: 859184138Smav return (&sc->bus_res->br_mem_rman); 860184138Smav case SYS_RES_IRQ: 861184138Smav // TODO 862184138Smav // return &sc->irq_rman; 863184138Smav return (NULL); 864184138Smav default: 865184138Smav return (NULL); 866184138Smav }; 867184138Smav } 868184138Smav 869184138Smav /* Quieten gcc */ 870184138Smav return (NULL); 871184138Smav} 872184138Smav 873184138Smav/** 874241600Sgonzo * Default implementation of BUS_ADD_CHILD() 875266200Sian */ 876266200Sianstatic device_t 877184138Smavbhndb_add_child(device_t dev, u_int order, const char *name, int unit) 878184138Smav{ 879184138Smav struct bhndb_devinfo *dinfo; 880184138Smav device_t child; 881184138Smav 882184138Smav child = device_add_child_ordered(dev, order, name, unit); 883184138Smav if (child == NULL) 884184138Smav return (NULL); 885184138Smav 886184138Smav dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT); 887184138Smav if (dinfo == NULL) { 888184138Smav device_delete_child(dev, child); 889184138Smav return (NULL); 890184138Smav } 891184138Smav 892184138Smav dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; 893184138Smav resource_list_init(&dinfo->resources); 894184138Smav 895184138Smav device_set_ivars(child, dinfo); 896184138Smav 897184138Smav return (child); 898184138Smav} 899184138Smav 900184138Smav/** 901184138Smav * Default implementation of BUS_CHILD_DELETED(). 902184138Smav */ 903254496Sianstatic void 904254496Sianbhndb_child_deleted(device_t dev, device_t child) 905254496Sian{ 906254496Sian struct bhndb_devinfo *dinfo = device_get_ivars(child); 907254496Sian if (dinfo != NULL) { 908254496Sian resource_list_free(&dinfo->resources); 909254496Sian free(dinfo, M_BHND); 910184138Smav } 911184138Smav 912184138Smav device_set_ivars(child, NULL); 913184138Smav} 914184138Smav 915184138Smav/** 916184138Smav * Default implementation of BHNDB_GET_CHIPID(). 917184138Smav */ 918184138Smavstatic const struct bhnd_chipid * 919184138Smavbhndb_get_chipid(device_t dev, device_t child) 920184138Smav{ 921184138Smav struct bhndb_softc *sc = device_get_softc(dev); 922184138Smav return (&sc->chipid); 923184138Smav} 924184138Smav 925184138Smav 926184138Smav/** 927184138Smav * Default implementation of BHNDB_IS_HW_DISABLED(). 928184138Smav */ 929184138Smavstatic bool 930184138Smavbhndb_is_hw_disabled(device_t dev, device_t child) { 931184138Smav struct bhndb_softc *sc; 932184138Smav struct bhnd_core_info core; 933184138Smav 934254423Sian sc = device_get_softc(dev); 935184138Smav 936254423Sian /* Requestor must be attached to the bhnd bus */ 937254423Sian if (device_get_parent(child) != sc->bus_dev) { 938254423Sian return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child)); 939254423Sian } 940254423Sian 941254423Sian /* Fetch core info */ 942254423Sian core = bhnd_get_core_info(child); 943254423Sian 944254423Sian /* Try to defer to the bhndb bus parent */ 945254423Sian if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, &core)) 946254423Sian return (true); 947254423Sian 948254423Sian /* Otherwise, we treat bridge-capable cores as unpopulated if they're 949184138Smav * not the configured host bridge */ 950184138Smav if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(&core))) 951184138Smav return (BHNDB_FIND_HOSTB_DEVICE(dev, sc->bus_dev) != child); 952184138Smav 953184138Smav /* Otherwise, assume the core is populated */ 954184138Smav return (false); 955184138Smav} 956184138Smav 957184138Smav/* ascending core index comparison used by bhndb_find_hostb_device() */ 958184138Smavstatic int 959241600Sgonzocompare_core_index(const void *lhs, const void *rhs) 960184138Smav{ 961184138Smav u_int left = bhnd_get_core_index(*(const device_t *) lhs); 962184138Smav u_int right = bhnd_get_core_index(*(const device_t *) rhs); 963241600Sgonzo 964184138Smav if (left < right) 965184138Smav return (-1); 966184138Smav else if (left > right) 967184138Smav return (1); 968184138Smav else 969254512Srpaulo return (0); 970254512Srpaulo} 971184138Smav 972184138Smav/** 973254512Srpaulo * Default bhndb(4) implementation of BHND_BUS_FIND_HOSTB_DEVICE(). 974254512Srpaulo * 975254512Srpaulo * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged 976254512Srpaulo * bhnd(4) devices to determine the hostb core: 977184138Smav * 978184138Smav * - The core must have a Broadcom vendor ID. 979184138Smav * - The core devclass must match the bridge type. 980184138Smav * - The core must be the first device on the bus with the bridged device 981184138Smav * class. 982184138Smav * 983184138Smav * @param dev The bhndb device 984184138Smav * @param child The requesting bhnd bus. 985184138Smav */ 986184138Smavstatic device_t 987184138Smavbhndb_find_hostb_device(device_t dev, device_t child) 988184138Smav{ 989184138Smav struct bhndb_softc *sc; 990184138Smav struct bhnd_core_match md; 991184138Smav device_t hostb_dev, *devlist; 992184138Smav int devcnt, error; 993184138Smav 994184138Smav sc = device_get_softc(dev); 995184138Smav 996247495Sgonzo /* Determine required device class and set up a match descriptor. */ 997184138Smav md = (struct bhnd_core_match) { 998184138Smav .vendor = BHND_MFGID_BCM, 999184138Smav .device = BHND_COREID_INVALID, 1000184138Smav .hwrev = { BHND_HWREV_INVALID, BHND_HWREV_INVALID }, 1001184138Smav .class = sc->bridge_class, 1002254512Srpaulo .unit = 0 1003184138Smav }; 1004184138Smav 1005184138Smav /* Must be the absolute first matching device on the bus. */ 1006184138Smav if ((error = device_get_children(child, &devlist, &devcnt))) 1007184138Smav return (false); 1008184138Smav 1009278688Sian /* Sort by core index value, ascending */ 1010184138Smav qsort(devlist, devcnt, sizeof(*devlist), compare_core_index); 1011184138Smav 1012254512Srpaulo /* Find the hostb device */ 1013254512Srpaulo hostb_dev = NULL; 1014184138Smav for (int i = 0; i < devcnt; i++) { 1015184138Smav if (bhnd_device_matches(devlist[i], &md)) { 1016184138Smav hostb_dev = devlist[i]; 1017254512Srpaulo break; 1018254512Srpaulo } 1019184138Smav } 1020278688Sian 1021184138Smav /* Clean up */ 1022184138Smav free(devlist, M_TEMP); 1023184138Smav 1024184138Smav return (hostb_dev); 1025184138Smav} 1026184138Smav 1027184138Smav/** 1028184138Smav * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE(). 1029184138Smav */ 1030184138Smavstatic struct resource * 1031184138Smavbhndb_alloc_resource(device_t dev, device_t child, int type, 1032184138Smav int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 1033184138Smav{ 1034184138Smav struct bhndb_softc *sc; 1035184138Smav struct resource_list_entry *rle; 1036184138Smav struct resource *rv; 1037184138Smav struct rman *rm; 1038184138Smav int error; 1039184138Smav bool passthrough, isdefault; 1040184138Smav 1041184138Smav sc = device_get_softc(dev); 1042184138Smav passthrough = (device_get_parent(child) != dev); 1043184138Smav isdefault = RMAN_IS_DEFAULT_RANGE(start, end); 1044184138Smav rle = NULL; 1045184138Smav 1046184138Smav /* Populate defaults */ 1047184138Smav if (!passthrough && isdefault) { 1048184138Smav /* Fetch the resource list entry. */ 1049184138Smav rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), 1050184138Smav type, *rid); 1051184138Smav if (rle == NULL) { 1052184138Smav device_printf(dev, 1053184138Smav "default resource %#x type %d for child %s " 1054187876Smav "not found\n", *rid, type, 1055187876Smav device_get_nameunit(child)); 1056187876Smav 1057241600Sgonzo return (NULL); 1058184138Smav } 1059184138Smav 1060184138Smav if (rle->res != NULL) { 1061184138Smav device_printf(dev, 1062266200Sian "resource entry %#x type %d for child %s is busy\n", 1063184138Smav *rid, type, device_get_nameunit(child)); 1064184138Smav 1065241600Sgonzo return (NULL); 1066241600Sgonzo } 1067184138Smav 1068184138Smav start = rle->start; 1069184138Smav end = rle->end; 1070184138Smav count = ulmax(count, rle->count); 1071184138Smav } 1072184138Smav 1073184138Smav /* Validate resource addresses */ 1074184138Smav if (start > end || count > ((end - start) + 1)) 1075187876Smav return (NULL); 1076187876Smav 1077187876Smav /* Fetch the resource manager */ 1078187876Smav rm = bhndb_get_rman(sc, child, type); 1079187876Smav if (rm == NULL) 1080187876Smav return (NULL); 1081184138Smav 1082184138Smav /* Make our reservation */ 1083184138Smav rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, 1084184138Smav child); 1085188724Smav if (rv == NULL) 1086188724Smav return (NULL); 1087241600Sgonzo 1088188724Smav rman_set_rid(rv, *rid); 1089188724Smav 1090188724Smav /* Activate */ 1091184138Smav if (flags & RF_ACTIVE) { 1092184138Smav error = bus_activate_resource(child, type, *rid, rv); 1093184138Smav if (error) { 1094241600Sgonzo device_printf(dev, 1095241600Sgonzo "failed to activate entry %#x type %d for " 1096184138Smav "child %s: %d\n", 1097184138Smav *rid, type, device_get_nameunit(child), error); 1098184138Smav 1099184138Smav rman_release_resource(rv); 1100184138Smav 1101184138Smav return (NULL); 1102184138Smav } 1103184138Smav } 1104184138Smav 1105184138Smav /* Update child's resource list entry */ 1106241600Sgonzo if (rle != NULL) { 1107241600Sgonzo rle->res = rv; 1108184138Smav rle->start = rman_get_start(rv); 1109184138Smav rle->end = rman_get_end(rv); 1110184138Smav rle->count = rman_get_size(rv); 1111184138Smav } 1112184138Smav 1113184138Smav return (rv); 1114185722Smav} 1115184138Smav 1116184138Smav/** 1117184138Smav * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE(). 1118184138Smav */ 1119184138Smavstatic int 1120184138Smavbhndb_release_resource(device_t dev, device_t child, int type, int rid, 1121184138Smav struct resource *r) 1122241600Sgonzo{ 1123241600Sgonzo int error; 1124184138Smav 1125184138Smav /* Deactivate resources */ 1126184138Smav if (rman_get_flags(r) & RF_ACTIVE) { 1127184138Smav error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r); 1128184138Smav if (error) 1129184138Smav return (error); 1130184138Smav } 1131185722Smav 1132184138Smav if ((error = rman_release_resource(r))) 1133184138Smav return (error); 1134184138Smav 1135184138Smav return (0); 1136184138Smav} 1137184138Smav 1138184138Smav/** 1139184138Smav * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE(). 1140184138Smav */ 1141184138Smavstatic int 1142184138Smavbhndb_adjust_resource(device_t dev, device_t child, int type, 1143184138Smav struct resource *r, rman_res_t start, rman_res_t end) 1144184138Smav{ 1145184138Smav struct bhndb_softc *sc; 1146184138Smav struct rman *rm; 1147184138Smav int error; 1148184138Smav 1149184138Smav sc = device_get_softc(dev); 1150184138Smav error = 0; 1151184138Smav 1152184138Smav /* Fetch resource manager */ 1153184138Smav rm = bhndb_get_rman(sc, child, type); 1154184138Smav if (rm == NULL) 1155184138Smav return (ENXIO); 1156184138Smav 1157184138Smav if (!rman_is_region_manager(r, rm)) 1158184138Smav return (ENXIO); 1159184138Smav 1160184138Smav /* If active, adjustment is limited by the assigned window. */ 1161184138Smav BHNDB_LOCK(sc); 1162184138Smav 1163184138Smav // TODO: Currently unsupported 1164184138Smav error = ENODEV; 1165184138Smav 1166184138Smav BHNDB_UNLOCK(sc); 1167184138Smav if (!error) 1168184138Smav error = rman_adjust_resource(r, start, end); 1169184138Smav 1170184138Smav return (error); 1171184138Smav} 1172184138Smav 1173184138Smav/** 1174184138Smav * Initialize child resource @p r with a virtual address, tag, and handle 1175184138Smav * copied from @p parent, adjusted to contain only the range defined by 1176246891Sgonzo * @p offsize and @p size. 1177184138Smav * 1178184138Smav * @param r The register to be initialized. 1179184138Smav * @param parent The parent bus resource that fully contains the subregion. 1180184138Smav * @param offset The subregion offset within @p parent. 1181184138Smav * @param size The subregion size. 1182184138Smav * @p r. 1183184138Smav */ 1184184138Smavstatic int 1185184138Smavbhndb_init_child_resource(struct resource *r, 1186184138Smav struct resource *parent, bhnd_size_t offset, bhnd_size_t size) 1187184138Smav{ 1188278688Sian bus_space_handle_t bh, child_bh; 1189184138Smav bus_space_tag_t bt; 1190184138Smav uintptr_t vaddr; 1191184138Smav int error; 1192247495Sgonzo 1193247495Sgonzo /* Fetch the parent resource's real bus values */ 1194247495Sgonzo vaddr = (uintptr_t) rman_get_virtual(parent); 1195247495Sgonzo bt = rman_get_bustag(parent); 1196247495Sgonzo bh = rman_get_bushandle(parent); 1197247495Sgonzo 1198247495Sgonzo /* Configure child resource with window-adjusted real bus values */ 1199247495Sgonzo vaddr += offset; 1200184138Smav error = bus_space_subregion(bt, bh, offset, size, &child_bh); 1201184138Smav if (error) 1202184138Smav return (error); 1203184138Smav 1204184138Smav rman_set_virtual(r, (void *) vaddr); 1205184138Smav rman_set_bustag(r, bt); 1206184138Smav rman_set_bushandle(r, child_bh); 1207184138Smav 1208184138Smav return (0); 1209184138Smav} 1210184138Smav 1211184138Smav/** 1212184138Smav * Attempt activation of a fixed register window mapping for @p child. 1213184138Smav * 1214184138Smav * @param sc BHNDB device state. 1215184138Smav * @param region The static region definition capable of mapping @p r. 1216184138Smav * @param child A child requesting resource activation. 1217184138Smav * @param type Resource type. 1218184138Smav * @param rid Resource identifier. 1219184138Smav * @param r Resource to be activated. 1220184138Smav * 1221184138Smav * @retval 0 if @p r was activated successfully 1222184138Smav * @retval ENOENT if no fixed register window was found. 1223184138Smav * @retval non-zero if @p r could not be activated. 1224184138Smav */ 1225184138Smavstatic int 1226184138Smavbhndb_activate_static_region(struct bhndb_softc *sc, 1227184138Smav struct bhndb_region *region, device_t child, int type, int rid, 1228184138Smav struct resource *r) 1229184138Smav{ 1230184138Smav struct resource *bridge_res; 1231184138Smav const struct bhndb_regwin *win; 1232184138Smav bhnd_size_t parent_offset; 1233184138Smav rman_res_t r_start, r_size; 1234184138Smav int error; 1235184138Smav 1236184138Smav win = region->static_regwin; 1237184138Smav 1238247495Sgonzo KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type), 1239247495Sgonzo ("can't activate non-static region")); 1240247495Sgonzo 1241247495Sgonzo r_start = rman_get_start(r); 1242247495Sgonzo r_size = rman_get_size(r); 1243247495Sgonzo 1244247495Sgonzo /* Find the corresponding bridge resource */ 1245278688Sian bridge_res = bhndb_find_regwin_resource(sc->bus_res, win); 1246278688Sian if (bridge_res == NULL) 1247278688Sian return (ENXIO); 1248278688Sian 1249278688Sian /* Calculate subregion offset within the parent resource */ 1250278688Sian parent_offset = r_start - region->addr; 1251278688Sian parent_offset += win->win_offset; 1252278688Sian 1253278688Sian /* Configure resource with its real bus values. */ 1254184138Smav error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size); 1255184138Smav if (error) 1256184138Smav return (error); 1257184138Smav 1258184138Smav /* Mark active */ 1259184138Smav if ((error = rman_activate_resource(r))) 1260184138Smav return (error); 1261184138Smav 1262184138Smav return (0); 1263184138Smav} 1264184138Smav 1265184138Smav/** 1266184138Smav * Attempt to allocate/retain a dynamic register window for @p r, returning 1267184138Smav * the retained window. 1268184138Smav * 1269184138Smav * @param sc The bhndb driver state. 1270184138Smav * @param r The resource for which a window will be retained. 1271184138Smav */ 1272241600Sgonzostatic struct bhndb_dw_alloc * 1273241600Sgonzobhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r) 1274184138Smav{ 1275241600Sgonzo struct bhndb_dw_alloc *dwa; 1276241600Sgonzo rman_res_t r_start, r_size; 1277241600Sgonzo int error; 1278241600Sgonzo 1279241600Sgonzo BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1280241600Sgonzo 1281241600Sgonzo r_start = rman_get_start(r); 1282241600Sgonzo r_size = rman_get_size(r); 1283241600Sgonzo 1284241600Sgonzo /* Look for an existing dynamic window we can reference */ 1285241600Sgonzo dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size); 1286184138Smav if (dwa != NULL) { 1287241600Sgonzo if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0) 1288241600Sgonzo return (dwa); 1289241600Sgonzo 1290241600Sgonzo return (NULL); 1291187876Smav } 1292241600Sgonzo 1293241600Sgonzo /* Otherwise, try to reserve a free window */ 1294241600Sgonzo dwa = bhndb_dw_next_free(sc->bus_res); 1295241600Sgonzo if (dwa == NULL) { 1296241600Sgonzo /* No free windows */ 1297241600Sgonzo return (NULL); 1298184138Smav } 1299241600Sgonzo 1300241600Sgonzo /* Set the window target */ 1301241600Sgonzo error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r), 1302241600Sgonzo rman_get_size(r)); 1303241600Sgonzo if (error) { 1304184138Smav device_printf(sc->dev, "dynamic window initialization " 1305241600Sgonzo "for 0x%llx-0x%llx failed: %d\n", 1306184138Smav (unsigned long long) r_start, 1307241600Sgonzo (unsigned long long) r_start + r_size - 1, 1308241600Sgonzo error); 1309241600Sgonzo return (NULL); 1310241600Sgonzo } 1311241600Sgonzo 1312241600Sgonzo /* Add our reservation */ 1313241600Sgonzo if (bhndb_dw_retain(sc->bus_res, dwa, r)) 1314241600Sgonzo return (NULL); 1315276287Sian 1316276287Sian return (dwa); 1317276287Sian} 1318241600Sgonzo 1319241600Sgonzo/** 1320241600Sgonzo * Activate a resource using any viable static or dynamic register window. 1321241600Sgonzo * 1322241600Sgonzo * @param sc The bhndb driver state. 1323241600Sgonzo * @param child The child holding ownership of @p r. 1324241600Sgonzo * @param type The type of the resource to be activated. 1325241600Sgonzo * @param rid The resource ID of @p r. 1326241600Sgonzo * @param r The resource to be activated 1327241600Sgonzo * @param[out] indirect On error and if not NULL, will be set to 'true' if 1328241600Sgonzo * the caller should instead use an indirect resource mapping. 1329241600Sgonzo * 1330241600Sgonzo * @retval 0 success 1331241600Sgonzo * @retval non-zero activation failed. 1332241600Sgonzo */ 1333241600Sgonzostatic int 1334241600Sgonzobhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type, 1335241600Sgonzo int rid, struct resource *r, bool *indirect) 1336241600Sgonzo{ 1337241600Sgonzo struct bhndb_region *region; 1338241600Sgonzo struct bhndb_dw_alloc *dwa; 1339241600Sgonzo bhndb_priority_t dw_priority; 1340241600Sgonzo rman_res_t r_start, r_size; 1341241600Sgonzo rman_res_t parent_offset; 1342241600Sgonzo int error; 1343184138Smav 1344184138Smav BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED); 1345241600Sgonzo 1346241600Sgonzo // TODO - IRQs 1347184138Smav if (type != SYS_RES_MEMORY) 1348184138Smav return (ENXIO); 1349184138Smav 1350184138Smav if (indirect) 1351184138Smav *indirect = false; 1352184138Smav 1353184138Smav r_start = rman_get_start(r); 1354222475Sjchandra r_size = rman_get_size(r); 1355184138Smav 1356184138Smav /* Activate native addrspace resources using the host address space */ 1357222475Sjchandra if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) { 1358184138Smav struct resource *parent; 1359184138Smav 1360222475Sjchandra /* Find the bridge resource referenced by the child */ 1361184138Smav parent = bhndb_find_resource_range(sc->bus_res, r_start, 1362184138Smav r_size); 1363222475Sjchandra if (parent == NULL) { 1364184138Smav device_printf(sc->dev, "host resource not found " 1365184138Smav "for 0x%llx-0x%llx\n", 1366222475Sjchandra (unsigned long long) r_start, 1367184138Smav (unsigned long long) r_start + r_size - 1); 1368184138Smav return (ENOENT); 1369222475Sjchandra } 1370184138Smav 1371184138Smav /* Initialize child resource with the real bus values */ 1372222475Sjchandra error = bhndb_init_child_resource(r, parent, 1373184138Smav r_start - rman_get_start(parent), r_size); 1374184138Smav if (error) 1375222475Sjchandra return (error); 1376184138Smav 1377184138Smav /* Try to activate child resource */ 1378222475Sjchandra return (rman_activate_resource(r)); 1379184138Smav } 1380184138Smav 1381222475Sjchandra /* Default to low priority */ 1382184138Smav dw_priority = BHNDB_PRIORITY_LOW; 1383184138Smav 1384222475Sjchandra /* Look for a bus region matching the resource's address range */ 1385184138Smav region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); 1386184138Smav if (region != NULL) 1387222475Sjchandra dw_priority = region->priority; 1388184138Smav 1389184138Smav /* Prefer static mappings over consuming a dynamic windows. */ 1390222475Sjchandra if (region && region->static_regwin) { 1391184138Smav error = bhndb_activate_static_region(sc, region, child, type, 1392184452Smav rid, r); 1393222475Sjchandra if (error) 1394184452Smav device_printf(sc->dev, "static window allocation " 1395184138Smav "for 0x%llx-0x%llx failed\n", 1396184138Smav (unsigned long long) r_start, 1397184138Smav (unsigned long long) r_start + r_size - 1); 1398184138Smav return (error); 1399241600Sgonzo } 1400241600Sgonzo 1401184138Smav /* A dynamic window will be required; is this resource high enough 1402184138Smav * priority to be reserved a dynamic window? */ 1403184138Smav if (dw_priority < sc->bus_res->min_prio) { 1404184138Smav if (indirect) 1405184138Smav *indirect = true; 1406184138Smav 1407184138Smav return (ENOMEM); 1408184138Smav } 1409184138Smav 1410184138Smav /* Find and retain a usable window */ 1411184138Smav BHNDB_LOCK(sc); { 1412184138Smav dwa = bhndb_retain_dynamic_window(sc, r); 1413184138Smav } BHNDB_UNLOCK(sc); 1414184138Smav 1415184138Smav if (dwa == NULL) { 1416184138Smav if (indirect) 1417184138Smav *indirect = true; 1418246886Sgonzo return (ENOMEM); 1419246886Sgonzo } 1420184138Smav 1421184138Smav /* Configure resource with its real bus values. */ 1422246886Sgonzo parent_offset = dwa->win->win_offset; 1423246886Sgonzo parent_offset += r_start - dwa->target; 1424246886Sgonzo 1425246886Sgonzo error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset, 1426246886Sgonzo dwa->win->win_size); 1427246886Sgonzo if (error) 1428246886Sgonzo goto failed; 1429246886Sgonzo 1430246886Sgonzo /* Mark active */ 1431246886Sgonzo if ((error = rman_activate_resource(r))) 1432184138Smav goto failed; 1433246886Sgonzo 1434246886Sgonzo return (0); 1435246886Sgonzo 1436246886Sgonzofailed: 1437246886Sgonzo /* Release our region allocation. */ 1438246886Sgonzo BHNDB_LOCK(sc); 1439246886Sgonzo bhndb_dw_release(sc->bus_res, dwa, r); 1440246886Sgonzo BHNDB_UNLOCK(sc); 1441246886Sgonzo 1442184138Smav return (error); 1443184138Smav} 1444184138Smav 1445184138Smav/** 1446184138Smav * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE(). 1447184138Smav * 1448184138Smav * Maps resource activation requests to a viable static or dynamic 1449184138Smav * register window, if any. 1450184138Smav */ 1451184138Smavstatic int 1452184138Smavbhndb_activate_resource(device_t dev, device_t child, int type, int rid, 1453184138Smav struct resource *r) 1454184138Smav{ 1455184138Smav struct bhndb_softc *sc = device_get_softc(dev); 1456184138Smav 1457184138Smav return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL)); 1458184138Smav} 1459184138Smav 1460184138Smav/** 1461184138Smav * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE(). 1462184138Smav */ 1463184138Smavstatic int 1464184138Smavbhndb_deactivate_resource(device_t dev, device_t child, int type, 1465184452Smav int rid, struct resource *r) 1466184138Smav{ 1467184138Smav struct bhndb_dw_alloc *dwa; 1468184138Smav struct bhndb_softc *sc; 1469184138Smav struct rman *rm; 1470184138Smav int error; 1471241600Sgonzo 1472 sc = device_get_softc(dev); 1473 1474 if ((rm = bhndb_get_rman(sc, child, type)) == NULL) 1475 return (EINVAL); 1476 1477 /* Mark inactive */ 1478 if ((error = rman_deactivate_resource(r))) 1479 return (error); 1480 1481 /* Free any dynamic window allocation. */ 1482 if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { 1483 BHNDB_LOCK(sc); 1484 dwa = bhndb_dw_find_resource(sc->bus_res, r); 1485 if (dwa != NULL) 1486 bhndb_dw_release(sc->bus_res, dwa, r); 1487 BHNDB_UNLOCK(sc); 1488 } 1489 1490 return (0); 1491} 1492 1493/** 1494 * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST(). 1495 */ 1496static struct resource_list * 1497bhndb_get_resource_list(device_t dev, device_t child) 1498{ 1499 struct bhndb_devinfo *dinfo = device_get_ivars(child); 1500 return (&dinfo->resources); 1501} 1502 1503/** 1504 * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE(). 1505 * 1506 * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to 1507 * be activated by the bridge. 1508 * 1509 * For BHNDB_ADDRSPACE_BRIDGED children, attempts to activate a static register 1510 * window, a dynamic register window, or configures @p r as an indirect 1511 * resource -- in that order. 1512 */ 1513static int 1514bhndb_activate_bhnd_resource(device_t dev, device_t child, 1515 int type, int rid, struct bhnd_resource *r) 1516{ 1517 struct bhndb_softc *sc; 1518 struct bhndb_region *region; 1519 rman_res_t r_start, r_size; 1520 int error; 1521 bool indirect; 1522 1523 KASSERT(!r->direct, 1524 ("direct flag set on inactive resource")); 1525 1526 KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE), 1527 ("RF_ACTIVE set on inactive resource")); 1528 1529 sc = device_get_softc(dev); 1530 1531 r_start = rman_get_start(r->res); 1532 r_size = rman_get_size(r->res); 1533 1534 /* Verify bridged address range's resource priority, and skip direct 1535 * allocation if the priority is too low. */ 1536 if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { 1537 bhndb_priority_t r_prio; 1538 1539 region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); 1540 if (region != NULL) 1541 r_prio = region->priority; 1542 else 1543 r_prio = BHNDB_PRIORITY_NONE; 1544 1545 /* If less than the minimum dynamic window priority, this 1546 * resource should always be indirect. */ 1547 if (r_prio < sc->bus_res->min_prio) 1548 return (0); 1549 } 1550 1551 /* Attempt direct activation */ 1552 error = bhndb_try_activate_resource(sc, child, type, rid, r->res, 1553 &indirect); 1554 if (!error) { 1555 r->direct = true; 1556 } else if (indirect) { 1557 /* The request was valid, but no viable register window is 1558 * available; indirection must be employed. */ 1559 error = 0; 1560 r->direct = false; 1561 } 1562 1563 if (BHNDB_DEBUG(PRIO) && 1564 bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) 1565 { 1566 device_printf(child, "activated 0x%llx-0x%llx as %s " 1567 "resource\n", 1568 (unsigned long long) r_start, 1569 (unsigned long long) r_start + r_size - 1, 1570 r->direct ? "direct" : "indirect"); 1571 } 1572 1573 return (error); 1574}; 1575 1576/** 1577 * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE(). 1578 */ 1579static int 1580bhndb_deactivate_bhnd_resource(device_t dev, device_t child, 1581 int type, int rid, struct bhnd_resource *r) 1582{ 1583 int error; 1584 1585 /* Indirect resources don't require activation */ 1586 if (!r->direct) 1587 return (0); 1588 1589 KASSERT(rman_get_flags(r->res) & RF_ACTIVE, 1590 ("RF_ACTIVE not set on direct resource")); 1591 1592 /* Perform deactivation */ 1593 error = bus_deactivate_resource(child, type, rid, r->res); 1594 if (!error) 1595 r->direct = false; 1596 1597 return (error); 1598}; 1599 1600/** 1601 * Slow path for bhndb_io_resource(). 1602 * 1603 * Iterates over the existing allocated dynamic windows looking for a viable 1604 * in-use region; the first matching region is returned. 1605 */ 1606static struct bhndb_dw_alloc * 1607bhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, 1608 bus_size_t size, bus_size_t *offset) 1609{ 1610 struct bhndb_resources *br; 1611 struct bhndb_dw_alloc *dwa; 1612 1613 BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1614 1615 br = sc->bus_res; 1616 1617 /* Search for an existing dynamic mapping of this address range. 1618 * Static regions are not searched, as a statically mapped 1619 * region would never be allocated as an indirect resource. */ 1620 for (size_t i = 0; i < br->dwa_count; i++) { 1621 const struct bhndb_regwin *win; 1622 1623 dwa = &br->dw_alloc[i]; 1624 win = dwa->win; 1625 1626 KASSERT(win->win_type == BHNDB_REGWIN_T_DYN, 1627 ("invalid register window type")); 1628 1629 /* Verify the range */ 1630 if (addr < dwa->target) 1631 continue; 1632 1633 if (addr + size > dwa->target + win->win_size) 1634 continue; 1635 1636 /* Found */ 1637 *offset = dwa->win->win_offset; 1638 *offset += addr - dwa->target; 1639 1640 return (dwa); 1641 } 1642 1643 /* not found */ 1644 return (NULL); 1645} 1646 1647/** 1648 * Find the bridge resource to be used for I/O requests. 1649 * 1650 * @param sc Bridge driver state. 1651 * @param addr The I/O target address. 1652 * @param size The size of the I/O operation to be performed at @p addr. 1653 * @param[out] offset The offset within the returned resource at which 1654 * to perform the I/O request. 1655 */ 1656static inline struct bhndb_dw_alloc * 1657bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, 1658 bus_size_t *offset) 1659{ 1660 struct bhndb_resources *br; 1661 struct bhndb_dw_alloc *dwa; 1662 int error; 1663 1664 BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1665 1666 br = sc->bus_res; 1667 1668 /* Try to fetch a free window */ 1669 dwa = bhndb_dw_next_free(br); 1670 1671 /* 1672 * If no dynamic windows are available, look for an existing 1673 * region that maps the target range. 1674 * 1675 * If none are found, this is a child driver bug -- our window 1676 * over-commit should only fail in the case where a child driver leaks 1677 * resources, or perform operations out-of-order. 1678 * 1679 * Broadcom HND chipsets are designed to not require register window 1680 * swapping during execution; as long as the child devices are 1681 * attached/detached correctly, using the hardware's required order 1682 * of operations, there should always be a window available for the 1683 * current operation. 1684 */ 1685 if (dwa == NULL) { 1686 dwa = bhndb_io_resource_slow(sc, addr, size, offset); 1687 if (dwa == NULL) { 1688 panic("register windows exhausted attempting to map " 1689 "0x%llx-0x%llx\n", 1690 (unsigned long long) addr, 1691 (unsigned long long) addr+size-1); 1692 } 1693 1694 return (dwa); 1695 } 1696 1697 /* Adjust the window if the I/O request won't fit in the current 1698 * target range. */ 1699 if (addr < dwa->target || 1700 (dwa->target + dwa->win->win_size) - addr < size) 1701 { 1702 error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr, 1703 size); 1704 if (error) { 1705 panic("failed to set register window target mapping " 1706 "0x%llx-0x%llx\n", 1707 (unsigned long long) addr, 1708 (unsigned long long) addr+size-1); 1709 } 1710 } 1711 1712 /* Calculate the offset and return */ 1713 *offset = (addr - dwa->target) + dwa->win->win_offset; 1714 return (dwa); 1715} 1716 1717/* 1718 * BHND_BUS_(READ|WRITE_* implementations 1719 */ 1720 1721/* bhndb_bus_(read|write) common implementation */ 1722#define BHNDB_IO_COMMON_SETUP(_io_size) \ 1723 struct bhndb_softc *sc; \ 1724 struct bhndb_dw_alloc *dwa; \ 1725 struct resource *io_res; \ 1726 bus_size_t io_offset; \ 1727 \ 1728 sc = device_get_softc(dev); \ 1729 \ 1730 BHNDB_LOCK(sc); \ 1731 dwa = bhndb_io_resource(sc, rman_get_start(r->res) + \ 1732 offset, _io_size, &io_offset); \ 1733 io_res = dwa->parent_res; \ 1734 \ 1735 KASSERT(!r->direct, \ 1736 ("bhnd_bus slow path used for direct resource")); \ 1737 \ 1738 KASSERT(rman_get_flags(io_res) & RF_ACTIVE, \ 1739 ("i/o resource is not active")); 1740 1741#define BHNDB_IO_COMMON_TEARDOWN() \ 1742 BHNDB_UNLOCK(sc); 1743 1744/* Defines a bhndb_bus_read_* method implementation */ 1745#define BHNDB_IO_READ(_type, _name) \ 1746static _type \ 1747bhndb_bus_read_ ## _name (device_t dev, device_t child, \ 1748 struct bhnd_resource *r, bus_size_t offset) \ 1749{ \ 1750 _type v; \ 1751 BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ 1752 v = bus_read_ ## _name (io_res, io_offset); \ 1753 BHNDB_IO_COMMON_TEARDOWN(); \ 1754 \ 1755 return (v); \ 1756} 1757 1758/* Defines a bhndb_bus_write_* method implementation */ 1759#define BHNDB_IO_WRITE(_type, _name) \ 1760static void \ 1761bhndb_bus_write_ ## _name (device_t dev, device_t child, \ 1762 struct bhnd_resource *r, bus_size_t offset, _type value) \ 1763{ \ 1764 BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ 1765 bus_write_ ## _name (io_res, io_offset, value); \ 1766 BHNDB_IO_COMMON_TEARDOWN(); \ 1767} 1768 1769/* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */ 1770#define BHNDB_IO_MISC(_type, _ptr, _op, _size) \ 1771static void \ 1772bhndb_bus_ ## _op ## _ ## _size (device_t dev, \ 1773 device_t child, struct bhnd_resource *r, bus_size_t offset, \ 1774 _type _ptr datap, bus_size_t count) \ 1775{ \ 1776 BHNDB_IO_COMMON_SETUP(sizeof(_type) * count); \ 1777 bus_ ## _op ## _ ## _size (io_res, io_offset, \ 1778 datap, count); \ 1779 BHNDB_IO_COMMON_TEARDOWN(); \ 1780} 1781 1782/* Defines a complete set of read/write methods */ 1783#define BHNDB_IO_METHODS(_type, _size) \ 1784 BHNDB_IO_READ(_type, _size) \ 1785 BHNDB_IO_WRITE(_type, _size) \ 1786 \ 1787 BHNDB_IO_READ(_type, stream_ ## _size) \ 1788 BHNDB_IO_WRITE(_type, stream_ ## _size) \ 1789 \ 1790 BHNDB_IO_MISC(_type, *, read_multi, _size) \ 1791 BHNDB_IO_MISC(_type, *, write_multi, _size) \ 1792 \ 1793 BHNDB_IO_MISC(_type, *, read_multi_stream, _size) \ 1794 BHNDB_IO_MISC(_type, *, write_multi_stream, _size) \ 1795 \ 1796 BHNDB_IO_MISC(_type, , set_multi, _size) \ 1797 BHNDB_IO_MISC(_type, , set_region, _size) \ 1798 BHNDB_IO_MISC(_type, *, read_region, _size) \ 1799 BHNDB_IO_MISC(_type, *, write_region, _size) \ 1800 \ 1801 BHNDB_IO_MISC(_type, *, read_region_stream, _size) \ 1802 BHNDB_IO_MISC(_type, *, write_region_stream, _size) 1803 1804BHNDB_IO_METHODS(uint8_t, 1); 1805BHNDB_IO_METHODS(uint16_t, 2); 1806BHNDB_IO_METHODS(uint32_t, 4); 1807 1808/** 1809 * Default bhndb(4) implementation of BHND_BUS_BARRIER(). 1810 */ 1811static void 1812bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r, 1813 bus_size_t offset, bus_size_t length, int flags) 1814{ 1815 BHNDB_IO_COMMON_SETUP(length); 1816 1817 bus_barrier(io_res, io_offset + offset, length, flags); 1818 1819 BHNDB_IO_COMMON_TEARDOWN(); 1820} 1821 1822/** 1823 * Default bhndb(4) implementation of BUS_SETUP_INTR(). 1824 */ 1825static int 1826bhndb_setup_intr(device_t dev, device_t child, struct resource *r, 1827 int flags, driver_filter_t filter, driver_intr_t handler, void *arg, 1828 void **cookiep) 1829{ 1830 // TODO 1831 return (EOPNOTSUPP); 1832} 1833 1834/** 1835 * Default bhndb(4) implementation of BUS_TEARDOWN_INTR(). 1836 */ 1837static int 1838bhndb_teardown_intr(device_t dev, device_t child, struct resource *r, 1839 void *cookie) 1840{ 1841 // TODO 1842 return (EOPNOTSUPP); 1843} 1844 1845/** 1846 * Default bhndb(4) implementation of BUS_CONFIG_INTR(). 1847 */ 1848static int 1849bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig, 1850 enum intr_polarity pol) 1851{ 1852 // TODO 1853 return (EOPNOTSUPP); 1854} 1855 1856/** 1857 * Default bhndb(4) implementation of BUS_BIND_INTR(). 1858 */ 1859static int 1860bhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu) 1861{ 1862 // TODO 1863 return (EOPNOTSUPP); 1864} 1865 1866/** 1867 * Default bhndb(4) implementation of BUS_DESCRIBE_INTR(). 1868 */ 1869static int 1870bhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, 1871 const char *descr) 1872{ 1873 // TODO 1874 return (EOPNOTSUPP); 1875} 1876 1877/** 1878 * Default bhndb(4) implementation of BUS_GET_DMA_TAG(). 1879 */ 1880static bus_dma_tag_t 1881bhndb_get_dma_tag(device_t dev, device_t child) 1882{ 1883 // TODO 1884 return (NULL); 1885} 1886 1887static device_method_t bhndb_methods[] = { 1888 /* Device interface */ \ 1889 DEVMETHOD(device_probe, bhndb_generic_probe), 1890 DEVMETHOD(device_detach, bhndb_generic_detach), 1891 DEVMETHOD(device_shutdown, bus_generic_shutdown), 1892 DEVMETHOD(device_suspend, bhndb_generic_suspend), 1893 DEVMETHOD(device_resume, bhndb_generic_resume), 1894 1895 /* Bus interface */ 1896 DEVMETHOD(bus_probe_nomatch, bhndb_probe_nomatch), 1897 DEVMETHOD(bus_print_child, bhndb_print_child), 1898 DEVMETHOD(bus_child_pnpinfo_str, bhndb_child_pnpinfo_str), 1899 DEVMETHOD(bus_child_location_str, bhndb_child_location_str), 1900 DEVMETHOD(bus_add_child, bhndb_add_child), 1901 DEVMETHOD(bus_child_deleted, bhndb_child_deleted), 1902 1903 DEVMETHOD(bus_alloc_resource, bhndb_alloc_resource), 1904 DEVMETHOD(bus_release_resource, bhndb_release_resource), 1905 DEVMETHOD(bus_activate_resource, bhndb_activate_resource), 1906 DEVMETHOD(bus_deactivate_resource, bhndb_deactivate_resource), 1907 1908 DEVMETHOD(bus_setup_intr, bhndb_setup_intr), 1909 DEVMETHOD(bus_teardown_intr, bhndb_teardown_intr), 1910 DEVMETHOD(bus_config_intr, bhndb_config_intr), 1911 DEVMETHOD(bus_bind_intr, bhndb_bind_intr), 1912 DEVMETHOD(bus_describe_intr, bhndb_describe_intr), 1913 1914 DEVMETHOD(bus_get_dma_tag, bhndb_get_dma_tag), 1915 1916 DEVMETHOD(bus_adjust_resource, bhndb_adjust_resource), 1917 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 1918 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 1919 DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 1920 DEVMETHOD(bus_get_resource_list, bhndb_get_resource_list), 1921 1922 DEVMETHOD(bus_read_ivar, bhndb_read_ivar), 1923 DEVMETHOD(bus_write_ivar, bhndb_write_ivar), 1924 1925 /* BHNDB interface */ 1926 DEVMETHOD(bhndb_get_chipid, bhndb_get_chipid), 1927 DEVMETHOD(bhndb_init_full_config, bhndb_generic_init_full_config), 1928 DEVMETHOD(bhndb_find_hostb_device, bhndb_find_hostb_device), 1929 DEVMETHOD(bhndb_suspend_resource, bhndb_suspend_resource), 1930 DEVMETHOD(bhndb_resume_resource, bhndb_resume_resource), 1931 1932 /* BHND interface */ 1933 DEVMETHOD(bhnd_bus_is_hw_disabled, bhndb_is_hw_disabled), 1934 DEVMETHOD(bhnd_bus_get_chipid, bhndb_get_chipid), 1935 DEVMETHOD(bhnd_bus_activate_resource, bhndb_activate_bhnd_resource), 1936 DEVMETHOD(bhnd_bus_deactivate_resource, bhndb_deactivate_bhnd_resource), 1937 DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var), 1938 DEVMETHOD(bhnd_bus_read_1, bhndb_bus_read_1), 1939 DEVMETHOD(bhnd_bus_read_2, bhndb_bus_read_2), 1940 DEVMETHOD(bhnd_bus_read_4, bhndb_bus_read_4), 1941 DEVMETHOD(bhnd_bus_write_1, bhndb_bus_write_1), 1942 DEVMETHOD(bhnd_bus_write_2, bhndb_bus_write_2), 1943 DEVMETHOD(bhnd_bus_write_4, bhndb_bus_write_4), 1944 1945 DEVMETHOD(bhnd_bus_read_stream_1, bhndb_bus_read_stream_1), 1946 DEVMETHOD(bhnd_bus_read_stream_2, bhndb_bus_read_stream_2), 1947 DEVMETHOD(bhnd_bus_read_stream_4, bhndb_bus_read_stream_4), 1948 DEVMETHOD(bhnd_bus_write_stream_1, bhndb_bus_write_stream_1), 1949 DEVMETHOD(bhnd_bus_write_stream_2, bhndb_bus_write_stream_2), 1950 DEVMETHOD(bhnd_bus_write_stream_4, bhndb_bus_write_stream_4), 1951 1952 DEVMETHOD(bhnd_bus_read_multi_1, bhndb_bus_read_multi_1), 1953 DEVMETHOD(bhnd_bus_read_multi_2, bhndb_bus_read_multi_2), 1954 DEVMETHOD(bhnd_bus_read_multi_4, bhndb_bus_read_multi_4), 1955 DEVMETHOD(bhnd_bus_write_multi_1, bhndb_bus_write_multi_1), 1956 DEVMETHOD(bhnd_bus_write_multi_2, bhndb_bus_write_multi_2), 1957 DEVMETHOD(bhnd_bus_write_multi_4, bhndb_bus_write_multi_4), 1958 1959 DEVMETHOD(bhnd_bus_read_multi_stream_1, bhndb_bus_read_multi_stream_1), 1960 DEVMETHOD(bhnd_bus_read_multi_stream_2, bhndb_bus_read_multi_stream_2), 1961 DEVMETHOD(bhnd_bus_read_multi_stream_4, bhndb_bus_read_multi_stream_4), 1962 DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1), 1963 DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2), 1964 DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4), 1965 1966 DEVMETHOD(bhnd_bus_set_multi_1, bhndb_bus_set_multi_1), 1967 DEVMETHOD(bhnd_bus_set_multi_2, bhndb_bus_set_multi_2), 1968 DEVMETHOD(bhnd_bus_set_multi_4, bhndb_bus_set_multi_4), 1969 DEVMETHOD(bhnd_bus_set_region_1, bhndb_bus_set_region_1), 1970 DEVMETHOD(bhnd_bus_set_region_2, bhndb_bus_set_region_2), 1971 DEVMETHOD(bhnd_bus_set_region_4, bhndb_bus_set_region_4), 1972 1973 DEVMETHOD(bhnd_bus_read_region_1, bhndb_bus_read_region_1), 1974 DEVMETHOD(bhnd_bus_read_region_2, bhndb_bus_read_region_2), 1975 DEVMETHOD(bhnd_bus_read_region_4, bhndb_bus_read_region_4), 1976 DEVMETHOD(bhnd_bus_write_region_1, bhndb_bus_write_region_1), 1977 DEVMETHOD(bhnd_bus_write_region_2, bhndb_bus_write_region_2), 1978 DEVMETHOD(bhnd_bus_write_region_4, bhndb_bus_write_region_4), 1979 1980 DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1), 1981 DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2), 1982 DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4), 1983 DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1), 1984 DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2), 1985 DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4), 1986 1987 DEVMETHOD(bhnd_bus_barrier, bhndb_bus_barrier), 1988 1989 DEVMETHOD_END 1990}; 1991 1992devclass_t bhndb_devclass; 1993 1994DEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc)); 1995 1996MODULE_VERSION(bhndb, 1); 1997MODULE_DEPEND(bhndb, bhnd, 1, 1, 1); 1998MODULE_DEPEND(bhndb, bhnd_chipc, 1, 1, 1); 1999