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