bhndb.c revision 302408
1170530Ssam/*- 2178354Ssam * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 3170530Ssam * All rights reserved. 4170530Ssam * 5170530Ssam * Redistribution and use in source and binary forms, with or without 6170530Ssam * modification, are permitted provided that the following conditions 7170530Ssam * are met: 8170530Ssam * 1. Redistributions of source code must retain the above copyright 9170530Ssam * notice, this list of conditions and the following disclaimer, 10170530Ssam * without modification. 11170530Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12170530Ssam * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13170530Ssam * redistribution must be conditioned upon including a substantially 14170530Ssam * similar Disclaimer requirement for further binary redistribution. 15170530Ssam * 16170530Ssam * NO WARRANTY 17170530Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18170530Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19170530Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20170530Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21170530Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22170530Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23170530Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24170530Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25170530Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26170530Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27170530Ssam * THE POSSIBILITY OF SUCH DAMAGES. 28170530Ssam */ 29170530Ssam 30170530Ssam#include <sys/cdefs.h> 31170530Ssam__FBSDID("$FreeBSD: stable/11/sys/dev/bhnd/bhndb/bhndb.c 302106 2016-06-23 01:15:35Z adrian $"); 32170530Ssam 33170530Ssam/* 34170530Ssam * Abstract BHND Bridge Device Driver 35170530Ssam * 36178354Ssam * Provides generic support for bridging from a parent bus (such as PCI) to 37170530Ssam * a BHND-compatible bus (e.g. bcma or siba). 38170530Ssam */ 39170530Ssam 40170530Ssam#include <sys/param.h> 41170530Ssam#include <sys/kernel.h> 42170530Ssam#include <sys/bus.h> 43170530Ssam#include <sys/module.h> 44170530Ssam#include <sys/systm.h> 45170530Ssam 46170530Ssam#include <machine/bus.h> 47170530Ssam#include <sys/rman.h> 48170530Ssam#include <machine/resource.h> 49170530Ssam 50178354Ssam#include <dev/bhnd/bhndvar.h> 51170530Ssam#include <dev/bhnd/bhndreg.h> 52170530Ssam 53170530Ssam#include <dev/bhnd/cores/chipc/chipcreg.h> 54170530Ssam#include <dev/bhnd/nvram/bhnd_nvram.h> 55170530Ssam 56178354Ssam#include "bhnd_chipc_if.h" 57178354Ssam#include "bhnd_nvram_if.h" 58178354Ssam 59178354Ssam#include "bhndbvar.h" 60178354Ssam#include "bhndb_bus_if.h" 61178354Ssam#include "bhndb_hwdata.h" 62178354Ssam#include "bhndb_private.h" 63178354Ssam 64178354Ssam/* Debugging flags */ 65178354Ssamstatic u_long bhndb_debug = 0; 66178354SsamTUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug); 67178354Ssam 68178354Ssamenum { 69178354Ssam BHNDB_DEBUG_PRIO = 1 << 0, 70178354Ssam}; 71178354Ssam 72178354Ssam#define BHNDB_DEBUG(_type) (BHNDB_DEBUG_ ## _type & bhndb_debug) 73170530Ssam 74170530Ssamstatic bool bhndb_hw_matches(device_t *devlist, 75170530Ssam int num_devs, 76170530Ssam const struct bhndb_hw *hw); 77170530Ssam 78170530Ssamstatic int bhndb_initialize_region_cfg( 79170530Ssam struct bhndb_softc *sc, device_t *devs, 80170530Ssam int ndevs, 81173273Ssam const struct bhndb_hw_priority *table, 82173273Ssam struct bhndb_resources *r); 83173273Ssam 84173273Ssamstatic int bhndb_find_hwspec(struct bhndb_softc *sc, 85173273Ssam device_t *devs, int ndevs, 86178354Ssam const struct bhndb_hw **hw); 87178354Ssam 88178354Ssamstatic int bhndb_read_chipid(struct bhndb_softc *sc, 89173273Ssam const struct bhndb_hwcfg *cfg, 90178354Ssam struct bhnd_chipid *result); 91178354Ssam 92178354Ssambhndb_addrspace bhndb_get_addrspace(struct bhndb_softc *sc, 93178354Ssam device_t child); 94178354Ssam 95178354Ssamstatic struct rman *bhndb_get_rman(struct bhndb_softc *sc, 96178354Ssam device_t child, int type); 97178354Ssam 98178354Ssamstatic int bhndb_init_child_resource(struct resource *r, 99178354Ssam struct resource *parent, 100178354Ssam bhnd_size_t offset, 101178354Ssam bhnd_size_t size); 102178354Ssam 103170530Ssamstatic int bhndb_activate_static_region( 104178354Ssam struct bhndb_softc *sc, 105178354Ssam struct bhndb_region *region, 106170530Ssam device_t child, int type, int rid, 107170530Ssam struct resource *r); 108170530Ssam 109170530Ssamstatic int bhndb_try_activate_resource( 110170530Ssam struct bhndb_softc *sc, device_t child, 111170530Ssam int type, int rid, struct resource *r, 112170530Ssam bool *indirect); 113170530Ssam 114170530Ssam 115170530Ssam/** 116170530Ssam * Default bhndb(4) implementation of DEVICE_PROBE(). 117170530Ssam * 118170530Ssam * This function provides the default bhndb implementation of DEVICE_PROBE(), 119170530Ssam * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge(). 120170530Ssam */ 121170530Ssamint 122170530Ssambhndb_generic_probe(device_t dev) 123178354Ssam{ 124170530Ssam return (BUS_PROBE_NOWILDCARD); 125170530Ssam} 126170530Ssam 127170530Ssamstatic void 128173273Ssambhndb_probe_nomatch(device_t dev, device_t child) 129173273Ssam{ 130178354Ssam const char *name; 131173273Ssam 132178354Ssam name = device_get_name(child); 133178354Ssam if (name == NULL) 134178354Ssam name = "unknown device"; 135178354Ssam 136173273Ssam device_printf(dev, "<%s> (no driver attached)\n", name); 137178354Ssam} 138178354Ssam 139178354Ssamstatic int 140178354Ssambhndb_print_child(device_t dev, device_t child) 141178354Ssam{ 142178354Ssam struct bhndb_softc *sc; 143178354Ssam struct resource_list *rl; 144178354Ssam int retval = 0; 145178354Ssam 146178354Ssam sc = device_get_softc(dev); 147178354Ssam 148178354Ssam retval += bus_print_child_header(dev, child); 149178354Ssam 150178354Ssam rl = BUS_GET_RESOURCE_LIST(dev, child); 151178354Ssam if (rl != NULL) { 152178354Ssam retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, 153170530Ssam "%#jx"); 154173273Ssam retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, 155173273Ssam "%jd"); 156170530Ssam } 157170530Ssam 158178354Ssam retval += bus_print_child_domain(dev, child); 159178354Ssam retval += bus_print_child_footer(dev, child); 160178354Ssam 161178354Ssam return (retval); 162178354Ssam} 163173273Ssam 164178354Ssamstatic int 165178354Ssambhndb_child_pnpinfo_str(device_t bus, device_t child, char *buf, 166178354Ssam size_t buflen) 167178354Ssam{ 168170530Ssam *buf = '\0'; 169170530Ssam return (0); 170178354Ssam} 171178354Ssam 172178354Ssamstatic int 173178354Ssambhndb_child_location_str(device_t dev, device_t child, char *buf, 174178354Ssam size_t buflen) 175178354Ssam{ 176170530Ssam struct bhndb_softc *sc; 177178354Ssam 178178354Ssam sc = device_get_softc(dev); 179178354Ssam 180170530Ssam snprintf(buf, buflen, "base=0x%llx", 181170530Ssam (unsigned long long) sc->chipid.enum_addr); 182170530Ssam return (0); 183178354Ssam} 184170530Ssam 185170530Ssam/** 186170530Ssam * Return true if @p devlist matches the @p hw specification. 187170530Ssam * 188170530Ssam * @param devlist A device table to match against. 189170530Ssam * @param num_devs The number of devices in @p devlist. 190170530Ssam * @param hw The hardware description to be matched against. 191170530Ssam */ 192170530Ssamstatic bool 193170530Ssambhndb_hw_matches(device_t *devlist, int num_devs, const struct bhndb_hw *hw) 194170530Ssam{ 195170530Ssam for (u_int i = 0; i < hw->num_hw_reqs; i++) { 196172226Ssam const struct bhnd_core_match *match; 197172226Ssam struct bhnd_core_info ci; 198170530Ssam bool found; 199170530Ssam 200178354Ssam match = &hw->hw_reqs[i]; 201170530Ssam found = false; 202170530Ssam 203170530Ssam for (int d = 0; d < num_devs; d++) { 204170530Ssam ci = bhnd_get_core_info(devlist[d]); 205170530Ssam if (!bhnd_core_matches(&ci, match)) 206170530Ssam continue; 207170530Ssam 208170530Ssam found = true; 209170530Ssam break; 210170530Ssam } 211170530Ssam 212170530Ssam if (!found) 213170530Ssam return (false); 214170530Ssam } 215170530Ssam 216170530Ssam return (true); 217170530Ssam} 218170530Ssam 219170530Ssam/** 220173273Ssam * Initialize the region maps and priority configuration in @p r using 221170530Ssam * the provided priority @p table and the set of devices attached to 222170530Ssam * the bridged @p bus_dev . 223170530Ssam * 224170530Ssam * @param sc The bhndb device state. 225170530Ssam * @param devs All devices enumerated on the bridged bhnd bus. 226170530Ssam * @param ndevs The length of @p devs. 227170530Ssam * @param table Hardware priority table to be used to determine the relative 228170530Ssam * priorities of per-core port resources. 229170530Ssam * @param r The resource state to be configured. 230170530Ssam */ 231170530Ssamstatic int 232170530Ssambhndb_initialize_region_cfg(struct bhndb_softc *sc, device_t *devs, int ndevs, 233170530Ssam const struct bhndb_hw_priority *table, struct bhndb_resources *r) 234170530Ssam{ 235178354Ssam const struct bhndb_hw_priority *hp; 236173462Ssam bhnd_addr_t addr; 237170530Ssam bhnd_size_t size; 238170530Ssam size_t prio_low, prio_default, prio_high; 239170530Ssam int error; 240170530Ssam 241170530Ssam /* The number of port regions per priority band that must be accessible 242178354Ssam * via dynamic register windows */ 243170530Ssam prio_low = 0; 244170530Ssam prio_default = 0; 245170530Ssam prio_high = 0; 246170530Ssam 247170530Ssam /* 248170530Ssam * Register bridge regions covering all statically mapped ports. 249170530Ssam */ 250170530Ssam for (int i = 0; i < ndevs; i++) { 251170530Ssam const struct bhndb_regwin *regw; 252170530Ssam device_t child; 253178354Ssam 254173462Ssam child = devs[i]; 255178354Ssam 256170530Ssam for (regw = r->cfg->register_windows; 257170530Ssam regw->win_type != BHNDB_REGWIN_T_INVALID; regw++) 258173462Ssam { 259170530Ssam /* Only core windows are supported */ 260170530Ssam if (regw->win_type != BHNDB_REGWIN_T_CORE) 261170530Ssam continue; 262178354Ssam 263170530Ssam /* Skip non-applicable register windows. */ 264170530Ssam if (!bhndb_regwin_matches_device(regw, child)) 265178354Ssam continue; 266170530Ssam 267170530Ssam /* Fetch the base address of the mapped port. */ 268170530Ssam error = bhnd_get_region_addr(child, 269178354Ssam regw->d.core.port_type, regw->d.core.port, 270170530Ssam regw->d.core.region, &addr, &size); 271170530Ssam if (error) 272170530Ssam return (error); 273170530Ssam 274170530Ssam /* 275170530Ssam * Always defer to the register window's size. 276170530Ssam * 277170530Ssam * If the port size is smaller than the window size, 278170530Ssam * this ensures that we fully utilize register windows 279170530Ssam * larger than the referenced port. 280170530Ssam * 281170530Ssam * If the port size is larger than the window size, this 282170530Ssam * ensures that we do not directly map the allocations 283170530Ssam * within the region to a too-small window. 284170530Ssam */ 285170530Ssam size = regw->win_size; 286170530Ssam 287170530Ssam /* 288170530Ssam * Add to the bus region list. 289170530Ssam * 290170530Ssam * The window priority for a statically mapped 291170530Ssam * region is always HIGH. 292170530Ssam */ 293170530Ssam error = bhndb_add_resource_region(r, addr, size, 294170530Ssam BHNDB_PRIORITY_HIGH, regw); 295170530Ssam if (error) 296170530Ssam return (error); 297170530Ssam } 298170530Ssam } 299170530Ssam 300170530Ssam /* 301170530Ssam * Perform priority accounting and register bridge regions for all 302170530Ssam * ports defined in the priority table 303170530Ssam */ 304170530Ssam for (int i = 0; i < ndevs; i++) { 305170530Ssam struct bhndb_region *region; 306178354Ssam device_t child; 307178354Ssam 308178354Ssam child = devs[i]; 309178354Ssam 310178354Ssam /* 311178354Ssam * Skip priority accounting for cores that ... 312178354Ssam */ 313178354Ssam 314178354Ssam /* ... do not require bridge resources */ 315178354Ssam if (bhnd_is_hw_disabled(child) || !device_is_enabled(child)) 316178354Ssam continue; 317178354Ssam 318178354Ssam /* ... do not have a priority table entry */ 319178354Ssam hp = bhndb_hw_priority_find_device(table, child); 320178354Ssam if (hp == NULL) 321178354Ssam continue; 322178354Ssam 323178354Ssam /* ... are explicitly disabled in the priority table. */ 324178354Ssam if (hp->priority == BHNDB_PRIORITY_NONE) 325178354Ssam continue; 326170530Ssam 327170530Ssam /* Determine the number of dynamic windows required and 328170530Ssam * register their bus_region entries. */ 329170530Ssam for (u_int i = 0; i < hp->num_ports; i++) { 330170530Ssam const struct bhndb_port_priority *pp; 331170530Ssam 332178354Ssam pp = &hp->ports[i]; 333170530Ssam 334170530Ssam /* Skip ports not defined on this device */ 335170530Ssam if (!bhnd_is_region_valid(child, pp->type, pp->port, 336170530Ssam pp->region)) 337170530Ssam { 338170530Ssam continue; 339170530Ssam } 340170530Ssam 341170530Ssam /* Fetch the address+size of the mapped port. */ 342170530Ssam error = bhnd_get_region_addr(child, pp->type, pp->port, 343170530Ssam pp->region, &addr, &size); 344170530Ssam if (error) 345170530Ssam return (error); 346178354Ssam 347170530Ssam /* Skip ports with an existing static mapping */ 348170530Ssam region = bhndb_find_resource_region(r, addr, size); 349170530Ssam if (region != NULL && region->static_regwin != NULL) 350170530Ssam continue; 351170530Ssam 352170530Ssam /* Define a dynamic region for this port */ 353170530Ssam error = bhndb_add_resource_region(r, addr, size, 354170530Ssam pp->priority, NULL); 355170530Ssam if (error) 356170530Ssam return (error); 357170530Ssam 358170530Ssam /* Update port mapping counts */ 359170530Ssam switch (pp->priority) { 360178354Ssam case BHNDB_PRIORITY_NONE: 361170530Ssam break; 362170530Ssam case BHNDB_PRIORITY_LOW: 363170530Ssam prio_low++; 364170530Ssam break; 365170530Ssam case BHNDB_PRIORITY_DEFAULT: 366170530Ssam prio_default++; 367170530Ssam break; 368170530Ssam case BHNDB_PRIORITY_HIGH: 369170530Ssam prio_high++; 370170530Ssam break; 371170530Ssam } 372170530Ssam } 373170530Ssam } 374170530Ssam 375170530Ssam /* Determine the minimum priority at which we'll allocate direct 376170530Ssam * register windows from our dynamic pool */ 377170530Ssam size_t prio_total = prio_low + prio_default + prio_high; 378170530Ssam if (prio_total <= r->dwa_count) { 379170530Ssam /* low+default+high priority regions get windows */ 380170530Ssam r->min_prio = BHNDB_PRIORITY_LOW; 381170530Ssam 382170530Ssam } else if (prio_default + prio_high <= r->dwa_count) { 383170530Ssam /* default+high priority regions get windows */ 384170530Ssam r->min_prio = BHNDB_PRIORITY_DEFAULT; 385170530Ssam 386170530Ssam } else { 387170530Ssam /* high priority regions get windows */ 388170530Ssam r->min_prio = BHNDB_PRIORITY_HIGH; 389170530Ssam } 390178354Ssam 391170530Ssam if (BHNDB_DEBUG(PRIO)) { 392173273Ssam struct bhndb_region *region; 393173273Ssam const char *direct_msg, *type_msg; 394173273Ssam bhndb_priority_t prio, prio_min; 395173273Ssam 396173273Ssam prio_min = r->min_prio; 397178354Ssam device_printf(sc->dev, "min_prio: %d\n", prio_min); 398170530Ssam 399170530Ssam STAILQ_FOREACH(region, &r->bus_regions, link) { 400173273Ssam prio = region->priority; 401170530Ssam 402173273Ssam direct_msg = prio >= prio_min ? "direct" : "indirect"; 403170530Ssam type_msg = region->static_regwin ? "static" : "dynamic"; 404170530Ssam 405173273Ssam device_printf(sc->dev, "region 0x%llx+0x%llx priority " 406170530Ssam "%u %s/%s\n", 407178354Ssam (unsigned long long) region->addr, 408170530Ssam (unsigned long long) region->size, 409170530Ssam region->priority, 410170530Ssam direct_msg, type_msg); 411173273Ssam } 412170530Ssam } 413170530Ssam 414170530Ssam return (0); 415170530Ssam} 416170530Ssam 417173273Ssam/** 418178354Ssam * Find a hardware specification for @p dev. 419173273Ssam * 420170530Ssam * @param sc The bhndb device state. 421173273Ssam * @param devs All devices enumerated on the bridged bhnd bus. 422170530Ssam * @param ndevs The length of @p devs. 423170530Ssam * @param[out] hw On success, the matched hardware specification. 424170530Ssam * with @p dev. 425173273Ssam * 426170530Ssam * @retval 0 success 427170530Ssam * @retval non-zero if an error occurs fetching device info for comparison. 428173273Ssam */ 429173273Ssamstatic int 430173273Ssambhndb_find_hwspec(struct bhndb_softc *sc, device_t *devs, int ndevs, 431173273Ssam const struct bhndb_hw **hw) 432173273Ssam{ 433173273Ssam const struct bhndb_hw *next, *hw_table; 434173273Ssam 435173273Ssam /* Search for the first matching hardware config. */ 436178354Ssam hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev); 437173273Ssam for (next = hw_table; next->hw_reqs != NULL; next++) { 438173273Ssam if (!bhndb_hw_matches(devs, ndevs, next)) 439173273Ssam continue; 440173273Ssam 441173273Ssam /* Found */ 442173273Ssam *hw = next; 443173273Ssam return (0); 444173273Ssam } 445173273Ssam 446173273Ssam return (ENOENT); 447173273Ssam} 448173273Ssam 449173273Ssam/** 450173273Ssam * Read the ChipCommon identification data for this device. 451173273Ssam * 452173273Ssam * @param sc bhndb device state. 453173273Ssam * @param cfg The hardware configuration to use when mapping the ChipCommon 454173273Ssam * registers. 455178354Ssam * @param[out] result the chip identification data. 456173273Ssam * 457173273Ssam * @retval 0 success 458173273Ssam * @retval non-zero if the ChipCommon identification data could not be read. 459173273Ssam */ 460173273Ssamstatic int 461173273Ssambhndb_read_chipid(struct bhndb_softc *sc, const struct bhndb_hwcfg *cfg, 462173273Ssam struct bhnd_chipid *result) 463173273Ssam{ 464173273Ssam const struct bhnd_chipid *parent_cid; 465173273Ssam const struct bhndb_regwin *cc_win; 466173273Ssam struct resource_spec rs; 467173273Ssam int error; 468173273Ssam 469173273Ssam /* Let our parent device override the discovery process */ 470178354Ssam parent_cid = BHNDB_BUS_GET_CHIPID(sc->parent_dev, sc->dev); 471178354Ssam if (parent_cid != NULL) { 472178354Ssam *result = *parent_cid; 473178354Ssam return (0); 474173273Ssam } 475173273Ssam 476173273Ssam /* Find a register window we can use to map the first CHIPC_CHIPID_SIZE 477173273Ssam * of ChipCommon registers. */ 478173273Ssam cc_win = bhndb_regwin_find_best(cfg->register_windows, 479173273Ssam BHND_DEVCLASS_CC, 0, BHND_PORT_DEVICE, 0, 0, CHIPC_CHIPID_SIZE); 480173273Ssam if (cc_win == NULL) { 481173273Ssam device_printf(sc->dev, "no chipcommon register window\n"); 482173273Ssam return (0); 483173273Ssam } 484173273Ssam 485173273Ssam /* We can assume a device without a static ChipCommon window uses the 486173273Ssam * default ChipCommon address. */ 487178354Ssam if (cc_win->win_type == BHNDB_REGWIN_T_DYN) { 488173273Ssam error = BHNDB_SET_WINDOW_ADDR(sc->dev, cc_win, 489173273Ssam BHND_DEFAULT_CHIPC_ADDR); 490173273Ssam 491173273Ssam if (error) { 492173273Ssam device_printf(sc->dev, "failed to set chipcommon " 493173273Ssam "register window\n"); 494173273Ssam return (error); 495173273Ssam } 496173273Ssam } 497173273Ssam 498173273Ssam /* Let the default bhnd implemenation alloc/release the resource and 499170530Ssam * perform the read */ 500170530Ssam rs.type = cc_win->res.type; 501170530Ssam rs.rid = cc_win->res.rid; 502173273Ssam rs.flags = RF_ACTIVE; 503170530Ssam 504170530Ssam return (bhnd_read_chipid(sc->parent_dev, &rs, cc_win->win_offset, 505170530Ssam result)); 506170530Ssam} 507170530Ssam 508170530Ssam/** 509170530Ssam * Helper function that must be called by subclass bhndb(4) drivers 510170530Ssam * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4) 511173273Ssam * APIs on the bridge device. 512173273Ssam * 513178354Ssam * @param dev The bridge device to attach. 514170530Ssam * @param bridge_devclass The device class of the bridging core. This is used 515170530Ssam * to automatically detect the bridge core, and to disable additional bridge 516170530Ssam * cores (e.g. PCMCIA on a PCIe device). 517170530Ssam */ 518170530Ssamint 519170530Ssambhndb_attach(device_t dev, bhnd_devclass_t bridge_devclass) 520170530Ssam{ 521170530Ssam struct bhndb_devinfo *dinfo; 522170530Ssam struct bhndb_softc *sc; 523170530Ssam const struct bhndb_hwcfg *cfg; 524170530Ssam int error; 525170530Ssam 526173273Ssam sc = device_get_softc(dev); 527173273Ssam sc->dev = dev; 528173273Ssam sc->parent_dev = device_get_parent(dev); 529173273Ssam sc->bridge_class = bridge_devclass; 530173273Ssam 531170530Ssam BHNDB_LOCK_INIT(sc); 532170530Ssam 533170530Ssam /* Read our chip identification data */ 534170530Ssam cfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, sc->dev); 535170530Ssam if ((error = bhndb_read_chipid(sc, cfg, &sc->chipid))) 536173273Ssam return (error); 537170530Ssam 538182827Ssam /* Populate generic resource allocation state. */ 539182827Ssam sc->bus_res = bhndb_alloc_resources(dev, sc->parent_dev, cfg); 540182827Ssam if (sc->bus_res == NULL) { 541182827Ssam return (ENXIO); 542182827Ssam } 543182827Ssam 544182827Ssam /* Attach our bridged bus device */ 545182827Ssam sc->bus_dev = BUS_ADD_CHILD(dev, 0, "bhnd", -1); 546182827Ssam if (sc->bus_dev == NULL) { 547182827Ssam error = ENXIO; 548182827Ssam goto failed; 549182827Ssam } 550182827Ssam 551182827Ssam /* Configure address space */ 552182827Ssam dinfo = device_get_ivars(sc->bus_dev); 553173273Ssam dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED; 554173273Ssam 555170530Ssam /* Finish attach */ 556170530Ssam return (bus_generic_attach(dev)); 557170530Ssam 558170530Ssamfailed: 559170530Ssam BHNDB_LOCK_DESTROY(sc); 560170530Ssam 561170530Ssam if (sc->bus_res != NULL) 562170530Ssam bhndb_free_resources(sc->bus_res); 563170530Ssam 564170530Ssam return (error); 565170530Ssam} 566173273Ssam 567170530Ssam/** 568170530Ssam * Default bhndb(4) implementation of BHNDB_INIT_FULL_CONFIG(). 569170530Ssam * 570170530Ssam * This function provides the default bhndb implementation of 571170530Ssam * BHNDB_INIT_FULL_CONFIG(), and must be called by any subclass driver 572170530Ssam * overriding BHNDB_INIT_FULL_CONFIG(). 573173273Ssam * 574170530Ssam * As documented by BHNDB_INIT_FULL_CONFIG, this function performs final 575170530Ssam * bridge configuration based on the hardware information enumerated by the 576170530Ssam * child bus, and will reset all resource allocation state on the bridge. 577173273Ssam * 578170530Ssam * When calling this method: 579170530Ssam * - Any bus resources previously allocated by @p child must be deallocated. 580170530Ssam * - The @p child bus must have performed initial enumeration -- but not 581173273Ssam * probe or attachment -- of its children. 582170530Ssam */ 583173273Ssamint 584173273Ssambhndb_generic_init_full_config(device_t dev, device_t child, 585173273Ssam const struct bhndb_hw_priority *hw_prio_table) 586173273Ssam{ 587173273Ssam struct bhndb_softc *sc; 588173273Ssam const struct bhndb_hw *hw; 589173273Ssam struct bhndb_resources *r; 590173273Ssam device_t *devs; 591173273Ssam device_t hostb; 592173273Ssam int ndevs; 593173273Ssam int error; 594173273Ssam 595173273Ssam sc = device_get_softc(dev); 596173273Ssam hostb = NULL; 597170530Ssam 598173273Ssam /* Fetch the full set of bhnd-attached cores */ 599173273Ssam if ((error = device_get_children(sc->bus_dev, &devs, &ndevs))) { 600173273Ssam device_printf(sc->dev, "unable to get children\n"); 601173273Ssam return (error); 602173273Ssam } 603170530Ssam 604173273Ssam /* Find our host bridge device */ 605173273Ssam hostb = BHNDB_FIND_HOSTB_DEVICE(dev, child); 606173273Ssam if (hostb == NULL) { 607173273Ssam device_printf(sc->dev, "no host bridge core found\n"); 608173273Ssam error = ENODEV; 609173273Ssam goto cleanup; 610173273Ssam } 611173273Ssam 612178354Ssam /* Find our full register window configuration */ 613173273Ssam if ((error = bhndb_find_hwspec(sc, devs, ndevs, &hw))) { 614173273Ssam device_printf(sc->dev, "unable to identify device, " 615173273Ssam " using generic bridge resource definitions\n"); 616173273Ssam error = 0; 617173273Ssam goto cleanup; 618173273Ssam } 619173273Ssam 620173273Ssam if (bootverbose || BHNDB_DEBUG(PRIO)) 621173273Ssam device_printf(sc->dev, "%s resource configuration\n", hw->name); 622173273Ssam 623173273Ssam /* Release existing resource state */ 624173273Ssam BHNDB_LOCK(sc); 625173273Ssam bhndb_free_resources(sc->bus_res); 626173273Ssam sc->bus_res = NULL; 627173273Ssam BHNDB_UNLOCK(sc); 628173273Ssam 629173273Ssam /* Allocate new resource state */ 630173273Ssam r = bhndb_alloc_resources(dev, sc->parent_dev, hw->cfg); 631178354Ssam if (r == NULL) { 632173273Ssam error = ENXIO; 633178354Ssam goto cleanup; 634173273Ssam } 635173273Ssam 636173273Ssam /* Initialize our resource priority configuration */ 637173273Ssam error = bhndb_initialize_region_cfg(sc, devs, ndevs, hw_prio_table, r); 638173273Ssam if (error) { 639178354Ssam bhndb_free_resources(r); 640173273Ssam goto cleanup; 641173273Ssam } 642173273Ssam 643173273Ssam /* Update our bridge state */ 644173273Ssam BHNDB_LOCK(sc); 645173273Ssam sc->bus_res = r; 646173273Ssam sc->hostb_dev = hostb; 647173273Ssam BHNDB_UNLOCK(sc); 648173273Ssam 649173273Ssamcleanup: 650173273Ssam free(devs, M_TEMP); 651178354Ssam return (error); 652173273Ssam} 653170530Ssam 654173273Ssam/** 655170530Ssam * Default bhndb(4) implementation of DEVICE_DETACH(). 656178354Ssam * 657170530Ssam * This function detaches any child devices, and if successful, releases all 658173273Ssam * resources held by the bridge device. 659173273Ssam */ 660173273Ssamint 661173273Ssambhndb_generic_detach(device_t dev) 662173273Ssam{ 663173273Ssam struct bhndb_softc *sc; 664173273Ssam int error; 665173273Ssam 666173273Ssam sc = device_get_softc(dev); 667173273Ssam 668173273Ssam /* Detach children */ 669173273Ssam if ((error = bus_generic_detach(dev))) 670170530Ssam return (error); 671170530Ssam 672173273Ssam /* Clean up our driver state. */ 673173273Ssam bhndb_free_resources(sc->bus_res); 674170530Ssam 675178354Ssam BHNDB_LOCK_DESTROY(sc); 676173273Ssam 677178354Ssam return (0); 678173273Ssam} 679173273Ssam 680173273Ssam/** 681173273Ssam * Default bhndb(4) implementation of DEVICE_SUSPEND(). 682178354Ssam * 683173273Ssam * This function calls bus_generic_suspend() (or implements equivalent 684170530Ssam * behavior). 685173273Ssam */ 686170530Ssamint 687173273Ssambhndb_generic_suspend(device_t dev) 688173273Ssam{ 689170530Ssam return (bus_generic_suspend(dev)); 690170530Ssam} 691170530Ssam 692170530Ssam/** 693170530Ssam * Default bhndb(4) implementation of DEVICE_RESUME(). 694170530Ssam * 695173273Ssam * This function calls bus_generic_resume() (or implements equivalent 696170530Ssam * behavior). 697170530Ssam */ 698170530Ssamint 699170530Ssambhndb_generic_resume(device_t dev) 700178354Ssam{ 701170530Ssam struct bhndb_softc *sc; 702170530Ssam struct bhndb_resources *bus_res; 703170530Ssam struct bhndb_dw_alloc *dwa; 704170530Ssam int error; 705170530Ssam 706173273Ssam sc = device_get_softc(dev); 707173273Ssam bus_res = sc->bus_res; 708178354Ssam 709173273Ssam /* Guarantee that all in-use dynamic register windows are mapped to 710173273Ssam * their previously configured target address. */ 711178354Ssam BHNDB_LOCK(sc); 712173273Ssam for (size_t i = 0; i < bus_res->dwa_count; i++) { 713173273Ssam dwa = &bus_res->dw_alloc[i]; 714170530Ssam 715170530Ssam /* Skip regions that were not previously used */ 716170530Ssam if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0) 717170530Ssam continue; 718170530Ssam 719170530Ssam /* Otherwise, ensure the register window is correct before 720170530Ssam * any children attempt MMIO */ 721170530Ssam error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); 722178354Ssam if (error) 723170530Ssam break; 724170530Ssam } 725178354Ssam BHNDB_UNLOCK(sc); 726170530Ssam 727170530Ssam /* Error restoring hardware state; children cannot be safely resumed */ 728178354Ssam if (error) { 729170530Ssam device_printf(dev, "Unable to restore hardware configuration; " 730173273Ssam "cannot resume: %d\n", error); 731173273Ssam return (error); 732170530Ssam } 733170530Ssam 734173273Ssam return (bus_generic_resume(dev)); 735170530Ssam} 736173273Ssam 737173273Ssam/** 738170530Ssam * Default implementation of BHNDB_SUSPEND_RESOURCE. 739178354Ssam */ 740173273Ssamstatic void 741170530Ssambhndb_suspend_resource(device_t dev, device_t child, int type, 742173273Ssam struct resource *r) 743173273Ssam{ 744178354Ssam struct bhndb_softc *sc; 745173273Ssam struct bhndb_dw_alloc *dwa; 746173273Ssam 747173273Ssam sc = device_get_softc(dev); 748173273Ssam 749173273Ssam // TODO: IRQs? 750173273Ssam if (type != SYS_RES_MEMORY) 751173273Ssam return; 752173273Ssam 753173273Ssam BHNDB_LOCK(sc); 754170530Ssam dwa = bhndb_dw_find_resource(sc->bus_res, r); 755173273Ssam if (dwa == NULL) { 756170530Ssam BHNDB_UNLOCK(sc); 757173273Ssam return; 758173273Ssam } 759170530Ssam 760178354Ssam if (BHNDB_DEBUG(PRIO)) 761173273Ssam device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n", 762173273Ssam type, rman_get_start(r), rman_get_size(r)); 763173273Ssam 764173273Ssam /* Release the resource's window reference */ 765173273Ssam bhndb_dw_release(sc->bus_res, dwa, r); 766173273Ssam BHNDB_UNLOCK(sc); 767178354Ssam} 768173273Ssam 769170530Ssam/** 770170530Ssam * Default implementation of BHNDB_RESUME_RESOURCE. 771170530Ssam */ 772170530Ssamstatic int 773170530Ssambhndb_resume_resource(device_t dev, device_t child, int type, 774170530Ssam struct resource *r) 775170530Ssam{ 776170530Ssam struct bhndb_softc *sc; 777170530Ssam 778170530Ssam sc = device_get_softc(dev); 779170530Ssam 780170530Ssam // TODO: IRQs? 781170530Ssam if (type != SYS_RES_MEMORY) 782170530Ssam return (0); 783173273Ssam 784173273Ssam /* Inactive resources don't require reallocation of bridge resources */ 785173273Ssam if (!(rman_get_flags(r) & RF_ACTIVE)) 786173273Ssam return (0); 787173273Ssam 788173273Ssam if (BHNDB_DEBUG(PRIO)) 789173273Ssam device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n", 790173273Ssam type, rman_get_start(r), rman_get_size(r)); 791170530Ssam 792170530Ssam return (bhndb_try_activate_resource(sc, rman_get_device(r), type, 793170530Ssam rman_get_rid(r), r, NULL)); 794170530Ssam} 795173273Ssam 796170530Ssam 797173273Ssam/** 798170530Ssam * Default bhndb(4) implementation of BUS_READ_IVAR(). 799170530Ssam */ 800170530Ssamstatic int 801170530Ssambhndb_read_ivar(device_t dev, device_t child, int index, 802170530Ssam uintptr_t *result) 803170530Ssam{ 804170530Ssam return (ENOENT); 805170530Ssam} 806170530Ssam 807170530Ssam/** 808170530Ssam * Default bhndb(4) implementation of BUS_WRITE_IVAR(). 809170530Ssam */ 810170530Ssamstatic int 811170530Ssambhndb_write_ivar(device_t dev, device_t child, int index, 812170530Ssam uintptr_t value) 813170530Ssam{ 814170530Ssam return (ENOENT); 815173273Ssam} 816173273Ssam 817173273Ssam/** 818173273Ssam * Return the address space for the given @p child device. 819173273Ssam */ 820170530Ssambhndb_addrspace 821178354Ssambhndb_get_addrspace(struct bhndb_softc *sc, device_t child) 822178354Ssam{ 823173273Ssam struct bhndb_devinfo *dinfo; 824173273Ssam device_t imd_dev; 825173273Ssam 826173273Ssam /* Find the directly attached parent of the requesting device */ 827170530Ssam imd_dev = child; 828170530Ssam while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev) 829170530Ssam imd_dev = device_get_parent(imd_dev); 830170530Ssam 831170530Ssam if (imd_dev == NULL) 832173273Ssam panic("bhndb address space request for non-child device %s\n", 833173273Ssam device_get_nameunit(child)); 834170530Ssam 835170530Ssam dinfo = device_get_ivars(imd_dev); 836178354Ssam return (dinfo->addrspace); 837178354Ssam} 838178354Ssam 839178354Ssam/** 840178354Ssam * Return the rman instance for a given resource @p type, if any. 841178354Ssam * 842178354Ssam * @param sc The bhndb device state. 843178354Ssam * @param child The requesting child. 844178354Ssam * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...) 845178354Ssam */ 846178354Ssamstatic struct rman * 847178354Ssambhndb_get_rman(struct bhndb_softc *sc, device_t child, int type) 848178354Ssam{ 849178354Ssam switch (bhndb_get_addrspace(sc, child)) { 850178354Ssam case BHNDB_ADDRSPACE_NATIVE: 851178354Ssam switch (type) { 852178354Ssam case SYS_RES_MEMORY: 853178354Ssam return (&sc->bus_res->ht_mem_rman); 854178354Ssam case SYS_RES_IRQ: 855178354Ssam return (NULL); 856178354Ssam default: 857178354Ssam return (NULL); 858178354Ssam }; 859178354Ssam 860178354Ssam case BHNDB_ADDRSPACE_BRIDGED: 861178354Ssam switch (type) { 862178354Ssam case SYS_RES_MEMORY: 863178354Ssam return (&sc->bus_res->br_mem_rman); 864178354Ssam case SYS_RES_IRQ: 865178354Ssam // TODO 866178354Ssam // return &sc->irq_rman; 867178354Ssam return (NULL); 868178354Ssam default: 869178354Ssam return (NULL); 870178354Ssam }; 871178354Ssam } 872178354Ssam 873178354Ssam /* Quieten gcc */ 874178354Ssam return (NULL); 875173273Ssam} 876173273Ssam 877173273Ssam/** 878173273Ssam * Default implementation of BUS_ADD_CHILD() 879173273Ssam */ 880173273Ssamstatic device_t 881173273Ssambhndb_add_child(device_t dev, u_int order, const char *name, int unit) 882173273Ssam{ 883173273Ssam struct bhndb_devinfo *dinfo; 884173273Ssam device_t child; 885173273Ssam 886173273Ssam child = device_add_child_ordered(dev, order, name, unit); 887173273Ssam if (child == NULL) 888173273Ssam return (NULL); 889173273Ssam 890173273Ssam dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT); 891173273Ssam if (dinfo == NULL) { 892173273Ssam device_delete_child(dev, child); 893173273Ssam return (NULL); 894173273Ssam } 895173273Ssam 896173273Ssam dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE; 897173273Ssam resource_list_init(&dinfo->resources); 898173273Ssam 899173273Ssam device_set_ivars(child, dinfo); 900173273Ssam 901173273Ssam return (child); 902173273Ssam} 903173273Ssam 904173273Ssam/** 905173273Ssam * Default implementation of BUS_CHILD_DELETED(). 906173273Ssam */ 907173273Ssamstatic void 908173273Ssambhndb_child_deleted(device_t dev, device_t child) 909173273Ssam{ 910173273Ssam struct bhndb_devinfo *dinfo = device_get_ivars(child); 911173273Ssam if (dinfo != NULL) { 912173273Ssam resource_list_free(&dinfo->resources); 913173273Ssam free(dinfo, M_BHND); 914173273Ssam } 915173273Ssam 916173273Ssam device_set_ivars(child, NULL); 917173273Ssam} 918173273Ssam 919173273Ssam/** 920173273Ssam * Default implementation of BHNDB_GET_CHIPID(). 921173273Ssam */ 922173273Ssamstatic const struct bhnd_chipid * 923173273Ssambhndb_get_chipid(device_t dev, device_t child) 924173273Ssam{ 925173273Ssam struct bhndb_softc *sc = device_get_softc(dev); 926173273Ssam return (&sc->chipid); 927178354Ssam} 928173273Ssam 929173273Ssam 930173273Ssam/** 931178354Ssam * Default implementation of BHNDB_IS_HW_DISABLED(). 932173273Ssam */ 933173273Ssamstatic bool 934173273Ssambhndb_is_hw_disabled(device_t dev, device_t child) { 935173273Ssam struct bhndb_softc *sc; 936173273Ssam struct bhnd_core_info core; 937173273Ssam 938173273Ssam sc = device_get_softc(dev); 939173273Ssam 940178354Ssam /* Requestor must be attached to the bhnd bus */ 941178354Ssam if (device_get_parent(child) != sc->bus_dev) { 942173273Ssam return (BHND_BUS_IS_HW_DISABLED(device_get_parent(dev), child)); 943173273Ssam } 944178354Ssam 945173273Ssam /* Fetch core info */ 946173273Ssam core = bhnd_get_core_info(child); 947173273Ssam 948173273Ssam /* Try to defer to the bhndb bus parent */ 949173273Ssam if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, &core)) 950173273Ssam return (true); 951173273Ssam 952173273Ssam /* Otherwise, we treat bridge-capable cores as unpopulated if they're 953178354Ssam * not the configured host bridge */ 954173273Ssam if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(&core))) 955173273Ssam return (BHNDB_FIND_HOSTB_DEVICE(dev, sc->bus_dev) != child); 956173273Ssam 957173273Ssam /* Otherwise, assume the core is populated */ 958173273Ssam return (false); 959173273Ssam} 960173273Ssam 961173273Ssam/* ascending core index comparison used by bhndb_find_hostb_device() */ 962173273Ssamstatic int 963173273Ssamcompare_core_index(const void *lhs, const void *rhs) 964173273Ssam{ 965173273Ssam u_int left = bhnd_get_core_index(*(const device_t *) lhs); 966173273Ssam u_int right = bhnd_get_core_index(*(const device_t *) rhs); 967173273Ssam 968173273Ssam if (left < right) 969173273Ssam return (-1); 970173273Ssam else if (left > right) 971173273Ssam return (1); 972173273Ssam else 973173273Ssam return (0); 974173273Ssam} 975173273Ssam 976173273Ssam/** 977173273Ssam * Default bhndb(4) implementation of BHND_BUS_FIND_HOSTB_DEVICE(). 978178354Ssam * 979178354Ssam * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged 980178354Ssam * bhnd(4) devices to determine the hostb core: 981178354Ssam * 982178354Ssam * - The core must have a Broadcom vendor ID. 983178354Ssam * - The core devclass must match the bridge type. 984178354Ssam * - The core must be the first device on the bus with the bridged device 985178354Ssam * class. 986178354Ssam * 987178354Ssam * @param dev The bhndb device 988178354Ssam * @param child The requesting bhnd bus. 989178354Ssam */ 990178354Ssamstatic device_t 991178354Ssambhndb_find_hostb_device(device_t dev, device_t child) 992178354Ssam{ 993178354Ssam struct bhndb_softc *sc; 994178354Ssam struct bhnd_device_match md; 995178354Ssam device_t hostb_dev, *devlist; 996178354Ssam int devcnt, error; 997178354Ssam 998178354Ssam sc = device_get_softc(dev); 999178354Ssam 1000178354Ssam /* Set up a match descriptor for the required device class. */ 1001178354Ssam md = (struct bhnd_device_match) { 1002173273Ssam BHND_MATCH_CORE_CLASS(sc->bridge_class), 1003173273Ssam BHND_MATCH_CORE_UNIT(0) 1004173273Ssam }; 1005173273Ssam 1006173273Ssam /* Must be the absolute first matching device on the bus. */ 1007173273Ssam if ((error = device_get_children(child, &devlist, &devcnt))) 1008173273Ssam return (false); 1009173273Ssam 1010173273Ssam /* Sort by core index value, ascending */ 1011173273Ssam qsort(devlist, devcnt, sizeof(*devlist), compare_core_index); 1012173273Ssam 1013178354Ssam /* Find the hostb device */ 1014178354Ssam hostb_dev = NULL; 1015178354Ssam for (int i = 0; i < devcnt; i++) { 1016178354Ssam if (bhnd_device_matches(devlist[i], &md)) { 1017173273Ssam hostb_dev = devlist[i]; 1018178354Ssam break; 1019178354Ssam } 1020178354Ssam } 1021173273Ssam 1022173273Ssam /* Clean up */ 1023173273Ssam free(devlist, M_TEMP); 1024173273Ssam 1025173273Ssam return (hostb_dev); 1026173273Ssam} 1027173273Ssam 1028173273Ssam/** 1029173273Ssam * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE(). 1030173273Ssam */ 1031173273Ssamstatic struct resource * 1032173273Ssambhndb_alloc_resource(device_t dev, device_t child, int type, 1033173273Ssam int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 1034173273Ssam{ 1035173273Ssam struct bhndb_softc *sc; 1036173273Ssam struct resource_list_entry *rle; 1037173273Ssam struct resource *rv; 1038173273Ssam struct rman *rm; 1039173273Ssam int error; 1040173273Ssam bool passthrough, isdefault; 1041173273Ssam 1042173273Ssam sc = device_get_softc(dev); 1043173273Ssam passthrough = (device_get_parent(child) != dev); 1044173273Ssam isdefault = RMAN_IS_DEFAULT_RANGE(start, end); 1045173273Ssam rle = NULL; 1046173273Ssam 1047173273Ssam /* Populate defaults */ 1048173273Ssam if (!passthrough && isdefault) { 1049173273Ssam /* Fetch the resource list entry. */ 1050173273Ssam rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child), 1051173273Ssam type, *rid); 1052173273Ssam if (rle == NULL) { 1053173273Ssam device_printf(dev, 1054173273Ssam "default resource %#x type %d for child %s " 1055173273Ssam "not found\n", *rid, type, 1056173273Ssam device_get_nameunit(child)); 1057173273Ssam 1058173273Ssam return (NULL); 1059173273Ssam } 1060173273Ssam 1061173273Ssam if (rle->res != NULL) { 1062173273Ssam device_printf(dev, 1063173273Ssam "resource entry %#x type %d for child %s is busy\n", 1064173273Ssam *rid, type, device_get_nameunit(child)); 1065173273Ssam 1066173273Ssam return (NULL); 1067173273Ssam } 1068173273Ssam 1069173273Ssam start = rle->start; 1070178354Ssam end = rle->end; 1071178354Ssam count = ulmax(count, rle->count); 1072178354Ssam } 1073178354Ssam 1074178354Ssam /* Validate resource addresses */ 1075178354Ssam if (start > end || count > ((end - start) + 1)) 1076178354Ssam return (NULL); 1077178354Ssam 1078178354Ssam /* Fetch the resource manager */ 1079173273Ssam rm = bhndb_get_rman(sc, child, type); 1080173273Ssam if (rm == NULL) 1081178354Ssam return (NULL); 1082173273Ssam 1083178354Ssam /* Make our reservation */ 1084178354Ssam rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, 1085178354Ssam child); 1086178354Ssam if (rv == NULL) 1087178354Ssam return (NULL); 1088178354Ssam 1089178354Ssam rman_set_rid(rv, *rid); 1090181197Ssam 1091178354Ssam /* Activate */ 1092178354Ssam if (flags & RF_ACTIVE) { 1093178354Ssam error = bus_activate_resource(child, type, *rid, rv); 1094178354Ssam if (error) { 1095178354Ssam device_printf(dev, 1096178354Ssam "failed to activate entry %#x type %d for " 1097178354Ssam "child %s: %d\n", 1098178354Ssam *rid, type, device_get_nameunit(child), error); 1099178354Ssam 1100181197Ssam rman_release_resource(rv); 1101178354Ssam 1102173273Ssam return (NULL); 1103173273Ssam } 1104173273Ssam } 1105173273Ssam 1106173273Ssam /* Update child's resource list entry */ 1107173273Ssam if (rle != NULL) { 1108173273Ssam rle->res = rv; 1109173273Ssam rle->start = rman_get_start(rv); 1110173273Ssam rle->end = rman_get_end(rv); 1111173273Ssam rle->count = rman_get_size(rv); 1112173273Ssam } 1113173273Ssam 1114173273Ssam return (rv); 1115173273Ssam} 1116173273Ssam 1117173273Ssam/** 1118173273Ssam * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE(). 1119173273Ssam */ 1120178354Ssamstatic int 1121173273Ssambhndb_release_resource(device_t dev, device_t child, int type, int rid, 1122173273Ssam struct resource *r) 1123173273Ssam{ 1124173273Ssam int error; 1125173273Ssam 1126173273Ssam /* Deactivate resources */ 1127173273Ssam if (rman_get_flags(r) & RF_ACTIVE) { 1128170530Ssam error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r); 1129170530Ssam if (error) 1130170530Ssam return (error); 1131170530Ssam } 1132170530Ssam 1133170530Ssam if ((error = rman_release_resource(r))) 1134170530Ssam return (error); 1135170530Ssam 1136170530Ssam return (0); 1137170530Ssam} 1138170530Ssam 1139170530Ssam/** 1140178354Ssam * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE(). 1141170530Ssam */ 1142170530Ssamstatic int 1143170530Ssambhndb_adjust_resource(device_t dev, device_t child, int type, 1144170530Ssam struct resource *r, rman_res_t start, rman_res_t end) 1145170530Ssam{ 1146170530Ssam struct bhndb_softc *sc; 1147170530Ssam struct rman *rm; 1148170530Ssam rman_res_t mstart, mend; 1149170530Ssam int error; 1150170530Ssam 1151170530Ssam sc = device_get_softc(dev); 1152170530Ssam error = 0; 1153170530Ssam 1154170530Ssam /* Verify basic constraints */ 1155170530Ssam if (end <= start) 1156173273Ssam return (EINVAL); 1157173273Ssam 1158178354Ssam /* Fetch resource manager */ 1159170530Ssam rm = bhndb_get_rman(sc, child, type); 1160170530Ssam if (rm == NULL) 1161170530Ssam return (ENXIO); 1162173273Ssam 1163173273Ssam if (!rman_is_region_manager(r, rm)) 1164173273Ssam return (ENXIO); 1165173273Ssam 1166173273Ssam BHNDB_LOCK(sc); 1167173273Ssam 1168170530Ssam /* If not active, allow any range permitted by the resource manager */ 1169170530Ssam if (!(rman_get_flags(r) & RF_ACTIVE)) 1170170530Ssam goto done; 1171170530Ssam 1172173273Ssam /* Otherwise, the range is limited to the existing register window 1173178354Ssam * mapping */ 1174170530Ssam error = bhndb_find_resource_limits(sc->bus_res, r, &mstart, &mend); 1175173273Ssam if (error) 1176170530Ssam goto done; 1177173273Ssam 1178170530Ssam if (start < mstart || end > mend) { 1179170530Ssam error = EINVAL; 1180170530Ssam goto done; 1181170530Ssam } 1182170530Ssam 1183170530Ssam /* Fall through */ 1184172055Ssamdone: 1185170530Ssam if (!error) 1186170530Ssam error = rman_adjust_resource(r, start, end); 1187170530Ssam 1188173273Ssam BHNDB_UNLOCK(sc); 1189173273Ssam return (error); 1190173273Ssam} 1191173273Ssam 1192173273Ssam/** 1193178354Ssam * Initialize child resource @p r with a virtual address, tag, and handle 1194178354Ssam * copied from @p parent, adjusted to contain only the range defined by 1195173273Ssam * @p offsize and @p size. 1196173273Ssam * 1197178354Ssam * @param r The register to be initialized. 1198173273Ssam * @param parent The parent bus resource that fully contains the subregion. 1199173273Ssam * @param offset The subregion offset within @p parent. 1200173273Ssam * @param size The subregion size. 1201173273Ssam * @p r. 1202170530Ssam */ 1203173273Ssamstatic int 1204173273Ssambhndb_init_child_resource(struct resource *r, 1205173273Ssam struct resource *parent, bhnd_size_t offset, bhnd_size_t size) 1206178354Ssam{ 1207173273Ssam bus_space_handle_t bh, child_bh; 1208173273Ssam bus_space_tag_t bt; 1209173273Ssam uintptr_t vaddr; 1210173273Ssam int error; 1211173273Ssam 1212178354Ssam /* Fetch the parent resource's real bus values */ 1213173273Ssam vaddr = (uintptr_t) rman_get_virtual(parent); 1214173273Ssam bt = rman_get_bustag(parent); 1215173273Ssam bh = rman_get_bushandle(parent); 1216173273Ssam 1217173273Ssam /* Configure child resource with window-adjusted real bus values */ 1218173273Ssam vaddr += offset; 1219178354Ssam error = bus_space_subregion(bt, bh, offset, size, &child_bh); 1220173273Ssam if (error) 1221173273Ssam return (error); 1222173273Ssam 1223173273Ssam rman_set_virtual(r, (void *) vaddr); 1224173273Ssam rman_set_bustag(r, bt); 1225173273Ssam rman_set_bushandle(r, child_bh); 1226173273Ssam 1227173273Ssam return (0); 1228173273Ssam} 1229173273Ssam 1230170530Ssam/** 1231170530Ssam * Attempt activation of a fixed register window mapping for @p child. 1232170530Ssam * 1233170530Ssam * @param sc BHNDB device state. 1234170530Ssam * @param region The static region definition capable of mapping @p r. 1235170530Ssam * @param child A child requesting resource activation. 1236170530Ssam * @param type Resource type. 1237170530Ssam * @param rid Resource identifier. 1238178354Ssam * @param r Resource to be activated. 1239170530Ssam * 1240170530Ssam * @retval 0 if @p r was activated successfully 1241170530Ssam * @retval ENOENT if no fixed register window was found. 1242170530Ssam * @retval non-zero if @p r could not be activated. 1243170530Ssam */ 1244170530Ssamstatic int 1245170530Ssambhndb_activate_static_region(struct bhndb_softc *sc, 1246170530Ssam struct bhndb_region *region, device_t child, int type, int rid, 1247170530Ssam struct resource *r) 1248170530Ssam{ 1249170530Ssam struct resource *bridge_res; 1250170530Ssam const struct bhndb_regwin *win; 1251170530Ssam bhnd_size_t parent_offset; 1252170530Ssam rman_res_t r_start, r_size; 1253178354Ssam int error; 1254170530Ssam 1255170530Ssam win = region->static_regwin; 1256170530Ssam 1257178354Ssam KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type), 1258170530Ssam ("can't activate non-static region")); 1259170530Ssam 1260170530Ssam r_start = rman_get_start(r); 1261170530Ssam r_size = rman_get_size(r); 1262170530Ssam 1263170530Ssam /* Find the corresponding bridge resource */ 1264170530Ssam bridge_res = bhndb_find_regwin_resource(sc->bus_res, win); 1265170530Ssam if (bridge_res == NULL) 1266170530Ssam return (ENXIO); 1267170530Ssam 1268170530Ssam /* Calculate subregion offset within the parent resource */ 1269170530Ssam parent_offset = r_start - region->addr; 1270170530Ssam parent_offset += win->win_offset; 1271170530Ssam 1272170530Ssam /* Configure resource with its real bus values. */ 1273170530Ssam error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size); 1274170530Ssam if (error) 1275170530Ssam return (error); 1276170530Ssam 1277170530Ssam /* Mark active */ 1278170530Ssam if ((error = rman_activate_resource(r))) 1279170530Ssam return (error); 1280170530Ssam 1281170530Ssam return (0); 1282178354Ssam} 1283170530Ssam 1284170530Ssam/** 1285170530Ssam * Attempt to allocate/retain a dynamic register window for @p r, returning 1286170530Ssam * the retained window. 1287170530Ssam * 1288170530Ssam * @param sc The bhndb driver state. 1289170530Ssam * @param r The resource for which a window will be retained. 1290170530Ssam */ 1291170530Ssamstatic struct bhndb_dw_alloc * 1292170530Ssambhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r) 1293170530Ssam{ 1294170530Ssam struct bhndb_dw_alloc *dwa; 1295170530Ssam rman_res_t r_start, r_size; 1296170530Ssam int error; 1297170530Ssam 1298170530Ssam BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1299170530Ssam 1300170530Ssam r_start = rman_get_start(r); 1301170530Ssam r_size = rman_get_size(r); 1302170530Ssam 1303170530Ssam /* Look for an existing dynamic window we can reference */ 1304170530Ssam dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size); 1305170530Ssam if (dwa != NULL) { 1306170530Ssam if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0) 1307170530Ssam return (dwa); 1308170530Ssam 1309170530Ssam return (NULL); 1310178354Ssam } 1311170530Ssam 1312170530Ssam /* Otherwise, try to reserve a free window */ 1313178354Ssam dwa = bhndb_dw_next_free(sc->bus_res); 1314170530Ssam if (dwa == NULL) { 1315170530Ssam /* No free windows */ 1316170530Ssam return (NULL); 1317170530Ssam } 1318170530Ssam 1319170530Ssam /* Set the window target */ 1320170530Ssam error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r), 1321170530Ssam rman_get_size(r)); 1322170530Ssam if (error) { 1323170530Ssam device_printf(sc->dev, "dynamic window initialization " 1324170530Ssam "for 0x%llx-0x%llx failed: %d\n", 1325170530Ssam (unsigned long long) r_start, 1326170530Ssam (unsigned long long) r_start + r_size - 1, 1327170530Ssam error); 1328170530Ssam return (NULL); 1329170530Ssam } 1330170530Ssam 1331170530Ssam /* Add our reservation */ 1332170530Ssam if (bhndb_dw_retain(sc->bus_res, dwa, r)) 1333170530Ssam return (NULL); 1334170530Ssam 1335170530Ssam return (dwa); 1336170530Ssam} 1337170530Ssam 1338170530Ssam/** 1339170530Ssam * Activate a resource using any viable static or dynamic register window. 1340170530Ssam * 1341170530Ssam * @param sc The bhndb driver state. 1342170530Ssam * @param child The child holding ownership of @p r. 1343170530Ssam * @param type The type of the resource to be activated. 1344170530Ssam * @param rid The resource ID of @p r. 1345170530Ssam * @param r The resource to be activated 1346170530Ssam * @param[out] indirect On error and if not NULL, will be set to 'true' if 1347170530Ssam * the caller should instead use an indirect resource mapping. 1348170530Ssam * 1349170530Ssam * @retval 0 success 1350170530Ssam * @retval non-zero activation failed. 1351170530Ssam */ 1352170530Ssamstatic int 1353170530Ssambhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type, 1354170530Ssam int rid, struct resource *r, bool *indirect) 1355170530Ssam{ 1356170530Ssam struct bhndb_region *region; 1357170530Ssam struct bhndb_dw_alloc *dwa; 1358170530Ssam bhndb_priority_t dw_priority; 1359170530Ssam rman_res_t r_start, r_size; 1360170530Ssam rman_res_t parent_offset; 1361170530Ssam int error; 1362170530Ssam 1363170530Ssam BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED); 1364170530Ssam 1365170530Ssam // TODO - IRQs 1366170530Ssam if (type != SYS_RES_MEMORY) 1367170530Ssam return (ENXIO); 1368170530Ssam 1369173273Ssam if (indirect) 1370173273Ssam *indirect = false; 1371173273Ssam 1372170530Ssam r_start = rman_get_start(r); 1373170530Ssam r_size = rman_get_size(r); 1374170530Ssam 1375170530Ssam /* Activate native addrspace resources using the host address space */ 1376170530Ssam if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) { 1377170530Ssam struct resource *parent; 1378170530Ssam 1379170530Ssam /* Find the bridge resource referenced by the child */ 1380170530Ssam parent = bhndb_find_resource_range(sc->bus_res, r_start, 1381170530Ssam r_size); 1382170530Ssam if (parent == NULL) { 1383170530Ssam device_printf(sc->dev, "host resource not found " 1384170530Ssam "for 0x%llx-0x%llx\n", 1385170530Ssam (unsigned long long) r_start, 1386170530Ssam (unsigned long long) r_start + r_size - 1); 1387170530Ssam return (ENOENT); 1388170530Ssam } 1389170530Ssam 1390170530Ssam /* Initialize child resource with the real bus values */ 1391170530Ssam error = bhndb_init_child_resource(r, parent, 1392170530Ssam r_start - rman_get_start(parent), r_size); 1393170530Ssam if (error) 1394170530Ssam return (error); 1395170530Ssam 1396170530Ssam /* Try to activate child resource */ 1397170530Ssam return (rman_activate_resource(r)); 1398170530Ssam } 1399170530Ssam 1400170530Ssam /* Default to low priority */ 1401170530Ssam dw_priority = BHNDB_PRIORITY_LOW; 1402178354Ssam 1403170530Ssam /* Look for a bus region matching the resource's address range */ 1404170530Ssam region = bhndb_find_resource_region(sc->bus_res, r_start, r_size); 1405170530Ssam if (region != NULL) 1406170530Ssam dw_priority = region->priority; 1407170530Ssam 1408170530Ssam /* Prefer static mappings over consuming a dynamic windows. */ 1409170530Ssam if (region && region->static_regwin) { 1410170530Ssam error = bhndb_activate_static_region(sc, region, child, type, 1411170530Ssam rid, r); 1412170530Ssam if (error) 1413170530Ssam device_printf(sc->dev, "static window allocation " 1414170530Ssam "for 0x%llx-0x%llx failed\n", 1415170530Ssam (unsigned long long) r_start, 1416170530Ssam (unsigned long long) r_start + r_size - 1); 1417170530Ssam return (error); 1418170530Ssam } 1419170530Ssam 1420170530Ssam /* A dynamic window will be required; is this resource high enough 1421170530Ssam * priority to be reserved a dynamic window? */ 1422170530Ssam if (dw_priority < sc->bus_res->min_prio) { 1423170530Ssam if (indirect) 1424178354Ssam *indirect = true; 1425170530Ssam 1426170530Ssam return (ENOMEM); 1427170530Ssam } 1428173273Ssam 1429173273Ssam /* Find and retain a usable window */ 1430173273Ssam BHNDB_LOCK(sc); { 1431173273Ssam dwa = bhndb_retain_dynamic_window(sc, r); 1432170530Ssam } BHNDB_UNLOCK(sc); 1433170530Ssam 1434170530Ssam if (dwa == NULL) { 1435170530Ssam if (indirect) 1436170530Ssam *indirect = true; 1437173273Ssam return (ENOMEM); 1438173273Ssam } 1439173273Ssam 1440173273Ssam /* Configure resource with its real bus values. */ 1441173273Ssam parent_offset = dwa->win->win_offset; 1442173273Ssam parent_offset += r_start - dwa->target; 1443178354Ssam 1444170530Ssam error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset, 1445170530Ssam dwa->win->win_size); 1446170530Ssam if (error) 1447170530Ssam goto failed; 1448173273Ssam 1449178354Ssam /* Mark active */ 1450173273Ssam if ((error = rman_activate_resource(r))) 1451173273Ssam goto failed; 1452173273Ssam 1453173273Ssam return (0); 1454173273Ssam 1455178354Ssamfailed: 1456170530Ssam /* Release our region allocation. */ 1457173273Ssam BHNDB_LOCK(sc); 1458170530Ssam bhndb_dw_release(sc->bus_res, dwa, r); 1459170530Ssam BHNDB_UNLOCK(sc); 1460170530Ssam 1461170530Ssam return (error); 1462170530Ssam} 1463170530Ssam 1464170530Ssam/** 1465170530Ssam * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE(). 1466170530Ssam * 1467170530Ssam * Maps resource activation requests to a viable static or dynamic 1468170530Ssam * register window, if any. 1469170530Ssam */ 1470170530Ssamstatic int 1471170530Ssambhndb_activate_resource(device_t dev, device_t child, int type, int rid, 1472170530Ssam struct resource *r) 1473170530Ssam{ 1474170530Ssam struct bhndb_softc *sc = device_get_softc(dev); 1475170530Ssam 1476173273Ssam return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL)); 1477173273Ssam} 1478175877Ssam 1479178354Ssam/** 1480173273Ssam * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE(). 1481173273Ssam */ 1482173273Ssamstatic int 1483173273Ssambhndb_deactivate_resource(device_t dev, device_t child, int type, 1484178354Ssam int rid, struct resource *r) 1485173273Ssam{ 1486173273Ssam struct bhndb_dw_alloc *dwa; 1487173273Ssam struct bhndb_softc *sc; 1488178354Ssam struct rman *rm; 1489173273Ssam int error; 1490173273Ssam 1491173273Ssam sc = device_get_softc(dev); 1492173273Ssam 1493173273Ssam if ((rm = bhndb_get_rman(sc, child, type)) == NULL) 1494178354Ssam return (EINVAL); 1495173273Ssam 1496173273Ssam /* Mark inactive */ 1497173273Ssam if ((error = rman_deactivate_resource(r))) 1498178354Ssam return (error); 1499170530Ssam 1500170530Ssam /* Free any dynamic window allocation. */ 1501170530Ssam if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { 1502170530Ssam BHNDB_LOCK(sc); 1503170530Ssam dwa = bhndb_dw_find_resource(sc->bus_res, r); 1504170530Ssam if (dwa != NULL) 1505170530Ssam bhndb_dw_release(sc->bus_res, dwa, r); 1506170530Ssam BHNDB_UNLOCK(sc); 1507170530Ssam } 1508170530Ssam 1509170530Ssam return (0); 1510170530Ssam} 1511170530Ssam 1512170530Ssam/** 1513170530Ssam * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST(). 1514178354Ssam */ 1515170530Ssamstatic struct resource_list * 1516170530Ssambhndb_get_resource_list(device_t dev, device_t child) 1517170530Ssam{ 1518170530Ssam struct bhndb_devinfo *dinfo = device_get_ivars(child); 1519170530Ssam return (&dinfo->resources); 1520170530Ssam} 1521170530Ssam 1522170530Ssam/** 1523170530Ssam * Default bhndb(4) implementation of BHND_BUS_ACTIVATE_RESOURCE(). 1524170530Ssam * 1525170530Ssam * For BHNDB_ADDRSPACE_NATIVE children, all resources may be assumed to 1526170530Ssam * be activated by the bridge. 1527170530Ssam * 1528170530Ssam * For BHNDB_ADDRSPACE_BRIDGED children, attempts to activate a static register 1529170530Ssam * window, a dynamic register window, or configures @p r as an indirect 1530170530Ssam * resource -- in that order. 1531170530Ssam */ 1532173273Ssamstatic int 1533170530Ssambhndb_activate_bhnd_resource(device_t dev, device_t child, 1534170530Ssam int type, int rid, struct bhnd_resource *r) 1535170530Ssam{ 1536170530Ssam struct bhndb_softc *sc; 1537170530Ssam struct bhndb_region *region; 1538170530Ssam rman_res_t r_start, r_size; 1539170530Ssam int error; 1540170530Ssam bool indirect; 1541170530Ssam 1542170530Ssam KASSERT(!r->direct, 1543170530Ssam ("direct flag set on inactive resource")); 1544170530Ssam 1545178354Ssam KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE), 1546170530Ssam ("RF_ACTIVE set on inactive resource")); 1547170530Ssam 1548170530Ssam sc = device_get_softc(dev); 1549170530Ssam 1550170530Ssam r_start = rman_get_start(r->res); 1551170530Ssam r_size = rman_get_size(r->res); 1552178354Ssam 1553170530Ssam /* Verify bridged address range's resource priority, and skip direct 1554170530Ssam * allocation if the priority is too low. */ 1555170530Ssam if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) { 1556178354Ssam bhndb_priority_t r_prio; 1557170530Ssam 1558170530Ssam region = bhndb_find_resource_region(sc->bus_res, r_start, 1559170530Ssam r_size); 1560170530Ssam if (region != NULL) 1561170530Ssam r_prio = region->priority; 1562170530Ssam else 1563170530Ssam r_prio = BHNDB_PRIORITY_NONE; 1564170530Ssam 1565170530Ssam /* If less than the minimum dynamic window priority, this 1566178354Ssam * resource should always be indirect. */ 1567170530Ssam if (r_prio < sc->bus_res->min_prio) 1568173273Ssam return (0); 1569170530Ssam } 1570170530Ssam 1571170530Ssam /* Attempt direct activation */ 1572170530Ssam error = bhndb_try_activate_resource(sc, child, type, rid, r->res, 1573173273Ssam &indirect); 1574178354Ssam if (!error) { 1575173273Ssam r->direct = true; 1576173273Ssam } else if (indirect) { 1577173273Ssam /* The request was valid, but no viable register window is 1578170530Ssam * available; indirection must be employed. */ 1579178354Ssam error = 0; 1580170530Ssam r->direct = false; 1581170530Ssam } 1582170530Ssam 1583178354Ssam if (BHNDB_DEBUG(PRIO) && 1584170530Ssam bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) 1585170530Ssam { 1586170530Ssam device_printf(child, "activated 0x%llx-0x%llx as %s " 1587170530Ssam "resource\n", 1588178354Ssam (unsigned long long) r_start, 1589170530Ssam (unsigned long long) r_start + r_size - 1, 1590170530Ssam r->direct ? "direct" : "indirect"); 1591170530Ssam } 1592178354Ssam 1593170530Ssam return (error); 1594170530Ssam}; 1595170530Ssam 1596170530Ssam/** 1597170530Ssam * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE(). 1598170530Ssam */ 1599170530Ssamstatic int 1600170530Ssambhndb_deactivate_bhnd_resource(device_t dev, device_t child, 1601170530Ssam int type, int rid, struct bhnd_resource *r) 1602178354Ssam{ 1603178354Ssam int error; 1604178354Ssam 1605178354Ssam /* Indirect resources don't require activation */ 1606178354Ssam if (!r->direct) 1607178354Ssam return (0); 1608178354Ssam 1609178354Ssam KASSERT(rman_get_flags(r->res) & RF_ACTIVE, 1610178354Ssam ("RF_ACTIVE not set on direct resource")); 1611178354Ssam 1612178354Ssam /* Perform deactivation */ 1613178354Ssam error = bus_deactivate_resource(child, type, rid, r->res); 1614178354Ssam if (!error) 1615178354Ssam r->direct = false; 1616178354Ssam 1617178354Ssam return (error); 1618178354Ssam}; 1619178354Ssam 1620178354Ssam/** 1621178354Ssam * Slow path for bhndb_io_resource(). 1622178354Ssam * 1623178354Ssam * Iterates over the existing allocated dynamic windows looking for a viable 1624178354Ssam * in-use region; the first matching region is returned. 1625178354Ssam */ 1626178354Ssamstatic struct bhndb_dw_alloc * 1627178354Ssambhndb_io_resource_slow(struct bhndb_softc *sc, bus_addr_t addr, 1628178354Ssam bus_size_t size, bus_size_t *offset) 1629178354Ssam{ 1630178354Ssam struct bhndb_resources *br; 1631178354Ssam struct bhndb_dw_alloc *dwa; 1632178354Ssam 1633178354Ssam BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1634178354Ssam 1635170530Ssam br = sc->bus_res; 1636170530Ssam 1637170530Ssam /* Search for an existing dynamic mapping of this address range. 1638170530Ssam * Static regions are not searched, as a statically mapped 1639170530Ssam * region would never be allocated as an indirect resource. */ 1640170530Ssam for (size_t i = 0; i < br->dwa_count; i++) { 1641170530Ssam const struct bhndb_regwin *win; 1642170530Ssam 1643170530Ssam dwa = &br->dw_alloc[i]; 1644170530Ssam win = dwa->win; 1645170530Ssam 1646170530Ssam KASSERT(win->win_type == BHNDB_REGWIN_T_DYN, 1647170530Ssam ("invalid register window type")); 1648170530Ssam 1649170530Ssam /* Verify the range */ 1650170530Ssam if (addr < dwa->target) 1651170530Ssam continue; 1652170530Ssam 1653170530Ssam if (addr + size > dwa->target + win->win_size) 1654173273Ssam continue; 1655173273Ssam 1656173273Ssam /* Found */ 1657170530Ssam *offset = dwa->win->win_offset; 1658170530Ssam *offset += addr - dwa->target; 1659170530Ssam 1660170530Ssam return (dwa); 1661170530Ssam } 1662170530Ssam 1663170530Ssam /* not found */ 1664170530Ssam return (NULL); 1665170530Ssam} 1666170530Ssam 1667170530Ssam/** 1668170530Ssam * Find the bridge resource to be used for I/O requests. 1669178354Ssam * 1670173273Ssam * @param sc Bridge driver state. 1671173273Ssam * @param addr The I/O target address. 1672173273Ssam * @param size The size of the I/O operation to be performed at @p addr. 1673178354Ssam * @param[out] offset The offset within the returned resource at which 1674178354Ssam * to perform the I/O request. 1675178354Ssam */ 1676178354Ssamstatic inline struct bhndb_dw_alloc * 1677170530Ssambhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size, 1678170530Ssam bus_size_t *offset) 1679170530Ssam{ 1680178953Ssam struct bhndb_resources *br; 1681178953Ssam struct bhndb_dw_alloc *dwa; 1682178953Ssam int error; 1683178953Ssam 1684170530Ssam BHNDB_LOCK_ASSERT(sc, MA_OWNED); 1685170530Ssam 1686170530Ssam br = sc->bus_res; 1687170530Ssam 1688170530Ssam /* Try to fetch a free window */ 1689173273Ssam dwa = bhndb_dw_next_free(br); 1690173273Ssam 1691173273Ssam /* 1692173273Ssam * If no dynamic windows are available, look for an existing 1693173273Ssam * region that maps the target range. 1694173273Ssam * 1695173273Ssam * If none are found, this is a child driver bug -- our window 1696178354Ssam * over-commit should only fail in the case where a child driver leaks 1697173273Ssam * resources, or perform operations out-of-order. 1698173273Ssam * 1699173273Ssam * Broadcom HND chipsets are designed to not require register window 1700173273Ssam * swapping during execution; as long as the child devices are 1701178354Ssam * attached/detached correctly, using the hardware's required order 1702173273Ssam * of operations, there should always be a window available for the 1703178354Ssam * current operation. 1704173273Ssam */ 1705173273Ssam if (dwa == NULL) { 1706173273Ssam dwa = bhndb_io_resource_slow(sc, addr, size, offset); 1707173273Ssam if (dwa == NULL) { 1708173273Ssam panic("register windows exhausted attempting to map " 1709173273Ssam "0x%llx-0x%llx\n", 1710173273Ssam (unsigned long long) addr, 1711173273Ssam (unsigned long long) addr+size-1); 1712178354Ssam } 1713173273Ssam 1714173273Ssam return (dwa); 1715178354Ssam } 1716173273Ssam 1717173273Ssam /* Adjust the window if the I/O request won't fit in the current 1718173273Ssam * target range. */ 1719173273Ssam if (addr < dwa->target || 1720170530Ssam (dwa->target + dwa->win->win_size) - addr < size) 1721170530Ssam { 1722170530Ssam error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr, 1723170530Ssam size); 1724170530Ssam if (error) { 1725170530Ssam panic("failed to set register window target mapping " 1726170530Ssam "0x%llx-0x%llx\n", 1727170530Ssam (unsigned long long) addr, 1728178354Ssam (unsigned long long) addr+size-1); 1729170530Ssam } 1730170530Ssam } 1731170530Ssam 1732170530Ssam /* Calculate the offset and return */ 1733170530Ssam *offset = (addr - dwa->target) + dwa->win->win_offset; 1734178354Ssam return (dwa); 1735170530Ssam} 1736170530Ssam 1737170530Ssam/* 1738170530Ssam * BHND_BUS_(READ|WRITE_* implementations 1739170530Ssam */ 1740170530Ssam 1741170530Ssam/* bhndb_bus_(read|write) common implementation */ 1742170530Ssam#define BHNDB_IO_COMMON_SETUP(_io_size) \ 1743170530Ssam struct bhndb_softc *sc; \ 1744170530Ssam struct bhndb_dw_alloc *dwa; \ 1745170530Ssam struct resource *io_res; \ 1746170530Ssam bus_size_t io_offset; \ 1747170530Ssam \ 1748170530Ssam sc = device_get_softc(dev); \ 1749170530Ssam \ 1750170530Ssam BHNDB_LOCK(sc); \ 1751170530Ssam dwa = bhndb_io_resource(sc, rman_get_start(r->res) + \ 1752170530Ssam offset, _io_size, &io_offset); \ 1753170530Ssam io_res = dwa->parent_res; \ 1754170530Ssam \ 1755170530Ssam KASSERT(!r->direct, \ 1756178354Ssam ("bhnd_bus slow path used for direct resource")); \ 1757170530Ssam \ 1758170530Ssam KASSERT(rman_get_flags(io_res) & RF_ACTIVE, \ 1759170530Ssam ("i/o resource is not active")); 1760170530Ssam 1761170530Ssam#define BHNDB_IO_COMMON_TEARDOWN() \ 1762170530Ssam BHNDB_UNLOCK(sc); 1763170530Ssam 1764170530Ssam/* Defines a bhndb_bus_read_* method implementation */ 1765170530Ssam#define BHNDB_IO_READ(_type, _name) \ 1766170530Ssamstatic _type \ 1767170530Ssambhndb_bus_read_ ## _name (device_t dev, device_t child, \ 1768170530Ssam struct bhnd_resource *r, bus_size_t offset) \ 1769170530Ssam{ \ 1770170530Ssam _type v; \ 1771170530Ssam BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ 1772178354Ssam v = bus_read_ ## _name (io_res, io_offset); \ 1773178354Ssam BHNDB_IO_COMMON_TEARDOWN(); \ 1774170530Ssam \ 1775170530Ssam return (v); \ 1776178354Ssam} 1777173273Ssam 1778173273Ssam/* Defines a bhndb_bus_write_* method implementation */ 1779170530Ssam#define BHNDB_IO_WRITE(_type, _name) \ 1780178354Ssamstatic void \ 1781170530Ssambhndb_bus_write_ ## _name (device_t dev, device_t child, \ 1782170530Ssam struct bhnd_resource *r, bus_size_t offset, _type value) \ 1783170530Ssam{ \ 1784170530Ssam BHNDB_IO_COMMON_SETUP(sizeof(_type)); \ 1785170530Ssam bus_write_ ## _name (io_res, io_offset, value); \ 1786170530Ssam BHNDB_IO_COMMON_TEARDOWN(); \ 1787170530Ssam} 1788170530Ssam 1789170530Ssam/* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */ 1790170530Ssam#define BHNDB_IO_MISC(_type, _ptr, _op, _size) \ 1791170530Ssamstatic void \ 1792170530Ssambhndb_bus_ ## _op ## _ ## _size (device_t dev, \ 1793170530Ssam device_t child, struct bhnd_resource *r, bus_size_t offset, \ 1794170530Ssam _type _ptr datap, bus_size_t count) \ 1795170530Ssam{ \ 1796170530Ssam BHNDB_IO_COMMON_SETUP(sizeof(_type) * count); \ 1797178354Ssam bus_ ## _op ## _ ## _size (io_res, io_offset, \ 1798170530Ssam datap, count); \ 1799170530Ssam BHNDB_IO_COMMON_TEARDOWN(); \ 1800170530Ssam} 1801170530Ssam 1802170530Ssam/* Defines a complete set of read/write methods */ 1803178354Ssam#define BHNDB_IO_METHODS(_type, _size) \ 1804170530Ssam BHNDB_IO_READ(_type, _size) \ 1805170530Ssam BHNDB_IO_WRITE(_type, _size) \ 1806170530Ssam \ 1807170530Ssam BHNDB_IO_READ(_type, stream_ ## _size) \ 1808170530Ssam BHNDB_IO_WRITE(_type, stream_ ## _size) \ 1809170530Ssam \ 1810170530Ssam BHNDB_IO_MISC(_type, *, read_multi, _size) \ 1811170530Ssam BHNDB_IO_MISC(_type, *, write_multi, _size) \ 1812170530Ssam \ 1813170530Ssam BHNDB_IO_MISC(_type, *, read_multi_stream, _size) \ 1814170530Ssam BHNDB_IO_MISC(_type, *, write_multi_stream, _size) \ 1815170530Ssam \ 1816170530Ssam BHNDB_IO_MISC(_type, , set_multi, _size) \ 1817178354Ssam BHNDB_IO_MISC(_type, , set_region, _size) \ 1818170530Ssam BHNDB_IO_MISC(_type, *, read_region, _size) \ 1819170530Ssam BHNDB_IO_MISC(_type, *, write_region, _size) \ 1820170530Ssam \ 1821170530Ssam BHNDB_IO_MISC(_type, *, read_region_stream, _size) \ 1822170530Ssam BHNDB_IO_MISC(_type, *, write_region_stream, _size) 1823170530Ssam 1824170530SsamBHNDB_IO_METHODS(uint8_t, 1); 1825170530SsamBHNDB_IO_METHODS(uint16_t, 2); 1826170530SsamBHNDB_IO_METHODS(uint32_t, 4); 1827170530Ssam 1828170530Ssam/** 1829170530Ssam * Default bhndb(4) implementation of BHND_BUS_BARRIER(). 1830170530Ssam */ 1831170530Ssamstatic void 1832170530Ssambhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r, 1833170530Ssam bus_size_t offset, bus_size_t length, int flags) 1834170530Ssam{ 1835170530Ssam BHNDB_IO_COMMON_SETUP(length); 1836170530Ssam 1837170530Ssam bus_barrier(io_res, io_offset + offset, length, flags); 1838170530Ssam 1839178354Ssam BHNDB_IO_COMMON_TEARDOWN(); 1840170530Ssam} 1841173273Ssam 1842173273Ssam/** 1843173273Ssam * Default bhndb(4) implementation of BUS_SETUP_INTR(). 1844173273Ssam */ 1845170530Ssamstatic int 1846170530Ssambhndb_setup_intr(device_t dev, device_t child, struct resource *r, 1847170530Ssam int flags, driver_filter_t filter, driver_intr_t handler, void *arg, 1848170530Ssam void **cookiep) 1849170530Ssam{ 1850170530Ssam // TODO 1851170530Ssam return (EOPNOTSUPP); 1852178354Ssam} 1853170530Ssam 1854170530Ssam/** 1855170530Ssam * Default bhndb(4) implementation of BUS_TEARDOWN_INTR(). 1856170530Ssam */ 1857170530Ssamstatic int 1858170530Ssambhndb_teardown_intr(device_t dev, device_t child, struct resource *r, 1859170530Ssam void *cookie) 1860170530Ssam{ 1861170530Ssam // TODO 1862170530Ssam return (EOPNOTSUPP); 1863170530Ssam} 1864170530Ssam 1865170530Ssam/** 1866170530Ssam * Default bhndb(4) implementation of BUS_CONFIG_INTR(). 1867180309Ssam */ 1868170530Ssamstatic int 1869170530Ssambhndb_config_intr(device_t dev, int irq, enum intr_trigger trig, 1870170530Ssam enum intr_polarity pol) 1871170530Ssam{ 1872178354Ssam // TODO 1873170530Ssam return (EOPNOTSUPP); 1874170530Ssam} 1875170530Ssam 1876170530Ssam/** 1877170530Ssam * Default bhndb(4) implementation of BUS_BIND_INTR(). 1878170530Ssam */ 1879170530Ssamstatic int 1880170530Ssambhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu) 1881170530Ssam{ 1882170530Ssam // TODO 1883170530Ssam return (EOPNOTSUPP); 1884178354Ssam} 1885170530Ssam 1886170530Ssam/** 1887178354Ssam * Default bhndb(4) implementation of BUS_DESCRIBE_INTR(). 1888170530Ssam */ 1889178354Ssamstatic int 1890170530Ssambhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, 1891170530Ssam const char *descr) 1892170530Ssam{ 1893170530Ssam // TODO 1894170530Ssam return (EOPNOTSUPP); 1895170530Ssam} 1896170530Ssam 1897170530Ssam/** 1898170530Ssam * Default bhndb(4) implementation of BUS_GET_DMA_TAG(). 1899178354Ssam */ 1900170530Ssamstatic bus_dma_tag_t 1901170530Ssambhndb_get_dma_tag(device_t dev, device_t child) 1902170530Ssam{ 1903170530Ssam // TODO 1904170530Ssam return (NULL); 1905170530Ssam} 1906170530Ssam 1907170530Ssamstatic device_method_t bhndb_methods[] = { 1908178354Ssam /* Device interface */ \ 1909170530Ssam DEVMETHOD(device_probe, bhndb_generic_probe), 1910170530Ssam DEVMETHOD(device_detach, bhndb_generic_detach), 1911178354Ssam DEVMETHOD(device_shutdown, bus_generic_shutdown), 1912178354Ssam DEVMETHOD(device_suspend, bhndb_generic_suspend), 1913170530Ssam DEVMETHOD(device_resume, bhndb_generic_resume), 1914170530Ssam 1915170530Ssam /* Bus interface */ 1916170530Ssam DEVMETHOD(bus_probe_nomatch, bhndb_probe_nomatch), 1917170530Ssam DEVMETHOD(bus_print_child, bhndb_print_child), 1918170530Ssam DEVMETHOD(bus_child_pnpinfo_str, bhndb_child_pnpinfo_str), 1919170530Ssam DEVMETHOD(bus_child_location_str, bhndb_child_location_str), 1920170530Ssam DEVMETHOD(bus_add_child, bhndb_add_child), 1921170530Ssam DEVMETHOD(bus_child_deleted, bhndb_child_deleted), 1922170530Ssam 1923170530Ssam DEVMETHOD(bus_alloc_resource, bhndb_alloc_resource), 1924170530Ssam DEVMETHOD(bus_release_resource, bhndb_release_resource), 1925170530Ssam DEVMETHOD(bus_activate_resource, bhndb_activate_resource), 1926170530Ssam DEVMETHOD(bus_deactivate_resource, bhndb_deactivate_resource), 1927170530Ssam 1928170530Ssam DEVMETHOD(bus_setup_intr, bhndb_setup_intr), 1929170530Ssam DEVMETHOD(bus_teardown_intr, bhndb_teardown_intr), 1930170530Ssam DEVMETHOD(bus_config_intr, bhndb_config_intr), 1931170530Ssam DEVMETHOD(bus_bind_intr, bhndb_bind_intr), 1932170530Ssam DEVMETHOD(bus_describe_intr, bhndb_describe_intr), 1933170530Ssam 1934170530Ssam DEVMETHOD(bus_get_dma_tag, bhndb_get_dma_tag), 1935170530Ssam 1936170530Ssam DEVMETHOD(bus_adjust_resource, bhndb_adjust_resource), 1937170530Ssam DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 1938170530Ssam DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 1939170530Ssam DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 1940170530Ssam DEVMETHOD(bus_get_resource_list, bhndb_get_resource_list), 1941170530Ssam 1942170530Ssam DEVMETHOD(bus_read_ivar, bhndb_read_ivar), 1943170530Ssam DEVMETHOD(bus_write_ivar, bhndb_write_ivar), 1944170530Ssam 1945170530Ssam /* BHNDB interface */ 1946170530Ssam DEVMETHOD(bhndb_get_chipid, bhndb_get_chipid), 1947178354Ssam DEVMETHOD(bhndb_init_full_config, bhndb_generic_init_full_config), 1948170530Ssam DEVMETHOD(bhndb_find_hostb_device, bhndb_find_hostb_device), 1949173865Ssam DEVMETHOD(bhndb_suspend_resource, bhndb_suspend_resource), 1950170530Ssam DEVMETHOD(bhndb_resume_resource, bhndb_resume_resource), 1951170530Ssam 1952178354Ssam /* BHND interface */ 1953173273Ssam DEVMETHOD(bhnd_bus_is_hw_disabled, bhndb_is_hw_disabled), 1954173273Ssam DEVMETHOD(bhnd_bus_get_chipid, bhndb_get_chipid), 1955173273Ssam DEVMETHOD(bhnd_bus_activate_resource, bhndb_activate_bhnd_resource), 1956173273Ssam DEVMETHOD(bhnd_bus_deactivate_resource, bhndb_deactivate_bhnd_resource), 1957173273Ssam DEVMETHOD(bhnd_bus_get_nvram_var, bhnd_bus_generic_get_nvram_var), 1958173273Ssam DEVMETHOD(bhnd_bus_read_1, bhndb_bus_read_1), 1959173273Ssam DEVMETHOD(bhnd_bus_read_2, bhndb_bus_read_2), 1960173273Ssam DEVMETHOD(bhnd_bus_read_4, bhndb_bus_read_4), 1961178354Ssam DEVMETHOD(bhnd_bus_write_1, bhndb_bus_write_1), 1962173273Ssam DEVMETHOD(bhnd_bus_write_2, bhndb_bus_write_2), 1963178354Ssam DEVMETHOD(bhnd_bus_write_4, bhndb_bus_write_4), 1964173273Ssam 1965173273Ssam DEVMETHOD(bhnd_bus_read_stream_1, bhndb_bus_read_stream_1), 1966173273Ssam DEVMETHOD(bhnd_bus_read_stream_2, bhndb_bus_read_stream_2), 1967173865Ssam DEVMETHOD(bhnd_bus_read_stream_4, bhndb_bus_read_stream_4), 1968173865Ssam DEVMETHOD(bhnd_bus_write_stream_1, bhndb_bus_write_stream_1), 1969173865Ssam DEVMETHOD(bhnd_bus_write_stream_2, bhndb_bus_write_stream_2), 1970173273Ssam DEVMETHOD(bhnd_bus_write_stream_4, bhndb_bus_write_stream_4), 1971173273Ssam 1972178354Ssam DEVMETHOD(bhnd_bus_read_multi_1, bhndb_bus_read_multi_1), 1973173273Ssam DEVMETHOD(bhnd_bus_read_multi_2, bhndb_bus_read_multi_2), 1974173273Ssam DEVMETHOD(bhnd_bus_read_multi_4, bhndb_bus_read_multi_4), 1975173273Ssam DEVMETHOD(bhnd_bus_write_multi_1, bhndb_bus_write_multi_1), 1976178354Ssam DEVMETHOD(bhnd_bus_write_multi_2, bhndb_bus_write_multi_2), 1977178354Ssam DEVMETHOD(bhnd_bus_write_multi_4, bhndb_bus_write_multi_4), 1978173273Ssam 1979170530Ssam DEVMETHOD(bhnd_bus_read_multi_stream_1, bhndb_bus_read_multi_stream_1), 1980178354Ssam DEVMETHOD(bhnd_bus_read_multi_stream_2, bhndb_bus_read_multi_stream_2), 1981170530Ssam DEVMETHOD(bhnd_bus_read_multi_stream_4, bhndb_bus_read_multi_stream_4), 1982178354Ssam DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1), 1983170530Ssam DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2), 1984170530Ssam DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4), 1985170530Ssam 1986170530Ssam DEVMETHOD(bhnd_bus_set_multi_1, bhndb_bus_set_multi_1), 1987170530Ssam DEVMETHOD(bhnd_bus_set_multi_2, bhndb_bus_set_multi_2), 1988173865Ssam DEVMETHOD(bhnd_bus_set_multi_4, bhndb_bus_set_multi_4), 1989173865Ssam DEVMETHOD(bhnd_bus_set_region_1, bhndb_bus_set_region_1), 1990173273Ssam DEVMETHOD(bhnd_bus_set_region_2, bhndb_bus_set_region_2), 1991170530Ssam DEVMETHOD(bhnd_bus_set_region_4, bhndb_bus_set_region_4), 1992170530Ssam 1993170530Ssam DEVMETHOD(bhnd_bus_read_region_1, bhndb_bus_read_region_1), 1994170530Ssam DEVMETHOD(bhnd_bus_read_region_2, bhndb_bus_read_region_2), 1995170530Ssam DEVMETHOD(bhnd_bus_read_region_4, bhndb_bus_read_region_4), 1996170530Ssam DEVMETHOD(bhnd_bus_write_region_1, bhndb_bus_write_region_1), 1997170530Ssam DEVMETHOD(bhnd_bus_write_region_2, bhndb_bus_write_region_2), 1998173273Ssam DEVMETHOD(bhnd_bus_write_region_4, bhndb_bus_write_region_4), 1999173273Ssam 2000173273Ssam DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1), 2001173273Ssam DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2), 2002173273Ssam DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4), 2003173273Ssam DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1), 2004173273Ssam DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2), 2005170530Ssam DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4), 2006170530Ssam 2007170530Ssam DEVMETHOD(bhnd_bus_barrier, bhndb_bus_barrier), 2008170530Ssam 2009170530Ssam DEVMETHOD_END 2010170530Ssam}; 2011170530Ssam 2012170530Ssamdevclass_t bhndb_devclass; 2013170530Ssam 2014170530SsamDEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc)); 2015170530Ssam 2016170530SsamMODULE_VERSION(bhndb, 1); 2017170530SsamMODULE_DEPEND(bhndb, bhnd, 1, 1, 1); 2018170530SsamMODULE_DEPEND(bhndb, bhnd_chipc, 1, 1, 1); 2019170530Ssam