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 36 #include "bhndb_private.h" 37 #include "bhndbvar.h" 38 39 /** 40 * Attach a BHND bridge device to @p parent. 41 * 42 * @param parent A parent PCI device. 43 * @param[out] bhndb On success, the probed and attached bhndb bridge device. 44 * @param unit The device unit number, or -1 to select the next available unit 45 * number. 46 * 47 * @retval 0 success 48 * @retval non-zero Failed to attach the bhndb device. 49 */ 50 int 51 bhndb_attach_bridge(device_t parent, device_t *bhndb, int unit) 52 { 53 int error; 54 55 *bhndb = device_add_child(parent, devclass_get_name(bhndb_devclass), 56 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 the resource containing @p win. 187 * 188 * @param br The bhndb resource state to search. 189 * @param win A register window. 190 * 191 * @retval resource the resource containing @p win. 192 * @retval NULL if no resource containing @p win can be found. 193 */ 194 struct resource * 195 bhndb_find_regwin_resource(struct bhndb_resources *br, 196 const struct bhndb_regwin *win) 197 { 198 const struct resource_spec *rspecs; 199 200 rspecs = br->cfg->resource_specs; 201 for (u_int i = 0; rspecs[i].type != -1; i++) { 202 if (win->res.type != rspecs[i].type) 203 continue; 204 205 if (win->res.rid != rspecs[i].rid) 206 continue; 207 208 /* Found declared resource */ 209 return (br->res[i]); 210 } 211 212 device_printf(br->dev, 213 "missing regwin resource spec (type=%d, rid=%d)\n", 214 win->res.type, win->res.rid); 215 216 return (NULL); 217 } 218 219 /** 220 * Allocate and initialize a new resource state structure, allocating 221 * bus resources from @p parent_dev according to @p cfg. 222 * 223 * @param dev The bridge device. 224 * @param parent_dev The parent device from which resources will be allocated. 225 * @param cfg The hardware configuration to be used. 226 */ 227 struct bhndb_resources * 228 bhndb_alloc_resources(device_t dev, device_t parent_dev, 229 const struct bhndb_hwcfg *cfg) 230 { 231 struct bhndb_resources *r; 232 const struct bhndb_regwin *win; 233 bus_size_t last_window_size; 234 size_t res_num; 235 u_int rnid; 236 int error; 237 bool free_parent_res; 238 239 free_parent_res = false; 240 241 r = malloc(sizeof(*r), M_BHND, M_NOWAIT|M_ZERO); 242 if (r == NULL) 243 return (NULL); 244 245 /* Basic initialization */ 246 r->dev = dev; 247 r->parent_dev = parent_dev; 248 r->cfg = cfg; 249 r->min_prio = BHNDB_PRIORITY_NONE; 250 STAILQ_INIT(&r->bus_regions); 251 252 /* Determine our bridge resource count from the hardware config. */ 253 res_num = 0; 254 for (size_t i = 0; cfg->resource_specs[i].type != -1; i++) 255 res_num++; 256 257 /* Allocate space for a non-const copy of our resource_spec 258 * table; this will be updated with the RIDs assigned by 259 * bus_alloc_resources. */ 260 r->res_spec = malloc(sizeof(r->res_spec[0]) * (res_num + 1), M_BHND, 261 M_NOWAIT); 262 if (r->res_spec == NULL) 263 goto failed; 264 265 /* Initialize and terminate the table */ 266 for (size_t i = 0; i < res_num; i++) 267 r->res_spec[i] = cfg->resource_specs[i]; 268 269 r->res_spec[res_num].type = -1; 270 271 /* Allocate space for our resource references */ 272 r->res = malloc(sizeof(r->res[0]) * res_num, M_BHND, M_NOWAIT); 273 if (r->res == NULL) 274 goto failed; 275 276 /* Allocate resources */ 277 error = bus_alloc_resources(r->parent_dev, r->res_spec, r->res); 278 if (error) { 279 device_printf(r->dev, 280 "could not allocate bridge resources on %s: %d\n", 281 device_get_nameunit(r->parent_dev), error); 282 goto failed; 283 } else { 284 free_parent_res = true; 285 } 286 287 /* Fetch the dynamic regwin count and verify that it does not exceed 288 * what is representable via our freelist bitmask. */ 289 r->dwa_count = bhndb_regwin_count(cfg->register_windows, 290 BHNDB_REGWIN_T_DYN); 291 if (r->dwa_count >= (8 * sizeof(r->dwa_freelist))) { 292 device_printf(r->dev, "max dynamic regwin count exceeded\n"); 293 goto failed; 294 } 295 296 /* Allocate the dynamic window allocation table. */ 297 r->dw_alloc = malloc(sizeof(r->dw_alloc[0]) * r->dwa_count, M_BHND, 298 M_NOWAIT); 299 if (r->dw_alloc == NULL) 300 goto failed; 301 302 /* Initialize the dynamic window table and freelist. */ 303 r->dwa_freelist = 0; 304 rnid = 0; 305 last_window_size = 0; 306 for (win = cfg->register_windows; 307 win->win_type != BHNDB_REGWIN_T_INVALID; win++) 308 { 309 struct bhndb_dw_alloc *dwa; 310 311 /* Skip non-DYN windows */ 312 if (win->win_type != BHNDB_REGWIN_T_DYN) 313 continue; 314 315 /* Validate the window size */ 316 if (win->win_size == 0) { 317 device_printf(r->dev, "ignoring zero-length dynamic " 318 "register window\n"); 319 continue; 320 } else if (last_window_size == 0) { 321 last_window_size = win->win_size; 322 } else if (last_window_size != win->win_size) { 323 /* 324 * No existing hardware should trigger this. 325 * 326 * If you run into this in the future, the dynamic 327 * window allocator and the resource priority system 328 * will need to be extended to support multiple register 329 * window allocation pools. 330 */ 331 device_printf(r->dev, "devices that vend multiple " 332 "dynamic register window sizes are not currently " 333 "supported\n"); 334 goto failed; 335 } 336 337 dwa = &r->dw_alloc[rnid]; 338 dwa->win = win; 339 dwa->parent_res = NULL; 340 dwa->rnid = rnid; 341 dwa->target = 0x0; 342 343 LIST_INIT(&dwa->refs); 344 345 /* Find and validate corresponding resource. */ 346 dwa->parent_res = bhndb_find_regwin_resource(r, win); 347 if (dwa->parent_res == NULL) 348 goto failed; 349 350 if (rman_get_size(dwa->parent_res) < win->win_offset + 351 win->win_size) 352 { 353 device_printf(r->dev, "resource %d too small for " 354 "register window with offset %llx and size %llx\n", 355 rman_get_rid(dwa->parent_res), 356 (unsigned long long) win->win_offset, 357 (unsigned long long) win->win_size); 358 359 error = EINVAL; 360 goto failed; 361 } 362 363 /* Add to freelist */ 364 r->dwa_freelist |= (1 << rnid); 365 366 rnid++; 367 } 368 369 return (r); 370 371 failed: 372 if (free_parent_res) 373 bus_release_resources(r->parent_dev, r->res_spec, r->res); 374 375 if (r->res != NULL) 376 free(r->res, M_BHND); 377 378 if (r->res_spec != NULL) 379 free(r->res_spec, M_BHND); 380 381 if (r->dw_alloc != NULL) 382 free(r->dw_alloc, M_BHND); 383 384 free (r, M_BHND); 385 386 return (NULL); 387 } 388 389 /** 390 * Deallocate the given bridge resource structure and any associated resources. 391 * 392 * @param br Resource state to be deallocated. 393 */ 394 void 395 bhndb_free_resources(struct bhndb_resources *br) 396 { 397 struct bhndb_region *region, *r_next; 398 struct bhndb_dw_alloc *dwa; 399 struct bhndb_dw_rentry *dwr, *dwr_next; 400 401 /* No window regions may still be held */ 402 if (__builtin_popcount(br->dwa_freelist) != br->dwa_count) { 403 device_printf(br->dev, "leaked %llu dynamic register regions\n", 404 (unsigned long long) br->dwa_count - br->dwa_freelist); 405 } 406 407 /* Release resources allocated through our parent. */ 408 bus_release_resources(br->parent_dev, br->res_spec, br->res); 409 410 /* Clean up resource reservations */ 411 for (size_t i = 0; i < br->dwa_count; i++) { 412 dwa = &br->dw_alloc[i]; 413 414 LIST_FOREACH_SAFE(dwr, &dwa->refs, dw_link, dwr_next) { 415 LIST_REMOVE(dwr, dw_link); 416 free(dwr, M_BHND); 417 } 418 } 419 420 /* Release bus regions */ 421 STAILQ_FOREACH_SAFE(region, &br->bus_regions, link, r_next) { 422 STAILQ_REMOVE(&br->bus_regions, region, bhndb_region, link); 423 free(region, M_BHND); 424 } 425 426 /* Free backing resource state structures */ 427 free(br->res, M_BHND); 428 free(br->res_spec, M_BHND); 429 free(br->dw_alloc, M_BHND); 430 } 431 432 /** 433 * Add a bus region entry to @p r for the given base @p addr and @p size. 434 * 435 * @param br The resource state to which the bus region entry will be added. 436 * @param addr The base address of this region. 437 * @param size The size of this region. 438 * @param priority The resource priority to be assigned to allocations 439 * made within this bus region. 440 * @param static_regwin If available, a static register window mapping this 441 * bus region entry. If not available, NULL. 442 * 443 * @retval 0 success 444 * @retval non-zero if adding the bus region fails. 445 */ 446 int 447 bhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, 448 bhnd_size_t size, bhndb_priority_t priority, 449 const struct bhndb_regwin *static_regwin) 450 { 451 struct bhndb_region *reg; 452 453 /* Insert in the bus resource list */ 454 reg = malloc(sizeof(*reg), M_BHND, M_NOWAIT); 455 if (reg == NULL) 456 return (ENOMEM); 457 458 *reg = (struct bhndb_region) { 459 .addr = addr, 460 .size = size, 461 .priority = priority, 462 .static_regwin = static_regwin 463 }; 464 465 STAILQ_INSERT_HEAD(&br->bus_regions, reg, link); 466 467 return (0); 468 } 469 470 /** 471 * Find a bus region that maps @p size bytes at @p addr. 472 * 473 * @param br The resource state to search. 474 * @param addr The requested starting address. 475 * @param size The requested size. 476 * 477 * @retval bhndb_region A region that fully contains the requested range. 478 * @retval NULL If no mapping region can be found. 479 */ 480 struct bhndb_region * 481 bhndb_find_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, 482 bhnd_size_t size) 483 { 484 struct bhndb_region *region; 485 486 STAILQ_FOREACH(region, &br->bus_regions, link) { 487 /* Request must fit within the region's mapping */ 488 if (addr < region->addr) 489 continue; 490 491 if (addr + size > region->addr + region->size) 492 continue; 493 494 return (region); 495 } 496 497 /* Not found */ 498 return (NULL); 499 } 500 501 /** 502 * Find the entry matching @p r in @p dwa's references, if any. 503 * 504 * @param dwa The dynamic window allocation to search 505 * @param r The resource to search for in @p dwa. 506 */ 507 static struct bhndb_dw_rentry * 508 bhndb_dw_find_resource_entry(struct bhndb_dw_alloc *dwa, struct resource *r) 509 { 510 struct bhndb_dw_rentry *rentry; 511 512 LIST_FOREACH(rentry, &dwa->refs, dw_link) { 513 struct resource *dw_res = rentry->dw_res; 514 515 /* Match dev/rid/addr/size */ 516 if (rman_get_device(dw_res) != rman_get_device(r) || 517 rman_get_rid(dw_res) != rman_get_rid(r) || 518 rman_get_start(dw_res) != rman_get_start(r) || 519 rman_get_size(dw_res) != rman_get_size(r)) 520 { 521 continue; 522 } 523 524 /* Matching allocation found */ 525 return (rentry); 526 } 527 528 return (NULL); 529 } 530 531 /** 532 * Find the dynamic region allocated for @p r, if any. 533 * 534 * @param br The resource state to search. 535 * @param r The resource to search for. 536 * 537 * @retval bhndb_dw_alloc The allocation record for @p r. 538 * @retval NULL if no dynamic window is allocated for @p r. 539 */ 540 struct bhndb_dw_alloc * 541 bhndb_dw_find_resource(struct bhndb_resources *br, struct resource *r) 542 { 543 struct bhndb_dw_alloc *dwa; 544 545 for (size_t i = 0; i < br->dwa_count; i++) { 546 dwa = &br->dw_alloc[i]; 547 548 /* Skip free dynamic windows */ 549 if (bhndb_dw_is_free(br, dwa)) 550 continue; 551 552 /* Matching allocation found? */ 553 if (bhndb_dw_find_resource_entry(dwa, r) != NULL) 554 return (dwa); 555 } 556 557 return (NULL); 558 } 559 560 /** 561 * Find an existing dynamic window mapping @p size bytes 562 * at @p addr. The window may or may not be free. 563 * 564 * @param br The resource state to search. 565 * @param addr The requested starting address. 566 * @param size The requested size. 567 * 568 * @retval bhndb_dw_alloc A window allocation that fully contains the requested 569 * range. 570 * @retval NULL If no mapping region can be found. 571 */ 572 struct bhndb_dw_alloc * 573 bhndb_dw_find_mapping(struct bhndb_resources *br, bhnd_addr_t addr, 574 bhnd_size_t size) 575 { 576 struct bhndb_dw_alloc *dwr; 577 const struct bhndb_regwin *win; 578 579 /* Search for an existing dynamic mapping of this address range. */ 580 for (size_t i = 0; i < br->dwa_count; i++) { 581 dwr = &br->dw_alloc[i]; 582 win = dwr->win; 583 584 /* Verify the range */ 585 if (addr < dwr->target) 586 continue; 587 588 if (addr + size > dwr->target + win->win_size) 589 continue; 590 591 /* Found a usable mapping */ 592 return (dwr); 593 } 594 595 /* not found */ 596 return (NULL); 597 } 598 599 /** 600 * Retain a reference to @p dwa for use by @p res. 601 * 602 * @param br The resource state owning @p dwa. 603 * @param dwa The allocation record to be retained. 604 * @param res The resource that will own a reference to @p dwa. 605 * 606 * @retval 0 success 607 * @retval ENOMEM Failed to allocate a new reference structure. 608 */ 609 int 610 bhndb_dw_retain(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, 611 struct resource *res) 612 { 613 struct bhndb_dw_rentry *rentry; 614 615 KASSERT(bhndb_dw_find_resource_entry(dwa, res) == NULL, 616 ("double-retain of dynamic window for same resource")); 617 618 /* Insert a reference entry; we use M_NOWAIT to allow use from 619 * within a non-sleepable lock */ 620 rentry = malloc(sizeof(*rentry), M_BHND, M_NOWAIT); 621 if (rentry == NULL) 622 return (ENOMEM); 623 624 rentry->dw_res = res; 625 LIST_INSERT_HEAD(&dwa->refs, rentry, dw_link); 626 627 /* Update the free list */ 628 br->dwa_freelist &= ~(1 << (dwa->rnid)); 629 630 return (0); 631 } 632 633 /** 634 * Release a reference to @p dwa previously retained by @p res. If the 635 * reference count of @p dwa reaches zero, it will be added to the 636 * free list. 637 * 638 * @param br The resource state owning @p dwa. 639 * @param dwa The allocation record to be released. 640 * @param res The resource that currently owns a reference to @p dwa. 641 */ 642 void 643 bhndb_dw_release(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, 644 struct resource *r) 645 { 646 struct bhndb_dw_rentry *rentry; 647 648 /* Find the rentry */ 649 rentry = bhndb_dw_find_resource_entry(dwa, r); 650 KASSERT(rentry != NULL, ("over release of resource entry")); 651 652 LIST_REMOVE(rentry, dw_link); 653 free(rentry, M_BHND); 654 655 /* If this was the last reference, update the free list */ 656 if (LIST_EMPTY(&dwa->refs)) 657 br->dwa_freelist |= (1 << (dwa->rnid)); 658 } 659 660 /** 661 * Attempt to set (or reset) the target address of @p dwa to map @p size bytes 662 * at @p addr. 663 * 664 * This will apply any necessary window alignment and verify that 665 * the window is capable of mapping the requested range prior to modifying 666 * therecord. 667 * 668 * @param dev The device on which to issue the BHNDB_SET_WINDOW_ADDR() request. 669 * @param br The resource state owning @p dwa. 670 * @param dwa The allocation record to be configured. 671 * @param addr The address to be mapped via @p dwa. 672 * @param size The number of bytes to be mapped at @p addr. 673 * 674 * @retval 0 success 675 * @retval non-zero no usable register window available. 676 */ 677 int 678 bhndb_dw_set_addr(device_t dev, struct bhndb_resources *br, 679 struct bhndb_dw_alloc *dwa, bus_addr_t addr, bus_size_t size) 680 { 681 const struct bhndb_regwin *rw; 682 bus_addr_t offset; 683 int error; 684 685 rw = dwa->win; 686 687 KASSERT(bhndb_dw_is_free(br, dwa), 688 ("attempting to set the target address on an in-use window")); 689 690 /* Page-align the target address */ 691 offset = addr % rw->win_size; 692 dwa->target = addr - offset; 693 694 /* Verify that the window is large enough for the full target */ 695 if (rw->win_size - offset < size) 696 return (ENOMEM); 697 698 /* Update the window target */ 699 error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); 700 if (error) { 701 dwa->target = 0x0; 702 return (error); 703 } 704 705 return (0); 706 } 707 708 /** 709 * Return the count of @p type register windows in @p table. 710 * 711 * @param table The table to search. 712 * @param type The required window type, or BHNDB_REGWIN_T_INVALID to 713 * count all register window types. 714 */ 715 size_t 716 bhndb_regwin_count(const struct bhndb_regwin *table, 717 bhndb_regwin_type_t type) 718 { 719 const struct bhndb_regwin *rw; 720 size_t count; 721 722 count = 0; 723 for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { 724 if (type == BHNDB_REGWIN_T_INVALID || rw->win_type == type) 725 count++; 726 } 727 728 return (count); 729 } 730 731 /** 732 * Search @p table for the first window with the given @p type. 733 * 734 * @param table The table to search. 735 * @param type The required window type. 736 * @param min_size The minimum window size. 737 * 738 * @retval bhndb_regwin The first matching window. 739 * @retval NULL If no window of the requested type could be found. 740 */ 741 const struct bhndb_regwin * 742 bhndb_regwin_find_type(const struct bhndb_regwin *table, 743 bhndb_regwin_type_t type, bus_size_t min_size) 744 { 745 const struct bhndb_regwin *rw; 746 747 for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) 748 { 749 if (rw->win_type == type && rw->win_size >= min_size) 750 return (rw); 751 } 752 753 return (NULL); 754 } 755 756 /** 757 * Search @p windows for the first matching core window. 758 * 759 * @param table The table to search. 760 * @param class The required core class. 761 * @param unit The required core unit, or -1. 762 * @param port_type The required port type. 763 * @param port The required port. 764 * @param region The required region. 765 * 766 * @retval bhndb_regwin The first matching window. 767 * @retval NULL If no matching window was found. 768 */ 769 const struct bhndb_regwin * 770 bhndb_regwin_find_core(const struct bhndb_regwin *table, bhnd_devclass_t class, 771 int unit, bhnd_port_type port_type, u_int port, u_int region) 772 { 773 const struct bhndb_regwin *rw; 774 775 for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) 776 { 777 if (rw->win_type != BHNDB_REGWIN_T_CORE) 778 continue; 779 780 if (rw->core.class != class) 781 continue; 782 783 if (unit != -1 && rw->core.unit != unit) 784 continue; 785 786 if (rw->core.port_type != port_type) 787 continue; 788 789 if (rw->core.port != port) 790 continue; 791 792 if (rw->core.region != region) 793 continue; 794 795 return (rw); 796 } 797 798 return (NULL); 799 } 800 801 /** 802 * Search @p windows for the best available window of at least @p min_size. 803 * 804 * Search order: 805 * - BHND_REGWIN_T_CORE 806 * - BHND_REGWIN_T_DYN 807 * 808 * @param table The table to search. 809 * @param class The required core class. 810 * @param unit The required core unit, or -1. 811 * @param port_type The required port type. 812 * @param port The required port. 813 * @param region The required region. 814 * @param min_size The minimum window size. 815 * 816 * @retval bhndb_regwin The first matching window. 817 * @retval NULL If no matching window was found. 818 */ 819 const struct bhndb_regwin * 820 bhndb_regwin_find_best(const struct bhndb_regwin *table, 821 bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, 822 u_int region, bus_size_t min_size) 823 { 824 const struct bhndb_regwin *rw; 825 826 /* Prefer a fixed core mapping */ 827 rw = bhndb_regwin_find_core(table, class, unit, port_type, 828 port, region); 829 if (rw != NULL) 830 return (rw); 831 832 /* Fall back on a generic dynamic window */ 833 return (bhndb_regwin_find_type(table, BHNDB_REGWIN_T_DYN, min_size)); 834 } 835 836 /** 837 * Return true if @p regw defines a static port register window, and 838 * the mapped port is actually defined on @p dev. 839 * 840 * @param regw A register window to match against. 841 * @param dev A bhnd(4) bus device. 842 */ 843 bool 844 bhndb_regwin_matches_device(const struct bhndb_regwin *regw, device_t dev) 845 { 846 /* Only core windows are supported */ 847 if (regw->win_type != BHNDB_REGWIN_T_CORE) 848 return (false); 849 850 /* Device class must match */ 851 if (bhnd_get_class(dev) != regw->core.class) 852 return (false); 853 854 /* Device unit must match */ 855 if (bhnd_get_core_unit(dev) != regw->core.unit) 856 return (false); 857 858 /* The regwin port/region must be defined. */ 859 if (!bhnd_is_region_valid(dev, regw->core.port_type, regw->core.port, 860 regw->core.region)) 861 { 862 return (false); 863 } 864 865 /* Matches */ 866 return (true); 867 } 868 869 /** 870 * Search for a core resource priority descriptor in @p table that matches 871 * @p device. 872 * 873 * @param table The table to search. 874 * @param device A bhnd(4) bus device. 875 */ 876 const struct bhndb_hw_priority * 877 bhndb_hw_priority_find_device(const struct bhndb_hw_priority *table, 878 device_t device) 879 { 880 const struct bhndb_hw_priority *hp; 881 882 for (hp = table; hp->ports != NULL; hp++) { 883 if (bhnd_device_matches(device, &hp->match)) 884 return (hp); 885 } 886 887 /* not found */ 888 return (NULL); 889 } 890