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_eromreg.h" 45 #include "bcma_eromvar.h" 46 47 int 48 bcma_probe(device_t dev) 49 { 50 device_set_desc(dev, "BCMA BHND bus"); 51 return (BUS_PROBE_DEFAULT); 52 } 53 54 int 55 bcma_attach(device_t dev) 56 { 57 struct bcma_devinfo *dinfo; 58 device_t *devs, child; 59 int ndevs; 60 int error; 61 62 63 if ((error = device_get_children(dev, &devs, &ndevs))) 64 return (error); 65 66 /* 67 * Map our children's agent register block. 68 */ 69 for (int i = 0; i < ndevs; i++) { 70 bhnd_addr_t addr; 71 bhnd_size_t size; 72 rman_res_t r_start, r_count, r_end; 73 74 child = devs[i]; 75 dinfo = device_get_ivars(child); 76 77 KASSERT(!device_is_suspended(child), 78 ("bcma(4) stateful suspend handling requires that devices " 79 "not be suspended before bcma_attach()")); 80 81 /* Verify that the agent register block exists and is 82 * mappable */ 83 if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1) 84 continue; 85 86 /* Fetch the address of the agent register block */ 87 error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0, 88 &addr, &size); 89 if (error) { 90 device_printf(dev, "failed fetching agent register " 91 "block address for core %d\n", i); 92 goto cleanup; 93 } 94 95 /* Allocate the resource */ 96 r_start = addr; 97 r_count = size; 98 r_end = r_start + r_count - 1; 99 100 dinfo->rid_agent = i + 1; 101 dinfo->res_agent = BHND_BUS_ALLOC_RESOURCE(dev, dev, 102 SYS_RES_MEMORY, &dinfo->rid_agent, r_start, r_end, r_count, 103 RF_ACTIVE); 104 if (dinfo->res_agent == NULL) { 105 device_printf(dev, "failed allocating agent register " 106 "block for core %d\n", i); 107 error = ENXIO; 108 goto cleanup; 109 } 110 } 111 112 cleanup: 113 free(devs, M_BHND); 114 if (error) 115 return (error); 116 117 return (bhnd_generic_attach(dev)); 118 } 119 120 int 121 bcma_detach(device_t dev) 122 { 123 return (bhnd_generic_detach(dev)); 124 } 125 126 static int 127 bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 128 { 129 const struct bcma_devinfo *dinfo; 130 const struct bhnd_core_info *ci; 131 132 dinfo = device_get_ivars(child); 133 ci = &dinfo->corecfg->core_info; 134 135 switch (index) { 136 case BHND_IVAR_VENDOR: 137 *result = ci->vendor; 138 return (0); 139 case BHND_IVAR_DEVICE: 140 *result = ci->device; 141 return (0); 142 case BHND_IVAR_HWREV: 143 *result = ci->hwrev; 144 return (0); 145 case BHND_IVAR_DEVICE_CLASS: 146 *result = bhnd_core_class(ci); 147 return (0); 148 case BHND_IVAR_VENDOR_NAME: 149 *result = (uintptr_t) bhnd_vendor_name(ci->vendor); 150 return (0); 151 case BHND_IVAR_DEVICE_NAME: 152 *result = (uintptr_t) bhnd_core_name(ci); 153 return (0); 154 case BHND_IVAR_CORE_INDEX: 155 *result = ci->core_idx; 156 return (0); 157 case BHND_IVAR_CORE_UNIT: 158 *result = ci->unit; 159 return (0); 160 default: 161 return (ENOENT); 162 } 163 } 164 165 static int 166 bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 167 { 168 switch (index) { 169 case BHND_IVAR_VENDOR: 170 case BHND_IVAR_DEVICE: 171 case BHND_IVAR_HWREV: 172 case BHND_IVAR_DEVICE_CLASS: 173 case BHND_IVAR_VENDOR_NAME: 174 case BHND_IVAR_DEVICE_NAME: 175 case BHND_IVAR_CORE_INDEX: 176 case BHND_IVAR_CORE_UNIT: 177 return (EINVAL); 178 default: 179 return (ENOENT); 180 } 181 } 182 183 static void 184 bcma_child_deleted(device_t dev, device_t child) 185 { 186 struct bcma_devinfo *dinfo = device_get_ivars(child); 187 if (dinfo != NULL) 188 bcma_free_dinfo(dev, dinfo); 189 } 190 191 static struct resource_list * 192 bcma_get_resource_list(device_t dev, device_t child) 193 { 194 struct bcma_devinfo *dinfo = device_get_ivars(child); 195 return (&dinfo->resources); 196 } 197 198 static device_t 199 bcma_find_hostb_device(device_t dev) 200 { 201 struct bcma_softc *sc = device_get_softc(dev); 202 203 /* This is set (or not) by the concrete bcma driver subclass. */ 204 return (sc->hostb_dev); 205 } 206 207 static int 208 bcma_reset_core(device_t dev, device_t child, uint16_t flags) 209 { 210 struct bcma_devinfo *dinfo; 211 212 if (device_get_parent(child) != dev) 213 BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags); 214 215 dinfo = device_get_ivars(child); 216 217 /* Can't reset the core without access to the agent registers */ 218 if (dinfo->res_agent == NULL) 219 return (ENODEV); 220 221 // TODO - perform reset 222 223 return (ENXIO); 224 } 225 226 static int 227 bcma_suspend_core(device_t dev, device_t child) 228 { 229 struct bcma_devinfo *dinfo; 230 231 if (device_get_parent(child) != dev) 232 BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child); 233 234 dinfo = device_get_ivars(child); 235 236 /* Can't suspend the core without access to the agent registers */ 237 if (dinfo->res_agent == NULL) 238 return (ENODEV); 239 240 // TODO - perform suspend 241 242 return (ENXIO); 243 } 244 245 static u_int 246 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type) 247 { 248 struct bcma_devinfo *dinfo; 249 250 /* delegate non-bus-attached devices to our parent */ 251 if (device_get_parent(child) != dev) 252 return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child, 253 type)); 254 255 dinfo = device_get_ivars(child); 256 switch (type) { 257 case BHND_PORT_DEVICE: 258 return (dinfo->corecfg->num_dev_ports); 259 case BHND_PORT_BRIDGE: 260 return (dinfo->corecfg->num_bridge_ports); 261 case BHND_PORT_AGENT: 262 return (dinfo->corecfg->num_wrapper_ports); 263 default: 264 device_printf(dev, "%s: unknown type (%d)\n", 265 __func__, 266 type); 267 return (0); 268 } 269 } 270 271 static u_int 272 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type, 273 u_int port_num) 274 { 275 struct bcma_devinfo *dinfo; 276 struct bcma_sport_list *ports; 277 struct bcma_sport *port; 278 279 /* delegate non-bus-attached devices to our parent */ 280 if (device_get_parent(child) != dev) 281 return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child, 282 type, port_num)); 283 284 dinfo = device_get_ivars(child); 285 ports = bcma_corecfg_get_port_list(dinfo->corecfg, type); 286 287 STAILQ_FOREACH(port, ports, sp_link) { 288 if (port->sp_num == port_num) 289 return (port->sp_num_maps); 290 } 291 292 /* not found */ 293 return (0); 294 } 295 296 static int 297 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, 298 u_int port_num, u_int region_num) 299 { 300 struct bcma_devinfo *dinfo; 301 struct bcma_map *map; 302 struct bcma_sport_list *ports; 303 struct bcma_sport *port; 304 305 dinfo = device_get_ivars(child); 306 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type); 307 308 STAILQ_FOREACH(port, ports, sp_link) { 309 if (port->sp_num != port_num) 310 continue; 311 312 STAILQ_FOREACH(map, &port->sp_maps, m_link) 313 if (map->m_region_num == region_num) 314 return map->m_rid; 315 } 316 317 return -1; 318 } 319 320 static int 321 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid, 322 bhnd_port_type *port_type, u_int *port_num, u_int *region_num) 323 { 324 struct bcma_devinfo *dinfo; 325 struct bcma_map *map; 326 struct bcma_sport_list *ports; 327 struct bcma_sport *port; 328 329 dinfo = device_get_ivars(child); 330 331 /* Ports are always memory mapped */ 332 if (type != SYS_RES_MEMORY) 333 return (EINVAL); 334 335 /* Starting with the most likely device list, search all three port 336 * lists */ 337 bhnd_port_type types[] = { 338 BHND_PORT_DEVICE, 339 BHND_PORT_AGENT, 340 BHND_PORT_BRIDGE 341 }; 342 343 for (int i = 0; i < nitems(types); i++) { 344 ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]); 345 346 STAILQ_FOREACH(port, ports, sp_link) { 347 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 348 if (map->m_rid != rid) 349 continue; 350 351 *port_type = port->sp_type; 352 *port_num = port->sp_num; 353 *region_num = map->m_region_num; 354 return (0); 355 } 356 } 357 } 358 359 return (ENOENT); 360 } 361 362 static int 363 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type, 364 u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size) 365 { 366 struct bcma_devinfo *dinfo; 367 struct bcma_map *map; 368 struct bcma_sport_list *ports; 369 struct bcma_sport *port; 370 371 dinfo = device_get_ivars(child); 372 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type); 373 374 /* Search the port list */ 375 STAILQ_FOREACH(port, ports, sp_link) { 376 if (port->sp_num != port_num) 377 continue; 378 379 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 380 if (map->m_region_num != region_num) 381 continue; 382 383 /* Found! */ 384 *addr = map->m_base; 385 *size = map->m_size; 386 return (0); 387 } 388 } 389 390 return (ENOENT); 391 } 392 393 /** 394 * Scan a device enumeration ROM table, adding all valid discovered cores to 395 * the bus. 396 * 397 * @param bus The bcma bus. 398 * @param erom_res An active resource mapping the EROM core. 399 * @param erom_offset Base offset of the EROM core's register mapping. 400 */ 401 int 402 bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset) 403 { 404 struct bcma_erom erom; 405 struct bcma_corecfg *corecfg; 406 struct bcma_devinfo *dinfo; 407 device_t child; 408 int error; 409 410 dinfo = NULL; 411 corecfg = NULL; 412 413 /* Initialize our reader */ 414 error = bcma_erom_open(&erom, erom_res, erom_offset); 415 if (error) 416 return (error); 417 418 /* Add all cores. */ 419 while (!error) { 420 /* Parse next core */ 421 error = bcma_erom_parse_corecfg(&erom, &corecfg); 422 if (error && error == ENOENT) { 423 return (0); 424 } else if (error) { 425 goto failed; 426 } 427 428 /* Allocate per-device bus info */ 429 dinfo = bcma_alloc_dinfo(bus, corecfg); 430 if (dinfo == NULL) { 431 error = ENXIO; 432 goto failed; 433 } 434 435 /* The dinfo instance now owns the corecfg value */ 436 corecfg = NULL; 437 438 /* Add the child device */ 439 child = device_add_child(bus, NULL, -1); 440 if (child == NULL) { 441 error = ENXIO; 442 goto failed; 443 } 444 445 /* The child device now owns the dinfo pointer */ 446 device_set_ivars(child, dinfo); 447 dinfo = NULL; 448 449 /* If pins are floating or the hardware is otherwise 450 * unpopulated, the device shouldn't be used. */ 451 if (bhnd_is_hw_disabled(child)) 452 device_disable(child); 453 } 454 455 /* Hit EOF parsing cores? */ 456 if (error == ENOENT) 457 return (0); 458 459 failed: 460 if (dinfo != NULL) 461 bcma_free_dinfo(bus, dinfo); 462 463 if (corecfg != NULL) 464 bcma_free_corecfg(corecfg); 465 466 return (error); 467 } 468 469 470 static device_method_t bcma_methods[] = { 471 /* Device interface */ 472 DEVMETHOD(device_probe, bcma_probe), 473 DEVMETHOD(device_attach, bcma_attach), 474 DEVMETHOD(device_detach, bcma_detach), 475 476 /* Bus interface */ 477 DEVMETHOD(bus_child_deleted, bcma_child_deleted), 478 DEVMETHOD(bus_read_ivar, bcma_read_ivar), 479 DEVMETHOD(bus_write_ivar, bcma_write_ivar), 480 DEVMETHOD(bus_get_resource_list, bcma_get_resource_list), 481 482 /* BHND interface */ 483 DEVMETHOD(bhnd_bus_find_hostb_device, bcma_find_hostb_device), 484 DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core), 485 DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core), 486 DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count), 487 DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count), 488 DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid), 489 DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid), 490 DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr), 491 492 DEVMETHOD_END 493 }; 494 495 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver); 496 MODULE_VERSION(bcma, 1); 497 MODULE_DEPEND(bcma, bhnd, 1, 1, 1); 498