The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/bhnd/bhndb/bhndb.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
    3  * Copyright (c) 2017 The FreeBSD Foundation
    4  * All rights reserved.
    5  *
    6  * Portions of this software were developed by Landon Fuller
    7  * under sponsorship from the FreeBSD Foundation.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer,
   14  *    without modification.
   15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
   16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
   17  *    redistribution must be conditioned upon including a substantially
   18  *    similar Disclaimer requirement for further binary redistribution.
   19  *
   20  * NO WARRANTY
   21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
   24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
   25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
   26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
   29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   31  * THE POSSIBILITY OF SUCH DAMAGES.
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __FBSDID("$FreeBSD$");
   36 
   37 /*
   38  * Abstract BHND Bridge Device Driver
   39  * 
   40  * Provides generic support for bridging from a parent bus (such as PCI) to
   41  * a BHND-compatible bus (e.g. bcma or siba).
   42  */
   43 
   44 #include <sys/param.h>
   45 #include <sys/kernel.h>
   46 #include <sys/bus.h>
   47 #include <sys/module.h>
   48 #include <sys/sbuf.h>
   49 #include <sys/systm.h>
   50 
   51 #include <machine/bus.h>
   52 #include <sys/rman.h>
   53 #include <machine/resource.h>
   54 
   55 #include <dev/bhnd/bhndvar.h>
   56 #include <dev/bhnd/bhndreg.h>
   57 
   58 #include <dev/bhnd/bhnd_erom.h>
   59 
   60 #include <dev/bhnd/cores/chipc/chipcreg.h>
   61 #include <dev/bhnd/nvram/bhnd_nvram.h>
   62 
   63 #include "bhnd_chipc_if.h"
   64 #include "bhnd_nvram_if.h"
   65 
   66 #include "bhndbvar.h"
   67 #include "bhndb_bus_if.h"
   68 #include "bhndb_hwdata.h"
   69 #include "bhndb_private.h"
   70 
   71 /* Debugging flags */
   72 static u_long bhndb_debug = 0;
   73 TUNABLE_ULONG("hw.bhndb.debug", &bhndb_debug);
   74 
   75 enum {
   76         BHNDB_DEBUG_PRIO = 1 << 0,
   77 };
   78 
   79 #define BHNDB_DEBUG(_type)      (BHNDB_DEBUG_ ## _type & bhndb_debug)
   80 
   81 static bool                      bhndb_hw_matches(struct bhndb_softc *sc,
   82                                      struct bhnd_core_info *cores, u_int ncores,
   83                                      const struct bhndb_hw *hw);
   84 
   85 static int                       bhndb_init_region_cfg(struct bhndb_softc *sc,
   86                                      bhnd_erom_t *erom,
   87                                      struct bhndb_resources *r,
   88                                      struct bhnd_core_info *cores, u_int ncores,
   89                                      const struct bhndb_hw_priority *table);
   90 
   91 static int                       bhndb_find_hwspec(struct bhndb_softc *sc,
   92                                      struct bhnd_core_info *cores, u_int ncores,
   93                                      const struct bhndb_hw **hw);
   94 
   95 bhndb_addrspace                  bhndb_get_addrspace(struct bhndb_softc *sc,
   96                                      device_t child);
   97 
   98 static struct rman              *bhndb_get_rman(struct bhndb_softc *sc,
   99                                      device_t child, int type);
  100 
  101 static int                       bhndb_init_child_resource(struct resource *r,
  102                                      struct resource *parent,
  103                                      bhnd_size_t offset,
  104                                      bhnd_size_t size);
  105 
  106 static int                       bhndb_activate_static_region(
  107                                      struct bhndb_softc *sc,
  108                                      struct bhndb_region *region, 
  109                                      device_t child, int type, int rid,
  110                                      struct resource *r);
  111 
  112 static int                       bhndb_try_activate_resource(
  113                                      struct bhndb_softc *sc, device_t child,
  114                                      int type, int rid, struct resource *r,
  115                                      bool *indirect);
  116 
  117 static inline struct bhndb_dw_alloc *bhndb_io_resource(struct bhndb_softc *sc,
  118                                         bus_addr_t addr, bus_size_t size,
  119                                         bus_size_t *offset, bool *stolen,
  120                                         bus_addr_t *restore);
  121 
  122 /**
  123  * Default bhndb(4) implementation of DEVICE_PROBE().
  124  * 
  125  * This function provides the default bhndb implementation of DEVICE_PROBE(),
  126  * and is compatible with bhndb(4) bridges attached via bhndb_attach_bridge().
  127  */
  128 int
  129 bhndb_generic_probe(device_t dev)
  130 {
  131         return (BUS_PROBE_NOWILDCARD);
  132 }
  133 
  134 static void
  135 bhndb_probe_nomatch(device_t dev, device_t child)
  136 {
  137         const char *name;
  138 
  139         name = device_get_name(child);
  140         if (name == NULL)
  141                 name = "unknown device";
  142 
  143         device_printf(dev, "<%s> (no driver attached)\n", name);
  144 }
  145 
  146 static int
  147 bhndb_print_child(device_t dev, device_t child)
  148 {
  149         struct resource_list    *rl;
  150         int                      retval = 0;
  151 
  152         retval += bus_print_child_header(dev, child);
  153 
  154         rl = BUS_GET_RESOURCE_LIST(dev, child);
  155         if (rl != NULL) {
  156                 retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY,
  157                     "%#jx");
  158                 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ,
  159                     "%jd");
  160         }
  161 
  162         retval += bus_print_child_domain(dev, child);
  163         retval += bus_print_child_footer(dev, child);
  164 
  165         return (retval);
  166 }
  167 
  168 static int
  169 bhndb_child_location(device_t dev, device_t child, struct sbuf *sb)
  170 {
  171         struct bhndb_softc *sc;
  172 
  173         sc = device_get_softc(dev);
  174 
  175         sbuf_printf(sb, "base=0x%llx",
  176             (unsigned long long) sc->chipid.enum_addr);
  177         return (0);
  178 }
  179 
  180 /**
  181  * Return true if @p cores matches the @p hw specification.
  182  *
  183  * @param sc BHNDB device state.
  184  * @param cores A device table to match against.
  185  * @param ncores The number of cores in @p cores.
  186  * @param hw The hardware description to be matched against.
  187  */
  188 static bool
  189 bhndb_hw_matches(struct bhndb_softc *sc, struct bhnd_core_info *cores,
  190     u_int ncores, const struct bhndb_hw *hw)
  191 {
  192         for (u_int i = 0; i < hw->num_hw_reqs; i++) {
  193                 const struct bhnd_core_match    *match;
  194                 bool                             found;
  195 
  196                 match =  &hw->hw_reqs[i];
  197                 found = false;
  198 
  199                 for (u_int d = 0; d < ncores; d++) {
  200                         struct bhnd_core_info *core = &cores[d];
  201                         
  202                         if (BHNDB_IS_CORE_DISABLED(sc->dev, sc->bus_dev, core))
  203                                 continue;
  204 
  205                         if (!bhnd_core_matches(core, match))
  206                                 continue;
  207 
  208                         found = true;
  209                         break;
  210                 }
  211 
  212                 if (!found)
  213                         return (false);
  214         }
  215 
  216         return (true);
  217 }
  218 
  219 /**
  220  * Initialize the region maps and priority configuration in @p br using
  221  * the priority @p table and the set of cores enumerated by @p erom.
  222  * 
  223  * @param sc The bhndb device state.
  224  * @param br The resource state to be configured.
  225  * @param erom EROM parser used to enumerate @p cores.
  226  * @param cores All cores enumerated on the bridged bhnd bus.
  227  * @param ncores The length of @p cores.
  228  * @param table Hardware priority table to be used to determine the relative
  229  * priorities of per-core port resources.
  230  */
  231 static int
  232 bhndb_init_region_cfg(struct bhndb_softc *sc, bhnd_erom_t *erom,
  233     struct bhndb_resources *br, struct bhnd_core_info *cores, u_int ncores,
  234     const struct bhndb_hw_priority *table)
  235 {
  236         const struct bhndb_hw_priority  *hp;
  237         bhnd_addr_t                      addr;
  238         bhnd_size_t                      size;
  239         size_t                           prio_low, prio_default, prio_high;
  240         int                              error;
  241 
  242         /* The number of port regions per priority band that must be accessible
  243          * via dynamic register windows */
  244         prio_low = 0;
  245         prio_default = 0;
  246         prio_high = 0;
  247 
  248         /* 
  249          * Register bridge regions covering all statically mapped ports.
  250          */
  251         for (u_int i = 0; i < ncores; i++) {
  252                 const struct bhndb_regwin       *regw;
  253                 struct bhnd_core_info           *core;
  254                 struct bhnd_core_match           md;
  255 
  256                 core = &cores[i];
  257                 md = bhnd_core_get_match_desc(core);
  258 
  259                 for (regw = br->cfg->register_windows;
  260                     regw->win_type != BHNDB_REGWIN_T_INVALID; regw++)
  261                 {
  262                         const struct bhndb_port_priority        *pp;
  263                         uint32_t                                 alloc_flags;
  264 
  265                         /* Only core windows are supported */
  266                         if (regw->win_type != BHNDB_REGWIN_T_CORE)
  267                                 continue;
  268 
  269                         /* Skip non-matching cores. */
  270                         if (!bhndb_regwin_match_core(regw, core))
  271                                 continue;
  272 
  273                         /* Fetch the base address of the mapped port */
  274                         error = bhnd_erom_lookup_core_addr(erom, &md,
  275                             regw->d.core.port_type,
  276                             regw->d.core.port,
  277                             regw->d.core.region,
  278                             NULL,
  279                             &addr,
  280                             &size);
  281                         if (error) {
  282                                 /* Skip non-applicable register windows */
  283                                 if (error == ENOENT)
  284                                         continue;
  285 
  286                                 return (error);
  287                         }
  288 
  289                         /*
  290                          * Apply the register window's region offset, if any.
  291                          */
  292                         if (regw->d.core.offset > size) {
  293                                 device_printf(sc->dev, "invalid register "
  294                                     "window offset %#jx for region %#jx+%#jx\n",
  295                                     regw->d.core.offset, addr, size);
  296                                 return (EINVAL);
  297                         }
  298 
  299                         addr += regw->d.core.offset;
  300 
  301                         /*
  302                          * Always defer to the register window's size.
  303                          * 
  304                          * If the port size is smaller than the window size,
  305                          * this ensures that we fully utilize register windows
  306                          * larger than the referenced port.
  307                          * 
  308                          * If the port size is larger than the window size, this
  309                          * ensures that we do not directly map the allocations
  310                          * within the region to a too-small window.
  311                          */
  312                         size = regw->win_size;
  313 
  314                         /* Fetch allocation flags from the corresponding port
  315                          * priority entry, if any */
  316                         pp = bhndb_hw_priorty_find_port(table, core,
  317                             regw->d.core.port_type, regw->d.core.port,
  318                             regw->d.core.region);
  319                         if (pp != NULL) {
  320                                 alloc_flags = pp->alloc_flags;
  321                         } else {
  322                                 alloc_flags = 0;
  323                         }
  324 
  325                         /*
  326                          * Add to the bus region list.
  327                          * 
  328                          * The window priority for a statically mapped region is
  329                          * always HIGH.
  330                          */
  331                         error = bhndb_add_resource_region(br, addr, size,
  332                             BHNDB_PRIORITY_HIGH, alloc_flags, regw);
  333                         if (error)
  334                                 return (error);
  335                 }
  336         }
  337 
  338         /*
  339          * Perform priority accounting and register bridge regions for all
  340          * ports defined in the priority table
  341          */
  342         for (u_int i = 0; i < ncores; i++) {
  343                 struct bhnd_core_info   *core;
  344                 struct bhnd_core_match   md;
  345 
  346                 core = &cores[i];
  347                 md = bhnd_core_get_match_desc(core);
  348 
  349                 /* 
  350                  * Skip priority accounting for cores that ...
  351                  */
  352                 
  353                 /* ... do not require bridge resources */
  354                 if (BHNDB_IS_CORE_DISABLED(sc->dev, sc->bus_dev, core))
  355                         continue;
  356 
  357                 /* ... do not have a priority table entry */
  358                 hp = bhndb_hw_priority_find_core(table, core);
  359                 if (hp == NULL)
  360                         continue;
  361 
  362                 /* ... are explicitly disabled in the priority table. */
  363                 if (hp->priority == BHNDB_PRIORITY_NONE)
  364                         continue;
  365 
  366                 /* Determine the number of dynamic windows required and
  367                  * register their bus_region entries. */
  368                 for (u_int i = 0; i < hp->num_ports; i++) {
  369                         const struct bhndb_port_priority *pp;
  370 
  371                         pp = &hp->ports[i];
  372 
  373                         /* Fetch the address+size of the mapped port. */
  374                         error = bhnd_erom_lookup_core_addr(erom, &md,
  375                             pp->type, pp->port, pp->region,
  376                             NULL, &addr, &size);
  377                         if (error) {
  378                                 /* Skip ports not defined on this device */
  379                                 if (error == ENOENT)
  380                                         continue;
  381 
  382                                 return (error);
  383                         }
  384 
  385                         /* Skip ports with an existing static mapping */
  386                         if (bhndb_has_static_region_mapping(br, addr, size))
  387                                 continue;
  388 
  389                         /* Define a dynamic region for this port */
  390                         error = bhndb_add_resource_region(br, addr, size,
  391                             pp->priority, pp->alloc_flags, NULL);
  392                         if (error)
  393                                 return (error);
  394 
  395                         /* Update port mapping counts */
  396                         switch (pp->priority) {
  397                         case BHNDB_PRIORITY_NONE:
  398                                 break;
  399                         case BHNDB_PRIORITY_LOW:
  400                                 prio_low++;
  401                                 break;
  402                         case BHNDB_PRIORITY_DEFAULT:
  403                                 prio_default++;
  404                                 break;
  405                         case BHNDB_PRIORITY_HIGH:
  406                                 prio_high++;
  407                                 break;
  408                         }
  409                 }
  410         }
  411 
  412         /* Determine the minimum priority at which we'll allocate direct
  413          * register windows from our dynamic pool */
  414         size_t prio_total = prio_low + prio_default + prio_high;
  415         if (prio_total <= br->dwa_count) {
  416                 /* low+default+high priority regions get windows */
  417                 br->min_prio = BHNDB_PRIORITY_LOW;
  418 
  419         } else if (prio_default + prio_high <= br->dwa_count) {
  420                 /* default+high priority regions get windows */
  421                 br->min_prio = BHNDB_PRIORITY_DEFAULT;
  422 
  423         } else {
  424                 /* high priority regions get windows */
  425                 br->min_prio = BHNDB_PRIORITY_HIGH;
  426         }
  427 
  428         if (BHNDB_DEBUG(PRIO)) {
  429                 struct bhndb_region     *region;
  430                 const char              *direct_msg, *type_msg;
  431                 bhndb_priority_t         prio, prio_min;
  432                 uint32_t                 flags;
  433 
  434                 prio_min = br->min_prio;
  435                 device_printf(sc->dev, "min_prio: %d\n", prio_min);
  436 
  437                 STAILQ_FOREACH(region, &br->bus_regions, link) {
  438                         prio = region->priority;
  439                         flags = region->alloc_flags;
  440 
  441                         direct_msg = prio >= prio_min ? "direct" : "indirect";
  442                         type_msg = region->static_regwin ? "static" : "dynamic";
  443 
  444                         device_printf(sc->dev, "region 0x%llx+0x%llx priority "
  445                             "%u %s/%s",
  446                             (unsigned long long) region->addr, 
  447                             (unsigned long long) region->size,
  448                             region->priority,
  449                             direct_msg, type_msg);
  450 
  451                         if (flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT)
  452                                 printf(" [overcommit]\n");
  453                         else
  454                                 printf("\n");
  455                 }
  456         }
  457 
  458         return (0);
  459 }
  460 
  461 /**
  462  * Find a hardware specification for @p dev.
  463  * 
  464  * @param sc The bhndb device state.
  465  * @param cores All cores enumerated on the bridged bhnd bus.
  466  * @param ncores The length of @p cores.
  467  * @param[out] hw On success, the matched hardware specification.
  468  * with @p dev.
  469  * 
  470  * @retval 0 success
  471  * @retval non-zero if an error occurs fetching device info for comparison.
  472  */
  473 static int
  474 bhndb_find_hwspec(struct bhndb_softc *sc, struct bhnd_core_info *cores,
  475     u_int ncores, const struct bhndb_hw **hw)
  476 {
  477         const struct bhndb_hw   *next, *hw_table;
  478 
  479         /* Search for the first matching hardware config. */
  480         hw_table = BHNDB_BUS_GET_HARDWARE_TABLE(sc->parent_dev, sc->dev);
  481         for (next = hw_table; next->hw_reqs != NULL; next++) {
  482                 if (!bhndb_hw_matches(sc, cores, ncores, next))
  483                         continue;
  484 
  485                 /* Found */
  486                 *hw = next;
  487                 return (0);
  488         }
  489 
  490         return (ENOENT);
  491 }
  492 
  493 /**
  494  * Helper function that must be called by subclass bhndb(4) drivers
  495  * when implementing DEVICE_ATTACH() before calling any bhnd(4) or bhndb(4)
  496  * APIs on the bridge device.
  497  * 
  498  * This function will add a bridged bhnd(4) child device with a device order of
  499  * BHND_PROBE_BUS. Any subclass bhndb(4) driver may use the BHND_PROBE_*
  500  * priority bands to add additional devices that will be attached in
  501  * their preferred order relative to the bridged bhnd(4) bus.
  502  * 
  503  * @param dev           The bridge device to attach.
  504  * @param cid           The bridged device's chip identification.
  505  * @param cores         The bridged device's core table.
  506  * @param ncores        The number of cores in @p cores.
  507  * @param bridge_core   Core info for the bhnd(4) core serving as the host
  508  *                      bridge.
  509  * @param erom_class    An erom parser class that may be used to parse
  510  *                      the bridged device's device enumeration table.
  511  */
  512 int
  513 bhndb_attach(device_t dev, struct bhnd_chipid *cid,
  514     struct bhnd_core_info *cores, u_int ncores,
  515     struct bhnd_core_info *bridge_core, bhnd_erom_class_t *erom_class)
  516 {
  517         struct bhndb_devinfo            *dinfo;
  518         struct bhndb_softc              *sc;
  519         const struct bhndb_hw           *hw;
  520         const struct bhndb_hwcfg        *hwcfg;
  521         const struct bhndb_hw_priority  *hwprio;
  522         struct bhnd_erom_io             *eio;
  523         bhnd_erom_t                     *erom;
  524         int                              error;
  525 
  526         sc = device_get_softc(dev);
  527         sc->dev = dev;
  528         sc->parent_dev = device_get_parent(dev);
  529         sc->bridge_core = *bridge_core;
  530         sc->chipid = *cid;
  531 
  532         if ((error = bhnd_service_registry_init(&sc->services)))
  533                 return (error);
  534 
  535         BHNDB_LOCK_INIT(sc);
  536 
  537         erom = NULL;
  538 
  539         /* Find a matching bridge hardware configuration */
  540         if ((error = bhndb_find_hwspec(sc, cores, ncores, &hw))) {
  541                 device_printf(sc->dev, "unable to identify device, "
  542                     " using generic bridge resource definitions\n");
  543 
  544                 hwcfg = BHNDB_BUS_GET_GENERIC_HWCFG(sc->parent_dev, dev);
  545                 hw = NULL;
  546         } else {
  547                 hwcfg = hw->cfg;
  548         }
  549 
  550         if (hw != NULL && (bootverbose || BHNDB_DEBUG(PRIO))) {
  551                 device_printf(sc->dev, "%s resource configuration\n", hw->name);
  552         }
  553 
  554         /* Allocate bridge resource state using the discovered hardware
  555          * configuration */
  556         sc->bus_res = bhndb_alloc_resources(sc->dev, sc->parent_dev, hwcfg);
  557         if (sc->bus_res == NULL) {
  558                 device_printf(sc->dev, "failed to allocate bridge resource "
  559                     "state\n");
  560                 error = ENOMEM;
  561                 goto failed;
  562         }
  563 
  564         /* Add our bridged bus device */
  565         sc->bus_dev = BUS_ADD_CHILD(dev, BHND_PROBE_BUS, "bhnd", -1);
  566         if (sc->bus_dev == NULL) {
  567                 error = ENXIO;
  568                 goto failed;
  569         }
  570 
  571         dinfo = device_get_ivars(sc->bus_dev);
  572         dinfo->addrspace = BHNDB_ADDRSPACE_BRIDGED;
  573 
  574         /* We can now use bhndb to perform bridging of SYS_RES_MEMORY resources;
  575          * we use this to instantiate an erom parser instance */
  576         eio = bhnd_erom_iores_new(sc->bus_dev, 0);
  577         if ((erom = bhnd_erom_alloc(erom_class, cid, eio)) == NULL) {
  578                 bhnd_erom_io_fini(eio);
  579                 error = ENXIO;
  580                 goto failed;
  581         }
  582 
  583         /* Populate our resource priority configuration */
  584         hwprio = BHNDB_BUS_GET_HARDWARE_PRIO(sc->parent_dev, sc->dev);
  585         error = bhndb_init_region_cfg(sc, erom, sc->bus_res, cores, ncores,
  586             hwprio);
  587         if (error) {
  588                 device_printf(sc->dev, "failed to initialize resource "
  589                     "priority configuration: %d\n", error);
  590                 goto failed;
  591         }
  592 
  593         /* Free our erom instance */
  594         bhnd_erom_free(erom);
  595         erom = NULL;
  596 
  597         return (0);
  598 
  599 failed:
  600         BHNDB_LOCK_DESTROY(sc);
  601 
  602         if (sc->bus_res != NULL)
  603                 bhndb_free_resources(sc->bus_res);
  604 
  605         if (erom != NULL)
  606                 bhnd_erom_free(erom);
  607 
  608         bhnd_service_registry_fini(&sc->services);
  609 
  610         return (error);
  611 }
  612 
  613 /**
  614  * Default bhndb(4) implementation of DEVICE_DETACH().
  615  * 
  616  * This function detaches any child devices, and if successful, releases all
  617  * resources held by the bridge device.
  618  */
  619 int
  620 bhndb_generic_detach(device_t dev)
  621 {
  622         struct bhndb_softc      *sc;
  623         int                      error;
  624 
  625         sc = device_get_softc(dev);
  626 
  627         /* Detach children */
  628         if ((error = bus_generic_detach(dev)))
  629                 return (error);
  630 
  631         /* Delete children */
  632         if ((error = device_delete_children(dev)))
  633                 return (error);
  634 
  635         /* Clean up our service registry */
  636         if ((error = bhnd_service_registry_fini(&sc->services)))
  637                 return (error);
  638 
  639         /* Clean up our driver state. */
  640         bhndb_free_resources(sc->bus_res);
  641 
  642         BHNDB_LOCK_DESTROY(sc);
  643 
  644         return (0);
  645 }
  646 
  647 /**
  648  * Default bhndb(4) implementation of DEVICE_SUSPEND().
  649  * 
  650  * This function calls bus_generic_suspend() (or implements equivalent
  651  * behavior).
  652  */
  653 int
  654 bhndb_generic_suspend(device_t dev)
  655 {
  656         return (bus_generic_suspend(dev));
  657 }
  658 
  659 /**
  660  * Default bhndb(4) implementation of DEVICE_RESUME().
  661  * 
  662  * This function calls bus_generic_resume() (or implements equivalent
  663  * behavior).
  664  */
  665 int
  666 bhndb_generic_resume(device_t dev)
  667 {
  668         struct bhndb_softc      *sc;
  669         struct bhndb_resources  *bus_res;
  670         struct bhndb_dw_alloc   *dwa;
  671         int                      error;
  672 
  673         sc = device_get_softc(dev);
  674         bus_res = sc->bus_res;
  675 
  676         /* Guarantee that all in-use dynamic register windows are mapped to
  677          * their previously configured target address. */
  678         BHNDB_LOCK(sc);
  679         error = 0;
  680         for (size_t i = 0; i < bus_res->dwa_count; i++) {
  681                 dwa = &bus_res->dw_alloc[i];
  682 
  683                 /* Skip regions that were not previously used */
  684                 if (bhndb_dw_is_free(bus_res, dwa) && dwa->target == 0x0)
  685                         continue;
  686 
  687                 /* Otherwise, ensure the register window is correct before
  688                  * any children attempt MMIO */
  689                 error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target);
  690                 if (error)
  691                         break;
  692         }
  693         BHNDB_UNLOCK(sc);
  694 
  695         /* Error restoring hardware state; children cannot be safely resumed */
  696         if (error) {
  697                 device_printf(dev, "Unable to restore hardware configuration; "
  698                     "cannot resume: %d\n", error);
  699                 return (error);
  700         }
  701 
  702         return (bus_generic_resume(dev));
  703 }
  704 
  705 /**
  706  * Default implementation of BHNDB_SUSPEND_RESOURCE.
  707  */
  708 static void
  709 bhndb_suspend_resource(device_t dev, device_t child, int type,
  710     struct resource *r)
  711 {
  712         struct bhndb_softc      *sc;
  713         struct bhndb_dw_alloc   *dwa;
  714 
  715         sc = device_get_softc(dev);
  716 
  717         /* Non-MMIO resources (e.g. IRQs) are handled solely by our parent */
  718         if (type != SYS_RES_MEMORY)
  719                 return;
  720 
  721         BHNDB_LOCK(sc);
  722         dwa = bhndb_dw_find_resource(sc->bus_res, r);
  723         if (dwa == NULL) {
  724                 BHNDB_UNLOCK(sc);
  725                 return;
  726         }
  727 
  728         if (BHNDB_DEBUG(PRIO))
  729                 device_printf(child, "suspend resource type=%d 0x%jx+0x%jx\n",
  730                     type, rman_get_start(r), rman_get_size(r));
  731 
  732         /* Release the resource's window reference */
  733         bhndb_dw_release(sc->bus_res, dwa, r);
  734         BHNDB_UNLOCK(sc);
  735 }
  736 
  737 /**
  738  * Default implementation of BHNDB_RESUME_RESOURCE.
  739  */
  740 static int
  741 bhndb_resume_resource(device_t dev, device_t child, int type,
  742     struct resource *r)
  743 {
  744         struct bhndb_softc      *sc;
  745 
  746         sc = device_get_softc(dev);
  747 
  748         /* Non-MMIO resources (e.g. IRQs) are handled solely by our parent */
  749         if (type != SYS_RES_MEMORY)
  750                 return (0);
  751 
  752         /* Inactive resources don't require reallocation of bridge resources */
  753         if (!(rman_get_flags(r) & RF_ACTIVE))
  754                 return (0);
  755 
  756         if (BHNDB_DEBUG(PRIO))
  757                 device_printf(child, "resume resource type=%d 0x%jx+0x%jx\n",
  758                     type, rman_get_start(r), rman_get_size(r));
  759 
  760         return (bhndb_try_activate_resource(sc, rman_get_device(r), type,
  761             rman_get_rid(r), r, NULL));
  762 }
  763 
  764 /**
  765  * Default bhndb(4) implementation of BUS_READ_IVAR().
  766  */
  767 static int
  768 bhndb_read_ivar(device_t dev, device_t child, int index,
  769     uintptr_t *result)
  770 {
  771         return (ENOENT);
  772 }
  773 
  774 /**
  775  * Default bhndb(4) implementation of BUS_WRITE_IVAR().
  776  */
  777 static int
  778 bhndb_write_ivar(device_t dev, device_t child, int index,
  779     uintptr_t value)
  780 {
  781         return (ENOENT);
  782 }
  783 
  784 /**
  785  * Return the address space for the given @p child device.
  786  */
  787 bhndb_addrspace
  788 bhndb_get_addrspace(struct bhndb_softc *sc, device_t child)
  789 {
  790         struct bhndb_devinfo    *dinfo;
  791         device_t                 imd_dev;
  792 
  793         /* Find the directly attached parent of the requesting device */
  794         imd_dev = child;
  795         while (imd_dev != NULL && device_get_parent(imd_dev) != sc->dev)
  796                 imd_dev = device_get_parent(imd_dev);
  797 
  798         if (imd_dev == NULL)
  799                 panic("bhndb address space request for non-child device %s\n",
  800                      device_get_nameunit(child));
  801 
  802         dinfo = device_get_ivars(imd_dev);
  803         return (dinfo->addrspace);
  804 }
  805 
  806 /**
  807  * Return the rman instance for a given resource @p type, if any.
  808  * 
  809  * @param sc The bhndb device state.
  810  * @param child The requesting child.
  811  * @param type The resource type (e.g. SYS_RES_MEMORY, SYS_RES_IRQ, ...)
  812  */
  813 static struct rman *
  814 bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type)
  815 {       
  816         switch (bhndb_get_addrspace(sc, child)) {
  817         case BHNDB_ADDRSPACE_NATIVE:
  818                 switch (type) {
  819                 case SYS_RES_MEMORY:
  820                         return (&sc->bus_res->ht_mem_rman);
  821                 case SYS_RES_IRQ:
  822                         return (NULL);
  823                 default:
  824                         return (NULL);
  825                 }
  826                 
  827         case BHNDB_ADDRSPACE_BRIDGED:
  828                 switch (type) {
  829                 case SYS_RES_MEMORY:
  830                         return (&sc->bus_res->br_mem_rman);
  831                 case SYS_RES_IRQ:
  832                         return (&sc->bus_res->br_irq_rman);
  833                 default:
  834                         return (NULL);
  835                 }
  836         }
  837 
  838         /* Quieten gcc */
  839         return (NULL);
  840 }
  841 
  842 /**
  843  * Default implementation of BUS_ADD_CHILD()
  844  */
  845 static device_t
  846 bhndb_add_child(device_t dev, u_int order, const char *name, int unit)
  847 {
  848         struct bhndb_devinfo    *dinfo;
  849         device_t                 child;
  850 
  851         child = device_add_child_ordered(dev, order, name, unit);
  852         if (child == NULL)
  853                 return (NULL);
  854 
  855         dinfo = malloc(sizeof(struct bhndb_devinfo), M_BHND, M_NOWAIT);
  856         if (dinfo == NULL) {
  857                 device_delete_child(dev, child);
  858                 return (NULL);
  859         }
  860 
  861         dinfo->addrspace = BHNDB_ADDRSPACE_NATIVE;
  862         resource_list_init(&dinfo->resources);
  863 
  864         device_set_ivars(child, dinfo);
  865 
  866         return (child);
  867 }
  868 
  869 /**
  870  * Default implementation of BUS_CHILD_DELETED().
  871  */
  872 static void
  873 bhndb_child_deleted(device_t dev, device_t child)
  874 {
  875         struct bhndb_devinfo *dinfo = device_get_ivars(child);
  876         if (dinfo != NULL) {
  877                 resource_list_free(&dinfo->resources);
  878                 free(dinfo, M_BHND);
  879         }
  880 
  881         device_set_ivars(child, NULL);
  882 }
  883 
  884 /**
  885  * Default implementation of BHNDB_GET_CHIPID().
  886  */
  887 static const struct bhnd_chipid *
  888 bhndb_get_chipid(device_t dev, device_t child)
  889 {
  890         struct bhndb_softc *sc = device_get_softc(dev);
  891         return (&sc->chipid);
  892 }
  893 
  894 /**
  895  * Default implementation of BHNDB_IS_CORE_DISABLED().
  896  */
  897 static bool
  898 bhndb_is_core_disabled(device_t dev, device_t child,
  899     struct bhnd_core_info *core)
  900 {
  901         struct bhndb_softc      *sc;
  902 
  903         sc = device_get_softc(dev);
  904 
  905         /* Try to defer to the bhndb bus parent */
  906         if (BHNDB_BUS_IS_CORE_DISABLED(sc->parent_dev, dev, core))
  907                 return (true);
  908 
  909         /* Otherwise, we treat bridge-capable cores as unpopulated if they're
  910          * not the configured host bridge */
  911         if (BHND_DEVCLASS_SUPPORTS_HOSTB(bhnd_core_class(core)))
  912                 return (!bhnd_cores_equal(core, &sc->bridge_core));
  913 
  914         /* Assume the core is populated */
  915         return (false);
  916 }
  917 
  918 /**
  919  * Default bhndb(4) implementation of BHNDB_GET_HOSTB_CORE().
  920  * 
  921  * This function uses a heuristic valid on all known PCI/PCIe/PCMCIA-bridged
  922  * bhnd(4) devices.
  923  */
  924 static int
  925 bhndb_get_hostb_core(device_t dev, device_t child, struct bhnd_core_info *core)
  926 {
  927         struct bhndb_softc *sc = device_get_softc(dev);
  928 
  929         *core = sc->bridge_core;
  930         return (0);
  931 }
  932 
  933 /**
  934  * Default bhndb(4) implementation of BHND_BUS_GET_SERVICE_REGISTRY().
  935  */
  936 static struct bhnd_service_registry *
  937 bhndb_get_service_registry(device_t dev, device_t child)
  938 {
  939         struct bhndb_softc *sc = device_get_softc(dev);
  940 
  941         return (&sc->services);
  942 }
  943 
  944 /**
  945  * Default bhndb(4) implementation of BUS_ALLOC_RESOURCE().
  946  */
  947 static struct resource *
  948 bhndb_alloc_resource(device_t dev, device_t child, int type,
  949     int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
  950 {
  951         struct bhndb_softc              *sc;
  952         struct resource_list_entry      *rle;
  953         struct resource                 *rv;
  954         struct rman                     *rm;
  955         int                              error;
  956         bool                             passthrough, isdefault;
  957 
  958         sc = device_get_softc(dev);
  959         passthrough = (device_get_parent(child) != dev);
  960         isdefault = RMAN_IS_DEFAULT_RANGE(start, end);
  961         rle = NULL;
  962 
  963         /* Fetch the resource manager */
  964         rm = bhndb_get_rman(sc, child, type);
  965         if (rm == NULL) {
  966                 /* Delegate to our parent device's bus; the requested
  967                  * resource type isn't handled locally. */
  968                 return (BUS_ALLOC_RESOURCE(device_get_parent(sc->parent_dev),
  969                     child, type, rid,  start, end, count, flags));
  970         }
  971 
  972         /* Populate defaults */
  973         if (!passthrough && isdefault) {
  974                 /* Fetch the resource list entry. */
  975                 rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
  976                     type, *rid);
  977                 if (rle == NULL) {
  978                         device_printf(dev,
  979                             "default resource %#x type %d for child %s "
  980                             "not found\n", *rid, type,
  981                             device_get_nameunit(child));
  982                         
  983                         return (NULL);
  984                 }
  985                 
  986                 if (rle->res != NULL) {
  987                         device_printf(dev,
  988                             "resource entry %#x type %d for child %s is busy\n",
  989                             *rid, type, device_get_nameunit(child));
  990                         
  991                         return (NULL);
  992                 }
  993 
  994                 start = rle->start;
  995                 end = rle->end;
  996                 count = ulmax(count, rle->count);
  997         }
  998 
  999         /* Validate resource addresses */
 1000         if (start > end || count > ((end - start) + 1))
 1001                 return (NULL);
 1002 
 1003         /* Make our reservation */
 1004         rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE,
 1005             child);
 1006         if (rv == NULL)
 1007                 return (NULL);
 1008 
 1009         rman_set_rid(rv, *rid);
 1010 
 1011         /* Activate */
 1012         if (flags & RF_ACTIVE) {
 1013                 error = bus_activate_resource(child, type, *rid, rv);
 1014                 if (error) {
 1015                         device_printf(dev,
 1016                             "failed to activate entry %#x type %d for "
 1017                                 "child %s: %d\n",
 1018                              *rid, type, device_get_nameunit(child), error);
 1019 
 1020                         rman_release_resource(rv);
 1021 
 1022                         return (NULL);
 1023                 }
 1024         }
 1025 
 1026         /* Update child's resource list entry */
 1027         if (rle != NULL) {
 1028                 rle->res = rv;
 1029                 rle->start = rman_get_start(rv);
 1030                 rle->end = rman_get_end(rv);
 1031                 rle->count = rman_get_size(rv);
 1032         }
 1033 
 1034         return (rv);
 1035 }
 1036 
 1037 /**
 1038  * Default bhndb(4) implementation of BUS_RELEASE_RESOURCE().
 1039  */
 1040 static int
 1041 bhndb_release_resource(device_t dev, device_t child, int type, int rid,
 1042     struct resource *r)
 1043 {
 1044         struct bhndb_softc              *sc;
 1045         struct resource_list_entry      *rle;
 1046         bool                             passthrough;
 1047         int                              error;
 1048 
 1049         sc = device_get_softc(dev);
 1050         passthrough = (device_get_parent(child) != dev);
 1051 
 1052         /* Delegate to our parent device's bus if the requested resource type
 1053          * isn't handled locally. */
 1054         if (bhndb_get_rman(sc, child, type) == NULL) {
 1055                 return (BUS_RELEASE_RESOURCE(device_get_parent(sc->parent_dev),
 1056                     child, type, rid, r));
 1057         }
 1058 
 1059         /* Deactivate resources */
 1060         if (rman_get_flags(r) & RF_ACTIVE) {
 1061                 error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r);
 1062                 if (error)
 1063                         return (error);
 1064         }
 1065 
 1066         if ((error = rman_release_resource(r)))
 1067                 return (error);
 1068 
 1069         if (!passthrough) {
 1070                 /* Clean resource list entry */
 1071                 rle = resource_list_find(BUS_GET_RESOURCE_LIST(dev, child),
 1072                     type, rid);
 1073                 if (rle != NULL)
 1074                         rle->res = NULL;
 1075         }
 1076 
 1077         return (0);
 1078 }
 1079 
 1080 /**
 1081  * Default bhndb(4) implementation of BUS_ADJUST_RESOURCE().
 1082  */
 1083 static int
 1084 bhndb_adjust_resource(device_t dev, device_t child, int type,
 1085     struct resource *r, rman_res_t start, rman_res_t end)
 1086 {
 1087         struct bhndb_softc              *sc;
 1088         struct rman                     *rm;
 1089         rman_res_t                       mstart, mend;
 1090         int                              error;
 1091 
 1092         sc = device_get_softc(dev);
 1093         error = 0;
 1094 
 1095         /* Delegate to our parent device's bus if the requested resource type
 1096          * isn't handled locally. */
 1097         rm = bhndb_get_rman(sc, child, type);
 1098         if (rm == NULL) {
 1099                 return (BUS_ADJUST_RESOURCE(device_get_parent(sc->parent_dev),
 1100                     child, type, r, start, end));
 1101         }
 1102 
 1103         /* Verify basic constraints */
 1104         if (end <= start)
 1105                 return (EINVAL);
 1106 
 1107         if (!rman_is_region_manager(r, rm))
 1108                 return (ENXIO);
 1109 
 1110         BHNDB_LOCK(sc);
 1111 
 1112         /* If not active, allow any range permitted by the resource manager */
 1113         if (!(rman_get_flags(r) & RF_ACTIVE))
 1114                 goto done;
 1115 
 1116         /* Otherwise, the range is limited by the bridged resource mapping */
 1117         error = bhndb_find_resource_limits(sc->bus_res, type, r, &mstart,
 1118             &mend);
 1119         if (error)
 1120                 goto done;
 1121 
 1122         if (start < mstart || end > mend) {
 1123                 error = EINVAL;
 1124                 goto done;
 1125         }
 1126 
 1127         /* Fall through */
 1128 done:
 1129         if (!error)
 1130                 error = rman_adjust_resource(r, start, end);
 1131 
 1132         BHNDB_UNLOCK(sc);
 1133         return (error);
 1134 }
 1135 
 1136 /**
 1137  * Initialize child resource @p r with a virtual address, tag, and handle
 1138  * copied from @p parent, adjusted to contain only the range defined by
 1139  * @p offsize and @p size.
 1140  * 
 1141  * @param r The register to be initialized.
 1142  * @param parent The parent bus resource that fully contains the subregion.
 1143  * @param offset The subregion offset within @p parent.
 1144  * @param size The subregion size.
 1145  * @p r.
 1146  */
 1147 static int
 1148 bhndb_init_child_resource(struct resource *r,
 1149     struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
 1150 {
 1151         bus_space_handle_t      bh, child_bh;
 1152         bus_space_tag_t         bt;
 1153         uintptr_t               vaddr;
 1154         int                     error;
 1155 
 1156         /* Fetch the parent resource's real bus values */
 1157         vaddr = (uintptr_t) rman_get_virtual(parent);
 1158         bt = rman_get_bustag(parent);
 1159         bh = rman_get_bushandle(parent);
 1160 
 1161         /* Configure child resource with window-adjusted real bus values */
 1162         vaddr += offset;
 1163         error = bus_space_subregion(bt, bh, offset, size, &child_bh);
 1164         if (error)
 1165                 return (error);
 1166 
 1167         rman_set_virtual(r, (void *) vaddr);
 1168         rman_set_bustag(r, bt);
 1169         rman_set_bushandle(r, child_bh);
 1170 
 1171         return (0);
 1172 }
 1173 
 1174 /**
 1175  * Attempt activation of a fixed register window mapping for @p child.
 1176  * 
 1177  * @param sc BHNDB device state.
 1178  * @param region The static region definition capable of mapping @p r.
 1179  * @param child A child requesting resource activation.
 1180  * @param type Resource type.
 1181  * @param rid Resource identifier.
 1182  * @param r Resource to be activated.
 1183  * 
 1184  * @retval 0 if @p r was activated successfully
 1185  * @retval ENOENT if no fixed register window was found.
 1186  * @retval non-zero if @p r could not be activated.
 1187  */
 1188 static int
 1189 bhndb_activate_static_region(struct bhndb_softc *sc,
 1190     struct bhndb_region *region, device_t child, int type, int rid,
 1191     struct resource *r)
 1192 {
 1193         struct resource                 *bridge_res;
 1194         const struct bhndb_regwin       *win;
 1195         bhnd_size_t                      parent_offset;
 1196         rman_res_t                       r_start, r_size;
 1197         int                              error;
 1198 
 1199         win = region->static_regwin;
 1200 
 1201         KASSERT(win != NULL && BHNDB_REGWIN_T_IS_STATIC(win->win_type),
 1202             ("can't activate non-static region"));
 1203 
 1204         r_start = rman_get_start(r);
 1205         r_size = rman_get_size(r);
 1206 
 1207         /* Find the corresponding bridge resource */
 1208         bridge_res = bhndb_host_resource_for_regwin(sc->bus_res->res, win);
 1209         if (bridge_res == NULL)
 1210                 return (ENXIO);
 1211 
 1212         /* Calculate subregion offset within the parent resource */
 1213         parent_offset = r_start - region->addr;
 1214         parent_offset += win->win_offset;
 1215 
 1216         /* Configure resource with its real bus values. */
 1217         error = bhndb_init_child_resource(r, bridge_res, parent_offset, r_size);
 1218         if (error)
 1219                 return (error);
 1220 
 1221         /* Mark active */
 1222         if ((error = rman_activate_resource(r)))
 1223                 return (error);
 1224 
 1225         return (0);
 1226 }
 1227 
 1228 /**
 1229  * Attempt to allocate/retain a dynamic register window for @p r, returning
 1230  * the retained window.
 1231  * 
 1232  * @param sc The bhndb driver state.
 1233  * @param r The resource for which a window will be retained.
 1234  */
 1235 static struct bhndb_dw_alloc *
 1236 bhndb_retain_dynamic_window(struct bhndb_softc *sc, struct resource *r)
 1237 {
 1238         struct bhndb_dw_alloc   *dwa;
 1239         rman_res_t               r_start, r_size;
 1240         int                      error;
 1241 
 1242         BHNDB_LOCK_ASSERT(sc, MA_OWNED);
 1243 
 1244         r_start = rman_get_start(r);
 1245         r_size = rman_get_size(r);
 1246 
 1247         /* Look for an existing dynamic window we can reference */
 1248         dwa = bhndb_dw_find_mapping(sc->bus_res, r_start, r_size);
 1249         if (dwa != NULL) {
 1250                 if (bhndb_dw_retain(sc->bus_res, dwa, r) == 0)
 1251                         return (dwa);
 1252 
 1253                 return (NULL);
 1254         }
 1255 
 1256         /* Otherwise, try to reserve a free window */
 1257         dwa = bhndb_dw_next_free(sc->bus_res);
 1258         if (dwa == NULL) {
 1259                 /* No free windows */
 1260                 return (NULL);
 1261         }
 1262 
 1263         /* Window must be large enough to map the entire resource */
 1264         if (dwa->win->win_size < rman_get_size(r))
 1265                 return (NULL);
 1266 
 1267         /* Set the window target */
 1268         error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, rman_get_start(r),
 1269             rman_get_size(r));
 1270         if (error) {
 1271                 device_printf(sc->dev, "dynamic window initialization "
 1272                         "for 0x%llx-0x%llx failed: %d\n",
 1273                         (unsigned long long) r_start,
 1274                         (unsigned long long) r_start + r_size - 1,
 1275                         error);
 1276                 return (NULL);
 1277         }
 1278 
 1279         /* Add our reservation */
 1280         if (bhndb_dw_retain(sc->bus_res, dwa, r))
 1281                 return (NULL);
 1282 
 1283         return (dwa);
 1284 }
 1285 
 1286 /**
 1287  * Activate a resource using any viable static or dynamic register window.
 1288  * 
 1289  * @param sc The bhndb driver state.
 1290  * @param child The child holding ownership of @p r.
 1291  * @param type The type of the resource to be activated.
 1292  * @param rid The resource ID of @p r.
 1293  * @param r The resource to be activated
 1294  * @param[out] indirect On error and if not NULL, will be set to 'true' if
 1295  * the caller should instead use an indirect resource mapping.
 1296  * 
 1297  * @retval 0 success
 1298  * @retval non-zero activation failed.
 1299  */
 1300 static int
 1301 bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type,
 1302     int rid, struct resource *r, bool *indirect)
 1303 {
 1304         struct bhndb_region     *region;
 1305         struct bhndb_dw_alloc   *dwa;
 1306         bhndb_priority_t         dw_priority;
 1307         rman_res_t               r_start, r_size;
 1308         rman_res_t               parent_offset;
 1309         int                      error;
 1310 
 1311         BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED);
 1312 
 1313         if (indirect != NULL)
 1314                 *indirect = false;
 1315 
 1316         switch (type) {
 1317         case SYS_RES_IRQ:
 1318                 /* IRQ resources are always directly mapped */
 1319                 return (rman_activate_resource(r));
 1320 
 1321         case SYS_RES_MEMORY:
 1322                 /* Handled below */
 1323                 break;
 1324 
 1325         default:
 1326                 device_printf(sc->dev, "unsupported resource type %d\n", type);
 1327                 return (ENXIO);
 1328         }
 1329 
 1330         /* Only MMIO resources can be mapped via register windows */
 1331         KASSERT(type == SYS_RES_MEMORY, ("invalid type: %d", type));
 1332 
 1333         r_start = rman_get_start(r);
 1334         r_size = rman_get_size(r);
 1335 
 1336         /* Activate native addrspace resources using the host address space */
 1337         if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_NATIVE) {
 1338                 struct resource *parent;
 1339 
 1340                 /* Find the bridge resource referenced by the child */
 1341                 parent = bhndb_host_resource_for_range(sc->bus_res->res,
 1342                     type, r_start, r_size);
 1343                 if (parent == NULL) {
 1344                         device_printf(sc->dev, "host resource not found "
 1345                              "for 0x%llx-0x%llx\n",
 1346                              (unsigned long long) r_start,
 1347                              (unsigned long long) r_start + r_size - 1);
 1348                         return (ENOENT);
 1349                 }
 1350 
 1351                 /* Initialize child resource with the real bus values */
 1352                 error = bhndb_init_child_resource(r, parent,
 1353                     r_start - rman_get_start(parent), r_size);
 1354                 if (error)
 1355                         return (error);
 1356 
 1357                 /* Try to activate child resource */
 1358                 return (rman_activate_resource(r));
 1359         }
 1360 
 1361         /* Default to low priority */
 1362         dw_priority = BHNDB_PRIORITY_LOW;
 1363 
 1364         /* Look for a bus region matching the resource's address range */
 1365         region = bhndb_find_resource_region(sc->bus_res, r_start, r_size);
 1366         if (region != NULL)
 1367                 dw_priority = region->priority;
 1368 
 1369         /* Prefer static mappings over consuming a dynamic windows. */
 1370         if (region && region->static_regwin) {
 1371                 error = bhndb_activate_static_region(sc, region, child, type,
 1372                     rid, r);
 1373                 if (error)
 1374                         device_printf(sc->dev, "static window allocation "
 1375                              "for 0x%llx-0x%llx failed\n",
 1376                              (unsigned long long) r_start,
 1377                              (unsigned long long) r_start + r_size - 1);
 1378                 return (error);
 1379         }
 1380 
 1381         /* A dynamic window will be required; is this resource high enough
 1382          * priority to be reserved a dynamic window? */
 1383         if (dw_priority < sc->bus_res->min_prio) {
 1384                 if (indirect)
 1385                         *indirect = true;
 1386 
 1387                 return (ENOMEM);
 1388         }
 1389 
 1390         /* Find and retain a usable window */
 1391         BHNDB_LOCK(sc); {
 1392                 dwa = bhndb_retain_dynamic_window(sc, r);
 1393         } BHNDB_UNLOCK(sc);
 1394 
 1395         if (dwa == NULL) {
 1396                 if (indirect)
 1397                         *indirect = true;
 1398                 return (ENOMEM);
 1399         }
 1400 
 1401         /* Configure resource with its real bus values. */
 1402         parent_offset = dwa->win->win_offset;
 1403         parent_offset += r_start - dwa->target;
 1404 
 1405         error = bhndb_init_child_resource(r, dwa->parent_res, parent_offset,
 1406             dwa->win->win_size);
 1407         if (error)
 1408                 goto failed;
 1409 
 1410         /* Mark active */
 1411         if ((error = rman_activate_resource(r)))
 1412                 goto failed;
 1413 
 1414         return (0);
 1415 
 1416 failed:
 1417         /* Release our region allocation. */
 1418         BHNDB_LOCK(sc);
 1419         bhndb_dw_release(sc->bus_res, dwa, r);
 1420         BHNDB_UNLOCK(sc);
 1421 
 1422         return (error);
 1423 }
 1424 
 1425 /**
 1426  * Default bhndb(4) implementation of BUS_ACTIVATE_RESOURCE().
 1427  */
 1428 static int
 1429 bhndb_activate_resource(device_t dev, device_t child, int type, int rid,
 1430     struct resource *r)
 1431 {
 1432         struct bhndb_softc *sc = device_get_softc(dev);
 1433 
 1434         /* Delegate directly to our parent device's bus if the requested
 1435          * resource type isn't handled locally. */
 1436         if (bhndb_get_rman(sc, child, type) == NULL) {
 1437                 return (BUS_ACTIVATE_RESOURCE(device_get_parent(sc->parent_dev),
 1438                     child, type, rid, r));
 1439         }
 1440 
 1441         return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL));
 1442 }
 1443 
 1444 /**
 1445  * Default bhndb(4) implementation of BUS_DEACTIVATE_RESOURCE().
 1446  */
 1447 static int
 1448 bhndb_deactivate_resource(device_t dev, device_t child, int type,
 1449     int rid, struct resource *r)
 1450 {
 1451         struct bhndb_dw_alloc   *dwa;
 1452         struct bhndb_softc      *sc;
 1453         struct rman             *rm;
 1454         int                      error;
 1455 
 1456         sc = device_get_softc(dev);
 1457 
 1458         /* Delegate directly to our parent device's bus if the requested
 1459          * resource type isn't handled locally. */
 1460         rm = bhndb_get_rman(sc, child, type);
 1461         if (rm == NULL) {
 1462                 return (BUS_DEACTIVATE_RESOURCE(
 1463                     device_get_parent(sc->parent_dev), child, type, rid, r));
 1464         }
 1465 
 1466         /* Mark inactive */
 1467         if ((error = rman_deactivate_resource(r)))
 1468                 return (error);
 1469 
 1470         switch (type) {
 1471         case SYS_RES_IRQ:
 1472                 /* No bridge-level state to be freed */
 1473                 return (0);
 1474 
 1475         case SYS_RES_MEMORY:
 1476                 /* Free any dynamic window allocation. */
 1477                 if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
 1478                         BHNDB_LOCK(sc);
 1479                         dwa = bhndb_dw_find_resource(sc->bus_res, r);
 1480                         if (dwa != NULL)
 1481                                 bhndb_dw_release(sc->bus_res, dwa, r);
 1482                         BHNDB_UNLOCK(sc);
 1483                 }
 1484 
 1485                 return (0);
 1486 
 1487         default:
 1488                 device_printf(dev, "unsupported resource type %d\n", type);
 1489                 return (ENXIO);
 1490         }
 1491 }
 1492 
 1493 /**
 1494  * Default bhndb(4) implementation of BUS_GET_RESOURCE_LIST().
 1495  */
 1496 static struct resource_list *
 1497 bhndb_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 are activated as direct
 1507  * resources via BUS_ACTIVATE_RESOURCE().
 1508  * 
 1509  * For BHNDB_ADDRSPACE_BRIDGED children, the resource priority is determined,
 1510  * and if possible, the resource is activated as a direct resource. For example,
 1511  * depending on resource priority and bridge resource availability, this
 1512  * function will attempt to activate SYS_RES_MEMORY resources using either a
 1513  * static register window, a dynamic register window, or it will configure @p r
 1514  * as an indirect resource -- in that order.
 1515  */
 1516 static int
 1517 bhndb_activate_bhnd_resource(device_t dev, device_t child,
 1518     int type, int rid, struct bhnd_resource *r)
 1519 {
 1520         struct bhndb_softc      *sc;
 1521         struct bhndb_region     *region;
 1522         bhndb_priority_t         r_prio;
 1523         rman_res_t               r_start, r_size;
 1524         int                      error;
 1525         bool                     indirect;
 1526 
 1527         KASSERT(!r->direct,
 1528             ("direct flag set on inactive resource"));
 1529 
 1530         KASSERT(!(rman_get_flags(r->res) & RF_ACTIVE),
 1531             ("RF_ACTIVE set on inactive resource"));
 1532 
 1533         sc = device_get_softc(dev);
 1534 
 1535         /* Delegate directly to BUS_ACTIVATE_RESOURCE() if the requested
 1536          * resource type isn't handled locally. */
 1537         if (bhndb_get_rman(sc, child, type) == NULL) {
 1538                 error = BUS_ACTIVATE_RESOURCE(dev, child, type, rid, r->res);
 1539                 if (error == 0)
 1540                         r->direct = true;
 1541                 return (error);
 1542         }
 1543 
 1544         r_start = rman_get_start(r->res);
 1545         r_size = rman_get_size(r->res);
 1546 
 1547         /* Determine the resource priority of bridged resources, and skip direct
 1548          * allocation if the priority is too low. */
 1549         if (bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED) {
 1550                 switch (type) {
 1551                 case SYS_RES_IRQ:
 1552                         /* IRQ resources are always direct */
 1553                         break;
 1554 
 1555                 case SYS_RES_MEMORY:
 1556                         region = bhndb_find_resource_region(sc->bus_res,
 1557                                 r_start, r_size);
 1558                         if (region != NULL)
 1559                                 r_prio = region->priority;
 1560                         else
 1561                                 r_prio = BHNDB_PRIORITY_NONE;
 1562 
 1563                         /* If less than the minimum dynamic window priority,
 1564                          * this resource should always be indirect. */
 1565                         if (r_prio < sc->bus_res->min_prio)
 1566                                 return (0);
 1567 
 1568                         break;
 1569 
 1570                 default:
 1571                         device_printf(dev, "unsupported resource type %d\n",
 1572                             type);
 1573                         return (ENXIO);
 1574                 }
 1575         }
 1576 
 1577         /* Attempt direct activation */
 1578         error = bhndb_try_activate_resource(sc, child, type, rid, r->res,
 1579             &indirect);
 1580         if (!error) {
 1581                 r->direct = true;
 1582         } else if (indirect) {
 1583                 /* The request was valid, but no viable register window is
 1584                  * available; indirection must be employed. */
 1585                 error = 0;
 1586                 r->direct = false;
 1587         }
 1588 
 1589         if (BHNDB_DEBUG(PRIO) &&
 1590             bhndb_get_addrspace(sc, child) == BHNDB_ADDRSPACE_BRIDGED)
 1591         {
 1592                 device_printf(child, "activated 0x%llx-0x%llx as %s "
 1593                     "resource\n",
 1594                     (unsigned long long) r_start, 
 1595                     (unsigned long long) r_start + r_size - 1,
 1596                     r->direct ? "direct" : "indirect");
 1597         }
 1598 
 1599         return (error);
 1600 }
 1601 
 1602 /**
 1603  * Default bhndb(4) implementation of BHND_BUS_DEACTIVATE_RESOURCE().
 1604  */
 1605 static int
 1606 bhndb_deactivate_bhnd_resource(device_t dev, device_t child,
 1607     int type, int rid, struct bhnd_resource *r)
 1608 {
 1609         int error;
 1610 
 1611         /* Indirect resources don't require activation */
 1612         if (!r->direct)
 1613                 return (0);
 1614 
 1615         KASSERT(rman_get_flags(r->res) & RF_ACTIVE,
 1616             ("RF_ACTIVE not set on direct resource"));
 1617 
 1618         /* Perform deactivation */
 1619         error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r->res);
 1620         if (!error)
 1621                 r->direct = false;
 1622 
 1623         return (error);
 1624 }
 1625 
 1626 /**
 1627  * Find the best available bridge resource allocation record capable of handling
 1628  * bus I/O requests of @p size at @p addr.
 1629  * 
 1630  * In order of preference, this function will either:
 1631  * 
 1632  * - Configure and return a free allocation record
 1633  * - Return an existing allocation record mapping the requested space, or
 1634  * - Steal, configure, and return an in-use allocation record.
 1635  * 
 1636  * Will panic if a usable record cannot be found.
 1637  * 
 1638  * @param sc Bridge driver state.
 1639  * @param addr The I/O target address.
 1640  * @param size The size of the I/O operation to be performed at @p addr. 
 1641  * @param[out] borrowed Set to true if the allocation record was borrowed to
 1642  * fulfill this request; the borrowed record maps the target address range,
 1643  * and must not be modified.
 1644  * @param[out] stolen Set to true if the allocation record was stolen to fulfill
 1645  * this request. If a stolen allocation record is returned,
 1646  * bhndb_io_resource_restore() must be called upon completion of the bus I/O
 1647  * request.
 1648  * @param[out] restore If the allocation record was stolen, this will be set
 1649  * to the target that must be restored.
 1650  */
 1651 static struct bhndb_dw_alloc *
 1652 bhndb_io_resource_get_window(struct bhndb_softc *sc, bus_addr_t addr,
 1653     bus_size_t size, bool *borrowed, bool *stolen, bus_addr_t *restore)
 1654 {
 1655         struct bhndb_resources  *br;
 1656         struct bhndb_dw_alloc   *dwa;
 1657         struct bhndb_region     *region;
 1658 
 1659         BHNDB_LOCK_ASSERT(sc, MA_OWNED);
 1660 
 1661         br = sc->bus_res;
 1662         *borrowed = false;
 1663         *stolen = false;
 1664 
 1665         /* Try to fetch a free window */
 1666         if ((dwa = bhndb_dw_next_free(br)) != NULL)
 1667                 return (dwa);
 1668 
 1669         /* Search for an existing dynamic mapping of this address range.
 1670          * Static regions are not searched, as a statically mapped
 1671          * region would never be allocated as an indirect resource. */
 1672         for (size_t i = 0; i < br->dwa_count; i++) {
 1673                 const struct bhndb_regwin *win;
 1674 
 1675                 dwa = &br->dw_alloc[i];
 1676                 win = dwa->win;
 1677 
 1678                 KASSERT(win->win_type == BHNDB_REGWIN_T_DYN,
 1679                         ("invalid register window type"));
 1680 
 1681                 /* Verify the range */
 1682                 if (addr < dwa->target)
 1683                         continue;
 1684 
 1685                 if (addr + size > dwa->target + win->win_size)
 1686                         continue;
 1687 
 1688                 /* Found */
 1689                 *borrowed = true;
 1690                 return (dwa);
 1691         }
 1692 
 1693         /* Try to steal a window; this should only be required on very early
 1694          * PCI_V0 (BCM4318, etc) Wi-Fi chipsets */
 1695         region = bhndb_find_resource_region(br, addr, size);
 1696         if (region == NULL)
 1697                 return (NULL);
 1698 
 1699         if ((region->alloc_flags & BHNDB_ALLOC_FULFILL_ON_OVERCOMMIT) == 0)
 1700                 return (NULL);
 1701 
 1702         /* Steal a window. This acquires our backing spinlock, disabling
 1703          * interrupts; the spinlock will be released by
 1704          * bhndb_dw_return_stolen() */
 1705         if ((dwa = bhndb_dw_steal(br, restore)) != NULL) {
 1706                 *stolen = true;
 1707                 return (dwa);
 1708         }
 1709 
 1710         panic("register windows exhausted attempting to map 0x%llx-0x%llx\n", 
 1711             (unsigned long long) addr, (unsigned long long) addr+size-1);
 1712 }
 1713 
 1714 /**
 1715  * Return a borrowed reference to a bridge resource allocation record capable
 1716  * of handling bus I/O requests of @p size at @p addr.
 1717  * 
 1718  * This will either return a reference to an existing allocation record mapping
 1719  * the requested space, or will configure and return a free allocation record.
 1720  * 
 1721  * Will panic if a usable record cannot be found.
 1722  * 
 1723  * @param sc Bridge driver state.
 1724  * @param addr The I/O target address.
 1725  * @param size The size of the I/O operation to be performed at @p addr. 
 1726  * @param[out] offset The offset within the returned resource at which
 1727  * to perform the I/O request.
 1728  * @param[out] stolen Set to true if the allocation record was stolen to fulfill
 1729  * this request. If a stolen allocation record is returned,
 1730  * bhndb_io_resource_restore() must be called upon completion of the bus I/O
 1731  * request.
 1732  * @param[out] restore If the allocation record was stolen, this will be set
 1733  * to the target that must be restored.
 1734  */
 1735 static inline struct bhndb_dw_alloc *
 1736 bhndb_io_resource(struct bhndb_softc *sc, bus_addr_t addr, bus_size_t size,
 1737     bus_size_t *offset, bool *stolen, bus_addr_t *restore)
 1738 {
 1739         struct bhndb_dw_alloc   *dwa;
 1740         bool                     borrowed;
 1741         int                      error;
 1742 
 1743         BHNDB_LOCK_ASSERT(sc, MA_OWNED);
 1744 
 1745         dwa = bhndb_io_resource_get_window(sc, addr, size, &borrowed, stolen,
 1746             restore);
 1747 
 1748         /* Adjust the window if the I/O request won't fit in the current
 1749          * target range. */
 1750         if (addr < dwa->target ||
 1751             addr > dwa->target + dwa->win->win_size ||
 1752             (dwa->target + dwa->win->win_size) - addr < size)
 1753         {
 1754                 /* Cannot modify target of borrowed windows */
 1755                 if (borrowed) {
 1756                         panic("borrowed register window does not map expected "
 1757                             "range 0x%llx-0x%llx\n", 
 1758                             (unsigned long long) addr,
 1759                             (unsigned long long) addr+size-1);
 1760                 }
 1761 
 1762                 error = bhndb_dw_set_addr(sc->dev, sc->bus_res, dwa, addr,
 1763                     size);
 1764                 if (error) {
 1765                     panic("failed to set register window target mapping "
 1766                             "0x%llx-0x%llx\n", 
 1767                             (unsigned long long) addr,
 1768                             (unsigned long long) addr+size-1);
 1769                 }
 1770         }
 1771 
 1772         /* Calculate the offset and return */
 1773         *offset = (addr - dwa->target) + dwa->win->win_offset;
 1774         return (dwa);
 1775 }
 1776 
 1777 /*
 1778  * BHND_BUS_(READ|WRITE_* implementations
 1779  */
 1780 
 1781 /* bhndb_bus_(read|write) common implementation */
 1782 #define BHNDB_IO_COMMON_SETUP(_io_size)                         \
 1783         struct bhndb_softc      *sc;                            \
 1784         struct bhndb_dw_alloc   *dwa;                           \
 1785         struct resource         *io_res;                        \
 1786         bus_size_t               io_offset;                     \
 1787         bus_addr_t               restore;               \
 1788         bool                     stolen;                        \
 1789                                                                 \
 1790         sc = device_get_softc(dev);                             \
 1791                                                                 \
 1792         BHNDB_LOCK(sc);                                         \
 1793         dwa = bhndb_io_resource(sc, rman_get_start(r->res) +    \
 1794             offset, _io_size, &io_offset, &stolen, &restore);   \
 1795         io_res = dwa->parent_res;                               \
 1796                                                                 \
 1797         KASSERT(!r->direct,                                     \
 1798             ("bhnd_bus slow path used for direct resource"));   \
 1799                                                                 \
 1800         KASSERT(rman_get_flags(io_res) & RF_ACTIVE,             \
 1801             ("i/o resource is not active"));
 1802 
 1803 #define BHNDB_IO_COMMON_TEARDOWN()                              \
 1804         if (stolen) {                                           \
 1805                 bhndb_dw_return_stolen(sc->dev, sc->bus_res,    \
 1806                     dwa, restore);                              \
 1807         }                                                       \
 1808         BHNDB_UNLOCK(sc);
 1809 
 1810 /* Defines a bhndb_bus_read_* method implementation */
 1811 #define BHNDB_IO_READ(_type, _name)                             \
 1812 static _type                                                    \
 1813 bhndb_bus_read_ ## _name (device_t dev, device_t child,         \
 1814     struct bhnd_resource *r, bus_size_t offset)                 \
 1815 {                                                               \
 1816         _type v;                                                \
 1817         BHNDB_IO_COMMON_SETUP(sizeof(_type));                   \
 1818         v = bus_read_ ## _name (io_res, io_offset);             \
 1819         BHNDB_IO_COMMON_TEARDOWN();                             \
 1820                                                                 \
 1821         return (v);                                             \
 1822 }
 1823 
 1824 /* Defines a bhndb_bus_write_* method implementation */
 1825 #define BHNDB_IO_WRITE(_type, _name)                            \
 1826 static void                                                     \
 1827 bhndb_bus_write_ ## _name (device_t dev, device_t child,        \
 1828     struct bhnd_resource *r, bus_size_t offset, _type value)    \
 1829 {                                                               \
 1830         BHNDB_IO_COMMON_SETUP(sizeof(_type));                   \
 1831         bus_write_ ## _name (io_res, io_offset, value);         \
 1832         BHNDB_IO_COMMON_TEARDOWN();                             \
 1833 }
 1834 
 1835 /* Defines a bhndb_bus_(read|write|set)_(multi|region)_* method */
 1836 #define BHNDB_IO_MISC(_type, _ptr, _op, _size)                  \
 1837 static void                                                     \
 1838 bhndb_bus_ ## _op ## _ ## _size (device_t dev,                  \
 1839     device_t child, struct bhnd_resource *r, bus_size_t offset, \
 1840     _type _ptr datap, bus_size_t count)                         \
 1841 {                                                               \
 1842         BHNDB_IO_COMMON_SETUP(sizeof(_type) * count);           \
 1843         bus_ ## _op ## _ ## _size (io_res, io_offset,           \
 1844             datap, count);                                      \
 1845         BHNDB_IO_COMMON_TEARDOWN();                             \
 1846 }
 1847 
 1848 /* Defines a complete set of read/write methods */
 1849 #define BHNDB_IO_METHODS(_type, _size)                          \
 1850         BHNDB_IO_READ(_type, _size)                             \
 1851         BHNDB_IO_WRITE(_type, _size)                            \
 1852                                                                 \
 1853         BHNDB_IO_READ(_type, stream_ ## _size)                  \
 1854         BHNDB_IO_WRITE(_type, stream_ ## _size)                 \
 1855                                                                 \
 1856         BHNDB_IO_MISC(_type, *, read_multi, _size)              \
 1857         BHNDB_IO_MISC(_type, *, write_multi, _size)             \
 1858                                                                 \
 1859         BHNDB_IO_MISC(_type, *, read_multi_stream, _size)       \
 1860         BHNDB_IO_MISC(_type, *, write_multi_stream, _size)      \
 1861                                                                 \
 1862         BHNDB_IO_MISC(_type,  , set_multi, _size)               \
 1863         BHNDB_IO_MISC(_type,  , set_region, _size)              \
 1864         BHNDB_IO_MISC(_type, *, read_region, _size)             \
 1865         BHNDB_IO_MISC(_type, *, write_region, _size)            \
 1866                                                                 \
 1867         BHNDB_IO_MISC(_type, *, read_region_stream, _size)      \
 1868         BHNDB_IO_MISC(_type, *, write_region_stream, _size)
 1869 
 1870 BHNDB_IO_METHODS(uint8_t, 1);
 1871 BHNDB_IO_METHODS(uint16_t, 2);
 1872 BHNDB_IO_METHODS(uint32_t, 4);
 1873 
 1874 /**
 1875  * Default bhndb(4) implementation of BHND_BUS_BARRIER().
 1876  */
 1877 static void 
 1878 bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r,
 1879     bus_size_t offset, bus_size_t length, int flags)
 1880 {
 1881         BHNDB_IO_COMMON_SETUP(length);
 1882 
 1883         bus_barrier(io_res, io_offset + offset, length, flags);
 1884 
 1885         BHNDB_IO_COMMON_TEARDOWN();
 1886 }
 1887 
 1888 /**
 1889  * Default bhndb(4) implementation of BHND_MAP_INTR().
 1890  */
 1891 static int
 1892 bhndb_bhnd_map_intr(device_t dev, device_t child, u_int intr, rman_res_t *irq)
 1893 {
 1894         u_int                    ivec;
 1895         int                      error;
 1896 
 1897         /* Is the intr valid? */
 1898         if (intr >= bhnd_get_intr_count(child))
 1899                 return (EINVAL);
 1900 
 1901         /* Fetch the interrupt vector */
 1902         if ((error = bhnd_get_intr_ivec(child, intr, &ivec)))
 1903                 return (error);
 1904 
 1905         /* Map directly to the actual backplane interrupt vector */
 1906         *irq = ivec;
 1907 
 1908         return (0);
 1909 }
 1910 
 1911 /**
 1912  * Default bhndb(4) implementation of BHND_UNMAP_INTR().
 1913  */
 1914 static void
 1915 bhndb_bhnd_unmap_intr(device_t dev, device_t child, rman_res_t irq)
 1916 {
 1917         /* No state to clean up */
 1918 }
 1919 
 1920 /**
 1921  * Default bhndb(4) implementation of BUS_SETUP_INTR().
 1922  */
 1923 static int
 1924 bhndb_setup_intr(device_t dev, device_t child, struct resource *r,
 1925     int flags, driver_filter_t filter, driver_intr_t handler, void *arg,
 1926     void **cookiep)
 1927 {
 1928         struct bhndb_softc              *sc;
 1929         struct bhndb_intr_isrc          *isrc;
 1930         struct bhndb_intr_handler       *ih;
 1931         int                              error;
 1932 
 1933         sc = device_get_softc(dev);
 1934 
 1935         /* Fetch the isrc */
 1936         if ((error = BHNDB_MAP_INTR_ISRC(dev, r, &isrc))) {
 1937                 device_printf(dev, "failed to fetch isrc: %d\n", error);
 1938                 return (error);
 1939         }
 1940 
 1941         /* Allocate new ihandler entry  */
 1942         ih = bhndb_alloc_intr_handler(child, r, isrc);
 1943         if (ih == NULL)
 1944                 return (ENOMEM);
 1945 
 1946         /* Perform actual interrupt setup via the host isrc */
 1947         error = bus_setup_intr(isrc->is_owner, isrc->is_res, flags, filter,
 1948             handler, arg, &ih->ih_cookiep);
 1949         if (error) {
 1950                 bhndb_free_intr_handler(ih);
 1951                 return (error);
 1952         }
 1953 
 1954         /* Add to our interrupt handler list */
 1955         BHNDB_LOCK(sc);
 1956         bhndb_register_intr_handler(sc->bus_res, ih);
 1957         BHNDB_UNLOCK(sc);
 1958 
 1959         /* Provide the interrupt handler entry as our cookiep value */
 1960         *cookiep = ih;
 1961         return (0);
 1962 }
 1963 
 1964 /**
 1965  * Default bhndb(4) implementation of BUS_TEARDOWN_INTR().
 1966  */
 1967 static int
 1968 bhndb_teardown_intr(device_t dev, device_t child, struct resource *r,
 1969     void *cookiep)
 1970 {
 1971         struct bhndb_softc              *sc;
 1972         struct bhndb_intr_handler       *ih;
 1973         struct bhndb_intr_isrc          *isrc;
 1974         int                              error;
 1975 
 1976         sc = device_get_softc(dev);
 1977 
 1978         /* Locate and claim ownership of the interrupt handler entry */
 1979         BHNDB_LOCK(sc);
 1980 
 1981         ih = bhndb_find_intr_handler(sc->bus_res, cookiep);
 1982         if (ih == NULL) {
 1983                 panic("%s requested teardown of invalid cookiep %p",
 1984                     device_get_nameunit(child), cookiep);
 1985         }
 1986 
 1987         bhndb_deregister_intr_handler(sc->bus_res, ih);
 1988 
 1989         BHNDB_UNLOCK(sc);
 1990 
 1991         /* Perform actual interrupt teardown via the host isrc */
 1992         isrc = ih->ih_isrc;
 1993         error = bus_teardown_intr(isrc->is_owner, isrc->is_res, ih->ih_cookiep);
 1994         if (error) {
 1995                 /* If teardown fails, we need to reinsert the handler entry
 1996                  * to allow later teardown */
 1997                 BHNDB_LOCK(sc);
 1998                 bhndb_register_intr_handler(sc->bus_res, ih);
 1999                 BHNDB_UNLOCK(sc);
 2000 
 2001                 return (error);
 2002         }
 2003 
 2004         /* Free the entry */
 2005         bhndb_free_intr_handler(ih);
 2006         return (0);
 2007 }
 2008 
 2009 /**
 2010  * Default bhndb(4) implementation of BUS_BIND_INTR().
 2011  */
 2012 static int
 2013 bhndb_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
 2014 {
 2015         struct bhndb_softc              *sc;
 2016         struct bhndb_intr_handler       *ih;
 2017         struct bhndb_intr_isrc          *isrc;
 2018 
 2019         sc = device_get_softc(dev);
 2020         isrc = NULL;
 2021 
 2022         /* Fetch the isrc corresponding to the child IRQ resource */
 2023         BHNDB_LOCK(sc);
 2024         STAILQ_FOREACH(ih, &sc->bus_res->bus_intrs, ih_link) {
 2025                 if (ih->ih_res == irq) {
 2026                         isrc = ih->ih_isrc;
 2027                         break;
 2028                 }
 2029         }
 2030         BHNDB_UNLOCK(sc);
 2031 
 2032         if (isrc == NULL) {
 2033                 panic("%s requested bind of invalid irq %#jx-%#jx",
 2034                     device_get_nameunit(child), rman_get_start(irq),
 2035                     rman_get_end(irq));
 2036         }
 2037 
 2038         /* Perform actual bind via the host isrc */
 2039         return (bus_bind_intr(isrc->is_owner, isrc->is_res, cpu));
 2040 }
 2041 
 2042 /**
 2043  * Default bhndb(4) implementation of BUS_DESCRIBE_INTR().
 2044  */
 2045 static int
 2046 bhndb_describe_intr(device_t dev, device_t child, struct resource *irq,
 2047     void *cookie, const char *descr)
 2048 {
 2049         struct bhndb_softc              *sc;
 2050         struct bhndb_intr_handler       *ih;
 2051         struct bhndb_intr_isrc          *isrc;
 2052 
 2053         sc = device_get_softc(dev);
 2054 
 2055         /* Locate the interrupt handler entry; the caller owns the handler
 2056          * reference, and thus our entry is guaranteed to remain valid after
 2057          * we drop out lock below. */
 2058         BHNDB_LOCK(sc);
 2059 
 2060         ih = bhndb_find_intr_handler(sc->bus_res, cookie);
 2061         if (ih == NULL) {
 2062                 panic("%s requested invalid cookiep %p",
 2063                     device_get_nameunit(child), cookie);
 2064         }
 2065 
 2066         isrc = ih->ih_isrc;
 2067 
 2068         BHNDB_UNLOCK(sc);
 2069 
 2070         /* Perform the actual request via the host isrc */
 2071         return (BUS_DESCRIBE_INTR(device_get_parent(isrc->is_owner),
 2072             isrc->is_owner, isrc->is_res, ih->ih_cookiep, descr));
 2073 }
 2074 
 2075 /**
 2076  * Default bhndb(4) implementation of BUS_CONFIG_INTR().
 2077  */
 2078 static int
 2079 bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig,
 2080     enum intr_polarity pol)
 2081 {
 2082         /* Unsupported */
 2083         return (ENXIO);
 2084 }
 2085 
 2086 /**
 2087  * Default bhndb(4) implementation of BUS_REMAP_INTR().
 2088  */
 2089 static int
 2090 bhndb_remap_intr(device_t dev, device_t child, u_int irq)
 2091 {
 2092         /* Unsupported */
 2093         return (ENXIO);
 2094 }
 2095 
 2096 /**
 2097  * Default bhndb(4) implementation of BHND_BUS_GET_DMA_TRANSLATION().
 2098  */
 2099 static inline int
 2100 bhndb_get_dma_translation(device_t dev, device_t child, u_int width,
 2101     uint32_t flags, bus_dma_tag_t *dmat,
 2102     struct bhnd_dma_translation *translation)
 2103 {
 2104         struct bhndb_softc                      *sc;
 2105         const struct bhndb_hwcfg                *hwcfg;
 2106         const struct bhnd_dma_translation       *match;
 2107         bus_dma_tag_t                            match_dmat;
 2108         bhnd_addr_t                              addr_mask, match_addr_mask;
 2109 
 2110         sc = device_get_softc(dev);
 2111         hwcfg = sc->bus_res->cfg;
 2112 
 2113         /* Is DMA supported? */
 2114         if (sc->bus_res->res->dma_tags == NULL)
 2115                 return (ENODEV);
 2116 
 2117         /* Is the requested width supported? */
 2118         if (width > BHND_DMA_ADDR_32BIT) {
 2119                 /* Backplane must support 64-bit addressing */
 2120                 if (!(sc->chipid.chip_caps & BHND_CAP_BP64))
 2121                         width = BHND_DMA_ADDR_32BIT;
 2122         }
 2123 
 2124         /* Find the best matching descriptor for the requested width */
 2125         addr_mask = BHND_DMA_ADDR_BITMASK(width);
 2126 
 2127         match = NULL;
 2128         match_addr_mask = 0x0;
 2129         match_dmat = NULL;
 2130 
 2131         for (size_t i = 0; i < sc->bus_res->res->num_dma_tags; i++) {
 2132                 const struct bhnd_dma_translation       *dwin;
 2133                 bhnd_addr_t                              masked;
 2134 
 2135                 dwin = &hwcfg->dma_translations[i];
 2136 
 2137                 /* The base address must be device addressable */
 2138                 if ((dwin->base_addr & addr_mask) != dwin->base_addr)
 2139                         continue;
 2140 
 2141                 /* The flags must match */
 2142                 if ((dwin->flags & flags) != flags)
 2143                         continue;
 2144 
 2145                 /* The window must cover at least part of our addressable
 2146                  * range */
 2147                 masked = (dwin->addr_mask | dwin->addrext_mask) & addr_mask;
 2148                 if (masked == 0)
 2149                         continue;
 2150 
 2151                 /* Is this a better match? */
 2152                 if (match == NULL || masked > match_addr_mask) {
 2153                         match = dwin;
 2154                         match_addr_mask = masked;
 2155                         match_dmat = sc->bus_res->res->dma_tags[i];
 2156                 }
 2157         }
 2158 
 2159         if (match == NULL || match_addr_mask == 0)
 2160                 return (ENOENT);
 2161 
 2162         if (dmat != NULL)
 2163                 *dmat = match_dmat;
 2164 
 2165         if (translation != NULL)
 2166                 *translation = *match;
 2167 
 2168         return (0);
 2169 }
 2170 
 2171 /**
 2172  * Default bhndb(4) implementation of BUS_GET_DMA_TAG().
 2173  */
 2174 static bus_dma_tag_t
 2175 bhndb_get_dma_tag(device_t dev, device_t child)
 2176 {
 2177         struct bhndb_softc *sc = device_get_softc(dev);
 2178 
 2179         /*
 2180          * A bridge may have multiple DMA translation descriptors, each with
 2181          * their own incompatible restrictions; drivers should in general call
 2182          * BHND_BUS_GET_DMA_TRANSLATION() to fetch both the best available DMA
 2183          * translation, and its corresponding DMA tag.
 2184          *
 2185          * Child drivers that do not use BHND_BUS_GET_DMA_TRANSLATION() are
 2186          * responsible for creating their own restricted DMA tag; since we
 2187          * cannot do this for them in BUS_GET_DMA_TAG(), we simply return the
 2188          * bridge parent's DMA tag directly; 
 2189          */
 2190         return (bus_get_dma_tag(sc->parent_dev));
 2191 }
 2192 
 2193 static device_method_t bhndb_methods[] = {
 2194         /* Device interface */ \
 2195         DEVMETHOD(device_probe,                 bhndb_generic_probe),
 2196         DEVMETHOD(device_detach,                bhndb_generic_detach),
 2197         DEVMETHOD(device_shutdown,              bus_generic_shutdown),
 2198         DEVMETHOD(device_suspend,               bhndb_generic_suspend),
 2199         DEVMETHOD(device_resume,                bhndb_generic_resume),
 2200 
 2201         /* Bus interface */
 2202         DEVMETHOD(bus_probe_nomatch,            bhndb_probe_nomatch),
 2203         DEVMETHOD(bus_print_child,              bhndb_print_child),
 2204         DEVMETHOD(bus_child_location,           bhndb_child_location),
 2205         DEVMETHOD(bus_add_child,                bhndb_add_child),
 2206         DEVMETHOD(bus_child_deleted,            bhndb_child_deleted),
 2207 
 2208         DEVMETHOD(bus_alloc_resource,           bhndb_alloc_resource),
 2209         DEVMETHOD(bus_release_resource,         bhndb_release_resource),
 2210         DEVMETHOD(bus_activate_resource,        bhndb_activate_resource),
 2211         DEVMETHOD(bus_deactivate_resource,      bhndb_deactivate_resource),
 2212 
 2213         DEVMETHOD(bus_setup_intr,               bhndb_setup_intr),
 2214         DEVMETHOD(bus_teardown_intr,            bhndb_teardown_intr),
 2215         DEVMETHOD(bus_config_intr,              bhndb_config_intr),
 2216         DEVMETHOD(bus_bind_intr,                bhndb_bind_intr),
 2217         DEVMETHOD(bus_describe_intr,            bhndb_describe_intr),
 2218         DEVMETHOD(bus_remap_intr,               bhndb_remap_intr),
 2219 
 2220         DEVMETHOD(bus_get_dma_tag,              bhndb_get_dma_tag),
 2221 
 2222         DEVMETHOD(bus_adjust_resource,          bhndb_adjust_resource),
 2223         DEVMETHOD(bus_set_resource,             bus_generic_rl_set_resource),
 2224         DEVMETHOD(bus_get_resource,             bus_generic_rl_get_resource),
 2225         DEVMETHOD(bus_delete_resource,          bus_generic_rl_delete_resource),
 2226         DEVMETHOD(bus_get_resource_list,        bhndb_get_resource_list),
 2227 
 2228         DEVMETHOD(bus_read_ivar,                bhndb_read_ivar),
 2229         DEVMETHOD(bus_write_ivar,               bhndb_write_ivar),
 2230 
 2231         /* BHNDB interface */
 2232         DEVMETHOD(bhndb_get_chipid,             bhndb_get_chipid),
 2233         DEVMETHOD(bhndb_is_core_disabled,       bhndb_is_core_disabled),
 2234         DEVMETHOD(bhndb_get_hostb_core,         bhndb_get_hostb_core),
 2235         DEVMETHOD(bhndb_suspend_resource,       bhndb_suspend_resource),
 2236         DEVMETHOD(bhndb_resume_resource,        bhndb_resume_resource),
 2237 
 2238         /* BHND interface */
 2239         DEVMETHOD(bhnd_bus_get_chipid,          bhndb_get_chipid),
 2240         DEVMETHOD(bhnd_bus_activate_resource,   bhndb_activate_bhnd_resource),
 2241         DEVMETHOD(bhnd_bus_deactivate_resource, bhndb_deactivate_bhnd_resource),
 2242         DEVMETHOD(bhnd_bus_get_nvram_var,       bhnd_bus_generic_get_nvram_var),
 2243         DEVMETHOD(bhnd_bus_map_intr,            bhndb_bhnd_map_intr),
 2244         DEVMETHOD(bhnd_bus_unmap_intr,          bhndb_bhnd_unmap_intr),
 2245         DEVMETHOD(bhnd_bus_get_dma_translation, bhndb_get_dma_translation),
 2246 
 2247         DEVMETHOD(bhnd_bus_get_service_registry,bhndb_get_service_registry),
 2248         DEVMETHOD(bhnd_bus_register_provider,   bhnd_bus_generic_sr_register_provider),
 2249         DEVMETHOD(bhnd_bus_deregister_provider, bhnd_bus_generic_sr_deregister_provider),
 2250         DEVMETHOD(bhnd_bus_retain_provider,     bhnd_bus_generic_sr_retain_provider),
 2251         DEVMETHOD(bhnd_bus_release_provider,    bhnd_bus_generic_sr_release_provider),
 2252 
 2253         DEVMETHOD(bhnd_bus_read_1,              bhndb_bus_read_1),
 2254         DEVMETHOD(bhnd_bus_read_2,              bhndb_bus_read_2),
 2255         DEVMETHOD(bhnd_bus_read_4,              bhndb_bus_read_4),
 2256         DEVMETHOD(bhnd_bus_write_1,             bhndb_bus_write_1),
 2257         DEVMETHOD(bhnd_bus_write_2,             bhndb_bus_write_2),
 2258         DEVMETHOD(bhnd_bus_write_4,             bhndb_bus_write_4),
 2259 
 2260         DEVMETHOD(bhnd_bus_read_stream_1,       bhndb_bus_read_stream_1),
 2261         DEVMETHOD(bhnd_bus_read_stream_2,       bhndb_bus_read_stream_2),
 2262         DEVMETHOD(bhnd_bus_read_stream_4,       bhndb_bus_read_stream_4),
 2263         DEVMETHOD(bhnd_bus_write_stream_1,      bhndb_bus_write_stream_1),
 2264         DEVMETHOD(bhnd_bus_write_stream_2,      bhndb_bus_write_stream_2),
 2265         DEVMETHOD(bhnd_bus_write_stream_4,      bhndb_bus_write_stream_4),
 2266 
 2267         DEVMETHOD(bhnd_bus_read_multi_1,        bhndb_bus_read_multi_1),
 2268         DEVMETHOD(bhnd_bus_read_multi_2,        bhndb_bus_read_multi_2),
 2269         DEVMETHOD(bhnd_bus_read_multi_4,        bhndb_bus_read_multi_4),
 2270         DEVMETHOD(bhnd_bus_write_multi_1,       bhndb_bus_write_multi_1),
 2271         DEVMETHOD(bhnd_bus_write_multi_2,       bhndb_bus_write_multi_2),
 2272         DEVMETHOD(bhnd_bus_write_multi_4,       bhndb_bus_write_multi_4),
 2273 
 2274         DEVMETHOD(bhnd_bus_read_multi_stream_1, bhndb_bus_read_multi_stream_1),
 2275         DEVMETHOD(bhnd_bus_read_multi_stream_2, bhndb_bus_read_multi_stream_2),
 2276         DEVMETHOD(bhnd_bus_read_multi_stream_4, bhndb_bus_read_multi_stream_4),
 2277         DEVMETHOD(bhnd_bus_write_multi_stream_1,bhndb_bus_write_multi_stream_1),
 2278         DEVMETHOD(bhnd_bus_write_multi_stream_2,bhndb_bus_write_multi_stream_2),
 2279         DEVMETHOD(bhnd_bus_write_multi_stream_4,bhndb_bus_write_multi_stream_4),
 2280 
 2281         DEVMETHOD(bhnd_bus_set_multi_1,         bhndb_bus_set_multi_1),
 2282         DEVMETHOD(bhnd_bus_set_multi_2,         bhndb_bus_set_multi_2),
 2283         DEVMETHOD(bhnd_bus_set_multi_4,         bhndb_bus_set_multi_4),
 2284         DEVMETHOD(bhnd_bus_set_region_1,        bhndb_bus_set_region_1),
 2285         DEVMETHOD(bhnd_bus_set_region_2,        bhndb_bus_set_region_2),
 2286         DEVMETHOD(bhnd_bus_set_region_4,        bhndb_bus_set_region_4),
 2287 
 2288         DEVMETHOD(bhnd_bus_read_region_1,       bhndb_bus_read_region_1),
 2289         DEVMETHOD(bhnd_bus_read_region_2,       bhndb_bus_read_region_2),
 2290         DEVMETHOD(bhnd_bus_read_region_4,       bhndb_bus_read_region_4),
 2291         DEVMETHOD(bhnd_bus_write_region_1,      bhndb_bus_write_region_1),
 2292         DEVMETHOD(bhnd_bus_write_region_2,      bhndb_bus_write_region_2),
 2293         DEVMETHOD(bhnd_bus_write_region_4,      bhndb_bus_write_region_4),
 2294 
 2295         DEVMETHOD(bhnd_bus_read_region_stream_1,bhndb_bus_read_region_stream_1),
 2296         DEVMETHOD(bhnd_bus_read_region_stream_2,bhndb_bus_read_region_stream_2),
 2297         DEVMETHOD(bhnd_bus_read_region_stream_4,bhndb_bus_read_region_stream_4),
 2298         DEVMETHOD(bhnd_bus_write_region_stream_1,bhndb_bus_write_region_stream_1),
 2299         DEVMETHOD(bhnd_bus_write_region_stream_2,bhndb_bus_write_region_stream_2),
 2300         DEVMETHOD(bhnd_bus_write_region_stream_4,bhndb_bus_write_region_stream_4),
 2301 
 2302         DEVMETHOD(bhnd_bus_barrier,             bhndb_bus_barrier),
 2303 
 2304         DEVMETHOD_END
 2305 };
 2306 
 2307 DEFINE_CLASS_0(bhndb, bhndb_driver, bhndb_methods, sizeof(struct bhndb_softc));
 2308 
 2309 MODULE_VERSION(bhndb, 1);
 2310 MODULE_DEPEND(bhndb, bhnd, 1, 1, 1);

Cache object: cb5959ddc1ee9a9dd64b3c9f0a5124b3


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.