]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/bhndb/bhndb_subr.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r302418, and update
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / bhndb / bhndb_subr.c
1 /*-
2  * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/limits.h>
36
37 #include "bhndb_private.h"
38 #include "bhndbvar.h"
39
40 /**
41  * Attach a BHND bridge device to @p parent.
42  * 
43  * @param parent A parent PCI device.
44  * @param[out] bhndb On success, the probed and attached bhndb bridge device.
45  * @param unit The device unit number, or -1 to select the next available unit
46  * number.
47  * 
48  * @retval 0 success
49  * @retval non-zero Failed to attach the bhndb device.
50  */
51 int
52 bhndb_attach_bridge(device_t parent, device_t *bhndb, int unit)
53 {
54         int error;
55
56         *bhndb = device_add_child(parent, "bhndb", unit);
57         if (*bhndb == NULL)
58                 return (ENXIO);
59
60         if (!(error = device_probe_and_attach(*bhndb)))
61                 return (0);
62
63         if ((device_delete_child(parent, *bhndb)))
64                 device_printf(parent, "failed to detach bhndb child\n");
65
66         return (error);
67 }
68
69 /*
70  * Call BHNDB_SUSPEND_RESOURCE() for all resources in @p rl.
71  */
72 static void
73 bhndb_do_suspend_resources(device_t dev, struct resource_list *rl)
74 {
75         struct resource_list_entry *rle;
76
77         /* Suspend all child resources. */
78         STAILQ_FOREACH(rle, rl, link) {
79                 /* Skip non-allocated resources */
80                 if (rle->res == NULL)
81                         continue;
82
83                 BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, rle->type,
84                     rle->res);
85         }
86 }
87
88 /**
89  * Helper function for implementing BUS_RESUME_CHILD() on bridged
90  * bhnd(4) buses.
91  * 
92  * This implementation of BUS_RESUME_CHILD() uses BUS_GET_RESOURCE_LIST()
93  * to find the child's resources and call BHNDB_SUSPEND_RESOURCE() for all
94  * child resources, ensuring that the device's allocated bridge resources
95  * will be available to other devices during bus resumption.
96  * 
97  * Before suspending any resources, @p child is suspended by 
98  * calling bhnd_generic_suspend_child().
99  * 
100  * If @p child is not a direct child of @p dev, suspension is delegated to
101  * the @p dev parent.
102  */
103 int
104 bhnd_generic_br_suspend_child(device_t dev, device_t child)
105 {
106         struct resource_list            *rl;
107         int                              error;
108
109         if (device_get_parent(child) != dev)
110                 BUS_SUSPEND_CHILD(device_get_parent(dev), child);
111
112         if (device_is_suspended(child))
113                 return (EBUSY);
114
115         /* Suspend the child device */
116         if ((error = bhnd_generic_suspend_child(dev, child)))
117                 return (error);
118
119         /* Fetch the resource list. If none, there's nothing else to do */
120         rl = BUS_GET_RESOURCE_LIST(device_get_parent(child), child);
121         if (rl == NULL)
122                 return (0);
123
124         /* Suspend all child resources. */
125         bhndb_do_suspend_resources(dev, rl);
126
127         return (0);
128 }
129
130 /**
131  * Helper function for implementing BUS_RESUME_CHILD() on bridged
132  * bhnd(4) bus devices.
133  * 
134  * This implementation of BUS_RESUME_CHILD() uses BUS_GET_RESOURCE_LIST()
135  * to find the child's resources and call BHNDB_RESUME_RESOURCE() for all
136  * child resources, before delegating to bhnd_generic_resume_child().
137  * 
138  * If resource resumption fails, @p child will not be resumed.
139  * 
140  * If @p child is not a direct child of @p dev, suspension is delegated to
141  * the @p dev parent.
142  */
143 int
144 bhnd_generic_br_resume_child(device_t dev, device_t child)
145 {
146         struct resource_list            *rl;
147         struct resource_list_entry      *rle;
148         int                              error;
149         
150         if (device_get_parent(child) != dev)
151                 BUS_RESUME_CHILD(device_get_parent(dev), child);
152
153         if (!device_is_suspended(child))
154                 return (EBUSY);
155
156         /* Fetch the resource list. If none, there's nothing else to do */
157         rl = BUS_GET_RESOURCE_LIST(device_get_parent(child), child);
158         if (rl == NULL)
159                 return (bhnd_generic_resume_child(dev, child));
160
161         /* Resume all resources */
162         STAILQ_FOREACH(rle, rl, link) {
163                 /* Skip non-allocated resources */
164                 if (rle->res == NULL)
165                         continue;
166
167                 error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev,
168                     rle->type, rle->res);
169                 if (error) {
170                         /* Put all resources back into a suspend state */
171                         bhndb_do_suspend_resources(dev, rl);
172                         return (error);
173                 }
174         }
175
176         /* Now that all resources are resumed, resume child */
177         if ((error = bhnd_generic_resume_child(dev, child))) {
178                 /* Put all resources back into a suspend state */
179                 bhndb_do_suspend_resources(dev, rl);
180         }
181
182         return (error);
183 }
184
185 /**
186  * Find a SYS_RES_MEMORY resource containing the given address range.
187  * 
188  * @param br The bhndb resource state to search.
189  * @param start The start address of the range to search for.
190  * @param count The size of the range to search for.
191  * 
192  * @retval resource the host resource containing the requested range.
193  * @retval NULL if no resource containing the requested range can be found.
194  */
195 struct resource *
196 bhndb_find_resource_range(struct bhndb_resources *br, rman_res_t start,
197      rman_res_t count)
198 {
199         KASSERT(br->res_avail, ("no host resources allocated"));
200
201         for (u_int i = 0; br->res_spec[i].type != -1; i++) {
202                 struct resource *r = br->res[i];
203
204                 if (br->res_spec->type != SYS_RES_MEMORY)
205                         continue;
206
207                 /* Verify range */
208                 if (rman_get_start(r) > start)
209                         continue;
210                 
211                 if (rman_get_end(r) < (start + count - 1))
212                         continue;
213
214                 return (r);
215         }
216
217         return (NULL);
218 }
219
220 /**
221  * Find the resource containing @p win.
222  * 
223  * @param br The bhndb resource state to search.
224  * @param win A register window.
225  * 
226  * @retval resource the resource containing @p win.
227  * @retval NULL if no resource containing @p win can be found.
228  */
229 struct resource *
230 bhndb_find_regwin_resource(struct bhndb_resources *br,
231     const struct bhndb_regwin *win)
232 {
233         const struct resource_spec *rspecs;
234
235         KASSERT(br->res_avail, ("no host resources allocated"));
236
237         rspecs = br->cfg->resource_specs;
238         for (u_int i = 0; rspecs[i].type != -1; i++) {                  
239                 if (win->res.type != rspecs[i].type)
240                         continue;
241
242                 if (win->res.rid != rspecs[i].rid)
243                         continue;
244
245                 /* Found declared resource */
246                 return (br->res[i]);
247         }
248
249         device_printf(br->dev,
250             "missing regwin resource spec (type=%d, rid=%d)\n",
251             win->res.type, win->res.rid);
252
253         return (NULL);
254 }
255
256 /**
257  * Allocate and initialize a new resource state structure.
258  * 
259  * @param dev The bridge device.
260  * @param parent_dev The parent device from which host resources should be 
261  * allocated.
262  * @param cfg The hardware configuration to be used.
263  */
264 struct bhndb_resources *
265 bhndb_alloc_resources(device_t dev, device_t parent_dev,
266     const struct bhndb_hwcfg *cfg)
267 {
268         struct bhndb_resources          *r;
269         const struct bhndb_regwin       *win;
270         bus_size_t                       last_window_size;
271         int                              rnid;
272         int                              error;
273         bool                             free_ht_mem, free_br_mem;
274
275         free_ht_mem = false;
276         free_br_mem = false;
277
278         r = malloc(sizeof(*r), M_BHND, M_NOWAIT|M_ZERO);
279         if (r == NULL)
280                 return (NULL);
281
282         /* Basic initialization */
283         r->dev = dev;
284         r->parent_dev = parent_dev;
285         r->cfg = cfg;
286         r->min_prio = BHNDB_PRIORITY_NONE;
287         STAILQ_INIT(&r->bus_regions);
288         
289         /* Initialize host address space resource manager. */
290         r->ht_mem_rman.rm_start = 0;
291         r->ht_mem_rman.rm_end = ~0;
292         r->ht_mem_rman.rm_type = RMAN_ARRAY;
293         r->ht_mem_rman.rm_descr = "BHNDB host memory";
294         if ((error = rman_init(&r->ht_mem_rman))) {
295                 device_printf(r->dev, "could not initialize ht_mem_rman\n");
296                 goto failed;
297         }
298         free_ht_mem = true;
299
300
301         /* Initialize resource manager for the bridged address space. */
302         r->br_mem_rman.rm_start = 0;
303         r->br_mem_rman.rm_end = BUS_SPACE_MAXADDR_32BIT;
304         r->br_mem_rman.rm_type = RMAN_ARRAY;
305         r->br_mem_rman.rm_descr = "BHNDB bridged memory";
306
307         if ((error = rman_init(&r->br_mem_rman))) {
308                 device_printf(r->dev, "could not initialize br_mem_rman\n");
309                 goto failed;
310         }
311         free_br_mem = true;
312
313         error = rman_manage_region(&r->br_mem_rman, 0, BUS_SPACE_MAXADDR_32BIT);
314         if (error) {
315                 device_printf(r->dev, "could not configure br_mem_rman\n");
316                 goto failed;
317         }
318
319         /* Fetch the dynamic regwin count and verify that it does not exceed
320          * what is representable via our freelist bitstring. */
321         r->dwa_count = bhndb_regwin_count(cfg->register_windows,
322             BHNDB_REGWIN_T_DYN);
323         if (r->dwa_count >= INT_MAX) {
324                 device_printf(r->dev, "max dynamic regwin count exceeded\n");
325                 goto failed;
326         }
327         
328         /* Allocate the dynamic window allocation table. */
329         r->dw_alloc = malloc(sizeof(r->dw_alloc[0]) * r->dwa_count, M_BHND,
330             M_NOWAIT);
331         if (r->dw_alloc == NULL)
332                 goto failed;
333
334         /* Allocate the dynamic window allocation freelist */
335         r->dwa_freelist = bit_alloc(r->dwa_count, M_BHND, M_NOWAIT);
336         if (r->dwa_freelist == NULL)
337                 goto failed;
338
339         /* Initialize the dynamic window table */
340         rnid = 0;
341         last_window_size = 0;
342         for (win = cfg->register_windows;
343             win->win_type != BHNDB_REGWIN_T_INVALID; win++)
344         {
345                 struct bhndb_dw_alloc *dwa;
346
347                 /* Skip non-DYN windows */
348                 if (win->win_type != BHNDB_REGWIN_T_DYN)
349                         continue;
350
351                 /* Validate the window size */
352                 if (win->win_size == 0) {
353                         device_printf(r->dev, "ignoring zero-length dynamic "
354                             "register window\n");
355                         continue;
356                 } else if (last_window_size == 0) {
357                         last_window_size = win->win_size;
358                 } else if (last_window_size != win->win_size) {
359                         /* 
360                          * No existing hardware should trigger this.
361                          * 
362                          * If you run into this in the future, the dynamic
363                          * window allocator and the resource priority system
364                          * will need to be extended to support multiple register
365                          * window allocation pools. 
366                          */
367                         device_printf(r->dev, "devices that vend multiple "
368                             "dynamic register window sizes are not currently "
369                             "supported\n");
370                         goto failed;
371                 }
372
373                 dwa = &r->dw_alloc[rnid];
374                 dwa->win = win;
375                 dwa->parent_res = NULL;
376                 dwa->rnid = rnid;
377                 dwa->target = 0x0;
378                 
379                 LIST_INIT(&dwa->refs);
380                 rnid++;
381         }
382
383         return (r);
384
385 failed:
386         if (free_ht_mem)
387                 rman_fini(&r->ht_mem_rman);
388
389         if (free_br_mem)
390                 rman_fini(&r->br_mem_rman);
391
392         if (r->dw_alloc != NULL)
393                 free(r->dw_alloc, M_BHND);
394
395         if (r->dwa_freelist != NULL)
396                 free(r->dwa_freelist, M_BHND);
397
398         free(r, M_BHND);
399
400         return (NULL);
401 }
402
403 /**
404  * Allocate host resources required by @p br, and initialize
405  * internal BHNDB_ADDRSPACE_NATIVE resource manager state.
406  * 
407  * @param br Resource state.
408  */
409 int
410 bhndb_alloc_host_resources(struct bhndb_resources *br)
411 {
412         size_t  res_num;
413         int     error;
414
415         KASSERT(!br->res_avail, ("host resources already allocated"));
416
417         /* Determine our bridge resource count from the hardware config. */
418         res_num = 0;
419         for (size_t i = 0; br->cfg->resource_specs[i].type != -1; i++)
420                 res_num++;
421
422         /* Allocate space for a non-const copy of our resource_spec
423          * table; this will be updated with the RIDs assigned by
424          * bus_alloc_resources. */
425         br->res_spec = malloc(sizeof(br->res_spec[0]) * (res_num + 1), M_BHND,
426             M_NOWAIT);
427         if (br->res_spec == NULL) {
428                 error = ENOMEM;
429                 goto failed;
430         }
431
432         /* Initialize and terminate the table */
433         for (size_t i = 0; i < res_num; i++)
434                 br->res_spec[i] = br->cfg->resource_specs[i];
435         
436         br->res_spec[res_num].type = -1;
437
438         /* Allocate space for our resource references */
439         br->res = malloc(sizeof(br->res[0]) * res_num, M_BHND, M_NOWAIT);
440         if (br->res == NULL) {
441                 error = ENOMEM;
442                 goto failed;
443         }
444
445         /* Allocate host resources */
446         error = bus_alloc_resources(br->parent_dev, br->res_spec, br->res);
447         if (error) {
448                 device_printf(br->dev,
449                     "could not allocate bridge resources on %s: %d\n",
450                     device_get_nameunit(br->parent_dev), error);
451                 goto failed;
452         } else {
453                 br->res_avail = true;
454         }
455
456         /* Populate (and validate) parent resource references for all
457          * dynamic windows */
458         for (size_t i = 0; i < br->dwa_count; i++) {
459                 struct bhndb_dw_alloc           *dwa;
460                 const struct bhndb_regwin       *win;
461
462                 dwa = &br->dw_alloc[i];
463                 win = dwa->win;
464
465                 /* Find and validate corresponding resource. */
466                 dwa->parent_res = bhndb_find_regwin_resource(br, win);
467                 if (dwa->parent_res == NULL) {
468                         device_printf(br->dev, "no host resource found for %u "
469                             "register window with offset %#jx and "
470                             "size %#jx\n",
471                             win->win_type,
472                             (uintmax_t)win->win_offset,
473                             (uintmax_t)win->win_size);
474
475                         error = ENXIO;
476                         goto failed;
477                 }
478
479                 if (rman_get_size(dwa->parent_res) < win->win_offset +
480                     win->win_size)
481                 {
482                         device_printf(br->dev, "resource %d too small for "
483                             "register window with offset %llx and size %llx\n",
484                             rman_get_rid(dwa->parent_res),
485                             (unsigned long long) win->win_offset,
486                             (unsigned long long) win->win_size);
487
488                         error = EINVAL;
489                         goto failed;
490                 }
491         }
492
493         /* Add allocated memory resources to our host memory resource manager */
494         for (u_int i = 0; br->res_spec[i].type != -1; i++) {
495                 struct resource *res;
496                 
497                 /* skip non-memory resources */
498                 if (br->res_spec[i].type != SYS_RES_MEMORY)
499                         continue;
500
501                 /* add host resource to set of managed regions */
502                 res = br->res[i];
503                 error = rman_manage_region(&br->ht_mem_rman,
504                     rman_get_start(res), rman_get_end(res));
505                 if (error) {
506                         device_printf(br->dev,
507                             "could not register host memory region with "
508                             "ht_mem_rman: %d\n", error);
509                         goto failed;
510                 }
511         }
512
513         return (0);
514
515 failed:
516         if (br->res_avail)
517                 bus_release_resources(br->parent_dev, br->res_spec, br->res);
518         
519         if (br->res != NULL)
520                 free(br->res, M_BHND);
521
522         if (br->res_spec != NULL)
523                 free(br->res_spec, M_BHND);
524
525         return (error);
526 }
527
528 /**
529  * Deallocate the given bridge resource structure and any associated resources.
530  * 
531  * @param br Resource state to be deallocated.
532  */
533 void
534 bhndb_free_resources(struct bhndb_resources *br)
535 {
536         struct bhndb_region     *region, *r_next;
537         struct bhndb_dw_alloc   *dwa;
538         struct bhndb_dw_rentry  *dwr, *dwr_next;
539
540         /* No window regions may still be held */
541         if (!bhndb_dw_all_free(br)) {
542                 for (int i = 0; i < br->dwa_count; i++) {
543                         dwa = &br->dw_alloc[i];
544
545                         /* Skip free dynamic windows */
546                         if (bhndb_dw_is_free(br, dwa))
547                                 continue;
548
549                         device_printf(br->dev,
550                             "leaked dynamic register window %d\n", dwa->rnid);
551                 }
552         }
553
554         /* Release resources allocated through our parent. */
555         if (br->res_avail)
556                 bus_release_resources(br->parent_dev, br->res_spec, br->res);
557
558         /* Clean up resource reservations */
559         for (size_t i = 0; i < br->dwa_count; i++) {
560                 dwa = &br->dw_alloc[i];
561
562                 LIST_FOREACH_SAFE(dwr, &dwa->refs, dw_link, dwr_next) {
563                         LIST_REMOVE(dwr, dw_link);
564                         free(dwr, M_BHND);
565                 }
566         }
567         
568         /* Release bus regions */
569         STAILQ_FOREACH_SAFE(region, &br->bus_regions, link, r_next) {
570                 STAILQ_REMOVE(&br->bus_regions, region, bhndb_region, link);
571                 free(region, M_BHND);
572         }
573
574         /* Release our resource managers */
575         rman_fini(&br->ht_mem_rman);
576         rman_fini(&br->br_mem_rman);
577
578         /* Free backing resource state structures */
579         if (br->res != NULL)
580                 free(br->res, M_BHND);
581
582         if (br->res_spec != NULL)
583                 free(br->res_spec, M_BHND);
584
585         free(br->dw_alloc, M_BHND);
586         free(br->dwa_freelist, M_BHND);
587 }
588
589 /**
590  * Add a bus region entry to @p r for the given base @p addr and @p size.
591  * 
592  * @param br The resource state to which the bus region entry will be added.
593  * @param addr The base address of this region.
594  * @param size The size of this region.
595  * @param priority The resource priority to be assigned to allocations
596  * made within this bus region.
597  * @param static_regwin If available, a static register window mapping this
598  * bus region entry. If not available, NULL.
599  * 
600  * @retval 0 success
601  * @retval non-zero if adding the bus region fails.
602  */
603 int
604 bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr,
605     bhnd_size_t size, bhndb_priority_t priority,
606     const struct bhndb_regwin *static_regwin)
607 {
608         struct bhndb_region     *reg;
609
610         /* Insert in the bus resource list */
611         reg = malloc(sizeof(*reg), M_BHND, M_NOWAIT);
612         if (reg == NULL)
613                 return (ENOMEM);
614
615         *reg = (struct bhndb_region) {
616                 .addr = addr,
617                 .size = size,
618                 .priority = priority,
619                 .static_regwin = static_regwin
620         };
621
622         STAILQ_INSERT_HEAD(&br->bus_regions, reg, link);
623
624         return (0);
625 }
626
627
628 /**
629  * Find the maximum start and end limits of the register window mapping
630  * resource @p r.
631  * 
632  * If the memory range is not mapped by an existing dynamic or static register
633  * window, ENOENT will be returned.
634  * 
635  * @param br The resource state to search.
636  * @param r The resource to search for in @p br.
637  * @param addr The requested starting address.
638  * @param size The requested size.
639  * 
640  * @retval bhndb_region A region that fully contains the requested range.
641  * @retval NULL If no mapping region can be found.
642  */
643 int
644 bhndb_find_resource_limits(struct bhndb_resources *br, struct resource *r,
645     rman_res_t *start, rman_res_t *end)
646 {
647         struct bhndb_dw_alloc   *dynamic;
648         struct bhndb_region     *sregion;
649
650         /* Check for an enclosing dynamic register window */
651         if ((dynamic = bhndb_dw_find_resource(br, r))) {
652                 *start = dynamic->target;
653                 *end = dynamic->target + dynamic->win->win_size - 1;
654                 return (0);
655         }
656
657         /* Check for a static region */
658         sregion = bhndb_find_resource_region(br, rman_get_start(r),
659             rman_get_size(r));
660         if (sregion != NULL && sregion->static_regwin != NULL) {
661                 *start = sregion->addr;
662                 *end = sregion->addr + sregion->size - 1;
663
664                 return (0);
665         }
666
667         /* Not found */
668         return (ENOENT);
669 }
670
671 /**
672  * Find the bus region that maps @p size bytes at @p addr.
673  * 
674  * @param br The resource state to search.
675  * @param addr The requested starting address.
676  * @param size The requested size.
677  * 
678  * @retval bhndb_region A region that fully contains the requested range.
679  * @retval NULL If no mapping region can be found.
680  */
681 struct bhndb_region *
682 bhndb_find_resource_region(struct bhndb_resources *br, bhnd_addr_t addr,
683     bhnd_size_t size)
684 {
685         struct bhndb_region *region;
686
687         STAILQ_FOREACH(region, &br->bus_regions, link) {
688                 /* Request must fit within the region's mapping  */
689                 if (addr < region->addr)
690                         continue;
691
692                 if (addr + size > region->addr + region->size)
693                         continue;
694
695                 return (region);
696         }
697
698         /* Not found */
699         return (NULL);
700 }
701
702 /**
703  * Find the entry matching @p r in @p dwa's references, if any.
704  * 
705  * @param dwa The dynamic window allocation to search
706  * @param r The resource to search for in @p dwa.
707  */
708 static struct bhndb_dw_rentry *
709 bhndb_dw_find_resource_entry(struct bhndb_dw_alloc *dwa, struct resource *r)
710 {
711         struct bhndb_dw_rentry  *rentry;
712
713         LIST_FOREACH(rentry, &dwa->refs, dw_link) {
714                 struct resource *dw_res = rentry->dw_res;
715
716                 /* Match dev/rid/addr/size */
717                 if (rman_get_device(dw_res)     != rman_get_device(r) ||
718                         rman_get_rid(dw_res)    != rman_get_rid(r) ||
719                         rman_get_start(dw_res)  != rman_get_start(r) ||
720                         rman_get_size(dw_res)   != rman_get_size(r))
721                 {
722                         continue;
723                 }
724
725                 /* Matching allocation found */
726                 return (rentry);
727         }
728
729         return (NULL);
730 }
731
732 /**
733  * Find the dynamic region allocated for @p r, if any.
734  * 
735  * @param br The resource state to search.
736  * @param r The resource to search for.
737  * 
738  * @retval bhndb_dw_alloc The allocation record for @p r.
739  * @retval NULL if no dynamic window is allocated for @p r.
740  */
741 struct bhndb_dw_alloc *
742 bhndb_dw_find_resource(struct bhndb_resources *br, struct resource *r)
743 {
744         struct bhndb_dw_alloc   *dwa;
745
746         for (size_t i = 0; i < br->dwa_count; i++) {
747                 dwa = &br->dw_alloc[i];
748
749                 /* Skip free dynamic windows */
750                 if (bhndb_dw_is_free(br, dwa))
751                         continue;
752
753                 /* Matching allocation found? */
754                 if (bhndb_dw_find_resource_entry(dwa, r) != NULL)
755                         return (dwa);
756         }
757
758         return (NULL);
759 }
760
761 /**
762  * Find an existing dynamic window mapping @p size bytes
763  * at @p addr. The window may or may not be free.
764  * 
765  * @param br The resource state to search.
766  * @param addr The requested starting address.
767  * @param size The requested size.
768  * 
769  * @retval bhndb_dw_alloc A window allocation that fully contains the requested
770  * range.
771  * @retval NULL If no mapping region can be found.
772  */
773 struct bhndb_dw_alloc *
774 bhndb_dw_find_mapping(struct bhndb_resources *br, bhnd_addr_t addr,
775     bhnd_size_t size)
776 {
777         struct bhndb_dw_alloc           *dwr;
778         const struct bhndb_regwin       *win;
779
780         /* Search for an existing dynamic mapping of this address range. */
781         for (size_t i = 0; i < br->dwa_count; i++) {
782                 dwr = &br->dw_alloc[i];
783                 win = dwr->win;
784
785                 /* Verify the range */
786                 if (addr < dwr->target)
787                         continue;
788
789                 if (addr + size > dwr->target + win->win_size)
790                         continue;
791
792                 /* Found a usable mapping */
793                 return (dwr);
794         }
795
796         /* not found */
797         return (NULL);
798 }
799
800 /**
801  * Retain a reference to @p dwa for use by @p res.
802  * 
803  * @param br The resource state owning @p dwa.
804  * @param dwa The allocation record to be retained.
805  * @param res The resource that will own a reference to @p dwa.
806  * 
807  * @retval 0 success
808  * @retval ENOMEM Failed to allocate a new reference structure.
809  */
810 int
811 bhndb_dw_retain(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa,
812     struct resource *res)
813 {
814         struct bhndb_dw_rentry *rentry;
815
816         KASSERT(bhndb_dw_find_resource_entry(dwa, res) == NULL,
817             ("double-retain of dynamic window for same resource"));
818
819         /* Insert a reference entry; we use M_NOWAIT to allow use from
820          * within a non-sleepable lock */
821         rentry = malloc(sizeof(*rentry), M_BHND, M_NOWAIT);
822         if (rentry == NULL)
823                 return (ENOMEM);
824
825         rentry->dw_res = res;
826         LIST_INSERT_HEAD(&dwa->refs, rentry, dw_link);
827
828         /* Update the free list */
829         bit_set(br->dwa_freelist, dwa->rnid);
830  
831         return (0);
832 }
833
834 /**
835  * Release a reference to @p dwa previously retained by @p res. If the
836  * reference count of @p dwa reaches zero, it will be added to the
837  * free list.
838  * 
839  * @param br The resource state owning @p dwa.
840  * @param dwa The allocation record to be released.
841  * @param res The resource that currently owns a reference to @p dwa.
842  */
843 void
844 bhndb_dw_release(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa,
845     struct resource *r)
846 {
847         struct bhndb_dw_rentry  *rentry;
848
849         /* Find the rentry */
850         rentry = bhndb_dw_find_resource_entry(dwa, r);
851         KASSERT(rentry != NULL, ("over release of resource entry"));
852
853         LIST_REMOVE(rentry, dw_link);
854         free(rentry, M_BHND);
855
856         /* If this was the last reference, update the free list */
857         if (LIST_EMPTY(&dwa->refs))
858                 bit_clear(br->dwa_freelist, dwa->rnid);
859 }
860
861 /**
862  * Attempt to set (or reset) the target address of @p dwa to map @p size bytes
863  * at @p addr.
864  * 
865  * This will apply any necessary window alignment and verify that
866  * the window is capable of mapping the requested range prior to modifying
867  * therecord.
868  * 
869  * @param dev The device on which to issue the BHNDB_SET_WINDOW_ADDR() request.
870  * @param br The resource state owning @p dwa.
871  * @param dwa The allocation record to be configured.
872  * @param addr The address to be mapped via @p dwa.
873  * @param size The number of bytes to be mapped at @p addr.
874  *
875  * @retval 0 success
876  * @retval non-zero no usable register window available.
877  */
878 int
879 bhndb_dw_set_addr(device_t dev, struct bhndb_resources *br,
880     struct bhndb_dw_alloc *dwa, bus_addr_t addr, bus_size_t size)
881 {
882         const struct bhndb_regwin       *rw;
883         bus_addr_t                       offset;
884         int                              error;
885
886         rw = dwa->win;
887
888         KASSERT(bhndb_dw_is_free(br, dwa),
889             ("attempting to set the target address on an in-use window"));
890
891         /* Page-align the target address */
892         offset = addr % rw->win_size;
893         dwa->target = addr - offset;
894
895         /* Verify that the window is large enough for the full target */
896         if (rw->win_size - offset < size)
897                 return (ENOMEM);
898         
899         /* Update the window target */
900         error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target);
901         if (error) {
902                 dwa->target = 0x0;
903                 return (error);
904         }
905
906         return (0);
907 }
908
909 /**
910  * Return the count of @p type register windows in @p table.
911  * 
912  * @param table The table to search.
913  * @param type The required window type, or BHNDB_REGWIN_T_INVALID to
914  * count all register window types.
915  */
916 size_t
917 bhndb_regwin_count(const struct bhndb_regwin *table,
918     bhndb_regwin_type_t type)
919 {
920         const struct bhndb_regwin       *rw;
921         size_t                           count;
922
923         count = 0;
924         for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) {
925                 if (type == BHNDB_REGWIN_T_INVALID || rw->win_type == type)
926                         count++;
927         }
928
929         return (count);
930 }
931
932 /**
933  * Search @p table for the first window with the given @p type.
934  * 
935  * @param table The table to search.
936  * @param type The required window type.
937  * @param min_size The minimum window size.
938  * 
939  * @retval bhndb_regwin The first matching window.
940  * @retval NULL If no window of the requested type could be found. 
941  */
942 const struct bhndb_regwin *
943 bhndb_regwin_find_type(const struct bhndb_regwin *table,
944     bhndb_regwin_type_t type, bus_size_t min_size)
945 {
946         const struct bhndb_regwin *rw;
947
948         for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++)
949         {
950                 if (rw->win_type == type && rw->win_size >= min_size)
951                         return (rw);
952         }
953
954         return (NULL);
955 }
956
957 /**
958  * Search @p windows for the first matching core window.
959  * 
960  * @param table The table to search.
961  * @param class The required core class.
962  * @param unit The required core unit, or -1.
963  * @param port_type The required port type.
964  * @param port The required port.
965  * @param region The required region.
966  *
967  * @retval bhndb_regwin The first matching window.
968  * @retval NULL If no matching window was found. 
969  */
970 const struct bhndb_regwin *
971 bhndb_regwin_find_core(const struct bhndb_regwin *table, bhnd_devclass_t class,
972     int unit, bhnd_port_type port_type, u_int port, u_int region)
973 {
974         const struct bhndb_regwin *rw;
975         
976         for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++)
977         {
978                 if (rw->win_type != BHNDB_REGWIN_T_CORE)
979                         continue;
980
981                 if (rw->d.core.class != class)
982                         continue;
983                 
984                 if (unit != -1 && rw->d.core.unit != unit)
985                         continue;
986
987                 if (rw->d.core.port_type != port_type)
988                         continue;
989
990                 if (rw->d.core.port != port)
991                         continue;
992                 
993                 if (rw->d.core.region != region)
994                         continue;
995
996                 return (rw);
997         }
998
999         return (NULL);
1000 }
1001
1002 /**
1003  * Search @p windows for the best available window of at least @p min_size.
1004  * 
1005  * Search order:
1006  * - BHND_REGWIN_T_CORE
1007  * - BHND_REGWIN_T_DYN
1008  * 
1009  * @param table The table to search.
1010  * @param class The required core class.
1011  * @param unit The required core unit, or -1.
1012  * @param port_type The required port type.
1013  * @param port The required port.
1014  * @param region The required region.
1015  * @param min_size The minimum window size.
1016  *
1017  * @retval bhndb_regwin The first matching window.
1018  * @retval NULL If no matching window was found. 
1019  */
1020 const struct bhndb_regwin *
1021 bhndb_regwin_find_best(const struct bhndb_regwin *table,
1022     bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port,
1023     u_int region, bus_size_t min_size)
1024 {
1025         const struct bhndb_regwin *rw;
1026
1027         /* Prefer a fixed core mapping */
1028         rw = bhndb_regwin_find_core(table, class, unit, port_type,
1029             port, region);
1030         if (rw != NULL)
1031                 return (rw);
1032
1033         /* Fall back on a generic dynamic window */
1034         return (bhndb_regwin_find_type(table, BHNDB_REGWIN_T_DYN, min_size));
1035 }
1036
1037 /**
1038  * Return true if @p regw defines a BHNDB_REGWIN_T_CORE register window
1039  * that matches against @p core.
1040  * 
1041  * @param regw A register window to match against.
1042  * @param core The bhnd(4) core info to match against @p regw.
1043  */
1044 bool
1045 bhndb_regwin_match_core(const struct bhndb_regwin *regw,
1046     struct bhnd_core_info *core)
1047 {
1048         /* Only core windows are supported */
1049         if (regw->win_type != BHNDB_REGWIN_T_CORE)
1050                 return (false);
1051
1052         /* Device class must match */
1053         if (bhnd_core_class(core) != regw->d.core.class)
1054                 return (false);
1055
1056         /* Device unit must match */
1057         if (core->unit != regw->d.core.unit)
1058                 return (false);
1059
1060         /* Matches */
1061         return (true);
1062 }
1063
1064 /**
1065  * Search for a core resource priority descriptor in @p table that matches
1066  * @p core.
1067  * 
1068  * @param table The table to search.
1069  * @param core The core to match against @p table.
1070  */
1071 const struct bhndb_hw_priority *
1072 bhndb_hw_priority_find_core(const struct bhndb_hw_priority *table,
1073     struct bhnd_core_info *core)
1074 {
1075         const struct bhndb_hw_priority  *hp;
1076
1077         for (hp = table; hp->ports != NULL; hp++) {
1078                 if (bhnd_core_matches(core, &hp->match))
1079                         return (hp);
1080         }
1081
1082         /* not found */
1083         return (NULL);
1084 }