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 return (erom_cls); 149 } 150 151 /** 152 * Allocate and return a new device enumeration table parser. 153 * 154 * @param cls The parser class for which an instance will be 155 * allocated. 156 * @param eio The bus I/O callbacks to use when reading the device 157 * enumeration table. 158 * @param cid The device's chip identifier. 159 * 160 * @retval non-NULL success 161 * @retval NULL if an error occured allocating or initializing the 162 * EROM parser. 163 */ 164 bhnd_erom_t * 165 bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid, 166 struct bhnd_erom_io *eio) 167 { 168 bhnd_erom_t *erom; 169 int error; 170 171 erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND, 172 M_WAITOK|M_ZERO); 173 174 if ((error = BHND_EROM_INIT(erom, cid, eio))) { 175 printf("error initializing %s parser at %#jx: %d\n", cls->name, 176 (uintmax_t)cid->enum_addr, error); 177 178 kobj_delete((kobj_t)erom, M_BHND); 179 return (NULL); 180 } 181 182 return (erom); 183 } 184 185 /** 186 * Perform static initialization of a device enumeration table parser. 187 * 188 * This may be used to initialize a caller-allocated erom instance state 189 * during early boot, prior to malloc availability. 190 * 191 * @param cls The parser class for which an instance will be 192 * allocated. 193 * @param erom The erom parser instance to initialize. 194 * @param esize The total available number of bytes allocated for 195 * @p erom. If this is less than is required by @p cls, 196 * ENOMEM will be returned. 197 * @param cid The device's chip identifier. 198 * @param eio The bus I/O callbacks to use when reading the device 199 * enumeration table. 200 * 201 * @retval 0 success 202 * @retval ENOMEM if @p esize is smaller than required by @p cls. 203 * @retval non-zero if an error occurs initializing the EROM parser, 204 * a regular unix error code will be returned. 205 */ 206 int 207 bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize, 208 const struct bhnd_chipid *cid, struct bhnd_erom_io *eio) 209 { 210 kobj_class_t kcls; 211 212 kcls = (kobj_class_t)cls; 213 214 /* Verify allocation size */ 215 if (kcls->size > esize) 216 return (ENOMEM); 217 218 /* Perform instance initialization */ 219 kobj_init_static((kobj_t)erom, kcls); 220 return (BHND_EROM_INIT(erom, cid, eio)); 221 } 222 223 /** 224 * Release any resources held by a @p erom parser previously 225 * initialized via bhnd_erom_init_static(). 226 * 227 * @param erom An erom parser instance previously initialized via 228 * bhnd_erom_init_static(). 229 */ 230 void 231 bhnd_erom_fini_static(bhnd_erom_t *erom) 232 { 233 return (BHND_EROM_FINI(erom)); 234 } 235 236 /** 237 * Release all resources held by a @p erom parser previously 238 * allocated via bhnd_erom_alloc(). 239 * 240 * @param erom An erom parser instance previously allocated via 241 * bhnd_erom_alloc(). 242 */ 243 void 244 bhnd_erom_free(bhnd_erom_t *erom) 245 { 246 BHND_EROM_FINI(erom); 247 kobj_delete((kobj_t)erom, M_BHND); 248 } 249 250 251 /** 252 * Attempt to map @p size bytes at @p addr, replacing any existing 253 * @p eio mapping. 254 * 255 * @param eio I/O instance state. 256 * @param addr The address to be mapped. 257 * @param size The number of bytes to be mapped at @p addr. 258 * 259 * @retval 0 success 260 * @retval non-zero if mapping @p addr otherwise fails, a regular 261 * unix error code should be returned. 262 */ 263 int 264 bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size) 265 { 266 return (eio->map(eio, addr, size)); 267 } 268 269 /** 270 * Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset 271 * relative to @p eio's current mapping. 272 * 273 * @param eio erom I/O callbacks 274 * @param offset read offset. 275 * @param width item width (1, 2, or 4 bytes). 276 */ 277 uint32_t 278 bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 279 { 280 return (eio->read(eio, offset, width)); 281 } 282 283 /** 284 * Free all resources held by @p eio. 285 */ 286 void 287 bhnd_erom_io_fini(struct bhnd_erom_io *eio) 288 { 289 if (eio->fini != NULL) 290 return (eio->fini(eio)); 291 } 292 293 /** 294 * Allocate, initialize, and return a new I/O instance that will perform 295 * mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid. 296 * 297 * @param dev The device to pass to bhnd_alloc_resource() and 298 * bhnd_release_resource() functions. 299 * @param rid The resource ID to be used when allocating memory resources. 300 */ 301 struct bhnd_erom_io * 302 bhnd_erom_iores_new(device_t dev, int rid) 303 { 304 struct bhnd_erom_iores *iores; 305 306 iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO); 307 iores->eio.map = bhnd_erom_iores_map; 308 iores->eio.read = bhnd_erom_iores_read; 309 iores->eio.fini = bhnd_erom_iores_fini; 310 311 iores->owner = dev; 312 iores->owner_rid = rid; 313 iores->mapped = NULL; 314 iores->mapped_rid = -1; 315 316 return (&iores->eio); 317 } 318 319 static int 320 bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 321 bhnd_size_t size) 322 { 323 struct bhnd_erom_iores *iores; 324 325 iores = (struct bhnd_erom_iores *)eio; 326 327 /* Sanity check the addr/size */ 328 if (size == 0) 329 return (EINVAL); 330 331 if (BHND_ADDR_MAX - size < addr) 332 return (EINVAL); /* would overflow */ 333 334 /* Check for an existing mapping */ 335 if (iores->mapped) { 336 /* If already mapped, nothing else to do */ 337 if (rman_get_start(iores->mapped->res) == addr && 338 rman_get_size(iores->mapped->res) == size) 339 { 340 return (0); 341 } 342 343 /* Otherwise, we need to drop the existing mapping */ 344 bhnd_release_resource(iores->owner, SYS_RES_MEMORY, 345 iores->mapped_rid, iores->mapped); 346 iores->mapped = NULL; 347 iores->mapped_rid = -1; 348 } 349 350 /* Try to allocate the new mapping */ 351 iores->mapped_rid = iores->owner_rid; 352 iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY, 353 &iores->mapped_rid, addr, addr+size-1, size, 354 RF_ACTIVE|RF_SHAREABLE); 355 if (iores->mapped == NULL) { 356 iores->mapped_rid = -1; 357 return (ENXIO); 358 } 359 360 return (0); 361 } 362 363 static uint32_t 364 bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 365 { 366 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio; 367 368 if (iores->mapped == NULL) 369 panic("read with invalid mapping"); 370 371 switch (width) { 372 case 1: 373 return (bhnd_bus_read_1(iores->mapped, offset)); 374 case 2: 375 return (bhnd_bus_read_2(iores->mapped, offset)); 376 case 4: 377 return (bhnd_bus_read_4(iores->mapped, offset)); 378 default: 379 panic("invalid width %u", width); 380 } 381 } 382 383 static void 384 bhnd_erom_iores_fini(struct bhnd_erom_io *eio) 385 { 386 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio; 387 388 /* Release any mapping */ 389 if (iores->mapped) { 390 bhnd_release_resource(iores->owner, SYS_RES_MEMORY, 391 iores->mapped_rid, iores->mapped); 392 iores->mapped = NULL; 393 iores->mapped_rid = -1; 394 } 395 396 free(eio, M_BHND); 397 } 398 399 /** 400 * Initialize an I/O instance that will perform mapping directly from the 401 * given bus space tag and handle. 402 * 403 * @param iobus The I/O instance to be initialized. 404 * @param addr The base address mapped by @p bsh. 405 * @param size The total size mapped by @p bsh. 406 * @param bst Bus space tag for @p bsh. 407 * @param bsh Bus space handle mapping the full bus enumeration space. 408 * 409 * @retval 0 success 410 * @retval non-zero if initializing @p iobus otherwise fails, a regular 411 * unix error code will be returned. 412 */ 413 int 414 bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr, 415 bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh) 416 { 417 iobus->eio.map = bhnd_erom_iobus_map; 418 iobus->eio.read = bhnd_erom_iobus_read; 419 iobus->eio.fini = NULL; 420 421 iobus->addr = addr; 422 iobus->size = size; 423 iobus->bst = bst; 424 iobus->bsh = bsh; 425 iobus->mapped = false; 426 427 return (0); 428 } 429 430 static int 431 bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, 432 bhnd_size_t size) 433 { 434 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio; 435 436 /* Sanity check the addr/size */ 437 if (size == 0) 438 return (EINVAL); 439 440 /* addr+size must not overflow */ 441 if (BHND_ADDR_MAX - size < addr) 442 return (EINVAL); 443 444 /* addr/size must fit within our bus tag's mapping */ 445 if (addr < iobus->addr || size > iobus->size) 446 return (ENXIO); 447 448 if (iobus->size - (addr - iobus->addr) < size) 449 return (ENXIO); 450 451 /* The new addr offset and size must be representible as a bus_size_t */ 452 if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE) 453 return (ENXIO); 454 455 if (size > BUS_SPACE_MAXSIZE) 456 return (ENXIO); 457 458 iobus->offset = addr - iobus->addr; 459 iobus->limit = size; 460 iobus->mapped = true; 461 462 return (0); 463 } 464 465 static uint32_t 466 bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width) 467 { 468 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio; 469 470 if (!iobus->mapped) 471 panic("no active mapping"); 472 473 if (iobus->limit < width || iobus->limit - width < offset) 474 panic("invalid offset %#jx", offset); 475 476 switch (width) { 477 case 1: 478 return (bus_space_read_1(iobus->bst, iobus->bsh, 479 iobus->offset + offset)); 480 case 2: 481 return (bus_space_read_2(iobus->bst, iobus->bsh, 482 iobus->offset + offset)); 483 case 4: 484 return (bus_space_read_4(iobus->bst, iobus->bsh, 485 iobus->offset + offset)); 486 default: 487 panic("invalid width %u", width); 488 } 489 } 490