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 = 0; 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 198 static int 199 bcma_reset_core(device_t dev, device_t child, uint16_t flags) 200 { 201 struct bcma_devinfo *dinfo; 202 203 if (device_get_parent(child) != dev) 204 BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags); 205 206 dinfo = device_get_ivars(child); 207 208 /* Can't reset the core without access to the agent registers */ 209 if (dinfo->res_agent == NULL) 210 return (ENODEV); 211 212 // TODO - perform reset 213 214 return (ENXIO); 215 } 216 217 static int 218 bcma_suspend_core(device_t dev, device_t child) 219 { 220 struct bcma_devinfo *dinfo; 221 222 if (device_get_parent(child) != dev) 223 BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child); 224 225 dinfo = device_get_ivars(child); 226 227 /* Can't suspend the core without access to the agent registers */ 228 if (dinfo->res_agent == NULL) 229 return (ENODEV); 230 231 // TODO - perform suspend 232 233 return (ENXIO); 234 } 235 236 static u_int 237 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type) 238 { 239 struct bcma_devinfo *dinfo; 240 241 /* delegate non-bus-attached devices to our parent */ 242 if (device_get_parent(child) != dev) 243 return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child, 244 type)); 245 246 dinfo = device_get_ivars(child); 247 switch (type) { 248 case BHND_PORT_DEVICE: 249 return (dinfo->corecfg->num_dev_ports); 250 case BHND_PORT_BRIDGE: 251 return (dinfo->corecfg->num_bridge_ports); 252 case BHND_PORT_AGENT: 253 return (dinfo->corecfg->num_wrapper_ports); 254 } 255 } 256 257 static u_int 258 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type, 259 u_int port_num) 260 { 261 struct bcma_devinfo *dinfo; 262 struct bcma_sport_list *ports; 263 struct bcma_sport *port; 264 265 /* delegate non-bus-attached devices to our parent */ 266 if (device_get_parent(child) != dev) 267 return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child, 268 type, port_num)); 269 270 dinfo = device_get_ivars(child); 271 ports = bcma_corecfg_get_port_list(dinfo->corecfg, type); 272 273 STAILQ_FOREACH(port, ports, sp_link) { 274 if (port->sp_num == port_num) 275 return (port->sp_num_maps); 276 } 277 278 /* not found */ 279 return (0); 280 } 281 282 static int 283 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type, 284 u_int port_num, u_int region_num) 285 { 286 struct bcma_devinfo *dinfo; 287 struct bcma_map *map; 288 struct bcma_sport_list *ports; 289 struct bcma_sport *port; 290 291 dinfo = device_get_ivars(child); 292 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type); 293 294 STAILQ_FOREACH(port, ports, sp_link) { 295 if (port->sp_num != port_num) 296 continue; 297 298 STAILQ_FOREACH(map, &port->sp_maps, m_link) 299 if (map->m_region_num == region_num) 300 return map->m_rid; 301 } 302 303 return -1; 304 } 305 306 static int 307 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid, 308 bhnd_port_type *port_type, u_int *port_num, u_int *region_num) 309 { 310 struct bcma_devinfo *dinfo; 311 struct bcma_map *map; 312 struct bcma_sport_list *ports; 313 struct bcma_sport *port; 314 315 dinfo = device_get_ivars(child); 316 317 /* Ports are always memory mapped */ 318 if (type != SYS_RES_MEMORY) 319 return (EINVAL); 320 321 /* Starting with the most likely device list, search all three port 322 * lists */ 323 bhnd_port_type types[] = { 324 BHND_PORT_DEVICE, 325 BHND_PORT_AGENT, 326 BHND_PORT_BRIDGE 327 }; 328 329 for (int i = 0; i < nitems(types); i++) { 330 ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]); 331 332 STAILQ_FOREACH(port, ports, sp_link) { 333 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 334 if (map->m_rid != rid) 335 continue; 336 337 *port_type = port->sp_type; 338 *port_num = port->sp_num; 339 *region_num = map->m_region_num; 340 return (0); 341 } 342 } 343 } 344 345 return (ENOENT); 346 } 347 348 static int 349 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type, 350 u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size) 351 { 352 struct bcma_devinfo *dinfo; 353 struct bcma_map *map; 354 struct bcma_sport_list *ports; 355 struct bcma_sport *port; 356 357 dinfo = device_get_ivars(child); 358 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type); 359 360 /* Search the port list */ 361 STAILQ_FOREACH(port, ports, sp_link) { 362 if (port->sp_num != port_num) 363 continue; 364 365 STAILQ_FOREACH(map, &port->sp_maps, m_link) { 366 if (map->m_region_num != region_num) 367 continue; 368 369 /* Found! */ 370 *addr = map->m_base; 371 *size = map->m_size; 372 return (0); 373 } 374 } 375 376 return (ENOENT); 377 } 378 379 /** 380 * Scan a device enumeration ROM table, adding all valid discovered cores to 381 * the bus. 382 * 383 * @param bus The bcma bus. 384 * @param erom_res An active resource mapping the EROM core. 385 * @param erom_offset Base offset of the EROM core's register mapping. 386 */ 387 int 388 bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset) 389 { 390 struct bcma_erom erom; 391 struct bcma_corecfg *corecfg; 392 struct bcma_devinfo *dinfo; 393 device_t child; 394 int error; 395 396 dinfo = NULL; 397 corecfg = NULL; 398 399 /* Initialize our reader */ 400 error = bcma_erom_open(&erom, erom_res, erom_offset); 401 if (error) 402 return (error); 403 404 /* Add all cores. */ 405 while (!error) { 406 /* Parse next core */ 407 error = bcma_erom_parse_corecfg(&erom, &corecfg); 408 if (error && error == ENOENT) { 409 return (0); 410 } else if (error) { 411 goto failed; 412 } 413 414 /* Allocate per-device bus info */ 415 dinfo = bcma_alloc_dinfo(bus, corecfg); 416 if (dinfo == NULL) { 417 error = ENXIO; 418 goto failed; 419 } 420 421 /* The dinfo instance now owns the corecfg value */ 422 corecfg = NULL; 423 424 /* Add the child device */ 425 child = device_add_child(bus, NULL, -1); 426 if (child == NULL) { 427 error = ENXIO; 428 goto failed; 429 } 430 431 /* The child device now owns the dinfo pointer */ 432 device_set_ivars(child, dinfo); 433 dinfo = NULL; 434 435 /* If pins are floating or the hardware is otherwise 436 * unpopulated, the device shouldn't be used. */ 437 if (bhnd_is_hw_disabled(child)) 438 device_disable(child); 439 } 440 441 /* Hit EOF parsing cores? */ 442 if (error == ENOENT) 443 return (0); 444 445 failed: 446 if (dinfo != NULL) 447 bcma_free_dinfo(bus, dinfo); 448 449 if (corecfg != NULL) 450 bcma_free_corecfg(corecfg); 451 452 return (error); 453 } 454 455 456 static device_method_t bcma_methods[] = { 457 /* Device interface */ 458 DEVMETHOD(device_probe, bcma_probe), 459 DEVMETHOD(device_attach, bcma_attach), 460 DEVMETHOD(device_detach, bcma_detach), 461 462 /* Bus interface */ 463 DEVMETHOD(bus_child_deleted, bcma_child_deleted), 464 DEVMETHOD(bus_read_ivar, bcma_read_ivar), 465 DEVMETHOD(bus_write_ivar, bcma_write_ivar), 466 DEVMETHOD(bus_get_resource_list, bcma_get_resource_list), 467 468 /* BHND interface */ 469 DEVMETHOD(bhnd_bus_reset_core, bcma_reset_core), 470 DEVMETHOD(bhnd_bus_suspend_core, bcma_suspend_core), 471 DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count), 472 DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count), 473 DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid), 474 DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid), 475 DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr), 476 477 DEVMETHOD_END 478 }; 479 480 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver); 481 MODULE_VERSION(bcma, 1); 482 MODULE_DEPEND(bcma, bhnd, 1, 1, 1); 483