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