2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
5 * Copyright (c) 2017 The FreeBSD Foundation
8 * Portions of this software were developed by Landon Fuller
9 * under sponsorship from the FreeBSD Foundation.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
39 #include <sys/param.h>
43 #include <machine/bus.h>
45 #include <machine/resource.h>
47 #include <dev/bhnd/bhndreg.h>
48 #include <dev/bhnd/bhndvar.h>
50 #include <dev/bhnd/bhnd_erom.h>
51 #include <dev/bhnd/bhnd_eromvar.h>
53 #include <dev/bhnd/cores/chipc/chipcreg.h>
55 static int bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
57 static int bhnd_erom_iores_tell(struct bhnd_erom_io *eio,
58 bhnd_addr_t *addr, bhnd_size_t *size);
59 static uint32_t bhnd_erom_iores_read(struct bhnd_erom_io *eio,
60 bhnd_size_t offset, u_int width);
61 static void bhnd_erom_iores_fini(struct bhnd_erom_io *eio);
63 static int bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
65 static int bhnd_erom_iobus_tell(struct bhnd_erom_io *eio,
66 bhnd_addr_t *addr, bhnd_size_t *size);
67 static uint32_t bhnd_erom_iobus_read(struct bhnd_erom_io *eio,
68 bhnd_size_t offset, u_int width);
71 * An implementation of bhnd_erom_io that manages mappings via
72 * bhnd_alloc_resource() and bhnd_release_resource().
74 struct bhnd_erom_iores {
75 struct bhnd_erom_io eio;
76 device_t owner; /**< device from which we'll allocate resources */
77 int owner_rid; /**< rid to use when allocating new mappings */
78 struct bhnd_resource *mapped; /**< current mapping, or NULL */
79 int mapped_rid; /**< resource ID of current mapping, or -1 */
83 * Fetch the device enumeration parser class from all bhnd(4)-compatible drivers
84 * registered for @p bus_devclass, probe @p eio for supporting parser classes,
85 * and return the best available supporting enumeration parser class.
87 * @param bus_devclass The bus device class to be queried for
88 * bhnd(4)-compatible drivers.
89 * @param eio An erom bus I/O instance, configured with a
90 * mapping of the first bus core.
91 * @param hint Identification hint used to identify the device.
92 * If the chipset supports standard chip
93 * identification registers within the first core,
94 * this parameter should be NULL.
95 * @param[out] cid On success, the probed chip identifier.
97 * @retval non-NULL on success, the best available EROM class.
98 * @retval NULL if no erom class returned a successful probe result for
102 bhnd_erom_probe_driver_classes(devclass_t bus_devclass,
103 struct bhnd_erom_io *eio, const struct bhnd_chipid *hint,
104 struct bhnd_chipid *cid)
108 bhnd_erom_class_t *erom_cls;
109 int error, prio, result;
114 /* Fetch all available drivers */
115 error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);
117 printf("error fetching bhnd(4) drivers for %s: %d\n",
118 devclass_get_name(bus_devclass), error);
122 /* Enumerate the drivers looking for the best available EROM class */
123 for (int i = 0; i < drv_count; i++) {
124 struct bhnd_chipid pcid;
125 bhnd_erom_class_t *cls;
127 /* The default implementation of BHND_BUS_GET_EROM_CLASS()
128 * returns NULL if unimplemented; this should always be safe
129 * to call on arbitrary drivers */
130 cls = bhnd_driver_get_erom_class(drivers[i]);
134 kobj_class_compile(cls);
137 result = bhnd_erom_probe(cls, eio, hint, &pcid);
139 /* The parser did not match if an error was returned */
143 /* Check for a new highest priority match */
144 if (erom_cls == NULL || result > prio) {
151 /* Terminate immediately on BUS_PROBE_SPECIFIC */
152 if (result == BUS_PROBE_SPECIFIC)
156 free(drivers, M_TEMP);
161 * Allocate and return a new device enumeration table parser.
163 * @param cls The parser class for which an instance will be
165 * @param eio The bus I/O callbacks to use when reading the device
167 * @param cid The device's chip identifier.
169 * @retval non-NULL success
170 * @retval NULL if an error occured allocating or initializing the
174 bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
175 struct bhnd_erom_io *eio)
180 erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
183 if ((error = BHND_EROM_INIT(erom, cid, eio))) {
184 printf("error initializing %s parser at %#jx: %d\n", cls->name,
185 (uintmax_t)cid->enum_addr, error);
187 kobj_delete((kobj_t)erom, M_BHND);
195 * Perform static initialization of a device enumeration table parser.
197 * This may be used to initialize a caller-allocated erom instance state
198 * during early boot, prior to malloc availability.
200 * @param cls The parser class for which an instance will be
202 * @param erom The erom parser instance to initialize.
203 * @param esize The total available number of bytes allocated for
204 * @p erom. If this is less than is required by @p cls,
205 * ENOMEM will be returned.
206 * @param cid The device's chip identifier.
207 * @param eio The bus I/O callbacks to use when reading the device
211 * @retval ENOMEM if @p esize is smaller than required by @p cls.
212 * @retval non-zero if an error occurs initializing the EROM parser,
213 * a regular unix error code will be returned.
216 bhnd_erom_init_static(bhnd_erom_class_t *cls, bhnd_erom_t *erom, size_t esize,
217 const struct bhnd_chipid *cid, struct bhnd_erom_io *eio)
221 kcls = (kobj_class_t)cls;
223 /* Verify allocation size */
224 if (kcls->size > esize)
227 /* Perform instance initialization */
228 kobj_init_static((kobj_t)erom, kcls);
229 return (BHND_EROM_INIT(erom, cid, eio));
233 * Release any resources held by a @p erom parser previously
234 * initialized via bhnd_erom_init_static().
236 * @param erom An erom parser instance previously initialized via
237 * bhnd_erom_init_static().
240 bhnd_erom_fini_static(bhnd_erom_t *erom)
242 return (BHND_EROM_FINI(erom));
246 * Release all resources held by a @p erom parser previously
247 * allocated via bhnd_erom_alloc().
249 * @param erom An erom parser instance previously allocated via
253 bhnd_erom_free(bhnd_erom_t *erom)
255 BHND_EROM_FINI(erom);
256 kobj_delete((kobj_t)erom, M_BHND);
260 * Read the chip identification registers mapped by @p eio, popuating @p cid
261 * with the parsed result
263 * @param eio A bus I/O instance, configured with a mapping
264 * of the ChipCommon core.
265 * @param[out] cid On success, the parsed chip identification.
268 * On early siba(4) devices, the ChipCommon core does not provide
269 * a valid CHIPC_ID_NUMCORE field. On these ChipCommon revisions
270 * (see CHIPC_NCORES_MIN_HWREV()), this function will parse and return
271 * an invalid `ncores` value.
274 bhnd_erom_read_chipid(struct bhnd_erom_io *eio, struct bhnd_chipid *cid)
278 uint32_t idreg, cc_caps;
281 /* Fetch ChipCommon address */
282 if ((error = bhnd_erom_io_tell(eio, &cc_addr, &cc_size)))
285 /* Read chip identifier */
286 idreg = bhnd_erom_io_read(eio, CHIPC_ID, 4);
288 /* Extract the basic chip info */
289 cid->chip_id = CHIPC_GET_BITS(idreg, CHIPC_ID_CHIP);
290 cid->chip_pkg = CHIPC_GET_BITS(idreg, CHIPC_ID_PKG);
291 cid->chip_rev = CHIPC_GET_BITS(idreg, CHIPC_ID_REV);
292 cid->chip_type = CHIPC_GET_BITS(idreg, CHIPC_ID_BUS);
293 cid->ncores = CHIPC_GET_BITS(idreg, CHIPC_ID_NUMCORE);
295 /* Populate EROM address */
296 if (BHND_CHIPTYPE_HAS_EROM(cid->chip_type)) {
297 cid->enum_addr = bhnd_erom_io_read(eio, CHIPC_EROMPTR, 4);
299 cid->enum_addr = cc_addr;
302 /* Populate capability flags */
303 cc_caps = bhnd_erom_io_read(eio, CHIPC_CAPABILITIES, 4);
304 cid->chip_caps = 0x0;
306 if (cc_caps & CHIPC_CAP_BKPLN64)
307 cid->chip_caps |= BHND_CAP_BP64;
309 if (cc_caps & CHIPC_CAP_PMU)
310 cid->chip_caps |= BHND_CAP_PMU;
317 * Attempt to map @p size bytes at @p addr, replacing any existing
320 * @param eio I/O instance state.
321 * @param addr The address to be mapped.
322 * @param size The number of bytes to be mapped at @p addr.
325 * @retval non-zero if mapping @p addr otherwise fails, a regular
326 * unix error code should be returned.
329 bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
331 return (eio->map(eio, addr, size));
335 * Return the address range mapped by @p eio, if any.
337 * @param eio I/O instance state.
338 * @param[out] addr The address mapped by @p eio.
339 * @param[out] size The number of bytes mapped at @p addr.
342 * @retval ENXIO if @p eio has no mapping.
345 bhnd_erom_io_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
348 return (eio->tell(eio, addr, size));
352 * Read a 1, 2, or 4 byte data item from @p eio, at the given @p offset
353 * relative to @p eio's current mapping.
355 * @param eio erom I/O callbacks
356 * @param offset read offset.
357 * @param width item width (1, 2, or 4 bytes).
360 bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
362 return (eio->read(eio, offset, width));
366 * Free all resources held by @p eio.
369 bhnd_erom_io_fini(struct bhnd_erom_io *eio)
371 if (eio->fini != NULL)
372 return (eio->fini(eio));
376 * Allocate, initialize, and return a new I/O instance that will perform
377 * mapping by allocating SYS_RES_MEMORY resources from @p dev using @p rid.
379 * @param dev The device to pass to bhnd_alloc_resource() and
380 * bhnd_release_resource() functions.
381 * @param rid The resource ID to be used when allocating memory resources.
383 struct bhnd_erom_io *
384 bhnd_erom_iores_new(device_t dev, int rid)
386 struct bhnd_erom_iores *iores;
388 iores = malloc(sizeof(*iores), M_BHND, M_WAITOK | M_ZERO);
389 iores->eio.map = bhnd_erom_iores_map;
390 iores->eio.tell = bhnd_erom_iores_tell;
391 iores->eio.read = bhnd_erom_iores_read;
392 iores->eio.fini = bhnd_erom_iores_fini;
395 iores->owner_rid = rid;
396 iores->mapped = NULL;
397 iores->mapped_rid = -1;
399 return (&iores->eio);
403 bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
406 struct bhnd_erom_iores *iores;
408 iores = (struct bhnd_erom_iores *)eio;
410 /* Sanity check the addr/size */
414 if (BHND_ADDR_MAX - size < addr)
415 return (EINVAL); /* would overflow */
417 /* Check for an existing mapping */
419 /* If already mapped, nothing else to do */
420 if (rman_get_start(iores->mapped->res) == addr &&
421 rman_get_size(iores->mapped->res) == size)
426 /* Otherwise, we need to drop the existing mapping */
427 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
428 iores->mapped_rid, iores->mapped);
429 iores->mapped = NULL;
430 iores->mapped_rid = -1;
433 /* Try to allocate the new mapping */
434 iores->mapped_rid = iores->owner_rid;
435 iores->mapped = bhnd_alloc_resource(iores->owner, SYS_RES_MEMORY,
436 &iores->mapped_rid, addr, addr+size-1, size,
437 RF_ACTIVE|RF_SHAREABLE);
438 if (iores->mapped == NULL) {
439 iores->mapped_rid = -1;
447 bhnd_erom_iores_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
450 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
452 if (iores->mapped == NULL)
455 *addr = rman_get_start(iores->mapped->res);
456 *size = rman_get_size(iores->mapped->res);
462 bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
464 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
466 if (iores->mapped == NULL)
467 panic("read with invalid mapping");
471 return (bhnd_bus_read_1(iores->mapped, offset));
473 return (bhnd_bus_read_2(iores->mapped, offset));
475 return (bhnd_bus_read_4(iores->mapped, offset));
477 panic("invalid width %u", width);
482 bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
484 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
486 /* Release any mapping */
488 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
489 iores->mapped_rid, iores->mapped);
490 iores->mapped = NULL;
491 iores->mapped_rid = -1;
498 * Initialize an I/O instance that will perform mapping directly from the
499 * given bus space tag and handle.
501 * @param iobus The I/O instance to be initialized.
502 * @param addr The base address mapped by @p bsh.
503 * @param size The total size mapped by @p bsh.
504 * @param bst Bus space tag for @p bsh.
505 * @param bsh Bus space handle mapping the full bus enumeration space.
508 * @retval non-zero if initializing @p iobus otherwise fails, a regular
509 * unix error code will be returned.
512 bhnd_erom_iobus_init(struct bhnd_erom_iobus *iobus, bhnd_addr_t addr,
513 bhnd_size_t size, bus_space_tag_t bst, bus_space_handle_t bsh)
515 iobus->eio.map = bhnd_erom_iobus_map;
516 iobus->eio.tell = bhnd_erom_iobus_tell;
517 iobus->eio.read = bhnd_erom_iobus_read;
518 iobus->eio.fini = NULL;
524 iobus->mapped = false;
530 bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
533 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
535 /* Sanity check the addr/size */
539 /* addr+size must not overflow */
540 if (BHND_ADDR_MAX - size < addr)
543 /* addr/size must fit within our bus tag's mapping */
544 if (addr < iobus->addr || size > iobus->size)
547 if (iobus->size - (addr - iobus->addr) < size)
550 /* The new addr offset and size must be representible as a bus_size_t */
551 if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
554 if (size > BUS_SPACE_MAXSIZE)
557 iobus->offset = addr - iobus->addr;
559 iobus->mapped = true;
565 bhnd_erom_iobus_tell(struct bhnd_erom_io *eio, bhnd_addr_t *addr,
568 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
573 *addr = iobus->addr + iobus->offset;
574 *size = iobus->limit;
580 bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
582 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
585 panic("no active mapping");
587 if (iobus->limit < width || iobus->limit - width < offset)
588 panic("invalid offset %#jx", offset);
592 return (bus_space_read_1(iobus->bst, iobus->bsh,
593 iobus->offset + offset));
595 return (bus_space_read_2(iobus->bst, iobus->bsh,
596 iobus->offset + offset));
598 return (bus_space_read_4(iobus->bst, iobus->bsh,
599 iobus->offset + offset));
601 panic("invalid width %u", width);