1296077Sadrian/*- 2296077Sadrian * Copyright (c) 2015 Landon Fuller <landon@landonf.org> 3296077Sadrian * All rights reserved. 4296077Sadrian * 5296077Sadrian * Redistribution and use in source and binary forms, with or without 6296077Sadrian * modification, are permitted provided that the following conditions 7296077Sadrian * are met: 8296077Sadrian * 1. Redistributions of source code must retain the above copyright 9296077Sadrian * notice, this list of conditions and the following disclaimer, 10296077Sadrian * without modification. 11296077Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12296077Sadrian * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13296077Sadrian * redistribution must be conditioned upon including a substantially 14296077Sadrian * similar Disclaimer requirement for further binary redistribution. 15296077Sadrian * 16296077Sadrian * NO WARRANTY 17296077Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18296077Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19296077Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20296077Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21296077Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22296077Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23296077Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24296077Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25296077Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26296077Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27296077Sadrian * THE POSSIBILITY OF SUCH DAMAGES. 28296077Sadrian */ 29296077Sadrian 30296077Sadrian#include <sys/cdefs.h> 31296077Sadrian__FBSDID("$FreeBSD: releng/11.0/sys/dev/bhnd/bhndb/bhndb_subr.c 300628 2016-05-24 21:20:17Z adrian $"); 32296077Sadrian 33296077Sadrian#include <sys/param.h> 34296077Sadrian#include <sys/kernel.h> 35296077Sadrian 36296077Sadrian#include "bhndb_private.h" 37296077Sadrian#include "bhndbvar.h" 38296077Sadrian 39296077Sadrian/** 40296077Sadrian * Attach a BHND bridge device to @p parent. 41296077Sadrian * 42296077Sadrian * @param parent A parent PCI device. 43296077Sadrian * @param[out] bhndb On success, the probed and attached bhndb bridge device. 44296077Sadrian * @param unit The device unit number, or -1 to select the next available unit 45296077Sadrian * number. 46296077Sadrian * 47296077Sadrian * @retval 0 success 48296077Sadrian * @retval non-zero Failed to attach the bhndb device. 49296077Sadrian */ 50296077Sadrianint 51296077Sadrianbhndb_attach_bridge(device_t parent, device_t *bhndb, int unit) 52296077Sadrian{ 53296077Sadrian int error; 54296077Sadrian 55299097Sadrian *bhndb = device_add_child(parent, "bhndb", unit); 56296077Sadrian if (*bhndb == NULL) 57296077Sadrian return (ENXIO); 58296077Sadrian 59296077Sadrian if (!(error = device_probe_and_attach(*bhndb))) 60296077Sadrian return (0); 61296077Sadrian 62296077Sadrian if ((device_delete_child(parent, *bhndb))) 63296077Sadrian device_printf(parent, "failed to detach bhndb child\n"); 64296077Sadrian 65296077Sadrian return (error); 66296077Sadrian} 67296077Sadrian 68296077Sadrian/* 69296077Sadrian * Call BHNDB_SUSPEND_RESOURCE() for all resources in @p rl. 70296077Sadrian */ 71296077Sadrianstatic void 72296077Sadrianbhndb_do_suspend_resources(device_t dev, struct resource_list *rl) 73296077Sadrian{ 74296077Sadrian struct resource_list_entry *rle; 75296077Sadrian 76296077Sadrian /* Suspend all child resources. */ 77296077Sadrian STAILQ_FOREACH(rle, rl, link) { 78296077Sadrian /* Skip non-allocated resources */ 79296077Sadrian if (rle->res == NULL) 80296077Sadrian continue; 81296077Sadrian 82296077Sadrian BHNDB_SUSPEND_RESOURCE(device_get_parent(dev), dev, rle->type, 83296077Sadrian rle->res); 84296077Sadrian } 85296077Sadrian} 86296077Sadrian 87296077Sadrian/** 88296077Sadrian * Helper function for implementing BUS_RESUME_CHILD() on bridged 89296077Sadrian * bhnd(4) buses. 90296077Sadrian * 91296077Sadrian * This implementation of BUS_RESUME_CHILD() uses BUS_GET_RESOURCE_LIST() 92296077Sadrian * to find the child's resources and call BHNDB_SUSPEND_RESOURCE() for all 93296077Sadrian * child resources, ensuring that the device's allocated bridge resources 94296077Sadrian * will be available to other devices during bus resumption. 95296077Sadrian * 96296077Sadrian * Before suspending any resources, @p child is suspended by 97296077Sadrian * calling bhnd_generic_suspend_child(). 98296077Sadrian * 99296077Sadrian * If @p child is not a direct child of @p dev, suspension is delegated to 100296077Sadrian * the @p dev parent. 101296077Sadrian */ 102296077Sadrianint 103296077Sadrianbhnd_generic_br_suspend_child(device_t dev, device_t child) 104296077Sadrian{ 105296077Sadrian struct resource_list *rl; 106296077Sadrian int error; 107296077Sadrian 108296077Sadrian if (device_get_parent(child) != dev) 109296077Sadrian BUS_SUSPEND_CHILD(device_get_parent(dev), child); 110296077Sadrian 111296077Sadrian if (device_is_suspended(child)) 112296077Sadrian return (EBUSY); 113296077Sadrian 114296077Sadrian /* Suspend the child device */ 115296077Sadrian if ((error = bhnd_generic_suspend_child(dev, child))) 116296077Sadrian return (error); 117296077Sadrian 118296077Sadrian /* Fetch the resource list. If none, there's nothing else to do */ 119296077Sadrian rl = BUS_GET_RESOURCE_LIST(device_get_parent(child), child); 120296077Sadrian if (rl == NULL) 121296077Sadrian return (0); 122296077Sadrian 123296077Sadrian /* Suspend all child resources. */ 124296077Sadrian bhndb_do_suspend_resources(dev, rl); 125296077Sadrian 126296077Sadrian return (0); 127296077Sadrian} 128296077Sadrian 129296077Sadrian/** 130296077Sadrian * Helper function for implementing BUS_RESUME_CHILD() on bridged 131296077Sadrian * bhnd(4) bus devices. 132296077Sadrian * 133296077Sadrian * This implementation of BUS_RESUME_CHILD() uses BUS_GET_RESOURCE_LIST() 134296077Sadrian * to find the child's resources and call BHNDB_RESUME_RESOURCE() for all 135296077Sadrian * child resources, before delegating to bhnd_generic_resume_child(). 136296077Sadrian * 137296077Sadrian * If resource resumption fails, @p child will not be resumed. 138296077Sadrian * 139296077Sadrian * If @p child is not a direct child of @p dev, suspension is delegated to 140296077Sadrian * the @p dev parent. 141296077Sadrian */ 142296077Sadrianint 143296077Sadrianbhnd_generic_br_resume_child(device_t dev, device_t child) 144296077Sadrian{ 145296077Sadrian struct resource_list *rl; 146296077Sadrian struct resource_list_entry *rle; 147296077Sadrian int error; 148296077Sadrian 149296077Sadrian if (device_get_parent(child) != dev) 150296077Sadrian BUS_RESUME_CHILD(device_get_parent(dev), child); 151296077Sadrian 152296077Sadrian if (!device_is_suspended(child)) 153296077Sadrian return (EBUSY); 154296077Sadrian 155296077Sadrian /* Fetch the resource list. If none, there's nothing else to do */ 156296077Sadrian rl = BUS_GET_RESOURCE_LIST(device_get_parent(child), child); 157296077Sadrian if (rl == NULL) 158296077Sadrian return (bhnd_generic_resume_child(dev, child)); 159296077Sadrian 160296077Sadrian /* Resume all resources */ 161296077Sadrian STAILQ_FOREACH(rle, rl, link) { 162296077Sadrian /* Skip non-allocated resources */ 163296077Sadrian if (rle->res == NULL) 164296077Sadrian continue; 165296077Sadrian 166296077Sadrian error = BHNDB_RESUME_RESOURCE(device_get_parent(dev), dev, 167296077Sadrian rle->type, rle->res); 168296077Sadrian if (error) { 169296077Sadrian /* Put all resources back into a suspend state */ 170296077Sadrian bhndb_do_suspend_resources(dev, rl); 171296077Sadrian return (error); 172296077Sadrian } 173296077Sadrian } 174296077Sadrian 175296077Sadrian /* Now that all resources are resumed, resume child */ 176296077Sadrian if ((error = bhnd_generic_resume_child(dev, child))) { 177296077Sadrian /* Put all resources back into a suspend state */ 178296077Sadrian bhndb_do_suspend_resources(dev, rl); 179296077Sadrian } 180296077Sadrian 181296077Sadrian return (error); 182296077Sadrian} 183296077Sadrian 184296077Sadrian/** 185298276Sadrian * Find a SYS_RES_MEMORY resource containing the given address range. 186298276Sadrian * 187298276Sadrian * @param br The bhndb resource state to search. 188298276Sadrian * @param start The start address of the range to search for. 189298276Sadrian * @param count The size of the range to search for. 190298276Sadrian * 191298276Sadrian * @retval resource the host resource containing the requested range. 192298276Sadrian * @retval NULL if no resource containing the requested range can be found. 193298276Sadrian */ 194298276Sadrianstruct resource * 195298276Sadrianbhndb_find_resource_range(struct bhndb_resources *br, rman_res_t start, 196298276Sadrian rman_res_t count) 197298276Sadrian{ 198298276Sadrian for (u_int i = 0; br->res_spec[i].type != -1; i++) { 199298276Sadrian struct resource *r = br->res[i]; 200298276Sadrian 201298276Sadrian if (br->res_spec->type != SYS_RES_MEMORY) 202298276Sadrian continue; 203298276Sadrian 204298276Sadrian /* Verify range */ 205298276Sadrian if (rman_get_start(r) > start) 206298276Sadrian continue; 207298276Sadrian 208298276Sadrian if (rman_get_end(r) < (start + count - 1)) 209298276Sadrian continue; 210298276Sadrian 211298276Sadrian return (r); 212298276Sadrian } 213298276Sadrian 214298276Sadrian return (NULL); 215298276Sadrian} 216298276Sadrian 217298276Sadrian/** 218296077Sadrian * Find the resource containing @p win. 219296077Sadrian * 220296077Sadrian * @param br The bhndb resource state to search. 221296077Sadrian * @param win A register window. 222296077Sadrian * 223296077Sadrian * @retval resource the resource containing @p win. 224296077Sadrian * @retval NULL if no resource containing @p win can be found. 225296077Sadrian */ 226296077Sadrianstruct resource * 227296077Sadrianbhndb_find_regwin_resource(struct bhndb_resources *br, 228296077Sadrian const struct bhndb_regwin *win) 229296077Sadrian{ 230296077Sadrian const struct resource_spec *rspecs; 231296077Sadrian 232296077Sadrian rspecs = br->cfg->resource_specs; 233296077Sadrian for (u_int i = 0; rspecs[i].type != -1; i++) { 234296077Sadrian if (win->res.type != rspecs[i].type) 235296077Sadrian continue; 236296077Sadrian 237296077Sadrian if (win->res.rid != rspecs[i].rid) 238296077Sadrian continue; 239296077Sadrian 240296077Sadrian /* Found declared resource */ 241296077Sadrian return (br->res[i]); 242296077Sadrian } 243296077Sadrian 244296077Sadrian device_printf(br->dev, 245296077Sadrian "missing regwin resource spec (type=%d, rid=%d)\n", 246296077Sadrian win->res.type, win->res.rid); 247296077Sadrian 248296077Sadrian return (NULL); 249296077Sadrian} 250296077Sadrian 251296077Sadrian/** 252296077Sadrian * Allocate and initialize a new resource state structure, allocating 253296077Sadrian * bus resources from @p parent_dev according to @p cfg. 254296077Sadrian * 255296077Sadrian * @param dev The bridge device. 256296077Sadrian * @param parent_dev The parent device from which resources will be allocated. 257296077Sadrian * @param cfg The hardware configuration to be used. 258296077Sadrian */ 259296077Sadrianstruct bhndb_resources * 260296077Sadrianbhndb_alloc_resources(device_t dev, device_t parent_dev, 261296077Sadrian const struct bhndb_hwcfg *cfg) 262296077Sadrian{ 263296077Sadrian struct bhndb_resources *r; 264296077Sadrian const struct bhndb_regwin *win; 265296077Sadrian bus_size_t last_window_size; 266296077Sadrian size_t res_num; 267296077Sadrian u_int rnid; 268296077Sadrian int error; 269296077Sadrian bool free_parent_res; 270298276Sadrian bool free_ht_mem, free_br_mem; 271296077Sadrian 272296077Sadrian free_parent_res = false; 273298276Sadrian free_ht_mem = false; 274298276Sadrian free_br_mem = false; 275296077Sadrian 276296077Sadrian r = malloc(sizeof(*r), M_BHND, M_NOWAIT|M_ZERO); 277296077Sadrian if (r == NULL) 278296077Sadrian return (NULL); 279296077Sadrian 280296077Sadrian /* Basic initialization */ 281296077Sadrian r->dev = dev; 282296077Sadrian r->parent_dev = parent_dev; 283296077Sadrian r->cfg = cfg; 284296077Sadrian r->min_prio = BHNDB_PRIORITY_NONE; 285296077Sadrian STAILQ_INIT(&r->bus_regions); 286296077Sadrian 287298276Sadrian /* Initialize host address space resource manager. */ 288298276Sadrian r->ht_mem_rman.rm_start = 0; 289298276Sadrian r->ht_mem_rman.rm_end = ~0; 290298276Sadrian r->ht_mem_rman.rm_type = RMAN_ARRAY; 291298276Sadrian r->ht_mem_rman.rm_descr = "BHNDB host memory"; 292298276Sadrian if ((error = rman_init(&r->ht_mem_rman))) { 293298276Sadrian device_printf(r->dev, "could not initialize ht_mem_rman\n"); 294298276Sadrian goto failed; 295298276Sadrian } 296298276Sadrian free_ht_mem = true; 297298276Sadrian 298298276Sadrian 299298276Sadrian /* Initialize resource manager for the bridged address space. */ 300298276Sadrian r->br_mem_rman.rm_start = 0; 301298276Sadrian r->br_mem_rman.rm_end = BUS_SPACE_MAXADDR_32BIT; 302298276Sadrian r->br_mem_rman.rm_type = RMAN_ARRAY; 303298276Sadrian r->br_mem_rman.rm_descr = "BHNDB bridged memory"; 304298276Sadrian 305298276Sadrian if ((error = rman_init(&r->br_mem_rman))) { 306298276Sadrian device_printf(r->dev, "could not initialize br_mem_rman\n"); 307298276Sadrian goto failed; 308298276Sadrian } 309298276Sadrian free_br_mem = true; 310298276Sadrian 311298276Sadrian error = rman_manage_region(&r->br_mem_rman, 0, BUS_SPACE_MAXADDR_32BIT); 312298276Sadrian if (error) { 313298276Sadrian device_printf(r->dev, "could not configure br_mem_rman\n"); 314298276Sadrian goto failed; 315298276Sadrian } 316298276Sadrian 317298276Sadrian 318296077Sadrian /* Determine our bridge resource count from the hardware config. */ 319296077Sadrian res_num = 0; 320296077Sadrian for (size_t i = 0; cfg->resource_specs[i].type != -1; i++) 321296077Sadrian res_num++; 322296077Sadrian 323296077Sadrian /* Allocate space for a non-const copy of our resource_spec 324296077Sadrian * table; this will be updated with the RIDs assigned by 325296077Sadrian * bus_alloc_resources. */ 326296077Sadrian r->res_spec = malloc(sizeof(r->res_spec[0]) * (res_num + 1), M_BHND, 327296077Sadrian M_NOWAIT); 328296077Sadrian if (r->res_spec == NULL) 329296077Sadrian goto failed; 330296077Sadrian 331296077Sadrian /* Initialize and terminate the table */ 332296077Sadrian for (size_t i = 0; i < res_num; i++) 333296077Sadrian r->res_spec[i] = cfg->resource_specs[i]; 334296077Sadrian 335296077Sadrian r->res_spec[res_num].type = -1; 336296077Sadrian 337296077Sadrian /* Allocate space for our resource references */ 338296077Sadrian r->res = malloc(sizeof(r->res[0]) * res_num, M_BHND, M_NOWAIT); 339296077Sadrian if (r->res == NULL) 340296077Sadrian goto failed; 341296077Sadrian 342296077Sadrian /* Allocate resources */ 343296077Sadrian error = bus_alloc_resources(r->parent_dev, r->res_spec, r->res); 344296077Sadrian if (error) { 345296077Sadrian device_printf(r->dev, 346296077Sadrian "could not allocate bridge resources on %s: %d\n", 347296077Sadrian device_get_nameunit(r->parent_dev), error); 348296077Sadrian goto failed; 349296077Sadrian } else { 350296077Sadrian free_parent_res = true; 351296077Sadrian } 352296077Sadrian 353298276Sadrian /* Add allocated memory resources to our host memory resource manager */ 354298276Sadrian for (u_int i = 0; r->res_spec[i].type != -1; i++) { 355298276Sadrian struct resource *res; 356298276Sadrian 357298276Sadrian /* skip non-memory resources */ 358298276Sadrian if (r->res_spec[i].type != SYS_RES_MEMORY) 359298276Sadrian continue; 360298276Sadrian 361298276Sadrian /* add host resource to set of managed regions */ 362298276Sadrian res = r->res[i]; 363298276Sadrian error = rman_manage_region(&r->ht_mem_rman, rman_get_start(res), 364298276Sadrian rman_get_end(res)); 365298276Sadrian if (error) { 366298276Sadrian device_printf(r->dev, 367298276Sadrian "could not register host memory region with " 368298276Sadrian "ht_mem_rman: %d\n", error); 369298276Sadrian goto failed; 370298276Sadrian } 371298276Sadrian } 372298276Sadrian 373296077Sadrian /* Fetch the dynamic regwin count and verify that it does not exceed 374296077Sadrian * what is representable via our freelist bitmask. */ 375296077Sadrian r->dwa_count = bhndb_regwin_count(cfg->register_windows, 376296077Sadrian BHNDB_REGWIN_T_DYN); 377296077Sadrian if (r->dwa_count >= (8 * sizeof(r->dwa_freelist))) { 378296077Sadrian device_printf(r->dev, "max dynamic regwin count exceeded\n"); 379296077Sadrian goto failed; 380296077Sadrian } 381296077Sadrian 382296077Sadrian /* Allocate the dynamic window allocation table. */ 383296077Sadrian r->dw_alloc = malloc(sizeof(r->dw_alloc[0]) * r->dwa_count, M_BHND, 384296077Sadrian M_NOWAIT); 385296077Sadrian if (r->dw_alloc == NULL) 386296077Sadrian goto failed; 387296077Sadrian 388296077Sadrian /* Initialize the dynamic window table and freelist. */ 389296077Sadrian r->dwa_freelist = 0; 390296077Sadrian rnid = 0; 391296077Sadrian last_window_size = 0; 392296077Sadrian for (win = cfg->register_windows; 393296077Sadrian win->win_type != BHNDB_REGWIN_T_INVALID; win++) 394296077Sadrian { 395296077Sadrian struct bhndb_dw_alloc *dwa; 396296077Sadrian 397296077Sadrian /* Skip non-DYN windows */ 398296077Sadrian if (win->win_type != BHNDB_REGWIN_T_DYN) 399296077Sadrian continue; 400296077Sadrian 401296077Sadrian /* Validate the window size */ 402296077Sadrian if (win->win_size == 0) { 403296077Sadrian device_printf(r->dev, "ignoring zero-length dynamic " 404296077Sadrian "register window\n"); 405296077Sadrian continue; 406296077Sadrian } else if (last_window_size == 0) { 407296077Sadrian last_window_size = win->win_size; 408296077Sadrian } else if (last_window_size != win->win_size) { 409296077Sadrian /* 410296077Sadrian * No existing hardware should trigger this. 411296077Sadrian * 412296077Sadrian * If you run into this in the future, the dynamic 413296077Sadrian * window allocator and the resource priority system 414296077Sadrian * will need to be extended to support multiple register 415296077Sadrian * window allocation pools. 416296077Sadrian */ 417296077Sadrian device_printf(r->dev, "devices that vend multiple " 418296077Sadrian "dynamic register window sizes are not currently " 419296077Sadrian "supported\n"); 420296077Sadrian goto failed; 421296077Sadrian } 422296077Sadrian 423296077Sadrian dwa = &r->dw_alloc[rnid]; 424296077Sadrian dwa->win = win; 425296077Sadrian dwa->parent_res = NULL; 426296077Sadrian dwa->rnid = rnid; 427296077Sadrian dwa->target = 0x0; 428296077Sadrian 429296077Sadrian LIST_INIT(&dwa->refs); 430296077Sadrian 431296077Sadrian /* Find and validate corresponding resource. */ 432296077Sadrian dwa->parent_res = bhndb_find_regwin_resource(r, win); 433296077Sadrian if (dwa->parent_res == NULL) 434296077Sadrian goto failed; 435296077Sadrian 436296077Sadrian if (rman_get_size(dwa->parent_res) < win->win_offset + 437296077Sadrian win->win_size) 438296077Sadrian { 439296077Sadrian device_printf(r->dev, "resource %d too small for " 440296077Sadrian "register window with offset %llx and size %llx\n", 441296077Sadrian rman_get_rid(dwa->parent_res), 442296077Sadrian (unsigned long long) win->win_offset, 443296077Sadrian (unsigned long long) win->win_size); 444296077Sadrian 445296077Sadrian error = EINVAL; 446296077Sadrian goto failed; 447296077Sadrian } 448296077Sadrian 449296077Sadrian /* Add to freelist */ 450296077Sadrian r->dwa_freelist |= (1 << rnid); 451296077Sadrian 452296077Sadrian rnid++; 453296077Sadrian } 454296077Sadrian 455296077Sadrian return (r); 456296077Sadrian 457296077Sadrianfailed: 458296077Sadrian if (free_parent_res) 459296077Sadrian bus_release_resources(r->parent_dev, r->res_spec, r->res); 460298276Sadrian 461298276Sadrian if (free_ht_mem) 462298276Sadrian rman_fini(&r->ht_mem_rman); 463296077Sadrian 464298276Sadrian if (free_br_mem) 465298276Sadrian rman_fini(&r->br_mem_rman); 466298276Sadrian 467296077Sadrian if (r->res != NULL) 468296077Sadrian free(r->res, M_BHND); 469296077Sadrian 470296077Sadrian if (r->res_spec != NULL) 471296077Sadrian free(r->res_spec, M_BHND); 472296077Sadrian 473296077Sadrian if (r->dw_alloc != NULL) 474296077Sadrian free(r->dw_alloc, M_BHND); 475296077Sadrian 476296077Sadrian free (r, M_BHND); 477296077Sadrian 478296077Sadrian return (NULL); 479296077Sadrian} 480296077Sadrian 481296077Sadrian/** 482296077Sadrian * Deallocate the given bridge resource structure and any associated resources. 483296077Sadrian * 484296077Sadrian * @param br Resource state to be deallocated. 485296077Sadrian */ 486296077Sadrianvoid 487296077Sadrianbhndb_free_resources(struct bhndb_resources *br) 488296077Sadrian{ 489296077Sadrian struct bhndb_region *region, *r_next; 490296077Sadrian struct bhndb_dw_alloc *dwa; 491296077Sadrian struct bhndb_dw_rentry *dwr, *dwr_next; 492296077Sadrian 493296077Sadrian /* No window regions may still be held */ 494296077Sadrian if (__builtin_popcount(br->dwa_freelist) != br->dwa_count) { 495296077Sadrian device_printf(br->dev, "leaked %llu dynamic register regions\n", 496296077Sadrian (unsigned long long) br->dwa_count - br->dwa_freelist); 497296077Sadrian } 498296077Sadrian 499296077Sadrian /* Release resources allocated through our parent. */ 500296077Sadrian bus_release_resources(br->parent_dev, br->res_spec, br->res); 501296077Sadrian 502296077Sadrian /* Clean up resource reservations */ 503296077Sadrian for (size_t i = 0; i < br->dwa_count; i++) { 504296077Sadrian dwa = &br->dw_alloc[i]; 505296077Sadrian 506296077Sadrian LIST_FOREACH_SAFE(dwr, &dwa->refs, dw_link, dwr_next) { 507296077Sadrian LIST_REMOVE(dwr, dw_link); 508296077Sadrian free(dwr, M_BHND); 509296077Sadrian } 510296077Sadrian } 511296077Sadrian 512296077Sadrian /* Release bus regions */ 513296077Sadrian STAILQ_FOREACH_SAFE(region, &br->bus_regions, link, r_next) { 514296077Sadrian STAILQ_REMOVE(&br->bus_regions, region, bhndb_region, link); 515296077Sadrian free(region, M_BHND); 516296077Sadrian } 517296077Sadrian 518298276Sadrian /* Release our resource managers */ 519298276Sadrian rman_fini(&br->ht_mem_rman); 520298276Sadrian rman_fini(&br->br_mem_rman); 521298276Sadrian 522296077Sadrian /* Free backing resource state structures */ 523296077Sadrian free(br->res, M_BHND); 524296077Sadrian free(br->res_spec, M_BHND); 525296077Sadrian free(br->dw_alloc, M_BHND); 526296077Sadrian} 527296077Sadrian 528296077Sadrian/** 529296077Sadrian * Add a bus region entry to @p r for the given base @p addr and @p size. 530296077Sadrian * 531296077Sadrian * @param br The resource state to which the bus region entry will be added. 532296077Sadrian * @param addr The base address of this region. 533296077Sadrian * @param size The size of this region. 534296077Sadrian * @param priority The resource priority to be assigned to allocations 535296077Sadrian * made within this bus region. 536296077Sadrian * @param static_regwin If available, a static register window mapping this 537296077Sadrian * bus region entry. If not available, NULL. 538296077Sadrian * 539296077Sadrian * @retval 0 success 540296077Sadrian * @retval non-zero if adding the bus region fails. 541296077Sadrian */ 542296077Sadrianint 543296077Sadrianbhndb_add_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, 544296077Sadrian bhnd_size_t size, bhndb_priority_t priority, 545296077Sadrian const struct bhndb_regwin *static_regwin) 546296077Sadrian{ 547296077Sadrian struct bhndb_region *reg; 548296077Sadrian 549296077Sadrian /* Insert in the bus resource list */ 550296077Sadrian reg = malloc(sizeof(*reg), M_BHND, M_NOWAIT); 551296077Sadrian if (reg == NULL) 552296077Sadrian return (ENOMEM); 553296077Sadrian 554296077Sadrian *reg = (struct bhndb_region) { 555296077Sadrian .addr = addr, 556296077Sadrian .size = size, 557296077Sadrian .priority = priority, 558296077Sadrian .static_regwin = static_regwin 559296077Sadrian }; 560296077Sadrian 561296077Sadrian STAILQ_INSERT_HEAD(&br->bus_regions, reg, link); 562296077Sadrian 563296077Sadrian return (0); 564296077Sadrian} 565296077Sadrian 566300251Sadrian 567296077Sadrian/** 568300251Sadrian * Find the maximum start and end limits of the register window mapping 569300251Sadrian * resource @p r. 570296077Sadrian * 571300251Sadrian * If the memory range is not mapped by an existing dynamic or static register 572300251Sadrian * window, ENOENT will be returned. 573300251Sadrian * 574296077Sadrian * @param br The resource state to search. 575300251Sadrian * @param r The resource to search for in @p br. 576296077Sadrian * @param addr The requested starting address. 577296077Sadrian * @param size The requested size. 578296077Sadrian * 579296077Sadrian * @retval bhndb_region A region that fully contains the requested range. 580296077Sadrian * @retval NULL If no mapping region can be found. 581296077Sadrian */ 582300251Sadrianint 583300251Sadrianbhndb_find_resource_limits(struct bhndb_resources *br, struct resource *r, 584300251Sadrian rman_res_t *start, rman_res_t *end) 585300251Sadrian{ 586300251Sadrian struct bhndb_dw_alloc *dynamic; 587300251Sadrian struct bhndb_region *sregion; 588300251Sadrian 589300251Sadrian /* Check for an enclosing dynamic register window */ 590300251Sadrian if ((dynamic = bhndb_dw_find_resource(br, r))) { 591300251Sadrian *start = dynamic->target; 592300251Sadrian *end = dynamic->target + dynamic->win->win_size - 1; 593300251Sadrian return (0); 594300251Sadrian } 595300251Sadrian 596300251Sadrian /* Check for a static region */ 597300251Sadrian sregion = bhndb_find_resource_region(br, rman_get_start(r), 598300251Sadrian rman_get_size(r)); 599300251Sadrian if (sregion != NULL && sregion->static_regwin != NULL) { 600300251Sadrian *start = sregion->addr; 601300251Sadrian *end = sregion->addr + sregion->size - 1; 602300251Sadrian 603300251Sadrian return (0); 604300251Sadrian } 605300251Sadrian 606300251Sadrian /* Not found */ 607300251Sadrian return (ENOENT); 608300251Sadrian} 609300251Sadrian 610300251Sadrian/** 611300251Sadrian * Find the bus region that maps @p size bytes at @p addr. 612300251Sadrian * 613300251Sadrian * @param br The resource state to search. 614300251Sadrian * @param addr The requested starting address. 615300251Sadrian * @param size The requested size. 616300251Sadrian * 617300251Sadrian * @retval bhndb_region A region that fully contains the requested range. 618300251Sadrian * @retval NULL If no mapping region can be found. 619300251Sadrian */ 620296077Sadrianstruct bhndb_region * 621296077Sadrianbhndb_find_resource_region(struct bhndb_resources *br, bhnd_addr_t addr, 622296077Sadrian bhnd_size_t size) 623296077Sadrian{ 624296077Sadrian struct bhndb_region *region; 625296077Sadrian 626296077Sadrian STAILQ_FOREACH(region, &br->bus_regions, link) { 627296077Sadrian /* Request must fit within the region's mapping */ 628296077Sadrian if (addr < region->addr) 629296077Sadrian continue; 630296077Sadrian 631296077Sadrian if (addr + size > region->addr + region->size) 632296077Sadrian continue; 633296077Sadrian 634296077Sadrian return (region); 635296077Sadrian } 636296077Sadrian 637296077Sadrian /* Not found */ 638296077Sadrian return (NULL); 639296077Sadrian} 640296077Sadrian 641296077Sadrian/** 642296077Sadrian * Find the entry matching @p r in @p dwa's references, if any. 643296077Sadrian * 644296077Sadrian * @param dwa The dynamic window allocation to search 645296077Sadrian * @param r The resource to search for in @p dwa. 646296077Sadrian */ 647296077Sadrianstatic struct bhndb_dw_rentry * 648296077Sadrianbhndb_dw_find_resource_entry(struct bhndb_dw_alloc *dwa, struct resource *r) 649296077Sadrian{ 650296077Sadrian struct bhndb_dw_rentry *rentry; 651296077Sadrian 652296077Sadrian LIST_FOREACH(rentry, &dwa->refs, dw_link) { 653296077Sadrian struct resource *dw_res = rentry->dw_res; 654296077Sadrian 655296077Sadrian /* Match dev/rid/addr/size */ 656296077Sadrian if (rman_get_device(dw_res) != rman_get_device(r) || 657296077Sadrian rman_get_rid(dw_res) != rman_get_rid(r) || 658296077Sadrian rman_get_start(dw_res) != rman_get_start(r) || 659296077Sadrian rman_get_size(dw_res) != rman_get_size(r)) 660296077Sadrian { 661296077Sadrian continue; 662296077Sadrian } 663296077Sadrian 664296077Sadrian /* Matching allocation found */ 665296077Sadrian return (rentry); 666296077Sadrian } 667296077Sadrian 668296077Sadrian return (NULL); 669296077Sadrian} 670296077Sadrian 671296077Sadrian/** 672296077Sadrian * Find the dynamic region allocated for @p r, if any. 673296077Sadrian * 674296077Sadrian * @param br The resource state to search. 675296077Sadrian * @param r The resource to search for. 676296077Sadrian * 677296077Sadrian * @retval bhndb_dw_alloc The allocation record for @p r. 678296077Sadrian * @retval NULL if no dynamic window is allocated for @p r. 679296077Sadrian */ 680296077Sadrianstruct bhndb_dw_alloc * 681296077Sadrianbhndb_dw_find_resource(struct bhndb_resources *br, struct resource *r) 682296077Sadrian{ 683296077Sadrian struct bhndb_dw_alloc *dwa; 684296077Sadrian 685296077Sadrian for (size_t i = 0; i < br->dwa_count; i++) { 686296077Sadrian dwa = &br->dw_alloc[i]; 687296077Sadrian 688296077Sadrian /* Skip free dynamic windows */ 689296077Sadrian if (bhndb_dw_is_free(br, dwa)) 690296077Sadrian continue; 691296077Sadrian 692296077Sadrian /* Matching allocation found? */ 693296077Sadrian if (bhndb_dw_find_resource_entry(dwa, r) != NULL) 694296077Sadrian return (dwa); 695296077Sadrian } 696296077Sadrian 697296077Sadrian return (NULL); 698296077Sadrian} 699296077Sadrian 700296077Sadrian/** 701296077Sadrian * Find an existing dynamic window mapping @p size bytes 702296077Sadrian * at @p addr. The window may or may not be free. 703296077Sadrian * 704296077Sadrian * @param br The resource state to search. 705296077Sadrian * @param addr The requested starting address. 706296077Sadrian * @param size The requested size. 707296077Sadrian * 708296077Sadrian * @retval bhndb_dw_alloc A window allocation that fully contains the requested 709296077Sadrian * range. 710296077Sadrian * @retval NULL If no mapping region can be found. 711296077Sadrian */ 712296077Sadrianstruct bhndb_dw_alloc * 713296077Sadrianbhndb_dw_find_mapping(struct bhndb_resources *br, bhnd_addr_t addr, 714296077Sadrian bhnd_size_t size) 715296077Sadrian{ 716296077Sadrian struct bhndb_dw_alloc *dwr; 717296077Sadrian const struct bhndb_regwin *win; 718296077Sadrian 719296077Sadrian /* Search for an existing dynamic mapping of this address range. */ 720296077Sadrian for (size_t i = 0; i < br->dwa_count; i++) { 721296077Sadrian dwr = &br->dw_alloc[i]; 722296077Sadrian win = dwr->win; 723296077Sadrian 724296077Sadrian /* Verify the range */ 725296077Sadrian if (addr < dwr->target) 726296077Sadrian continue; 727296077Sadrian 728296077Sadrian if (addr + size > dwr->target + win->win_size) 729296077Sadrian continue; 730296077Sadrian 731296077Sadrian /* Found a usable mapping */ 732296077Sadrian return (dwr); 733296077Sadrian } 734296077Sadrian 735296077Sadrian /* not found */ 736296077Sadrian return (NULL); 737296077Sadrian} 738296077Sadrian 739296077Sadrian/** 740296077Sadrian * Retain a reference to @p dwa for use by @p res. 741296077Sadrian * 742296077Sadrian * @param br The resource state owning @p dwa. 743296077Sadrian * @param dwa The allocation record to be retained. 744296077Sadrian * @param res The resource that will own a reference to @p dwa. 745296077Sadrian * 746296077Sadrian * @retval 0 success 747296077Sadrian * @retval ENOMEM Failed to allocate a new reference structure. 748296077Sadrian */ 749296077Sadrianint 750296077Sadrianbhndb_dw_retain(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, 751296077Sadrian struct resource *res) 752296077Sadrian{ 753296077Sadrian struct bhndb_dw_rentry *rentry; 754296077Sadrian 755296077Sadrian KASSERT(bhndb_dw_find_resource_entry(dwa, res) == NULL, 756296077Sadrian ("double-retain of dynamic window for same resource")); 757296077Sadrian 758296077Sadrian /* Insert a reference entry; we use M_NOWAIT to allow use from 759296077Sadrian * within a non-sleepable lock */ 760296077Sadrian rentry = malloc(sizeof(*rentry), M_BHND, M_NOWAIT); 761296077Sadrian if (rentry == NULL) 762296077Sadrian return (ENOMEM); 763296077Sadrian 764296077Sadrian rentry->dw_res = res; 765296077Sadrian LIST_INSERT_HEAD(&dwa->refs, rentry, dw_link); 766296077Sadrian 767296077Sadrian /* Update the free list */ 768296077Sadrian br->dwa_freelist &= ~(1 << (dwa->rnid)); 769296077Sadrian 770296077Sadrian return (0); 771296077Sadrian} 772296077Sadrian 773296077Sadrian/** 774296077Sadrian * Release a reference to @p dwa previously retained by @p res. If the 775296077Sadrian * reference count of @p dwa reaches zero, it will be added to the 776296077Sadrian * free list. 777296077Sadrian * 778296077Sadrian * @param br The resource state owning @p dwa. 779296077Sadrian * @param dwa The allocation record to be released. 780296077Sadrian * @param res The resource that currently owns a reference to @p dwa. 781296077Sadrian */ 782296077Sadrianvoid 783296077Sadrianbhndb_dw_release(struct bhndb_resources *br, struct bhndb_dw_alloc *dwa, 784296077Sadrian struct resource *r) 785296077Sadrian{ 786296077Sadrian struct bhndb_dw_rentry *rentry; 787296077Sadrian 788296077Sadrian /* Find the rentry */ 789296077Sadrian rentry = bhndb_dw_find_resource_entry(dwa, r); 790296077Sadrian KASSERT(rentry != NULL, ("over release of resource entry")); 791296077Sadrian 792296077Sadrian LIST_REMOVE(rentry, dw_link); 793296077Sadrian free(rentry, M_BHND); 794296077Sadrian 795296077Sadrian /* If this was the last reference, update the free list */ 796296077Sadrian if (LIST_EMPTY(&dwa->refs)) 797296077Sadrian br->dwa_freelist |= (1 << (dwa->rnid)); 798296077Sadrian} 799296077Sadrian 800296077Sadrian/** 801296077Sadrian * Attempt to set (or reset) the target address of @p dwa to map @p size bytes 802296077Sadrian * at @p addr. 803296077Sadrian * 804296077Sadrian * This will apply any necessary window alignment and verify that 805296077Sadrian * the window is capable of mapping the requested range prior to modifying 806296077Sadrian * therecord. 807296077Sadrian * 808296077Sadrian * @param dev The device on which to issue the BHNDB_SET_WINDOW_ADDR() request. 809296077Sadrian * @param br The resource state owning @p dwa. 810296077Sadrian * @param dwa The allocation record to be configured. 811296077Sadrian * @param addr The address to be mapped via @p dwa. 812296077Sadrian * @param size The number of bytes to be mapped at @p addr. 813296077Sadrian * 814296077Sadrian * @retval 0 success 815296077Sadrian * @retval non-zero no usable register window available. 816296077Sadrian */ 817296077Sadrianint 818296077Sadrianbhndb_dw_set_addr(device_t dev, struct bhndb_resources *br, 819296077Sadrian struct bhndb_dw_alloc *dwa, bus_addr_t addr, bus_size_t size) 820296077Sadrian{ 821296077Sadrian const struct bhndb_regwin *rw; 822296077Sadrian bus_addr_t offset; 823296077Sadrian int error; 824296077Sadrian 825296077Sadrian rw = dwa->win; 826296077Sadrian 827296077Sadrian KASSERT(bhndb_dw_is_free(br, dwa), 828296077Sadrian ("attempting to set the target address on an in-use window")); 829296077Sadrian 830296077Sadrian /* Page-align the target address */ 831296077Sadrian offset = addr % rw->win_size; 832296077Sadrian dwa->target = addr - offset; 833296077Sadrian 834296077Sadrian /* Verify that the window is large enough for the full target */ 835296077Sadrian if (rw->win_size - offset < size) 836296077Sadrian return (ENOMEM); 837296077Sadrian 838296077Sadrian /* Update the window target */ 839296077Sadrian error = BHNDB_SET_WINDOW_ADDR(dev, dwa->win, dwa->target); 840296077Sadrian if (error) { 841296077Sadrian dwa->target = 0x0; 842296077Sadrian return (error); 843296077Sadrian } 844296077Sadrian 845296077Sadrian return (0); 846296077Sadrian} 847296077Sadrian 848296077Sadrian/** 849296077Sadrian * Return the count of @p type register windows in @p table. 850296077Sadrian * 851296077Sadrian * @param table The table to search. 852296077Sadrian * @param type The required window type, or BHNDB_REGWIN_T_INVALID to 853296077Sadrian * count all register window types. 854296077Sadrian */ 855296077Sadriansize_t 856296077Sadrianbhndb_regwin_count(const struct bhndb_regwin *table, 857296077Sadrian bhndb_regwin_type_t type) 858296077Sadrian{ 859296077Sadrian const struct bhndb_regwin *rw; 860296077Sadrian size_t count; 861296077Sadrian 862296077Sadrian count = 0; 863296077Sadrian for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) { 864296077Sadrian if (type == BHNDB_REGWIN_T_INVALID || rw->win_type == type) 865296077Sadrian count++; 866296077Sadrian } 867296077Sadrian 868296077Sadrian return (count); 869296077Sadrian} 870296077Sadrian 871296077Sadrian/** 872296077Sadrian * Search @p table for the first window with the given @p type. 873296077Sadrian * 874296077Sadrian * @param table The table to search. 875296077Sadrian * @param type The required window type. 876296077Sadrian * @param min_size The minimum window size. 877296077Sadrian * 878296077Sadrian * @retval bhndb_regwin The first matching window. 879296077Sadrian * @retval NULL If no window of the requested type could be found. 880296077Sadrian */ 881296077Sadrianconst struct bhndb_regwin * 882296077Sadrianbhndb_regwin_find_type(const struct bhndb_regwin *table, 883296077Sadrian bhndb_regwin_type_t type, bus_size_t min_size) 884296077Sadrian{ 885296077Sadrian const struct bhndb_regwin *rw; 886296077Sadrian 887296077Sadrian for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) 888296077Sadrian { 889296077Sadrian if (rw->win_type == type && rw->win_size >= min_size) 890296077Sadrian return (rw); 891296077Sadrian } 892296077Sadrian 893296077Sadrian return (NULL); 894296077Sadrian} 895296077Sadrian 896296077Sadrian/** 897296077Sadrian * Search @p windows for the first matching core window. 898296077Sadrian * 899296077Sadrian * @param table The table to search. 900296077Sadrian * @param class The required core class. 901296077Sadrian * @param unit The required core unit, or -1. 902296077Sadrian * @param port_type The required port type. 903296077Sadrian * @param port The required port. 904296077Sadrian * @param region The required region. 905296077Sadrian * 906296077Sadrian * @retval bhndb_regwin The first matching window. 907296077Sadrian * @retval NULL If no matching window was found. 908296077Sadrian */ 909296077Sadrianconst struct bhndb_regwin * 910296077Sadrianbhndb_regwin_find_core(const struct bhndb_regwin *table, bhnd_devclass_t class, 911296077Sadrian int unit, bhnd_port_type port_type, u_int port, u_int region) 912296077Sadrian{ 913296077Sadrian const struct bhndb_regwin *rw; 914296077Sadrian 915296077Sadrian for (rw = table; rw->win_type != BHNDB_REGWIN_T_INVALID; rw++) 916296077Sadrian { 917296077Sadrian if (rw->win_type != BHNDB_REGWIN_T_CORE) 918296077Sadrian continue; 919296077Sadrian 920299135Sadrian if (rw->d.core.class != class) 921296077Sadrian continue; 922296077Sadrian 923299135Sadrian if (unit != -1 && rw->d.core.unit != unit) 924296077Sadrian continue; 925296077Sadrian 926299135Sadrian if (rw->d.core.port_type != port_type) 927296077Sadrian continue; 928296077Sadrian 929299135Sadrian if (rw->d.core.port != port) 930296077Sadrian continue; 931296077Sadrian 932299135Sadrian if (rw->d.core.region != region) 933296077Sadrian continue; 934296077Sadrian 935296077Sadrian return (rw); 936296077Sadrian } 937296077Sadrian 938296077Sadrian return (NULL); 939296077Sadrian} 940296077Sadrian 941296077Sadrian/** 942296077Sadrian * Search @p windows for the best available window of at least @p min_size. 943296077Sadrian * 944296077Sadrian * Search order: 945296077Sadrian * - BHND_REGWIN_T_CORE 946296077Sadrian * - BHND_REGWIN_T_DYN 947296077Sadrian * 948296077Sadrian * @param table The table to search. 949296077Sadrian * @param class The required core class. 950296077Sadrian * @param unit The required core unit, or -1. 951296077Sadrian * @param port_type The required port type. 952296077Sadrian * @param port The required port. 953296077Sadrian * @param region The required region. 954296077Sadrian * @param min_size The minimum window size. 955296077Sadrian * 956296077Sadrian * @retval bhndb_regwin The first matching window. 957296077Sadrian * @retval NULL If no matching window was found. 958296077Sadrian */ 959296077Sadrianconst struct bhndb_regwin * 960296077Sadrianbhndb_regwin_find_best(const struct bhndb_regwin *table, 961296077Sadrian bhnd_devclass_t class, int unit, bhnd_port_type port_type, u_int port, 962296077Sadrian u_int region, bus_size_t min_size) 963296077Sadrian{ 964296077Sadrian const struct bhndb_regwin *rw; 965296077Sadrian 966296077Sadrian /* Prefer a fixed core mapping */ 967296077Sadrian rw = bhndb_regwin_find_core(table, class, unit, port_type, 968296077Sadrian port, region); 969296077Sadrian if (rw != NULL) 970296077Sadrian return (rw); 971296077Sadrian 972296077Sadrian /* Fall back on a generic dynamic window */ 973296077Sadrian return (bhndb_regwin_find_type(table, BHNDB_REGWIN_T_DYN, min_size)); 974296077Sadrian} 975296077Sadrian 976296077Sadrian/** 977296077Sadrian * Return true if @p regw defines a static port register window, and 978296077Sadrian * the mapped port is actually defined on @p dev. 979296077Sadrian * 980296077Sadrian * @param regw A register window to match against. 981296077Sadrian * @param dev A bhnd(4) bus device. 982296077Sadrian */ 983296077Sadrianbool 984296077Sadrianbhndb_regwin_matches_device(const struct bhndb_regwin *regw, device_t dev) 985296077Sadrian{ 986296077Sadrian /* Only core windows are supported */ 987296077Sadrian if (regw->win_type != BHNDB_REGWIN_T_CORE) 988296077Sadrian return (false); 989296077Sadrian 990296077Sadrian /* Device class must match */ 991299135Sadrian if (bhnd_get_class(dev) != regw->d.core.class) 992296077Sadrian return (false); 993296077Sadrian 994296077Sadrian /* Device unit must match */ 995299135Sadrian if (bhnd_get_core_unit(dev) != regw->d.core.unit) 996296077Sadrian return (false); 997296077Sadrian 998296077Sadrian /* The regwin port/region must be defined. */ 999299135Sadrian if (!bhnd_is_region_valid(dev, regw->d.core.port_type, regw->d.core.port, 1000299135Sadrian regw->d.core.region)) 1001296077Sadrian { 1002296077Sadrian return (false); 1003296077Sadrian } 1004296077Sadrian 1005296077Sadrian /* Matches */ 1006296077Sadrian return (true); 1007296077Sadrian} 1008296077Sadrian 1009296077Sadrian/** 1010296077Sadrian * Search for a core resource priority descriptor in @p table that matches 1011296077Sadrian * @p device. 1012296077Sadrian * 1013296077Sadrian * @param table The table to search. 1014296077Sadrian * @param device A bhnd(4) bus device. 1015296077Sadrian */ 1016296077Sadrianconst struct bhndb_hw_priority * 1017296077Sadrianbhndb_hw_priority_find_device(const struct bhndb_hw_priority *table, 1018296077Sadrian device_t device) 1019296077Sadrian{ 1020300628Sadrian const struct bhndb_hw_priority *hp; 1021300628Sadrian struct bhnd_core_info ci; 1022296077Sadrian 1023300628Sadrian ci = bhnd_get_core_info(device); 1024300628Sadrian 1025296077Sadrian for (hp = table; hp->ports != NULL; hp++) { 1026300628Sadrian if (bhnd_core_matches(&ci, &hp->match)) 1027296077Sadrian return (hp); 1028296077Sadrian } 1029296077Sadrian 1030296077Sadrian /* not found */ 1031296077Sadrian return (NULL); 1032296077Sadrian} 1033