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