1 /*- 2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> 3 * Copyright (c) 2017 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * Portions of this software were developed by Landon Fuller 7 * under sponsorship from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer, 14 * without modification. 15 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 16 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 17 * redistribution must be conditioned upon including a substantially 18 * similar Disclaimer requirement for further binary redistribution. 19 * 20 * NO WARRANTY 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 24 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 25 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 26 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 29 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGES. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/bus.h> 39 #include <sys/kobj.h> 40 41 #include <machine/bus.h> 42 #include <sys/rman.h> 43 #include <machine/resource.h> 44 45 #include <dev/bhnd/bhndvar.h> 46 #include <dev/bhnd/bhnd_erom.h> 47 #include <dev/bhnd/bhnd_eromvar.h> 48 49 static int bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 50 bhnd_size_t size); 51 static uint32_t bhnd_erom_iores_read(struct bhnd_erom_io *eio, 52 bhnd_size_t offset, u_int width); 53 static void bhnd_erom_iores_fini(struct bhnd_erom_io *eio); 54 55 static int bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 56 bhnd_size_t size); 57 static uint32_t bhnd_erom_iobus_read(struct bhnd_erom_io *eio, 58 bhnd_size_t offset, u_int width); 59 60 /** 61 * An implementation of bhnd_erom_io that manages mappings via 62 * bhnd_alloc_resource() and bhnd_release_resource(). 63 */ 64 struct bhnd_erom_iores { 65 struct bhnd_erom_io eio; 66 device_t owner; /**< device from which we'll allocate resources */ 67 int owner_rid; /**< rid to use when allocating new mappings */ 68 struct bhnd_resource *mapped; /**< current mapping, or NULL */ 69 int mapped_rid; /**< resource ID of current mapping, or -1 */ 70 }; 71 72 /** 73 * Fetch the device enumeration parser class from all bhnd(4)-compatible drivers 74 * registered for @p bus_devclass, probe @p eio for supporting parser classes, 75 * and return the best available supporting enumeration parser class. 76 * 77 * @param bus_devclass The bus device class to be queried for 78 * bhnd(4)-compatible drivers. 79 * @param eio An erom bus I/O instance, configured with a 80 * mapping of the first bus core. 81 * @param hint Identification hint used to identify the device. 82 * If the chipset supports standard chip 83 * identification registers within the first core, 84 * this parameter should be NULL. 85 * @param[out] cid On success, the probed chip identifier. 86 * 87 * @retval non-NULL on success, the best available EROM class. 88 * @retval NULL if no erom class returned a successful probe result for 89 * @p eio. 90 */ 91 bhnd_erom_class_t * 92 bhnd_erom_probe_driver_classes(devclass_t bus_devclass, 93 struct bhnd_erom_io *eio, const struct bhnd_chipid *hint, 94 struct bhnd_chipid *cid) 95 { 96 driver_t **drivers; 97 int drv_count; 98 bhnd_erom_class_t *erom_cls; 99 int error, prio, result; 100 101 erom_cls = NULL; 102 prio = 0; 103 104 /* Fetch all available drivers */ 105 error = devclass_get_drivers(bus_devclass, &drivers, &drv_count); 106 if (error) { 107 printf("error fetching bhnd(4) drivers for %s: %d\n", 108 devclass_get_name(bus_devclass), error); 109 return (NULL); 110 } 111 112 /* Enumerate the drivers looking for the best available EROM class */ 113 for (int i = 0; i < drv_count; i++) { 114 struct bhnd_chipid pcid; 115 bhnd_erom_class_t *cls; 116 117 /* The default implementation of BHND_BUS_GET_EROM_CLASS() 118 * returns NULL if unimplemented; this should always be safe 119 * to call on arbitrary drivers */ 120 cls = bhnd_driver_get_erom_class(drivers[i]); 121 if (cls == NULL) 122 continue; 123 124 kobj_class_compile(cls); 125 126 /* Probe the bus */ 127 result = bhnd_erom_probe(cls, eio, hint, &pcid); 128 129 /* The parser did not match if an error was returned */ 130 if (result > 0) 131 continue; 132 133 /* Check for a new highest priority match */ 134 if (erom_cls == NULL || result > prio) { 135 prio = result; 136 137 *cid = pcid; 138 erom_cls = cls; 139 } 140 141 /* Terminate immediately on BUS_PROBE_SPECIFIC */ 142 if (result == BUS_PROBE_SPECIFIC) 143 break; 144 } 145 146 return (erom_cls); 147 } 148 149 /** 150 * Allocate and return a new device enumeration table parser. 151 * 152 * @param cls The parser class for which an instance will be 153 * allocated. 154 * @param eio The bus I/O callbacks to use when reading the device 155 * enumeration table. 156 * @param cid The device's chip identifier. 157 * 158 * @retval non-NULL success 159 * @retval NULL if an error occured allocating or initializing the 160 * EROM parser. 161 */ 162 bhnd_erom_t * 163 bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid, 164 struct bhnd_erom_io *eio) 165 { 166 bhnd_erom_t *erom; 167 int error; 168 169 erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND, 170 M_WAITOK|M_ZERO); 171 172 if ((error = BHND_EROM_INIT(erom, cid, eio))) { 173 printf("error initializing %s parser at %#jx: %d\n", cls->name, 174 (uintmax_t)cid->enum_addr, error); 175 176 kobj_delete((kobj_t)erom, M_BHND); 177 return (NULL); 178 } 179 180 return (erom); 181 } 182 183 /** 184 * Perform static initialization of a device enumeration table parser. 185 * 186 * This may be used to initialize a caller-allocated erom instance state 187 * during early boot, prior to malloc availability. 188 * 189 * @param cls The parser class for which an instance will be 190 * allocated. 191 * @param erom The erom parser instance to initialize. 192 * @param esize The total available number of bytes allocated for 193 * @p erom. If this is less than is required by @p cls, 194 * ENOMEM will be returned. 195 * @param cid The device's chip identifier. 196 * @param eio The bus I/O callbacks to use when reading the device 197 * enumeration table. 198 * 199 * @retval 0 success 200 * @retval ENOMEM if @p esize is smaller than required by @p cls. 201 * @retval non-zero if an error occurs initializing the EROM parser, 202 * a regular unix error code will be returned. 203 */ 204 int 205 bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize, 206 const struct bhnd_chipid *cid, struct bhnd_erom_io *eio) 207 { 208 kobj_class_t kcls; 209 210 kcls = (kobj_class_t)cls; 211 212 /* Verify allocation size */ 213 if (kcls->size > esize) 214 return (ENOMEM); 215 216 /* Perform instance initialization */ 217 kobj_init_static((kobj_t)erom, kcls); 218 return (BHND_EROM_INIT(erom, cid, eio)); 219 } 220 221 /** 222 * Release any resources held by a @p erom parser previously 223 * initialized via bhnd_erom_init_static(). 224 * 225 * @param erom An erom parser instance previously initialized via 226 * bhnd_erom_init_static(). 227 */ 228 void 229 bhnd_erom_fini_static(bhnd_erom_t *erom) 230 { 231 return (BHND_EROM_FINI(erom)); 232 } 233 234 /** 235 * Release all resources held by a @p erom parser previously 236 * allocated via bhnd_erom_alloc(). 237 * 238 * @param erom An erom parser instance previously allocated via 239 * bhnd_erom_alloc(). 240 */ 241 void 242 bhnd_erom_free(bhnd_erom_t *erom) 243 { 244 BHND_EROM_FINI(erom); 245 kobj_delete((kobj_t)erom, M_BHND); 246 } 247 248 249 /** 250 * Attempt to map @p size bytes at @p addr, replacing any existing 251 * @p eio mapping. 252 * 253 * @param eio I/O instance state. 254 * @param addr The address to be mapped. 255 * @param size The number of bytes to be mapped at @p addr. 256 * 257 * @retval 0 success 258 * @retval non-zero if mapping @p addr otherwise fails, a regular 259 * unix error code should be returned. 260 */ 261 int 262 bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size) 263 { 264 return (eio->map(eio, addr, size)); 265 } 266 267 /** 268 * Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset 269 * relative to @p eio's current mapping. 270 * 271 * @param eio erom I/O callbacks 272 * @param offset read offset. 273 * @param width item width (1, 2, or 4 bytes). 274 */ 275 uint32_t 276 bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 277 { 278 return (eio->read(eio, offset, width)); 279 } 280 281 /** 282 * Free all resources held by @p eio. 283 */ 284 void 285 bhnd_erom_io_fini(struct bhnd_erom_io *eio) 286 { 287 if (eio->fini != NULL) 288 return (eio->fini(eio)); 289 } 290 291 /** 292 * Allocate, initialize, and return a new I/O instance that will perform 293 * mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid. 294 * 295 * @param dev The device to pass to bhnd_alloc_resource() and 296 * bhnd_release_resource() functions. 297 * @param rid The resource ID to be used when allocating memory resources. 298 */ 299 struct bhnd_erom_io * 300 bhnd_erom_iores_new(device_t dev, int rid) 301 { 302 struct bhnd_erom_iores *iores; 303 304 iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO); 305 iores->eio.map = bhnd_erom_iores_map; 306 iores->eio.read = bhnd_erom_iores_read; 307 iores->eio.fini = bhnd_erom_iores_fini; 308 309 iores->owner = dev; 310 iores->owner_rid = rid; 311 iores->mapped = NULL; 312 iores->mapped_rid = -1; 313 314 return (&iores->eio); 315 } 316 317 static int 318 bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 319 bhnd_size_t size) 320 { 321 struct bhnd_erom_iores *iores; 322 323 iores = (struct bhnd_erom_iores *)eio; 324 325 /* Sanity check the addr/size */ 326 if (size == 0) 327 return (EINVAL); 328 329 if (BHND_ADDR_MAX - size < addr) 330 return (EINVAL); /* would overflow */ 331 332 /* Check for an existing mapping */ 333 if (iores->mapped) { 334 /* If already mapped, nothing else to do */ 335 if (rman_get_start(iores->mapped->res) == addr && 336 rman_get_size(iores->mapped->res) == size) 337 { 338 return (0); 339 } 340 341 /* Otherwise, we need to drop the existing mapping */ 342 bhnd_release_resource(iores->owner, SYS_RES_MEMORY, 343 iores->mapped_rid, iores->mapped); 344 iores->mapped = NULL; 345 iores->mapped_rid = -1; 346 } 347 348 /* Try to allocate the new mapping */ 349 iores->mapped_rid = iores->owner_rid; 350 iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY, 351 &iores->mapped_rid, addr, addr+size-1, size, 352 RF_ACTIVE|RF_SHAREABLE); 353 if (iores->mapped == NULL) { 354 iores->mapped_rid = -1; 355 return (ENXIO); 356 } 357 358 return (0); 359 } 360 361 static uint32_t 362 bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 363 { 364 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio; 365 366 if (iores->mapped == NULL) 367 panic("read with invalid mapping"); 368 369 switch (width) { 370 case 1: 371 return (bhnd_bus_read_1(iores->mapped, offset)); 372 case 2: 373 return (bhnd_bus_read_2(iores->mapped, offset)); 374 case 4: 375 return (bhnd_bus_read_4(iores->mapped, offset)); 376 default: 377 panic("invalid width %u", width); 378 } 379 } 380 381 static void 382 bhnd_erom_iores_fini(struct bhnd_erom_io *eio) 383 { 384 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio; 385 386 /* Release any mapping */ 387 if (iores->mapped) { 388 bhnd_release_resource(iores->owner, SYS_RES_MEMORY, 389 iores->mapped_rid, iores->mapped); 390 iores->mapped = NULL; 391 iores->mapped_rid = -1; 392 } 393 394 free(eio, M_BHND); 395 } 396 397 /** 398 * Initialize an I/O instance that will perform mapping directly from the 399 * given bus space tag and handle. 400 * 401 * @param addr The base address mapped by @p bsh. 402 * @param size The total size mapped by @p bsh. 403 * @param bst Bus space tag for @p bsh. 404 * @param bsh Bus space handle mapping the full bus enumeration space. 405 * 406 * @retval 0 success 407 * @retval non-zero if initializing @p iobus otherwise fails, a regular 408 * unix error code will be returned. 409 */ 410 int 411 bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr, 412 bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh) 413 { 414 iobus->eio.map = bhnd_erom_iobus_map; 415 iobus->eio.read = bhnd_erom_iobus_read; 416 iobus->eio.fini = NULL; 417 418 iobus->addr = addr; 419 iobus->size = size; 420 iobus->bst = bst; 421 iobus->bsh = bsh; 422 iobus->mapped = false; 423 424 return (0); 425 } 426 427 static int 428 bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 429 bhnd_size_t size) 430 { 431 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio; 432 433 /* Sanity check the addr/size */ 434 if (size == 0) 435 return (EINVAL); 436 437 /* addr+size must not overflow */ 438 if (BHND_ADDR_MAX - size < addr) 439 return (EINVAL); 440 441 /* addr/size must fit within our bus tag's mapping */ 442 if (addr < iobus->addr || size > iobus->size) 443 return (ENXIO); 444 445 if (iobus->size - (addr - iobus->addr) < size) 446 return (ENXIO); 447 448 /* The new addr offset and size must be representible as a bus_size_t */ 449 if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE) 450 return (ENXIO); 451 452 if (size > BUS_SPACE_MAXSIZE) 453 return (ENXIO); 454 455 iobus->offset = addr - iobus->addr; 456 iobus->limit = size; 457 iobus->mapped = true; 458 459 return (0); 460 } 461 462 static uint32_t 463 bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 464 { 465 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio; 466 467 if (!iobus->mapped) 468 panic("no active mapping"); 469 470 if (iobus->limit < width || iobus->limit - width < offset) 471 panic("invalid offset %#jx", offset); 472 473 switch (width) { 474 case 1: 475 return (bus_space_read_1(iobus->bst, iobus->bsh, 476 iobus->offset + offset)); 477 case 2: 478 return (bus_space_read_2(iobus->bst, iobus->bsh, 479 iobus->offset + offset)); 480 case 4: 481 return (bus_space_read_4(iobus->bst, iobus->bsh, 482 iobus->offset + offset)); 483 default: 484 panic("invalid width %u", width); 485 } 486 } 487