1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> 5 * Copyright (c) 2017 The FreeBSD Foundation 6 * All rights reserved. 7 * 8 * Portions of this software were developed by Landon Fuller 9 * under sponsorship from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 19 * redistribution must be conditioned upon including a substantially 20 * similar Disclaimer requirement for further binary redistribution. 21 * 22 * NO WARRANTY 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 26 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 27 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 28 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 31 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 33 * THE POSSIBILITY OF SUCH DAMAGES. 34 */ 35 36 #include <sys/cdefs.h> 37 #include <sys/param.h> 38 #include <sys/bus.h> 39 #include <sys/kernel.h> 40 #include <sys/limits.h> 41 #include <sys/systm.h> 42 43 #include <machine/bus.h> 44 #include <machine/resource.h> 45 46 #include <dev/bhnd/bhndvar.h> 47 48 #include "bcma_dmp.h" 49 50 #include "bcmavar.h" 51 52 /* Return the resource ID for a device's agent register allocation */ 53 #define BCMA_AGENT_RID(_dinfo) \ 54 (BCMA_AGENT_RID_BASE + BCMA_DINFO_COREIDX(_dinfo)) 55 56 /** 57 * Allocate and initialize new core config structure. 58 * 59 * @param core_index Core index on the bus. 60 * @param core_unit Core unit number. 61 * @param vendor Core designer. 62 * @param device Core identifier (e.g. part number). 63 * @param hwrev Core revision. 64 */ 65 struct bcma_corecfg * 66 bcma_alloc_corecfg(u_int core_index, int core_unit, uint16_t vendor, 67 uint16_t device, uint8_t hwrev) 68 { 69 struct bcma_corecfg *cfg; 70 71 cfg = malloc(sizeof(*cfg), M_BHND, M_NOWAIT); 72 if (cfg == NULL) 73 return NULL; 74 75 cfg->core_info = (struct bhnd_core_info) { 76 .vendor = vendor, 77 .device = device, 78 .hwrev = hwrev, 79 .core_idx = core_index, 80 .unit = core_unit 81 }; 82 83 STAILQ_INIT(&cfg->master_ports); 84 cfg->num_master_ports = 0; 85 86 STAILQ_INIT(&cfg->dev_ports); 87 cfg->num_dev_ports = 0; 88 89 STAILQ_INIT(&cfg->bridge_ports); 90 cfg->num_bridge_ports = 0; 91 92 STAILQ_INIT(&cfg->wrapper_ports); 93 cfg->num_wrapper_ports = 0; 94 95 return (cfg); 96 } 97 98 /** 99 * Deallocate the given core config and any associated resources. 100 * 101 * @param corecfg Core info to be deallocated. 102 */ 103 void 104 bcma_free_corecfg(struct bcma_corecfg *corecfg) 105 { 106 struct bcma_mport *mport, *mnext; 107 struct bcma_sport *sport, *snext; 108 109 STAILQ_FOREACH_SAFE(mport, &corecfg->master_ports, mp_link, mnext) { 110 free(mport, M_BHND); 111 } 112 113 STAILQ_FOREACH_SAFE(sport, &corecfg->dev_ports, sp_link, snext) { 114 bcma_free_sport(sport); 115 } 116 117 STAILQ_FOREACH_SAFE(sport, &corecfg->bridge_ports, sp_link, snext) { 118 bcma_free_sport(sport); 119 } 120 121 STAILQ_FOREACH_SAFE(sport, &corecfg->wrapper_ports, sp_link, snext) { 122 bcma_free_sport(sport); 123 } 124 125 free(corecfg, M_BHND); 126 } 127 128 /** 129 * Return the @p cfg port list for @p type. 130 * 131 * @param cfg The core configuration. 132 * @param type The requested port type. 133 */ 134 struct bcma_sport_list * 135 bcma_corecfg_get_port_list(struct bcma_corecfg *cfg, bhnd_port_type type) 136 { 137 switch (type) { 138 case BHND_PORT_DEVICE: 139 return (&cfg->dev_ports); 140 break; 141 case BHND_PORT_BRIDGE: 142 return (&cfg->bridge_ports); 143 break; 144 case BHND_PORT_AGENT: 145 return (&cfg->wrapper_ports); 146 break; 147 default: 148 return (NULL); 149 } 150 } 151 152 /** 153 * Populate the resource list and bcma_map RIDs using the maps defined on 154 * @p ports. 155 * 156 * @param bus The requesting bus device. 157 * @param dinfo The device info instance to be initialized. 158 * @param ports The set of ports to be enumerated 159 */ 160 static void 161 bcma_dinfo_init_port_resource_info(device_t bus, struct bcma_devinfo *dinfo, 162 struct bcma_sport_list *ports) 163 { 164 struct bcma_map *map; 165 struct bcma_sport *port; 166 bhnd_addr_t end; 167 168 STAILQ_FOREACH(port, ports, sp_link) { 169 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 170 /* 171 * Create the corresponding device resource list entry. 172 * 173 * We necessarily skip registration if the region's 174 * device memory range is not representable via 175 * rman_res_t. 176 * 177 * When rman_res_t is migrated to uintmax_t, any 178 * range should be representable. 179 */ 180 end = map->m_base + map->m_size; 181 if (map->m_base <= RM_MAX_END && end <= RM_MAX_END) { 182 map->m_rid = resource_list_add_next( 183 &dinfo->resources, SYS_RES_MEMORY, 184 map->m_base, end, map->m_size); 185 } else if (bootverbose) { 186 device_printf(bus, 187 "core%u %s%u.%u: region %llx-%llx extends " 188 "beyond supported addressable range\n", 189 dinfo->corecfg->core_info.core_idx, 190 bhnd_port_type_name(port->sp_type), 191 port->sp_num, map->m_region_num, 192 (unsigned long long) map->m_base, 193 (unsigned long long) end); 194 } 195 } 196 } 197 } 198 199 /** 200 * Allocate the per-core agent register block for a device info structure. 201 * 202 * If an agent0.0 region is not defined on @p dinfo, the device info 203 * agent resource is set to NULL and 0 is returned. 204 * 205 * @param bus The requesting bus device. 206 * @param child The bcma child device. 207 * @param dinfo The device info associated with @p child 208 * 209 * @retval 0 success 210 * @retval non-zero resource allocation failed. 211 */ 212 static int 213 bcma_dinfo_init_agent(device_t bus, device_t child, struct bcma_devinfo *dinfo) 214 { 215 bhnd_addr_t addr; 216 bhnd_size_t size; 217 rman_res_t r_start, r_count, r_end; 218 int error; 219 220 KASSERT(dinfo->res_agent == NULL, ("double allocation of agent")); 221 222 /* Verify that the agent register block exists and is 223 * mappable */ 224 if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1) 225 return (0); /* nothing to do */ 226 227 /* Fetch the address of the agent register block */ 228 error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0, 229 &addr, &size); 230 if (error) { 231 device_printf(bus, "failed fetching agent register block " 232 "address for core %u\n", BCMA_DINFO_COREIDX(dinfo)); 233 return (error); 234 } 235 236 /* Allocate the resource */ 237 r_start = addr; 238 r_count = size; 239 r_end = r_start + r_count - 1; 240 241 dinfo->rid_agent = BCMA_AGENT_RID(dinfo); 242 dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(bus, bus, SYS_RES_MEMORY, 243 &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE|RF_SHAREABLE); 244 if (dinfo->res_agent == NULL) { 245 device_printf(bus, "failed allocating agent register block for " 246 "core %u\n", BCMA_DINFO_COREIDX(dinfo)); 247 return (ENXIO); 248 } 249 250 return (0); 251 } 252 253 /** 254 * Populate the list of interrupts for a device info structure 255 * previously initialized via bcma_dinfo_alloc_agent(). 256 * 257 * If an agent0.0 region is not mapped on @p dinfo, the OOB interrupt bank is 258 * assumed to be unavailable and 0 is returned. 259 * 260 * @param bus The requesting bus device. 261 * @param dinfo The device info instance to be initialized. 262 */ 263 static int 264 bcma_dinfo_init_intrs(device_t bus, device_t child, 265 struct bcma_devinfo *dinfo) 266 { 267 uint32_t dmpcfg, oobw; 268 269 /* Agent block must be mapped */ 270 if (dinfo->res_agent == NULL) 271 return (0); 272 273 /* Agent must support OOB */ 274 dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG); 275 if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB)) 276 return (0); 277 278 /* Fetch width of the OOB interrupt bank */ 279 oobw = bhnd_bus_read_4(dinfo->res_agent, 280 BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR)); 281 if (oobw >= BCMA_OOB_NUM_SEL) { 282 device_printf(bus, "ignoring invalid OOBOUTWIDTH for core %u: " 283 "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw); 284 return (0); 285 } 286 287 /* Fetch OOBSEL busline values and populate list of interrupt 288 * descriptors */ 289 for (uint32_t sel = 0; sel < oobw; sel++) { 290 struct bcma_intr *intr; 291 uint32_t selout; 292 uint8_t line; 293 294 if (dinfo->num_intrs == UINT_MAX) 295 return (ENOMEM); 296 297 selout = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT( 298 BCMA_OOB_BANK_INTR, sel)); 299 300 line = (selout >> BCMA_DMP_OOBSEL_SHIFT(sel)) & 301 BCMA_DMP_OOBSEL_BUSLINE_MASK; 302 303 intr = bcma_alloc_intr(BCMA_OOB_BANK_INTR, sel, line); 304 if (intr == NULL) { 305 device_printf(bus, "failed allocating interrupt " 306 "descriptor %#x for core %u\n", sel, 307 BCMA_DINFO_COREIDX(dinfo)); 308 return (ENOMEM); 309 } 310 311 STAILQ_INSERT_HEAD(&dinfo->intrs, intr, i_link); 312 dinfo->num_intrs++; 313 } 314 315 return (0); 316 } 317 318 /** 319 * Allocate and return a new empty device info structure. 320 * 321 * @param bus The requesting bus device. 322 * 323 * @retval NULL if allocation failed. 324 */ 325 struct bcma_devinfo * 326 bcma_alloc_dinfo(device_t bus) 327 { 328 struct bcma_devinfo *dinfo; 329 330 dinfo = malloc(sizeof(struct bcma_devinfo), M_BHND, M_NOWAIT|M_ZERO); 331 if (dinfo == NULL) 332 return (NULL); 333 334 dinfo->corecfg = NULL; 335 dinfo->res_agent = NULL; 336 dinfo->rid_agent = -1; 337 338 STAILQ_INIT(&dinfo->intrs); 339 dinfo->num_intrs = 0; 340 341 resource_list_init(&dinfo->resources); 342 343 return (dinfo); 344 } 345 346 /** 347 * Initialize a device info structure previously allocated via 348 * bcma_alloc_dinfo, assuming ownership of the provided core 349 * configuration. 350 * 351 * @param bus The requesting bus device. 352 * @param child The bcma child device. 353 * @param dinfo The device info associated with @p child 354 * @param corecfg Device core configuration; ownership of this value 355 * will be assumed by @p dinfo. 356 * 357 * @retval 0 success 358 * @retval non-zero initialization failed. 359 */ 360 int 361 bcma_init_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo, 362 struct bcma_corecfg *corecfg) 363 { 364 struct bcma_intr *intr; 365 int error; 366 367 KASSERT(dinfo->corecfg == NULL, ("dinfo previously initialized")); 368 369 /* Save core configuration value */ 370 dinfo->corecfg = corecfg; 371 372 /* The device ports must always be initialized first to ensure that 373 * rid 0 maps to the first device port */ 374 bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->dev_ports); 375 bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->bridge_ports); 376 bcma_dinfo_init_port_resource_info(bus, dinfo, &corecfg->wrapper_ports); 377 378 /* Now that we've defined the port resources, we can map the device's 379 * agent registers (if any) */ 380 if ((error = bcma_dinfo_init_agent(bus, child, dinfo))) 381 goto failed; 382 383 /* With agent registers mapped, we can populate the device's interrupt 384 * descriptors */ 385 if ((error = bcma_dinfo_init_intrs(bus, child, dinfo))) 386 goto failed; 387 388 /* Finally, map the interrupt descriptors */ 389 STAILQ_FOREACH(intr, &dinfo->intrs, i_link) { 390 /* Already mapped? */ 391 if (intr->i_mapped) 392 continue; 393 394 /* Map the interrupt */ 395 error = BHND_BUS_MAP_INTR(bus, child, intr->i_sel, 396 &intr->i_irq); 397 if (error) { 398 device_printf(bus, "failed mapping interrupt line %u " 399 "for core %u: %d\n", intr->i_sel, 400 BCMA_DINFO_COREIDX(dinfo), error); 401 goto failed; 402 } 403 404 intr->i_mapped = true; 405 406 /* Add to resource list */ 407 intr->i_rid = resource_list_add_next(&dinfo->resources, 408 SYS_RES_IRQ, intr->i_irq, intr->i_irq, 1); 409 } 410 411 return (0); 412 413 failed: 414 /* Owned by the caller on failure */ 415 dinfo->corecfg = NULL; 416 417 return (error); 418 } 419 420 /** 421 * Deallocate the given device info structure and any associated resources. 422 * 423 * @param bus The requesting bus device. 424 * @param dinfo Device info to be deallocated. 425 */ 426 void 427 bcma_free_dinfo(device_t bus, device_t child, struct bcma_devinfo *dinfo) 428 { 429 struct bcma_intr *intr, *inext; 430 431 resource_list_free(&dinfo->resources); 432 433 if (dinfo->corecfg != NULL) 434 bcma_free_corecfg(dinfo->corecfg); 435 436 /* Release agent resource, if any */ 437 if (dinfo->res_agent != NULL) { 438 bhnd_release_resource(bus, SYS_RES_MEMORY, dinfo->rid_agent, 439 dinfo->res_agent); 440 } 441 442 /* Clean up interrupt descriptors */ 443 STAILQ_FOREACH_SAFE(intr, &dinfo->intrs, i_link, inext) { 444 STAILQ_REMOVE(&dinfo->intrs, intr, bcma_intr, i_link); 445 446 /* Release our IRQ mapping */ 447 if (intr->i_mapped) { 448 BHND_BUS_UNMAP_INTR(bus, child, intr->i_irq); 449 intr->i_mapped = false; 450 } 451 452 bcma_free_intr(intr); 453 } 454 455 free(dinfo, M_BHND); 456 } 457 458 /** 459 * Allocate and initialize a new interrupt descriptor. 460 * 461 * @param bank OOB bank. 462 * @param sel OOB selector. 463 * @param line OOB bus line. 464 */ 465 struct bcma_intr * 466 bcma_alloc_intr(uint8_t bank, uint8_t sel, uint8_t line) 467 { 468 struct bcma_intr *intr; 469 470 if (bank >= BCMA_OOB_NUM_BANKS) 471 return (NULL); 472 473 if (sel >= BCMA_OOB_NUM_SEL) 474 return (NULL); 475 476 if (line >= BCMA_OOB_NUM_BUSLINES) 477 return (NULL); 478 479 intr = malloc(sizeof(*intr), M_BHND, M_NOWAIT); 480 if (intr == NULL) 481 return (NULL); 482 483 intr->i_bank = bank; 484 intr->i_sel = sel; 485 intr->i_busline = line; 486 intr->i_mapped = false; 487 intr->i_irq = 0; 488 489 return (intr); 490 } 491 492 /** 493 * Deallocate all resources associated with the given interrupt descriptor. 494 * 495 * @param intr Interrupt descriptor to be deallocated. 496 */ 497 void 498 bcma_free_intr(struct bcma_intr *intr) 499 { 500 KASSERT(!intr->i_mapped, ("interrupt %u still mapped", intr->i_sel)); 501 502 free(intr, M_BHND); 503 } 504 505 /** 506 * Allocate and initialize new slave port descriptor. 507 * 508 * @param port_num Per-core port number. 509 * @param port_type Port type. 510 */ 511 struct bcma_sport * 512 bcma_alloc_sport(bcma_pid_t port_num, bhnd_port_type port_type) 513 { 514 struct bcma_sport *sport; 515 516 sport = malloc(sizeof(struct bcma_sport), M_BHND, M_NOWAIT); 517 if (sport == NULL) 518 return NULL; 519 520 sport->sp_num = port_num; 521 sport->sp_type = port_type; 522 sport->sp_num_maps = 0; 523 STAILQ_INIT(&sport->sp_maps); 524 525 return sport; 526 } 527 528 /** 529 * Deallocate all resources associated with the given port descriptor. 530 * 531 * @param sport Port descriptor to be deallocated. 532 */ 533 void 534 bcma_free_sport(struct bcma_sport *sport) { 535 struct bcma_map *map, *mapnext; 536 537 STAILQ_FOREACH_SAFE(map, &sport->sp_maps, m_link, mapnext) { 538 free(map, M_BHND); 539 } 540 541 free(sport, M_BHND); 542 } 543 544 /** 545 * Given a bcma(4) child's device info, spin waiting for the device's DMP 546 * resetstatus register to clear. 547 * 548 * @param child The bcma(4) child device. 549 * @param dinfo The @p child device info. 550 * 551 * @retval 0 success 552 * @retval ENODEV if @p dinfo does not map an agent register resource. 553 * @retval ETIMEDOUT if timeout occurs 554 */ 555 int 556 bcma_dmp_wait_reset(device_t child, struct bcma_devinfo *dinfo) 557 { 558 uint32_t rst; 559 560 if (dinfo->res_agent == NULL) 561 return (ENODEV); 562 563 /* 300us should be long enough, but there are references to this 564 * requiring up to 10ms when performing reset of an 80211 core 565 * after a MAC PSM microcode watchdog event. */ 566 for (int i = 0; i < 10000; i += 10) { 567 rst = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETSTATUS); 568 if (rst == 0) 569 return (0); 570 571 DELAY(10); 572 } 573 574 device_printf(child, "BCMA_DMP_RESETSTATUS timeout\n"); 575 return (ETIMEDOUT); 576 } 577 578 /** 579 * Set the bcma(4) child's DMP resetctrl register value, and then wait 580 * for all backplane operations to complete. 581 * 582 * @param child The bcma(4) child device. 583 * @param dinfo The @p child device info. 584 * @param value The new ioctrl value to set. 585 * 586 * @retval 0 success 587 * @retval ENODEV if @p dinfo does not map an agent register resource. 588 * @retval ETIMEDOUT if timeout occurs waiting for reset completion 589 */ 590 int 591 bcma_dmp_write_reset(device_t child, struct bcma_devinfo *dinfo, uint32_t value) 592 { 593 uint32_t rst; 594 595 if (dinfo->res_agent == NULL) 596 return (ENODEV); 597 598 /* Already in requested reset state? */ 599 rst = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL); 600 if (rst == value) 601 return (0); 602 603 bhnd_bus_write_4(dinfo->res_agent, BCMA_DMP_RESETCTRL, value); 604 bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_RESETCTRL); /* read-back */ 605 DELAY(10); 606 607 return (bcma_dmp_wait_reset(child, dinfo)); 608 } 609