2 * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
3 * Copyright (c) 2017 The FreeBSD Foundation
6 * Portions of this software were developed by Landon Fuller
7 * under sponsorship from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
37 #include <sys/param.h>
41 #include <machine/bus.h>
43 #include <machine/resource.h>
45 #include <dev/bhnd/bhndvar.h>
46 #include <dev/bhnd/bhnd_erom.h>
47 #include <dev/bhnd/bhnd_eromvar.h>
49 static int bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
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);
55 static int bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
57 static uint32_t bhnd_erom_iobus_read(struct bhnd_erom_io *eio,
58 bhnd_size_t offset, u_int width);
61 * An implementation of bhnd_erom_io that manages mappings via
62 * bhnd_alloc_resource() and bhnd_release_resource().
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 */
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.
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.
87 * @retval non-NULL on success, the best available EROM class.
88 * @retval NULL if no erom class returned a successful probe result for
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)
98 bhnd_erom_class_t *erom_cls;
99 int error, prio, result;
104 /* Fetch all available drivers */
105 error = devclass_get_drivers(bus_devclass, &drivers, &drv_count);
107 printf("error fetching bhnd(4) drivers for %s: %d\n",
108 devclass_get_name(bus_devclass), error);
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;
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]);
124 kobj_class_compile(cls);
127 result = bhnd_erom_probe(cls, eio, hint, &pcid);
129 /* The parser did not match if an error was returned */
133 /* Check for a new highest priority match */
134 if (erom_cls == NULL || result > prio) {
141 /* Terminate immediately on BUS_PROBE_SPECIFIC */
142 if (result == BUS_PROBE_SPECIFIC)
150 * Allocate and return a new device enumeration table parser.
152 * @param cls The parser class for which an instance will be
154 * @param eio The bus I/O callbacks to use when reading the device
156 * @param cid The device's chip identifier.
158 * @retval non-NULL success
159 * @retval NULL if an error occured allocating or initializing the
163 bhnd_erom_alloc(bhnd_erom_class_t *cls, const struct bhnd_chipid *cid,
164 struct bhnd_erom_io *eio)
169 erom = (bhnd_erom_t *)kobj_create((kobj_class_t)cls, M_BHND,
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);
176 kobj_delete((kobj_t)erom, M_BHND);
184 * Perform static initialization of a device enumeration table parser.
186 * This may be used to initialize a caller-allocated erom instance state
187 * during early boot, prior to malloc availability.
189 * @param cls The parser class for which an instance will be
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
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.
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)
210 kcls = (kobj_class_t)cls;
212 /* Verify allocation size */
213 if (kcls->size > esize)
216 /* Perform instance initialization */
217 kobj_init_static((kobj_t)erom, kcls);
218 return (BHND_EROM_INIT(erom, cid, eio));
222 * Release any resources held by a @p erom parser previously
223 * initialized via bhnd_erom_init_static().
225 * @param erom An erom parser instance previously initialized via
226 * bhnd_erom_init_static().
229 bhnd_erom_fini_static(bhnd_erom_t *erom)
231 return (BHND_EROM_FINI(erom));
235 * Release all resources held by a @p erom parser previously
236 * allocated via bhnd_erom_alloc().
238 * @param erom An erom parser instance previously allocated via
242 bhnd_erom_free(bhnd_erom_t *erom)
244 BHND_EROM_FINI(erom);
245 kobj_delete((kobj_t)erom, M_BHND);
250 * Attempt to map @p size bytes at @p addr, replacing any existing
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.
258 * @retval non-zero if mapping @p addr otherwise fails, a regular
259 * unix error code should be returned.
262 bhnd_erom_io_map(struct bhnd_erom_io *eio, bhnd_addr_t addr, bhnd_size_t size)
264 return (eio->map(eio, addr, size));
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.
271 * @param eio erom I/O callbacks
272 * @param offset read offset.
273 * @param width item width (1, 2, or 4 bytes).
276 bhnd_erom_io_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
278 return (eio->read(eio, offset, width));
282 * Free all resources held by @p eio.
285 bhnd_erom_io_fini(struct bhnd_erom_io *eio)
287 if (eio->fini != NULL)
288 return (eio->fini(eio));
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.
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.
299 struct bhnd_erom_io *
300 bhnd_erom_iores_new(device_t dev, int rid)
302 struct bhnd_erom_iores *iores;
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;
310 iores->owner_rid = rid;
311 iores->mapped = NULL;
312 iores->mapped_rid = -1;
314 return (&iores->eio);
318 bhnd_erom_iores_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
321 struct bhnd_erom_iores *iores;
323 iores = (struct bhnd_erom_iores *)eio;
325 /* Sanity check the addr/size */
329 if (BHND_ADDR_MAX - size < addr)
330 return (EINVAL); /* would overflow */
332 /* Check for an existing mapping */
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)
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;
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;
362 bhnd_erom_iores_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
364 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
366 if (iores->mapped == NULL)
367 panic("read with invalid mapping");
371 return (bhnd_bus_read_1(iores->mapped, offset));
373 return (bhnd_bus_read_2(iores->mapped, offset));
375 return (bhnd_bus_read_4(iores->mapped, offset));
377 panic("invalid width %u", width);
382 bhnd_erom_iores_fini(struct bhnd_erom_io *eio)
384 struct bhnd_erom_iores *iores = (struct bhnd_erom_iores *)eio;
386 /* Release any mapping */
388 bhnd_release_resource(iores->owner, SYS_RES_MEMORY,
389 iores->mapped_rid, iores->mapped);
390 iores->mapped = NULL;
391 iores->mapped_rid = -1;
398 * Initialize an I/O instance that will perform mapping directly from the
399 * given bus space tag and handle.
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.
407 * @retval non-zero if initializing @p iobus otherwise fails, a regular
408 * unix error code will be returned.
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)
414 iobus->eio.map = bhnd_erom_iobus_map;
415 iobus->eio.read = bhnd_erom_iobus_read;
416 iobus->eio.fini = NULL;
422 iobus->mapped = false;
428 bhnd_erom_iobus_map(struct bhnd_erom_io *eio, bhnd_addr_t addr,
431 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
433 /* Sanity check the addr/size */
437 /* addr+size must not overflow */
438 if (BHND_ADDR_MAX - size < addr)
441 /* addr/size must fit within our bus tag's mapping */
442 if (addr < iobus->addr || size > iobus->size)
445 if (iobus->size - (addr - iobus->addr) < size)
448 /* The new addr offset and size must be representible as a bus_size_t */
449 if ((addr - iobus->addr) > BUS_SPACE_MAXSIZE)
452 if (size > BUS_SPACE_MAXSIZE)
455 iobus->offset = addr - iobus->addr;
457 iobus->mapped = true;
463 bhnd_erom_iobus_read(struct bhnd_erom_io *eio, bhnd_size_t offset, u_int width)
465 struct bhnd_erom_iobus *iobus = (struct bhnd_erom_iobus *)eio;
468 panic("no active mapping");
470 if (iobus->limit < width || iobus->limit - width < offset)
471 panic("invalid offset %#jx", offset);
475 return (bus_space_read_1(iobus->bst, iobus->bsh,
476 iobus->offset + offset));
478 return (bus_space_read_2(iobus->bst, iobus->bsh,
479 iobus->offset + offset));
481 return (bus_space_read_4(iobus->bst, iobus->bsh,
482 iobus->offset + offset));
484 panic("invalid width %u", width);