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/bus.h> 35 #include <sys/kernel.h> 36 #include <sys/malloc.h> 37 #include <sys/module.h> 38 #include <sys/systm.h> 39 40 #include <machine/bus.h> 41 42 #include "bcmavar.h" 43 44 #include "bcma_dmp.h" 45 46 #include "bcma_eromreg.h" 47 #include "bcma_eromvar.h" 48 49 #include <dev/bhnd/bhnd_core.h> 50 51 /* RID used when allocating EROM table */ 52 #define BCMA_EROM_RID 0 53 54 static bhnd_erom_class_t * 55 bcma_get_erom_class(driver_t *driver) 56 { 57 return (&bcma_erom_parser); 58 } 59 60 int 61 bcma_probe(device_t dev) 62 { 63 device_set_desc(dev, "BCMA BHND bus"); 64 return (BUS_PROBE_DEFAULT); 65 } 66 67 /** 68 * Default bcma(4) bus driver implementation of DEVICE_ATTACH(). 69 * 70 * This implementation initializes internal bcma(4) state and performs 71 * bus enumeration, and must be called by subclassing drivers in 72 * DEVICE_ATTACH() before any other bus methods. 73 */ 74 int 75 bcma_attach(device_t dev) 76 { 77 int error; 78 79 /* Enumerate children */ 80 if ((error = bcma_add_children(dev))) { 81 device_delete_children(dev); 82 return (error); 83 } 84 85 return (0); 86 } 87 88 int 89 bcma_detach(device_t dev) 90 { 91 return (bhnd_generic_detach(dev)); 92 } 93 94 static int 95 bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 96 { 97 const struct bcma_devinfo *dinfo; 98 const struct bhnd_core_info *ci; 99 100 dinfo = device_get_ivars(child); 101 ci = &dinfo->corecfg->core_info; 102 103 switch (index) { 104 case BHND_IVAR_VENDOR: 105 *result = ci->vendor; 106 return (0); 107 case BHND_IVAR_DEVICE: 108 *result = ci->device; 109 return (0); 110 case BHND_IVAR_HWREV: 111 *result = ci->hwrev; 112 return (0); 113 case BHND_IVAR_DEVICE_CLASS: 114 *result = bhnd_core_class(ci); 115 return (0); 116 case BHND_IVAR_VENDOR_NAME: 117 *result = (uintptr_t) bhnd_vendor_name(ci->vendor); 118 return (0); 119 case BHND_IVAR_DEVICE_NAME: 120 *result = (uintptr_t) bhnd_core_name(ci); 121 return (0); 122 case BHND_IVAR_CORE_INDEX: 123 *result = ci->core_idx; 124 return (0); 125 case BHND_IVAR_CORE_UNIT: 126 *result = ci->unit; 127 return (0); 128 default: 129 return (ENOENT); 130 } 131 } 132 133 static int 134 bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 135 { 136 switch (index) { 137 case BHND_IVAR_VENDOR: 138 case BHND_IVAR_DEVICE: 139 case BHND_IVAR_HWREV: 140 case BHND_IVAR_DEVICE_CLASS: 141 case BHND_IVAR_VENDOR_NAME: 142 case BHND_IVAR_DEVICE_NAME: 143 case BHND_IVAR_CORE_INDEX: 144 case BHND_IVAR_CORE_UNIT: 145 return (EINVAL); 146 default: 147 return (ENOENT); 148 } 149 } 150 151 static struct resource_list * 152 bcma_get_resource_list(device_t dev, device_t child) 153 { 154 struct bcma_devinfo *dinfo = device_get_ivars(child); 155 return (&dinfo->resources); 156 } 157 158 static int 159 bcma_reset_core(device_t dev, device_t child, uint16_t flags) 160 { 161 struct bcma_devinfo *dinfo; 162 163 if (device_get_parent(child) != dev) 164 BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags); 165 166 dinfo = device_get_ivars(child); 167 168 /* Can't reset the core without access to the agent registers */ 169 if (dinfo->res_agent == NULL) 170 return (ENODEV); 171 172 /* Start reset */ 173 bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, BHND_RESET_CF_ENABLE); 174 bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF); 175 DELAY(10); 176 177 /* Disable clock */ 178 bhnd_bus_write_4(dinfo->res_agent, BHND_CF, flags); 179 bhnd_bus_read_4(dinfo->res_agent, BHND_CF); 180 DELAY(10); 181 182 /* Enable clocks & force clock gating */ 183 bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | 184 BHND_CF_FGC | flags); 185 bhnd_bus_read_4(dinfo->res_agent, BHND_CF); 186 DELAY(10); 187 188 /* Complete reset */ 189 bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, 0); 190 bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF); 191 DELAY(10); 192 193 /* Release force clock gating */ 194 bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | flags); 195 bhnd_bus_read_4(dinfo->res_agent, BHND_CF); 196 DELAY(10); 197 198 return (0); 199 } 200 201 static int 202 bcma_suspend_core(device_t dev, device_t child) 203 { 204 struct bcma_devinfo *dinfo; 205 206 if (device_get_parent(child) != dev) 207 BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child); 208 209 dinfo = device_get_ivars(child); 210 211 /* Can't suspend the core without access to the agent registers */ 212 if (dinfo->res_agent == NULL) 213 return (ENODEV); 214 215 // TODO - perform suspend 216 217 return (ENXIO); 218 } 219 220 static uint32_t 221 bcma_read_config(device_t dev, device_t child, bus_size_t offset, u_int width) 222 { 223 struct bcma_devinfo *dinfo; 224 struct bhnd_resource *r; 225 226 /* Must be a directly attached child core */ 227 if (device_get_parent(child) != dev) 228 return (UINT32_MAX); 229 230 /* Fetch the agent registers */ 231 dinfo = device_get_ivars(child); 232 if ((r = dinfo->res_agent) == NULL) 233 return (UINT32_MAX); 234 235 /* Verify bounds */ 236 if (offset > rman_get_size(r->res)) 237 return (UINT32_MAX); 238 239 if (rman_get_size(r->res) - offset < width) 240 return (UINT32_MAX); 241 242 switch (width) { 243 case 1: 244 return (bhnd_bus_read_1(r, offset)); 245 case 2: 246 return (bhnd_bus_read_2(r, offset)); 247 case 4: 248 return (bhnd_bus_read_4(r, offset)); 249 default: 250 return (UINT32_MAX); 251 } 252 } 253 254 static void 255 bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val, 256 u_int width) 257 { 258 struct bcma_devinfo *dinfo; 259 struct bhnd_resource *r; 260 261 /* Must be a directly attached child core */ 262 if (device_get_parent(child) != dev) 263 return; 264 265 /* Fetch the agent registers */ 266 dinfo = device_get_ivars(child); 267 if ((r = dinfo->res_agent) == NULL) 268 return; 269 270 /* Verify bounds */ 271 if (offset > rman_get_size(r->res)) 272 return; 273 274 if (rman_get_size(r->res) - offset < width) 275 return; 276 277 switch (width) { 278 case 1: 279 bhnd_bus_write_1(r, offset, val); 280 break; 281 case 2: 282 bhnd_bus_write_2(r, offset, val); 283 break; 284 case 4: 285 bhnd_bus_write_4(r, offset, val); 286 break; 287 default: 288 break; 289 } 290 } 291 292 static u_int 293 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type) 294 { 295 struct bcma_devinfo *dinfo; 296 297 /* delegate non-bus-attached devices to our parent */ 298 if (device_get_parent(child) != dev) 299 return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child, 300 type)); 301 302 dinfo = device_get_ivars(child); 303 switch (type) { 304 case BHND_PORT_DEVICE: 305 return (dinfo->corecfg->num_dev_ports); 306 case BHND_PORT_BRIDGE: 307 return (dinfo->corecfg->num_bridge_ports); 308 case BHND_PORT_AGENT: 309 return (dinfo->corecfg->num_wrapper_ports); 310 default: 311 device_printf(dev, "%s: unknown type (%d)\n", 312 __func__, 313 type); 314 return (0); 315 } 316 } 317 318 static u_int 319 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type, 320 u_int port_num) 321 { 322 struct bcma_devinfo *dinfo; 323 struct bcma_sport_list *ports; 324 struct bcma_sport *port; 325 326 /* delegate non-bus-attached devices to our parent */ 327 if (device_get_parent(child) != dev) 328 return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child, 329 type, port_num)); 330 331 dinfo = device_get_ivars(child); 332 ports = bcma_corecfg_get_port_list(dinfo->corecfg, type); 333 334 STAILQ_FOREACH(port, ports, sp_link) { 335 if (port->sp_num == port_num) 336 return (port->sp_num_maps); 337 } 338 339 /* not found */ 340 return (0); 341 } 342 343 static int 344 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, 345 u_int port_num, u_int region_num) 346 { 347 struct bcma_devinfo *dinfo; 348 struct bcma_map *map; 349 struct bcma_sport_list *ports; 350 struct bcma_sport *port; 351 352 dinfo = device_get_ivars(child); 353 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type); 354 355 STAILQ_FOREACH(port, ports, sp_link) { 356 if (port->sp_num != port_num) 357 continue; 358 359 STAILQ_FOREACH(map, &port->sp_maps, m_link) 360 if (map->m_region_num == region_num) 361 return map->m_rid; 362 } 363 364 return -1; 365 } 366 367 static int 368 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid, 369 bhnd_port_type *port_type, u_int *port_num, u_int *region_num) 370 { 371 struct bcma_devinfo *dinfo; 372 struct bcma_map *map; 373 struct bcma_sport_list *ports; 374 struct bcma_sport *port; 375 376 dinfo = device_get_ivars(child); 377 378 /* Ports are always memory mapped */ 379 if (type != SYS_RES_MEMORY) 380 return (EINVAL); 381 382 /* Starting with the most likely device list, search all three port 383 * lists */ 384 bhnd_port_type types[] = { 385 BHND_PORT_DEVICE, 386 BHND_PORT_AGENT, 387 BHND_PORT_BRIDGE 388 }; 389 390 for (int i = 0; i < nitems(types); i++) { 391 ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]); 392 393 STAILQ_FOREACH(port, ports, sp_link) { 394 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 395 if (map->m_rid != rid) 396 continue; 397 398 *port_type = port->sp_type; 399 *port_num = port->sp_num; 400 *region_num = map->m_region_num; 401 return (0); 402 } 403 } 404 } 405 406 return (ENOENT); 407 } 408 409 static int 410 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type, 411 u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size) 412 { 413 struct bcma_devinfo *dinfo; 414 struct bcma_map *map; 415 struct bcma_sport_list *ports; 416 struct bcma_sport *port; 417 418 dinfo = device_get_ivars(child); 419 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type); 420 421 /* Search the port list */ 422 STAILQ_FOREACH(port, ports, sp_link) { 423 if (port->sp_num != port_num) 424 continue; 425 426 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 427 if (map->m_region_num != region_num) 428 continue; 429 430 /* Found! */ 431 *addr = map->m_base; 432 *size = map->m_size; 433 return (0); 434 } 435 } 436 437 return (ENOENT); 438 } 439 440 /** 441 * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT(). 442 * 443 * This implementation consults @p child's agent register block, 444 * returning the number of interrupt output lines routed to @p child. 445 */ 446 int 447 bcma_get_intr_count(device_t dev, device_t child) 448 { 449 struct bcma_devinfo *dinfo; 450 uint32_t dmpcfg, oobw; 451 452 dinfo = device_get_ivars(child); 453 454 /* Agent block must be mapped */ 455 if (dinfo->res_agent == NULL) 456 return (0); 457 458 /* Agent must support OOB */ 459 dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG); 460 if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB)) 461 return (0); 462 463 /* Return OOB width as interrupt count */ 464 oobw = bhnd_bus_read_4(dinfo->res_agent, 465 BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR)); 466 if (oobw > BCMA_OOB_NUM_SEL) { 467 device_printf(dev, "ignoring invalid OOBOUTWIDTH for core %u: " 468 "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw); 469 return (0); 470 } 471 472 return (oobw); 473 } 474 475 /** 476 * Default bcma(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC(). 477 * 478 * This implementation consults @p child's agent register block, 479 * returning the interrupt output line routed to @p child, at OOB selector 480 * @p intr. 481 */ 482 int 483 bcma_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec) 484 { 485 struct bcma_devinfo *dinfo; 486 uint32_t oobsel; 487 488 dinfo = device_get_ivars(child); 489 490 /* Interrupt ID must be valid. */ 491 if (intr >= bcma_get_intr_count(dev, child)) 492 return (ENXIO); 493 494 /* Fetch OOBSEL busline value */ 495 KASSERT(dinfo->res_agent != NULL, ("missing agent registers")); 496 oobsel = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT( 497 BCMA_OOB_BANK_INTR, intr)); 498 *ivec = (oobsel >> BCMA_DMP_OOBSEL_SHIFT(intr)) & 499 BCMA_DMP_OOBSEL_BUSLINE_MASK; 500 501 return (0); 502 } 503 504 static struct bhnd_devinfo * 505 bcma_alloc_bhnd_dinfo(device_t dev) 506 { 507 struct bcma_devinfo *dinfo = bcma_alloc_dinfo(dev); 508 return ((struct bhnd_devinfo *)dinfo); 509 } 510 511 static void 512 bcma_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo) 513 { 514 bcma_free_dinfo(dev, (struct bcma_devinfo *)dinfo); 515 } 516 517 /** 518 * Scan the device enumeration ROM table, adding all valid discovered cores to 519 * the bus. 520 * 521 * @param bus The bcma bus. 522 */ 523 int 524 bcma_add_children(device_t bus) 525 { 526 bhnd_erom_t *erom; 527 struct bcma_erom *bcma_erom; 528 const struct bhnd_chipid *cid; 529 struct bcma_corecfg *corecfg; 530 struct bcma_devinfo *dinfo; 531 device_t child; 532 int error; 533 534 cid = BHND_BUS_GET_CHIPID(bus, bus); 535 corecfg = NULL; 536 537 /* Allocate our EROM parser */ 538 erom = bhnd_erom_alloc(&bcma_erom_parser, cid, bus, BCMA_EROM_RID); 539 if (erom == NULL) 540 return (ENODEV); 541 542 /* Add all cores. */ 543 bcma_erom = (struct bcma_erom *)erom; 544 while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) { 545 int nintr; 546 547 /* Add the child device */ 548 child = BUS_ADD_CHILD(bus, 0, NULL, -1); 549 if (child == NULL) { 550 error = ENXIO; 551 goto cleanup; 552 } 553 554 /* Initialize device ivars */ 555 dinfo = device_get_ivars(child); 556 if ((error = bcma_init_dinfo(bus, dinfo, corecfg))) 557 goto cleanup; 558 559 /* The dinfo instance now owns the corecfg value */ 560 corecfg = NULL; 561 562 /* Allocate device's agent registers, if any */ 563 if ((error = bcma_dinfo_alloc_agent(bus, child, dinfo))) 564 goto cleanup; 565 566 /* Assign interrupts */ 567 nintr = bhnd_get_intr_count(child); 568 for (int rid = 0; rid < nintr; rid++) { 569 error = BHND_BUS_ASSIGN_INTR(bus, child, rid); 570 if (error) { 571 device_printf(bus, "failed to assign interrupt " 572 "%d to core %u: %d\n", rid, 573 BCMA_DINFO_COREIDX(dinfo), error); 574 } 575 } 576 577 /* If pins are floating or the hardware is otherwise 578 * unpopulated, the device shouldn't be used. */ 579 if (bhnd_is_hw_disabled(child)) 580 device_disable(child); 581 582 /* Issue bus callback for fully initialized child. */ 583 BHND_BUS_CHILD_ADDED(bus, child); 584 } 585 586 /* EOF while parsing cores is expected */ 587 if (error == ENOENT) 588 error = 0; 589 590 cleanup: 591 bhnd_erom_free(erom); 592 593 if (corecfg != NULL) 594 bcma_free_corecfg(corecfg); 595 596 if (error) 597 device_delete_children(bus); 598 599 return (error); 600 } 601 602 603 static device_method_t bcma_methods[] = { 604 /* Device interface */ 605 DEVMETHOD(device_probe, bcma_probe), 606 DEVMETHOD(device_attach, bcma_attach), 607 DEVMETHOD(device_detach, bcma_detach), 608 609 /* Bus interface */ 610 DEVMETHOD(bus_read_ivar, bcma_read_ivar), 611 DEVMETHOD(bus_write_ivar, bcma_write_ivar), 612 DEVMETHOD(bus_get_resource_list, bcma_get_resource_list), 613 614 /* BHND interface */ 615 DEVMETHOD(bhnd_bus_get_erom_class, bcma_get_erom_class), 616 DEVMETHOD(bhnd_bus_alloc_devinfo, bcma_alloc_bhnd_dinfo), 617 DEVMETHOD(bhnd_bus_free_devinfo, bcma_free_bhnd_dinfo), 618 DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core), 619 DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core), 620 DEVMETHOD(bhnd_bus_read_config, bcma_read_config), 621 DEVMETHOD(bhnd_bus_write_config, bcma_write_config), 622 DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count), 623 DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count), 624 DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid), 625 DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid), 626 DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr), 627 DEVMETHOD(bhnd_bus_get_intr_count, bcma_get_intr_count), 628 DEVMETHOD(bhnd_bus_get_core_ivec, bcma_get_core_ivec), 629 630 DEVMETHOD_END 631 }; 632 633 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver); 634 MODULE_VERSION(bcma, 1); 635 MODULE_DEPEND(bcma, bhnd, 1, 1, 1); 636