xref: /freebsd/sys/dev/bhnd/bcma/bcma_erom.c (revision d93a896ef95946b0bf1219866fcb324b78543444)
1 /*-
2  * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/limits.h>
37 #include <sys/systm.h>
38 
39 #include <machine/bus.h>
40 #include <machine/resource.h>
41 
42 #include <dev/bhnd/cores/chipc/chipcreg.h>
43 
44 #include "bcma_eromreg.h"
45 #include "bcma_eromvar.h"
46 
47 /*
48  * BCMA Enumeration ROM (EROM) Table
49  *
50  * Provides auto-discovery of BCMA cores on Broadcom's HND SoC.
51  *
52  * The EROM core address can be found at BCMA_CC_EROM_ADDR within the
53  * ChipCommon registers. The table itself is comprised of 32-bit
54  * type-tagged entries, organized into an array of variable-length
55  * core descriptor records.
56  *
57  * The final core descriptor is followed by a 32-bit BCMA_EROM_TABLE_EOF (0xF)
58  * marker.
59  */
60 
61 struct bcma_erom_io;
62 
63 static const char	*bcma_erom_entry_type_name (uint8_t entry);
64 
65 static uint32_t		 bcma_eio_read4(struct bcma_erom_io *io,
66 			     bus_size_t offset);
67 
68 static int		 bcma_erom_read32(struct bcma_erom *erom,
69 			     uint32_t *entry);
70 static int		 bcma_erom_skip32(struct bcma_erom *erom);
71 
72 static int		 bcma_erom_skip_core(struct bcma_erom *erom);
73 static int		 bcma_erom_skip_mport(struct bcma_erom *erom);
74 static int		 bcma_erom_skip_sport_region(struct bcma_erom *erom);
75 
76 static int		 bcma_erom_seek_next(struct bcma_erom *erom,
77 			     uint8_t etype);
78 static int		 bcma_erom_region_to_port_type(struct bcma_erom *erom,
79 			     uint8_t region_type, bhnd_port_type *port_type);
80 
81 
82 static int		 bcma_erom_peek32(struct bcma_erom *erom,
83 			     uint32_t *entry);
84 
85 static bus_size_t	 bcma_erom_tell(struct bcma_erom *erom);
86 static void		 bcma_erom_seek(struct bcma_erom *erom,
87 			     bus_size_t offset);
88 static void		 bcma_erom_reset(struct bcma_erom *erom);
89 
90 static int		 bcma_erom_seek_matching_core(struct bcma_erom *sc,
91 			     const struct bhnd_core_match *desc,
92 			     struct bhnd_core_info *core);
93 
94 static int		 bcma_erom_parse_core(struct bcma_erom *erom,
95 			     struct bcma_erom_core *core);
96 
97 static int		 bcma_erom_parse_mport(struct bcma_erom *erom,
98 			     struct bcma_erom_mport *mport);
99 
100 static int		 bcma_erom_parse_sport_region(struct bcma_erom *erom,
101 			     struct bcma_erom_sport_region *region);
102 
103 static void		 bcma_erom_to_core_info(const struct bcma_erom_core *core,
104 			     u_int core_idx, int core_unit,
105 			     struct bhnd_core_info *info);
106 
107 /**
108  * BCMA EROM generic I/O context
109  */
110 struct bcma_erom_io {
111 	struct bhnd_resource	*res;		/**< memory resource, or NULL if initialized
112 						     with bus space tag and handle */
113 	int			 rid;		/**< memory resource id, or -1 */
114 
115 	bus_space_tag_t		 bst;		/**< bus space tag, if any */
116 	bus_space_handle_t	 bsh;		/**< bus space handle, if any */
117 
118 	bus_size_t	 	 start;		/**< base read offset */
119 };
120 
121 /**
122  * BCMA EROM per-instance state.
123  */
124 struct bcma_erom {
125 	struct bhnd_erom	obj;
126 	device_t	 	dev;	/**< parent device, or NULL if none. */
127 	struct bcma_erom_io	io;	/**< I/O context */
128 	bus_size_t	 	offset;	/**< current read offset */
129 };
130 
131 #define	EROM_LOG(erom, fmt, ...)	do {				\
132 	if (erom->dev != NULL) {					\
133 		device_printf(erom->dev, "erom[0x%llx]: " fmt,	\
134 		    (unsigned long long) (erom->offset), ##__VA_ARGS__);\
135 	} else {							\
136 		printf("erom[0x%llx]: " fmt,				\
137 		    (unsigned long long) (erom->offset), ##__VA_ARGS__);\
138 	}								\
139 } while(0)
140 
141 /** Return the type name for an EROM entry */
142 static const char *
143 bcma_erom_entry_type_name (uint8_t entry)
144 {
145 	switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
146 	case BCMA_EROM_ENTRY_TYPE_CORE:
147 		return "core";
148 	case BCMA_EROM_ENTRY_TYPE_MPORT:
149 		return "mport";
150 	case BCMA_EROM_ENTRY_TYPE_REGION:
151 		return "region";
152 	default:
153 		return "unknown";
154 	}
155 }
156 
157 
158 /**
159  * Read a 32-bit value from an EROM I/O context.
160  *
161  * @param io EROM I/O context.
162  * @param offset Read offset.
163  */
164 static uint32_t
165 bcma_eio_read4(struct bcma_erom_io *io, bus_size_t offset)
166 {
167 	bus_size_t read_off;
168 
169 	read_off = io->start + offset;
170 	if (io->res != NULL)
171 		return (bhnd_bus_read_4(io->res, read_off));
172 	else
173 		return (bus_space_read_4(io->bst, io->bsh, read_off));
174 }
175 
176 /* Initialize bcma_erom resource I/O context */
177 static void
178 bcma_eio_init(struct bcma_erom_io *io, struct bhnd_resource *res, int rid,
179     bus_size_t offset)
180 {
181 	io->res = res;
182 	io->rid = rid;
183 	io->start = offset;
184 }
185 
186 /* Initialize bcma_erom bus space I/O context */
187 static void
188 bcma_eio_init_static(struct bcma_erom_io *io, bus_space_tag_t bst,
189     bus_space_handle_t bsh, bus_size_t offset)
190 {
191 	io->res = NULL;
192 	io->rid = -1;
193 	io->bst = bst;
194 	io->bsh = bsh;
195 	io->start = offset;
196 }
197 
198 /* BCMA implementation of BHND_EROM_INIT() */
199 static int
200 bcma_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
201     device_t parent, int rid)
202 {
203 	struct bcma_erom	*sc;
204 	struct bhnd_resource	*res;
205 
206 	sc = (struct bcma_erom *)erom;
207 	sc->dev = parent;
208 	sc->offset = 0;
209 
210 	res = bhnd_alloc_resource(parent, SYS_RES_MEMORY, &rid, cid->enum_addr,
211 	    cid->enum_addr + BCMA_EROM_TABLE_SIZE - 1, BCMA_EROM_TABLE_SIZE,
212 	    RF_ACTIVE|RF_SHAREABLE);
213 
214 	if (res == NULL)
215 		return (ENOMEM);
216 
217 	bcma_eio_init(&sc->io, res, rid, BCMA_EROM_TABLE_START);
218 
219 	return (0);
220 }
221 
222 /* BCMA implementation of BHND_EROM_INIT_STATIC() */
223 static int
224 bcma_erom_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
225     bus_space_tag_t bst, bus_space_handle_t bsh)
226 {
227 	struct bcma_erom	*sc;
228 
229 	sc = (struct bcma_erom *)erom;
230 	sc->dev = NULL;
231 	sc->offset = 0;
232 
233 	bcma_eio_init_static(&sc->io, bst, bsh, BCMA_EROM_TABLE_START);
234 
235 	return (0);
236 }
237 
238 /* Common implementation of BHND_EROM_PROBE/BHND_EROM_PROBE_STATIC */
239 static int
240 bcma_erom_probe_common(struct bcma_erom_io *io, const struct bhnd_chipid *hint,
241     struct bhnd_chipid *cid)
242 {
243 	uint32_t	idreg, eromptr;
244 
245 	/* Hints aren't supported; all BCMA devices have a ChipCommon
246 	 * core */
247 	if (hint != NULL)
248 		return (EINVAL);
249 
250 	/* Confirm CHIPC_EROMPTR availability */
251 	idreg = bcma_eio_read4(io, CHIPC_ID);
252 	if (!BHND_CHIPTYPE_HAS_EROM(CHIPC_GET_BITS(idreg, CHIPC_ID_BUS)))
253 		return (ENXIO);
254 
255 	/* Fetch EROM address */
256 	eromptr = bcma_eio_read4(io, CHIPC_EROMPTR);
257 
258 	/* Parse chip identifier */
259 	*cid = bhnd_parse_chipid(idreg, eromptr);
260 
261 	/* Verify chip type */
262 	switch (cid->chip_type) {
263 		case BHND_CHIPTYPE_BCMA:
264 			return (BUS_PROBE_DEFAULT);
265 
266 		case BHND_CHIPTYPE_BCMA_ALT:
267 		case BHND_CHIPTYPE_UBUS:
268 			return (BUS_PROBE_GENERIC);
269 
270 		default:
271 			return (ENXIO);
272 	}
273 }
274 
275 static int
276 bcma_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res,
277     bus_size_t offset, const struct bhnd_chipid *hint, struct bhnd_chipid *cid)
278 {
279 	struct bcma_erom_io io;
280 
281 	bcma_eio_init(&io, res, rman_get_rid(res->res),
282 	    offset + BCMA_EROM_TABLE_START);
283 
284 	return (bcma_erom_probe_common(&io, hint, cid));
285 }
286 
287 static int
288 bcma_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst,
289      bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint,
290      struct bhnd_chipid *cid)
291 {
292 	struct bcma_erom_io io;
293 
294 	bcma_eio_init_static(&io, bst, bsh, BCMA_EROM_TABLE_START);
295 	return (bcma_erom_probe_common(&io, hint, cid));
296 }
297 
298 
299 static void
300 bcma_erom_fini(bhnd_erom_t *erom)
301 {
302 	struct bcma_erom *sc = (struct bcma_erom *)erom;
303 
304 	if (sc->io.res != NULL) {
305 		bhnd_release_resource(sc->dev, SYS_RES_MEMORY, sc->io.rid,
306 		    sc->io.res);
307 
308 		sc->io.res = NULL;
309 		sc->io.rid = -1;
310 	}
311 }
312 
313 static int
314 bcma_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
315     struct bhnd_core_info *core)
316 {
317 	struct bcma_erom *sc = (struct bcma_erom *)erom;
318 
319 	/* Search for the first matching core */
320 	return (bcma_erom_seek_matching_core(sc, desc, core));
321 }
322 
323 static int
324 bcma_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
325     bhnd_port_type port_type, u_int port_num, u_int region_num,
326     struct bhnd_core_info *core, bhnd_addr_t *addr, bhnd_size_t *size)
327 {
328 	struct bcma_erom	*sc;
329 	struct bcma_erom_core	 ec;
330 	uint32_t		 entry;
331 	uint8_t			 region_port, region_type;
332 	bool			 found;
333 	int			 error;
334 
335 	sc = (struct bcma_erom *)erom;
336 
337 	/* Seek to the first matching core and provide the core info
338 	 * to the caller */
339 	if ((error = bcma_erom_seek_matching_core(sc, desc, core)))
340 		return (error);
341 
342 	if ((error = bcma_erom_parse_core(sc, &ec)))
343 		return (error);
344 
345 	/* Skip master ports */
346 	for (u_long i = 0; i < ec.num_mport; i++) {
347 		if ((error = bcma_erom_skip_mport(sc)))
348 			return (error);
349 	}
350 
351 	/* Seek to the region block for the given port type */
352 	found = false;
353 	while (1) {
354 		bhnd_port_type	p_type;
355 		uint8_t		r_type;
356 
357 		if ((error = bcma_erom_peek32(sc, &entry)))
358 			return (error);
359 
360 		if (!BCMA_EROM_ENTRY_IS(entry, REGION))
361 			return (ENOENT);
362 
363 		/* Expected region type? */
364 		r_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
365 		error = bcma_erom_region_to_port_type(sc, r_type, &p_type);
366 		if (error)
367 			return (error);
368 
369 		if (p_type == port_type) {
370 			found = true;
371 			break;
372 		}
373 
374 		/* Skip to next entry */
375 		if ((error = bcma_erom_skip_sport_region(sc)))
376 			return (error);
377 	}
378 
379 	if (!found)
380 		return (ENOENT);
381 
382 	/* Found the appropriate port type block; now find the region records
383 	 * for the given port number */
384 	found = false;
385 	for (u_int i = 0; i <= port_num; i++) {
386 		bhnd_port_type	p_type;
387 
388 		if ((error = bcma_erom_peek32(sc, &entry)))
389 			return (error);
390 
391 		if (!BCMA_EROM_ENTRY_IS(entry, REGION))
392 			return (ENOENT);
393 
394 		/* Fetch the type/port of the first region entry */
395 		region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
396 		region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
397 
398 		/* Have we found the region entries for the desired port? */
399 		if (i == port_num) {
400 			error = bcma_erom_region_to_port_type(sc, region_type,
401 			    &p_type);
402 			if (error)
403 				return (error);
404 
405 			if (p_type == port_type)
406 				found = true;
407 
408 			break;
409 		}
410 
411 		/* Otherwise, seek to next block of region records */
412 		while (1) {
413 			uint8_t	next_type, next_port;
414 
415 			if ((error = bcma_erom_skip_sport_region(sc)))
416 				return (error);
417 
418 			if ((error = bcma_erom_peek32(sc, &entry)))
419 				return (error);
420 
421 			if (!BCMA_EROM_ENTRY_IS(entry, REGION))
422 				return (ENOENT);
423 
424 			next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
425 			next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
426 
427 			if (next_type != region_type ||
428 			    next_port != region_port)
429 				break;
430 		}
431 	}
432 
433 	if (!found)
434 		return (ENOENT);
435 
436 	/* Finally, search for the requested region number */
437 	for (u_int i = 0; i <= region_num; i++) {
438 		struct bcma_erom_sport_region	region;
439 		uint8_t				next_port, next_type;
440 
441 		if ((error = bcma_erom_peek32(sc, &entry)))
442 			return (error);
443 
444 		if (!BCMA_EROM_ENTRY_IS(entry, REGION))
445 			return (ENOENT);
446 
447 		/* Check for the end of the region block */
448 		next_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
449 		next_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
450 
451 		if (next_type != region_type ||
452 		    next_port != region_port)
453 			break;
454 
455 		/* Parse the region */
456 		if ((error = bcma_erom_parse_sport_region(sc, &region)))
457 			return (error);
458 
459 		/* Is this our target region_num? */
460 		if (i == region_num) {
461 			/* Found */
462 			*addr = region.base_addr;
463 			*size = region.size;
464 			return (0);
465 		}
466 	}
467 
468 	/* Not found */
469 	return (ENOENT);
470 };
471 
472 static int
473 bcma_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
474     u_int *num_cores)
475 {
476 	struct bcma_erom	*sc;
477 	struct bhnd_core_info	*buffer;
478 	bus_size_t		 initial_offset;
479 	u_int			 count;
480 	int			 error;
481 
482 	sc = (struct bcma_erom *)erom;
483 
484 	buffer = NULL;
485 	initial_offset = bcma_erom_tell(sc);
486 
487 	/* Determine the core count */
488 	bcma_erom_reset(sc);
489 	for (count = 0, error = 0; !error; count++) {
490 		struct bcma_erom_core core;
491 
492 		/* Seek to the first readable core entry */
493 		error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE);
494 		if (error == ENOENT)
495 			break;
496 		else if (error)
497 			goto cleanup;
498 
499 		/* Read past the core descriptor */
500 		if ((error = bcma_erom_parse_core(sc, &core)))
501 			goto cleanup;
502 	}
503 
504 	/* Allocate our output buffer */
505 	buffer = malloc(sizeof(struct bhnd_core_info) * count, M_BHND,
506 	    M_NOWAIT);
507 	if (buffer == NULL) {
508 		error = ENOMEM;
509 		goto cleanup;
510 	}
511 
512 	/* Parse all core descriptors */
513 	bcma_erom_reset(sc);
514 	for (u_int i = 0; i < count; i++) {
515 		struct bcma_erom_core	core;
516 		int			unit;
517 
518 		/* Parse the core */
519 		error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE);
520 		if (error)
521 			goto cleanup;
522 
523 		error = bcma_erom_parse_core(sc, &core);
524 		if (error)
525 			goto cleanup;
526 
527 		/* Determine the unit number */
528 		unit = 0;
529 		for (u_int j = 0; j < i; j++) {
530 			if (buffer[i].vendor == buffer[j].vendor &&
531 			    buffer[i].device == buffer[j].device)
532 				unit++;
533 		}
534 
535 		/* Convert to a bhnd info record */
536 		bcma_erom_to_core_info(&core, i, unit, &buffer[i]);
537 	}
538 
539 cleanup:
540 	if (!error) {
541 		*cores = buffer;
542 		*num_cores = count;
543 	} else {
544 		if (buffer != NULL)
545 			free(buffer, M_BHND);
546 	}
547 
548 	/* Restore the initial position */
549 	bcma_erom_seek(sc, initial_offset);
550 	return (error);
551 }
552 
553 static void
554 bcma_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores)
555 {
556 	free(cores, M_BHND);
557 }
558 
559 /**
560  * Return the current read position.
561  */
562 static bus_size_t
563 bcma_erom_tell(struct bcma_erom *erom)
564 {
565 	return (erom->offset);
566 }
567 
568 /**
569  * Seek to an absolute read position.
570  */
571 static void
572 bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset)
573 {
574 	erom->offset = offset;
575 }
576 
577 /**
578  * Read a 32-bit entry value from the EROM table without advancing the
579  * read position.
580  *
581  * @param erom EROM read state.
582  * @param entry Will contain the read result on success.
583  * @retval 0 success
584  * @retval ENOENT The end of the EROM table was reached.
585  * @retval non-zero The read could not be completed.
586  */
587 static int
588 bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry)
589 {
590 	if (erom->offset >= (BCMA_EROM_TABLE_SIZE - sizeof(uint32_t))) {
591 		EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n");
592 		return (EINVAL);
593 	}
594 
595 	*entry = bcma_eio_read4(&erom->io, erom->offset);
596 	return (0);
597 }
598 
599 /**
600  * Read a 32-bit entry value from the EROM table.
601  *
602  * @param erom EROM read state.
603  * @param entry Will contain the read result on success.
604  * @retval 0 success
605  * @retval ENOENT The end of the EROM table was reached.
606  * @retval non-zero The read could not be completed.
607  */
608 static int
609 bcma_erom_read32(struct bcma_erom *erom, uint32_t *entry)
610 {
611 	int error;
612 
613 	if ((error = bcma_erom_peek32(erom, entry)) == 0)
614 		erom->offset += 4;
615 
616 	return (error);
617 }
618 
619 /**
620  * Read and discard 32-bit entry value from the EROM table.
621  *
622  * @param erom EROM read state.
623  * @retval 0 success
624  * @retval ENOENT The end of the EROM table was reached.
625  * @retval non-zero The read could not be completed.
626  */
627 static int
628 bcma_erom_skip32(struct bcma_erom *erom)
629 {
630 	uint32_t	entry;
631 
632 	return bcma_erom_read32(erom, &entry);
633 }
634 
635 /**
636  * Read and discard a core descriptor from the EROM table.
637  *
638  * @param erom EROM read state.
639  * @retval 0 success
640  * @retval ENOENT The end of the EROM table was reached.
641  * @retval non-zero The read could not be completed.
642  */
643 static int
644 bcma_erom_skip_core(struct bcma_erom *erom)
645 {
646 	struct bcma_erom_core core;
647 	return (bcma_erom_parse_core(erom, &core));
648 }
649 
650 /**
651  * Read and discard a master port descriptor from the EROM table.
652  *
653  * @param erom EROM read state.
654  * @retval 0 success
655  * @retval ENOENT The end of the EROM table was reached.
656  * @retval non-zero The read could not be completed.
657  */
658 static int
659 bcma_erom_skip_mport(struct bcma_erom *erom)
660 {
661 	struct bcma_erom_mport mp;
662 	return (bcma_erom_parse_mport(erom, &mp));
663 }
664 
665 /**
666  * Read and discard a port region descriptor from the EROM table.
667  *
668  * @param erom EROM read state.
669  * @retval 0 success
670  * @retval ENOENT The end of the EROM table was reached.
671  * @retval non-zero The read could not be completed.
672  */
673 static int
674 bcma_erom_skip_sport_region(struct bcma_erom *erom)
675 {
676 	struct bcma_erom_sport_region r;
677 	return (bcma_erom_parse_sport_region(erom, &r));
678 }
679 
680 /**
681  * Seek to the next entry matching the given EROM entry type.
682  *
683  * @param erom EROM read state.
684  * @param etype  One of BCMA_EROM_ENTRY_TYPE_CORE,
685  * BCMA_EROM_ENTRY_TYPE_MPORT, or BCMA_EROM_ENTRY_TYPE_REGION.
686  * @retval 0 success
687  * @retval ENOENT The end of the EROM table was reached.
688  * @retval non-zero Reading or parsing the descriptor failed.
689  */
690 static int
691 bcma_erom_seek_next(struct bcma_erom *erom, uint8_t etype)
692 {
693 	uint32_t			entry;
694 	int				error;
695 
696 	/* Iterate until we hit an entry matching the requested type. */
697 	while (!(error = bcma_erom_peek32(erom, &entry))) {
698 		/* Handle EOF */
699 		if (entry == BCMA_EROM_TABLE_EOF)
700 			return (ENOENT);
701 
702 		/* Invalid entry */
703 		if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID))
704 			return (EINVAL);
705 
706 		/* Entry type matches? */
707 		if (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE) == etype)
708 			return (0);
709 
710 		/* Skip non-matching entry types. */
711 		switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
712 		case BCMA_EROM_ENTRY_TYPE_CORE:
713 			if ((error = bcma_erom_skip_core(erom)))
714 				return (error);
715 
716 			break;
717 
718 		case BCMA_EROM_ENTRY_TYPE_MPORT:
719 			if ((error = bcma_erom_skip_mport(erom)))
720 				return (error);
721 
722 			break;
723 
724 		case BCMA_EROM_ENTRY_TYPE_REGION:
725 			if ((error = bcma_erom_skip_sport_region(erom)))
726 				return (error);
727 			break;
728 
729 		default:
730 			/* Unknown entry type! */
731 			return (EINVAL);
732 		}
733 	}
734 
735 	return (error);
736 }
737 
738 /**
739  * Return the read position to the start of the EROM table.
740  *
741  * @param erom EROM read state.
742  */
743 static void
744 bcma_erom_reset(struct bcma_erom *erom)
745 {
746 	erom->offset = 0;
747 }
748 
749 /**
750  * Seek to the first core entry matching @p desc.
751  *
752  * @param erom EROM read state.
753  * @param desc The core match descriptor.
754  * @param[out] core On success, the matching core info. If the core info
755  * is not desired, a NULL pointer may be provided.
756  * @retval 0 success
757  * @retval ENOENT The end of the EROM table was reached before @p index was
758  * found.
759  * @retval non-zero Reading or parsing failed.
760  */
761 static int
762 bcma_erom_seek_matching_core(struct bcma_erom *sc,
763     const struct bhnd_core_match *desc, struct bhnd_core_info *core)
764 {
765 	struct bhnd_core_match	 imatch;
766 	bus_size_t		 core_offset, next_offset;
767 	int			 error;
768 
769 	/* Seek to table start. */
770 	bcma_erom_reset(sc);
771 
772 	/* We can't determine a core's unit number during the initial scan. */
773 	imatch = *desc;
774 	imatch.m.match.core_unit = 0;
775 
776 	/* Locate the first matching core */
777 	for (u_int i = 0; i < UINT_MAX; i++) {
778 		struct bcma_erom_core	ec;
779 		struct bhnd_core_info	ci;
780 
781 		/* Seek to the next core */
782 		error = bcma_erom_seek_next(sc, BCMA_EROM_ENTRY_TYPE_CORE);
783 		if (error)
784 			return (error);
785 
786 		/* Save the core offset */
787 		core_offset = bcma_erom_tell(sc);
788 
789 		/* Parse the core */
790 		if ((error = bcma_erom_parse_core(sc, &ec)))
791 			return (error);
792 
793 		bcma_erom_to_core_info(&ec, i, 0, &ci);
794 
795 		/* Check for initial match */
796 		if (!bhnd_core_matches(&ci, &imatch))
797 			continue;
798 
799 		/* Re-scan preceding cores to determine the unit number. */
800 		next_offset = bcma_erom_tell(sc);
801 		bcma_erom_reset(sc);
802 		for (u_int j = 0; j < i; j++) {
803 			/* Parse the core */
804 			error = bcma_erom_seek_next(sc,
805 			    BCMA_EROM_ENTRY_TYPE_CORE);
806 			if (error)
807 				return (error);
808 
809 			if ((error = bcma_erom_parse_core(sc, &ec)))
810 				return (error);
811 
812 			/* Bump the unit number? */
813 			if (ec.vendor == ci.vendor && ec.device == ci.device)
814 				ci.unit++;
815 		}
816 
817 		/* Check for full match against now-valid unit number */
818 		if (!bhnd_core_matches(&ci, desc)) {
819 			/* Reposition to allow reading the next core */
820 			bcma_erom_seek(sc, next_offset);
821 			continue;
822 		}
823 
824 		/* Found; seek to the core's initial offset and provide
825 		 * the core info to the caller */
826 		bcma_erom_seek(sc, core_offset);
827 		if (core != NULL)
828 			*core = ci;
829 
830 		return (0);
831 	}
832 
833 	/* Not found, or a parse error occured */
834 	return (error);
835 }
836 
837 /**
838  * Read the next core descriptor from the EROM table.
839  *
840  * @param erom EROM read state.
841  * @param[out] core On success, will be populated with the parsed core
842  * descriptor data.
843  * @retval 0 success
844  * @retval ENOENT The end of the EROM table was reached.
845  * @retval non-zero Reading or parsing the core descriptor failed.
846  */
847 static int
848 bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core)
849 {
850 	uint32_t	entry;
851 	int		error;
852 
853 	/* Parse CoreDescA */
854 	if ((error = bcma_erom_read32(erom, &entry)))
855 		return (error);
856 
857 	/* Handle EOF */
858 	if (entry == BCMA_EROM_TABLE_EOF)
859 		return (ENOENT);
860 
861 	if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
862 		EROM_LOG(erom, "Unexpected EROM entry 0x%x (type=%s)\n",
863                    entry, bcma_erom_entry_type_name(entry));
864 
865 		return (EINVAL);
866 	}
867 
868 	core->vendor = BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER);
869 	core->device = BCMA_EROM_GET_ATTR(entry, COREA_ID);
870 
871 	/* Parse CoreDescB */
872 	if ((error = bcma_erom_read32(erom, &entry)))
873 		return (error);
874 
875 	if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
876 		return (EINVAL);
877 	}
878 
879 	core->rev = BCMA_EROM_GET_ATTR(entry, COREB_REV);
880 	core->num_mport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP);
881 	core->num_dport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP);
882 	core->num_mwrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP);
883 	core->num_swrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WSP);
884 
885 	return (0);
886 }
887 
888 /**
889  * Read the next master port descriptor from the EROM table.
890  *
891  * @param erom EROM read state.
892  * @param[out] mport On success, will be populated with the parsed
893  * descriptor data.
894  * @retval 0 success
895  * @retval non-zero Reading or parsing the descriptor failed.
896  */
897 static int
898 bcma_erom_parse_mport(struct bcma_erom *erom, struct bcma_erom_mport *mport)
899 {
900 	uint32_t	entry;
901 	int		error;
902 
903 	/* Parse the master port descriptor */
904 	if ((error = bcma_erom_read32(erom, &entry)))
905 		return (error);
906 
907 	if (!BCMA_EROM_ENTRY_IS(entry, MPORT))
908 		return (EINVAL);
909 
910 	mport->port_vid = BCMA_EROM_GET_ATTR(entry, MPORT_ID);
911 	mport->port_num = BCMA_EROM_GET_ATTR(entry, MPORT_NUM);
912 
913 	return (0);
914 }
915 
916 /**
917  * Read the next slave port region descriptor from the EROM table.
918  *
919  * @param erom EROM read state.
920  * @param[out] mport On success, will be populated with the parsed
921  * descriptor data.
922  * @retval 0 success
923  * @retval ENOENT The end of the region descriptor table was reached.
924  * @retval non-zero Reading or parsing the descriptor failed.
925  */
926 static int
927 bcma_erom_parse_sport_region(struct bcma_erom *erom,
928     struct bcma_erom_sport_region *region)
929 {
930 	uint32_t	entry;
931 	uint8_t		size_type;
932 	int		error;
933 
934 	/* Peek at the region descriptor */
935 	if (bcma_erom_peek32(erom, &entry))
936 		return (EINVAL);
937 
938 	/* A non-region entry signals the end of the region table */
939 	if (!BCMA_EROM_ENTRY_IS(entry, REGION)) {
940 		return (ENOENT);
941 	} else {
942 		bcma_erom_skip32(erom);
943 	}
944 
945 	region->base_addr = BCMA_EROM_GET_ATTR(entry, REGION_BASE);
946 	region->region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
947 	region->region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
948 	size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE);
949 
950 	/* If region address is 64-bit, fetch the high bits. */
951 	if (BCMA_EROM_GET_ATTR(entry, REGION_64BIT)) {
952 		if ((error = bcma_erom_read32(erom, &entry)))
953 			return (error);
954 
955 		region->base_addr |= ((bhnd_addr_t) entry << 32);
956 	}
957 
958 	/* Parse the region size; it's either encoded as the binary logarithm
959 	 * of the number of 4K pages (i.e. log2 n), or its encoded as a
960 	 * 32-bit/64-bit literal value directly following the current entry. */
961 	if (size_type == BCMA_EROM_REGION_SIZE_OTHER) {
962 		if ((error = bcma_erom_read32(erom, &entry)))
963 			return (error);
964 
965 		region->size = BCMA_EROM_GET_ATTR(entry, RSIZE_VAL);
966 
967 		if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) {
968 			if ((error = bcma_erom_read32(erom, &entry)))
969 				return (error);
970 			region->size |= ((bhnd_size_t) entry << 32);
971 		}
972 	} else {
973 		region->size = BCMA_EROM_REGION_SIZE_BASE << size_type;
974 	}
975 
976 	/* Verify that addr+size does not overflow. */
977 	if (region->size != 0 &&
978 	    BHND_ADDR_MAX - (region->size - 1) < region->base_addr)
979 	{
980 		EROM_LOG(erom, "%s%u: invalid address map %llx:%llx\n",
981 		    bcma_erom_entry_type_name(region->region_type),
982 		    region->region_port,
983 		    (unsigned long long) region->base_addr,
984 		    (unsigned long long) region->size);
985 
986 		return (EINVAL);
987 	}
988 
989 	return (0);
990 }
991 
992 /**
993  * Convert a bcma_erom_core record to its bhnd_core_info representation.
994  *
995  * @param core EROM core record to convert.
996  * @param core_idx The core index of @p core.
997  * @param core_unit The core unit of @p core.
998  * @param[out] info The populated bhnd_core_info representation.
999  */
1000 static void
1001 bcma_erom_to_core_info(const struct bcma_erom_core *core, u_int core_idx,
1002     int core_unit, struct bhnd_core_info *info)
1003 {
1004 	info->vendor = core->vendor;
1005 	info->device = core->device;
1006 	info->hwrev = core->rev;
1007 	info->core_idx = core_idx;
1008 	info->unit = core_unit;
1009 }
1010 
1011 /**
1012  * Map an EROM region type to its corresponding port type.
1013  *
1014  * @param region_type Region type value.
1015  * @param[out] port_type On success, the corresponding port type.
1016  */
1017 static int
1018 bcma_erom_region_to_port_type(struct bcma_erom *erom, uint8_t region_type,
1019     bhnd_port_type *port_type)
1020 {
1021 	switch (region_type) {
1022 	case BCMA_EROM_REGION_TYPE_DEVICE:
1023 		*port_type = BHND_PORT_DEVICE;
1024 		return (0);
1025 	case BCMA_EROM_REGION_TYPE_BRIDGE:
1026 		*port_type = BHND_PORT_BRIDGE;
1027 		return (0);
1028 	case BCMA_EROM_REGION_TYPE_MWRAP:
1029 	case BCMA_EROM_REGION_TYPE_SWRAP:
1030 		*port_type = BHND_PORT_AGENT;
1031 		return (0);
1032 	default:
1033 		EROM_LOG(erom, "unsupported region type %hhx\n",
1034 			region_type);
1035 		return (EINVAL);
1036 	}
1037 }
1038 
1039 /**
1040  * Register all MMIO region descriptors for the given slave port.
1041  *
1042  * @param erom EROM read state.
1043  * @param corecfg Core info to be populated with the scanned port regions.
1044  * @param port_num Port index for which regions will be parsed.
1045  * @param region_type The region type to be parsed.
1046  * @param[out] offset The offset at which to perform parsing. On success, this
1047  * will be updated to point to the next EROM table entry.
1048  */
1049 static int
1050 bcma_erom_corecfg_fill_port_regions(struct bcma_erom *erom,
1051     struct bcma_corecfg *corecfg, bcma_pid_t port_num,
1052     uint8_t region_type)
1053 {
1054 	struct bcma_sport	*sport;
1055 	struct bcma_sport_list	*sports;
1056 	bus_size_t		 entry_offset;
1057 	int			 error;
1058 	bhnd_port_type		 port_type;
1059 
1060 	error = 0;
1061 
1062 	/* Determine the port type for this region type. */
1063 	error = bcma_erom_region_to_port_type(erom, region_type, &port_type);
1064 	if (error)
1065 		return (error);
1066 
1067 	/* Fetch the list to be populated */
1068 	sports = bcma_corecfg_get_port_list(corecfg, port_type);
1069 
1070 	/* Allocate a new port descriptor */
1071 	sport = bcma_alloc_sport(port_num, port_type);
1072 	if (sport == NULL)
1073 		return (ENOMEM);
1074 
1075 	/* Read all address regions defined for this port */
1076 	for (bcma_rmid_t region_num = 0;; region_num++) {
1077 		struct bcma_map			*map;
1078 		struct bcma_erom_sport_region	 spr;
1079 
1080 		/* No valid port definition should come anywhere near
1081 		 * BCMA_RMID_MAX. */
1082 		if (region_num == BCMA_RMID_MAX) {
1083 			EROM_LOG(erom, "core%u %s%u: region count reached "
1084 			    "upper limit of %u\n",
1085 			    corecfg->core_info.core_idx,
1086 			    bhnd_port_type_name(port_type),
1087 			    port_num, BCMA_RMID_MAX);
1088 
1089 			error = EINVAL;
1090 			goto cleanup;
1091 		}
1092 
1093 		/* Parse the next region entry. */
1094 		entry_offset = bcma_erom_tell(erom);
1095 		error = bcma_erom_parse_sport_region(erom, &spr);
1096 		if (error && error != ENOENT) {
1097 			EROM_LOG(erom, "core%u %s%u.%u: invalid slave port "
1098 			    "address region\n",
1099 			    corecfg->core_info.core_idx,
1100 			    bhnd_port_type_name(port_type),
1101 			    port_num, region_num);
1102 			goto cleanup;
1103 		}
1104 
1105 		/* ENOENT signals no further region entries */
1106 		if (error == ENOENT) {
1107 			/* No further entries */
1108 			error = 0;
1109 			break;
1110 		}
1111 
1112 		/* A region or type mismatch also signals no further region
1113 		 * entries */
1114 		if (spr.region_port != port_num ||
1115 		    spr.region_type != region_type)
1116 		{
1117 			/* We don't want to consume this entry */
1118 			bcma_erom_seek(erom, entry_offset);
1119 
1120 			error = 0;
1121 			goto cleanup;
1122 		}
1123 
1124 		/*
1125 		 * Create the map entry.
1126 		 */
1127 		map = malloc(sizeof(struct bcma_map), M_BHND, M_NOWAIT);
1128 		if (map == NULL) {
1129 			error = ENOMEM;
1130 			goto cleanup;
1131 		}
1132 
1133 		map->m_region_num = region_num;
1134 		map->m_base = spr.base_addr;
1135 		map->m_size = spr.size;
1136 		map->m_rid = -1;
1137 
1138 		/* Add the region map to the port */
1139 		STAILQ_INSERT_TAIL(&sport->sp_maps, map, m_link);
1140 		sport->sp_num_maps++;
1141 	}
1142 
1143 cleanup:
1144 	/* Append the new port descriptor on success, or deallocate the
1145 	 * partially parsed descriptor on failure. */
1146 	if (error == 0) {
1147 		STAILQ_INSERT_TAIL(sports, sport, sp_link);
1148 	} else if (sport != NULL) {
1149 		bcma_free_sport(sport);
1150 	}
1151 
1152 	return error;
1153 }
1154 
1155 /**
1156  * Parse the next core entry from the EROM table and produce a bcma_corecfg
1157  * to be owned by the caller.
1158  *
1159  * @param erom A bcma EROM instance.
1160  * @param[out] result On success, the core's device info. The caller inherits
1161  * ownership of this allocation.
1162  *
1163  * @return If successful, returns 0. If the end of the EROM table is hit,
1164  * ENOENT will be returned. On error, returns a non-zero error value.
1165  */
1166 int
1167 bcma_erom_next_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result)
1168 {
1169 	struct bcma_corecfg	*cfg;
1170 	struct bcma_erom_core	 core;
1171 	uint8_t			 first_region_type;
1172 	bus_size_t		 initial_offset;
1173 	u_int			 core_index;
1174 	int			 core_unit;
1175 	int			 error;
1176 
1177 	cfg = NULL;
1178 	initial_offset = bcma_erom_tell(erom);
1179 
1180 	/* Parse the next core entry */
1181 	if ((error = bcma_erom_parse_core(erom, &core)))
1182 		return (error);
1183 
1184 	/* Determine the core's index and unit numbers */
1185 	bcma_erom_reset(erom);
1186 	core_unit = 0;
1187 	core_index = 0;
1188 	for (; bcma_erom_tell(erom) != initial_offset; core_index++) {
1189 		struct bcma_erom_core prev_core;
1190 
1191 		/* Parse next core */
1192 		error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
1193 		if (error)
1194 			return (error);
1195 
1196 		if ((error = bcma_erom_parse_core(erom, &prev_core)))
1197 			return (error);
1198 
1199 		/* Is earlier unit? */
1200 		if (core.vendor == prev_core.vendor &&
1201 		    core.device == prev_core.device)
1202 		{
1203 			core_unit++;
1204 		}
1205 
1206 		/* Seek to next core */
1207 		error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
1208 		if (error)
1209 			return (error);
1210 	}
1211 
1212 	/* We already parsed the core descriptor */
1213 	if ((error = bcma_erom_skip_core(erom)))
1214 		return (error);
1215 
1216 	/* Allocate our corecfg */
1217 	cfg = bcma_alloc_corecfg(core_index, core_unit, core.vendor,
1218 	    core.device, core.rev);
1219 	if (cfg == NULL)
1220 		return (ENOMEM);
1221 
1222 	/* These are 5-bit values in the EROM table, and should never be able
1223 	 * to overflow BCMA_PID_MAX. */
1224 	KASSERT(core.num_mport <= BCMA_PID_MAX, ("unsupported mport count"));
1225 	KASSERT(core.num_dport <= BCMA_PID_MAX, ("unsupported dport count"));
1226 	KASSERT(core.num_mwrap + core.num_swrap <= BCMA_PID_MAX,
1227 	    ("unsupported wport count"));
1228 
1229 	if (bootverbose) {
1230 		EROM_LOG(erom,
1231 		    "core%u: %s %s (cid=%hx, rev=%hu, unit=%d)\n",
1232 		    core_index,
1233 		    bhnd_vendor_name(core.vendor),
1234 		    bhnd_find_core_name(core.vendor, core.device),
1235 		    core.device, core.rev, core_unit);
1236 	}
1237 
1238 	cfg->num_master_ports = core.num_mport;
1239 	cfg->num_dev_ports = 0;		/* determined below */
1240 	cfg->num_bridge_ports = 0;	/* determined blow */
1241 	cfg->num_wrapper_ports = core.num_mwrap + core.num_swrap;
1242 
1243 	/* Parse Master Port Descriptors */
1244 	for (uint8_t i = 0; i < core.num_mport; i++) {
1245 		struct bcma_mport	*mport;
1246 		struct bcma_erom_mport	 mpd;
1247 
1248 		/* Parse the master port descriptor */
1249 		error = bcma_erom_parse_mport(erom, &mpd);
1250 		if (error)
1251 			goto failed;
1252 
1253 		/* Initialize a new bus mport structure */
1254 		mport = malloc(sizeof(struct bcma_mport), M_BHND, M_NOWAIT);
1255 		if (mport == NULL) {
1256 			error = ENOMEM;
1257 			goto failed;
1258 		}
1259 
1260 		mport->mp_vid = mpd.port_vid;
1261 		mport->mp_num = mpd.port_num;
1262 
1263 		/* Update dinfo */
1264 		STAILQ_INSERT_TAIL(&cfg->master_ports, mport, mp_link);
1265 	}
1266 
1267 
1268 	/*
1269 	 * Determine whether this is a bridge device; if so, we can
1270 	 * expect the first sequence of address region descriptors to
1271 	 * be of EROM_REGION_TYPE_BRIDGE instead of
1272 	 * BCMA_EROM_REGION_TYPE_DEVICE.
1273 	 *
1274 	 * It's unclear whether this is the correct mechanism by which we
1275 	 * should detect/handle bridge devices, but this approach matches
1276 	 * that of (some of) Broadcom's published drivers.
1277 	 */
1278 	if (core.num_dport > 0) {
1279 		uint32_t entry;
1280 
1281 		if ((error = bcma_erom_peek32(erom, &entry)))
1282 			goto failed;
1283 
1284 		if (BCMA_EROM_ENTRY_IS(entry, REGION) &&
1285 		    BCMA_EROM_GET_ATTR(entry, REGION_TYPE) == BCMA_EROM_REGION_TYPE_BRIDGE)
1286 		{
1287 			first_region_type = BCMA_EROM_REGION_TYPE_BRIDGE;
1288 			cfg->num_dev_ports = 0;
1289 			cfg->num_bridge_ports = core.num_dport;
1290 		} else {
1291 			first_region_type = BCMA_EROM_REGION_TYPE_DEVICE;
1292 			cfg->num_dev_ports = core.num_dport;
1293 			cfg->num_bridge_ports = 0;
1294 		}
1295 	}
1296 
1297 	/* Device/bridge port descriptors */
1298 	for (uint8_t sp_num = 0; sp_num < core.num_dport; sp_num++) {
1299 		error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num,
1300 		    first_region_type);
1301 
1302 		if (error)
1303 			goto failed;
1304 	}
1305 
1306 	/* Wrapper (aka device management) descriptors (for master ports). */
1307 	for (uint8_t sp_num = 0; sp_num < core.num_mwrap; sp_num++) {
1308 		error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num,
1309 		    BCMA_EROM_REGION_TYPE_MWRAP);
1310 
1311 		if (error)
1312 			goto failed;
1313 	}
1314 
1315 
1316 	/* Wrapper (aka device management) descriptors (for slave ports). */
1317 	for (uint8_t i = 0; i < core.num_swrap; i++) {
1318 		/* Slave wrapper ports are not numbered distinctly from master
1319 		 * wrapper ports. */
1320 
1321 		/*
1322 		 * Broadcom DDR1/DDR2 Memory Controller
1323 		 * (cid=82e, rev=1, unit=0, d/mw/sw = 2/0/1 ) ->
1324 		 * bhnd0: erom[0xdc]: core6 agent0.0: mismatch got: 0x1 (0x2)
1325 		 *
1326 		 * ARM BP135 AMBA3 AXI to APB Bridge
1327 		 * (cid=135, rev=0, unit=0, d/mw/sw = 1/0/1 ) ->
1328 		 * bhnd0: erom[0x124]: core9 agent1.0: mismatch got: 0x0 (0x2)
1329 		 *
1330 		 * core.num_mwrap
1331 		 * ===>
1332 		 * (core.num_mwrap > 0) ?
1333 		 *           core.num_mwrap :
1334 		 *           ((core.vendor == BHND_MFGID_BCM) ? 1 : 0)
1335 		 */
1336 		uint8_t sp_num;
1337 		sp_num = (core.num_mwrap > 0) ?
1338 				core.num_mwrap :
1339 				((core.vendor == BHND_MFGID_BCM) ? 1 : 0) + i;
1340 		error = bcma_erom_corecfg_fill_port_regions(erom, cfg, sp_num,
1341 		    BCMA_EROM_REGION_TYPE_SWRAP);
1342 
1343 		if (error)
1344 			goto failed;
1345 	}
1346 
1347 	/*
1348 	 * Seek to the next core entry (if any), skipping any dangling/invalid
1349 	 * region entries.
1350 	 *
1351 	 * On the BCM4706, the EROM entry for the memory controller core
1352 	 * (0x4bf/0x52E) contains a dangling/unused slave wrapper port region
1353 	 * descriptor.
1354 	 */
1355 	if ((error = bcma_erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE))) {
1356 		if (error != ENOENT)
1357 			goto failed;
1358 	}
1359 
1360 	*result = cfg;
1361 	return (0);
1362 
1363 failed:
1364 	if (cfg != NULL)
1365 		bcma_free_corecfg(cfg);
1366 
1367 	return error;
1368 }
1369 
1370 static int
1371 bcma_erom_dump(bhnd_erom_t *erom)
1372 {
1373 	struct bcma_erom	*sc;
1374 	uint32_t		entry;
1375 	int			error;
1376 
1377 	sc = (struct bcma_erom *)erom;
1378 
1379 	bcma_erom_reset(sc);
1380 
1381 	while (!(error = bcma_erom_read32(sc, &entry))) {
1382 		/* Handle EOF */
1383 		if (entry == BCMA_EROM_TABLE_EOF) {
1384 			EROM_LOG(sc, "EOF\n");
1385 			return (0);
1386 		}
1387 
1388 		/* Invalid entry */
1389 		if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID)) {
1390 			EROM_LOG(sc, "invalid EROM entry %#x\n", entry);
1391 			return (EINVAL);
1392 		}
1393 
1394 		switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
1395 		case BCMA_EROM_ENTRY_TYPE_CORE: {
1396 			/* CoreDescA */
1397 			EROM_LOG(sc, "coreA (0x%x)\n", entry);
1398 			EROM_LOG(sc, "\tdesigner:\t0x%x\n",
1399 			    BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER));
1400 			EROM_LOG(sc, "\tid:\t\t0x%x\n",
1401 			    BCMA_EROM_GET_ATTR(entry, COREA_ID));
1402 			EROM_LOG(sc, "\tclass:\t\t0x%x\n",
1403 			    BCMA_EROM_GET_ATTR(entry, COREA_CLASS));
1404 
1405 			/* CoreDescB */
1406 			if ((error = bcma_erom_read32(sc, &entry))) {
1407 				EROM_LOG(sc, "error reading CoreDescB: %d\n",
1408 				    error);
1409 				return (error);
1410 			}
1411 
1412 			if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
1413 				EROM_LOG(sc, "invalid core descriptor; found "
1414 				    "unexpected entry %#x (type=%s)\n",
1415 				    entry, bcma_erom_entry_type_name(entry));
1416 				return (EINVAL);
1417 			}
1418 
1419 			EROM_LOG(sc, "coreB (0x%x)\n", entry);
1420 			EROM_LOG(sc, "\trev:\t0x%x\n",
1421 			    BCMA_EROM_GET_ATTR(entry, COREB_REV));
1422 			EROM_LOG(sc, "\tnummp:\t0x%x\n",
1423 			    BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP));
1424 			EROM_LOG(sc, "\tnumdp:\t0x%x\n",
1425 			    BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP));
1426 			EROM_LOG(sc, "\tnumwmp:\t0x%x\n",
1427 			    BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP));
1428 			EROM_LOG(sc, "\tnumwsp:\t0x%x\n",
1429 			    BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP));
1430 
1431 			break;
1432 		}
1433 		case BCMA_EROM_ENTRY_TYPE_MPORT:
1434 			EROM_LOG(sc, "\tmport 0x%x\n", entry);
1435 			EROM_LOG(sc, "\t\tport:\t0x%x\n",
1436 			    BCMA_EROM_GET_ATTR(entry, MPORT_NUM));
1437 			EROM_LOG(sc, "\t\tid:\t\t0x%x\n",
1438 			    BCMA_EROM_GET_ATTR(entry, MPORT_ID));
1439 			break;
1440 
1441 		case BCMA_EROM_ENTRY_TYPE_REGION: {
1442 			bool	addr64;
1443 			uint8_t	size_type;
1444 
1445 			addr64 = (BCMA_EROM_GET_ATTR(entry, REGION_64BIT) != 0);
1446 			size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE);
1447 
1448 			EROM_LOG(sc, "\tregion 0x%x:\n", entry);
1449 			EROM_LOG(sc, "\t\t%s:\t0x%x\n",
1450 			    addr64 ? "baselo" : "base",
1451 			    BCMA_EROM_GET_ATTR(entry, REGION_BASE));
1452 			EROM_LOG(sc, "\t\tport:\t0x%x\n",
1453 			    BCMA_EROM_GET_ATTR(entry, REGION_PORT));
1454 			EROM_LOG(sc, "\t\ttype:\t0x%x\n",
1455 			    BCMA_EROM_GET_ATTR(entry, REGION_TYPE));
1456 			EROM_LOG(sc, "\t\tsztype:\t0x%hhx\n", size_type);
1457 
1458 			/* Read the base address high bits */
1459 			if (addr64) {
1460 				if ((error = bcma_erom_read32(sc, &entry))) {
1461 					EROM_LOG(sc, "error reading region "
1462 					    "base address high bits %d\n",
1463 					    error);
1464 					return (error);
1465 				}
1466 
1467 				EROM_LOG(sc, "\t\tbasehi:\t0x%x\n", entry);
1468 			}
1469 
1470 			/* Read extended size descriptor */
1471 			if (size_type == BCMA_EROM_REGION_SIZE_OTHER) {
1472 				bool size64;
1473 
1474 				if ((error = bcma_erom_read32(sc, &entry))) {
1475 					EROM_LOG(sc, "error reading region "
1476 					    "size descriptor %d\n",
1477 					    error);
1478 					return (error);
1479 				}
1480 
1481 				if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT))
1482 					size64 = true;
1483 				else
1484 					size64 = false;
1485 
1486 				EROM_LOG(sc, "\t\t%s:\t0x%x\n",
1487 				    size64 ? "sizelo" : "size",
1488 				    BCMA_EROM_GET_ATTR(entry, RSIZE_VAL));
1489 
1490 				if (size64) {
1491 					error = bcma_erom_read32(sc, &entry);
1492 					if (error) {
1493 						EROM_LOG(sc, "error reading "
1494 						    "region size high bits: "
1495 						    "%d\n", error);
1496 						return (error);
1497 					}
1498 
1499 					EROM_LOG(sc, "\t\tsizehi:\t0x%x\n",
1500 					    entry);
1501 				}
1502 			}
1503 			break;
1504 		}
1505 
1506 		default:
1507 			EROM_LOG(sc, "unknown EROM entry 0x%x (type=%s)\n",
1508 			    entry, bcma_erom_entry_type_name(entry));
1509 			return (EINVAL);
1510 		}
1511 	}
1512 
1513 	if (error == ENOENT)
1514 		EROM_LOG(sc, "BCMA EROM table missing terminating EOF\n");
1515 	else if (error)
1516 		EROM_LOG(sc, "EROM read failed: %d\n", error);
1517 
1518 	return (error);
1519 }
1520 
1521 static kobj_method_t bcma_erom_methods[] = {
1522 	KOBJMETHOD(bhnd_erom_probe,		bcma_erom_probe),
1523 	KOBJMETHOD(bhnd_erom_probe_static,	bcma_erom_probe_static),
1524 	KOBJMETHOD(bhnd_erom_init,		bcma_erom_init),
1525 	KOBJMETHOD(bhnd_erom_init_static,	bcma_erom_init_static),
1526 	KOBJMETHOD(bhnd_erom_fini,		bcma_erom_fini),
1527 	KOBJMETHOD(bhnd_erom_get_core_table,	bcma_erom_get_core_table),
1528 	KOBJMETHOD(bhnd_erom_free_core_table,	bcma_erom_free_core_table),
1529 	KOBJMETHOD(bhnd_erom_lookup_core,	bcma_erom_lookup_core),
1530 	KOBJMETHOD(bhnd_erom_lookup_core_addr,	bcma_erom_lookup_core_addr),
1531 	KOBJMETHOD(bhnd_erom_dump,		bcma_erom_dump),
1532 
1533 	KOBJMETHOD_END
1534 };
1535 
1536 BHND_EROM_DEFINE_CLASS(bcma_erom, bcma_erom_parser, bcma_erom_methods, sizeof(struct bcma_erom));
1537