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