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 "bcma_eromreg.h"
43#include "bcma_eromvar.h"
44
45/*
46 * BCMA Enumeration ROM (EROM) Table
47 *
48 * Provides auto-discovery of BCMA cores on Broadcom's HND SoC.
49 *
50 * The EROM core address can be found at BCMA_CC_EROM_ADDR within the
51 * ChipCommon registers. The table itself is comprised of 32-bit
52 * type-tagged entries, organized into an array of variable-length
53 * core descriptor records.
54 *
55 * The final core descriptor is followed by a 32-bit BCMA_EROM_TABLE_EOF (0xF)
56 * marker.
57 */
58
59static const char	*erom_entry_type_name (uint8_t entry);
60static int		 erom_read32(struct bcma_erom *erom, uint32_t *entry);
61static int		 erom_skip32(struct bcma_erom *erom);
62
63static int		 erom_skip_core(struct bcma_erom *erom);
64static int		 erom_skip_mport(struct bcma_erom *erom);
65static int		 erom_skip_sport_region(struct bcma_erom *erom);
66
67static int		 erom_seek_next(struct bcma_erom *erom, uint8_t etype);
68
69#define	EROM_LOG(erom, fmt, ...)	\
70	device_printf(erom->dev, "erom[0x%llx]: " fmt, \
71	    (unsigned long long) (erom->offset), ##__VA_ARGS__);
72
73/**
74 * Open an EROM table for reading.
75 *
76 * @param[out] erom On success, will be populated with a valid EROM
77 * read state.
78 * @param r An active resource mapping the EROM core.
79 * @param offset Offset of the EROM core within @p resource.
80 *
81 * @retval 0 success
82 * @retval non-zero if the erom table could not be opened.
83 */
84int
85bcma_erom_open(struct bcma_erom *erom, struct resource *r, bus_size_t offset)
86{
87	/* Initialize the EROM reader */
88	erom->dev = rman_get_device(r);
89	erom->r = r;
90	erom->start = offset + BCMA_EROM_TABLE_START;
91	erom->offset = 0;
92
93	return (0);
94}
95
96/** Return the type name for an EROM entry */
97static const char *
98erom_entry_type_name (uint8_t entry)
99{
100	switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
101	case BCMA_EROM_ENTRY_TYPE_CORE:
102		return "core";
103	case BCMA_EROM_ENTRY_TYPE_MPORT:
104		return "mport";
105	case BCMA_EROM_ENTRY_TYPE_REGION:
106		return "region";
107	default:
108		return "unknown";
109	}
110}
111
112/**
113 * Return the current read position.
114 */
115bus_size_t
116bcma_erom_tell(struct bcma_erom *erom)
117{
118	return (erom->offset);
119}
120
121/**
122 * Seek to an absolute read position.
123 */
124void
125bcma_erom_seek(struct bcma_erom *erom, bus_size_t offset)
126{
127	erom->offset = offset;
128}
129
130/**
131 * Read a 32-bit entry value from the EROM table without advancing the
132 * read position.
133 *
134 * @param erom EROM read state.
135 * @param entry Will contain the read result on success.
136 * @retval 0 success
137 * @retval ENOENT The end of the EROM table was reached.
138 * @retval non-zero The read could not be completed.
139 */
140int
141bcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry)
142{
143	if (erom->offset >= BCMA_EROM_TABLE_SIZE) {
144		EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n");
145		return (EINVAL);
146	}
147
148	*entry = bus_read_4(erom->r, erom->start + erom->offset);
149	return (0);
150}
151
152/**
153 * Read a 32-bit entry value from the EROM table.
154 *
155 * @param erom EROM read state.
156 * @param entry Will contain the read result on success.
157 * @retval 0 success
158 * @retval ENOENT The end of the EROM table was reached.
159 * @retval non-zero The read could not be completed.
160 */
161static int
162erom_read32(struct bcma_erom *erom, uint32_t *entry)
163{
164	int error;
165
166	if ((error = bcma_erom_peek32(erom, entry)) == 0)
167		erom->offset += 4;
168
169	return (error);
170}
171
172/**
173 * Read and discard 32-bit entry value from the EROM table.
174 *
175 * @param erom EROM read state.
176 * @retval 0 success
177 * @retval ENOENT The end of the EROM table was reached.
178 * @retval non-zero The read could not be completed.
179 */
180static int
181erom_skip32(struct bcma_erom *erom)
182{
183	uint32_t	entry;
184
185	return erom_read32(erom, &entry);
186}
187
188/**
189 * Read and discard a core descriptor from the EROM table.
190 *
191 * @param erom EROM read state.
192 * @retval 0 success
193 * @retval ENOENT The end of the EROM table was reached.
194 * @retval non-zero The read could not be completed.
195 */
196static int
197erom_skip_core(struct bcma_erom *erom)
198{
199	struct bcma_erom_core core;
200	return (bcma_erom_parse_core(erom, &core));
201}
202
203/**
204 * Read and discard a master port descriptor from the EROM table.
205 *
206 * @param erom EROM read state.
207 * @retval 0 success
208 * @retval ENOENT The end of the EROM table was reached.
209 * @retval non-zero The read could not be completed.
210 */
211static int
212erom_skip_mport(struct bcma_erom *erom)
213{
214	struct bcma_erom_mport mp;
215	return (bcma_erom_parse_mport(erom, &mp));
216}
217
218/**
219 * Read and discard a port region descriptor from the EROM table.
220 *
221 * @param erom EROM read state.
222 * @retval 0 success
223 * @retval ENOENT The end of the EROM table was reached.
224 * @retval non-zero The read could not be completed.
225 */
226static int
227erom_skip_sport_region(struct bcma_erom *erom)
228{
229	struct bcma_erom_sport_region r;
230	return (bcma_erom_parse_sport_region(erom, &r));
231}
232
233/**
234 * Seek to the next entry matching the given EROM entry type.
235 *
236 * @param erom EROM read state.
237 * @param etype  One of BCMA_EROM_ENTRY_TYPE_CORE,
238 * BCMA_EROM_ENTRY_TYPE_MPORT, or BCMA_EROM_ENTRY_TYPE_REGION.
239 * @retval 0 success
240 * @retval ENOENT The end of the EROM table was reached.
241 * @retval non-zero Reading or parsing the descriptor failed.
242 */
243static int
244erom_seek_next(struct bcma_erom *erom, uint8_t etype)
245{
246	uint32_t			entry;
247	int				error;
248
249	/* Iterate until we hit an entry matching the requested type. */
250	while (!(error = bcma_erom_peek32(erom, &entry))) {
251		/* Handle EOF */
252		if (entry == BCMA_EROM_TABLE_EOF)
253			return (ENOENT);
254
255		/* Invalid entry */
256		if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID))
257			return (EINVAL);
258
259		/* Entry type matches? */
260		if (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE) == etype)
261			return (0);
262
263		/* Skip non-matching entry types. */
264		switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
265		case BCMA_EROM_ENTRY_TYPE_CORE:
266			if ((error = erom_skip_core(erom)))
267				return (error);
268
269			break;
270
271		case BCMA_EROM_ENTRY_TYPE_MPORT:
272			if ((error = erom_skip_mport(erom)))
273				return (error);
274
275			break;
276
277		case BCMA_EROM_ENTRY_TYPE_REGION:
278			if ((error = erom_skip_sport_region(erom)))
279				return (error);
280			break;
281
282		default:
283			/* Unknown entry type! */
284			return (EINVAL);
285		}
286	}
287
288	return (error);
289}
290
291/**
292 * Return the read position to the start of the EROM table.
293 *
294 * @param erom EROM read state.
295 */
296void
297bcma_erom_reset(struct bcma_erom *erom)
298{
299	erom->offset = 0;
300}
301
302/**
303 * Seek to the requested core entry.
304 *
305 * @param erom EROM read state.
306 * @param core_index Index of the core to seek to.
307 * @retval 0 success
308 * @retval ENOENT The end of the EROM table was reached before @p index was
309 * found.
310 * @retval non-zero Reading or parsing failed.
311 */
312int
313bcma_erom_seek_core_index(struct bcma_erom *erom, u_int core_index)
314{
315	int error;
316
317	/* Start search at top of EROM */
318	bcma_erom_reset(erom);
319
320	/* Skip core descriptors till we hit the requested entry */
321	for (u_int i = 0; i < core_index; i++) {
322		struct bcma_erom_core core;
323
324		/* Read past the core descriptor */
325		if ((error = bcma_erom_parse_core(erom, &core)))
326			return (error);
327
328		/* Seek to the next readable core entry */
329		error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
330		if (error)
331			return (error);
332	}
333
334	return (0);
335}
336
337
338/**
339 * Read the next core descriptor from the EROM table.
340 *
341 * @param erom EROM read state.
342 * @param[out] core On success, will be populated with the parsed core
343 * descriptor data.
344 * @retval 0 success
345 * @retval ENOENT The end of the EROM table was reached.
346 * @retval non-zero Reading or parsing the core descriptor failed.
347 */
348int
349bcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core)
350{
351	uint32_t	entry;
352	int		error;
353
354	/* Parse CoreDescA */
355	if ((error = erom_read32(erom, &entry)))
356		return (error);
357
358	/* Handle EOF */
359	if (entry == BCMA_EROM_TABLE_EOF)
360		return (ENOENT);
361
362	if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
363		EROM_LOG(erom, "Unexpected EROM entry 0x%x (type=%s)\n",
364                   entry, erom_entry_type_name(entry));
365
366		return (EINVAL);
367	}
368
369	core->vendor = BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER);
370	core->device = BCMA_EROM_GET_ATTR(entry, COREA_ID);
371
372	/* Parse CoreDescB */
373	if ((error = erom_read32(erom, &entry)))
374		return (error);
375
376	if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
377		return (EINVAL);
378	}
379
380	core->rev = BCMA_EROM_GET_ATTR(entry, COREB_REV);
381	core->num_mport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP);
382	core->num_dport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP);
383	core->num_mwrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP);
384	core->num_swrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WSP);
385
386	return (0);
387}
388
389/**
390 * Read the next master port descriptor from the EROM table.
391 *
392 * @param erom EROM read state.
393 * @param[out] mport On success, will be populated with the parsed
394 * descriptor data.
395 * @retval 0 success
396 * @retval non-zero Reading or parsing the descriptor failed.
397 */
398int
399bcma_erom_parse_mport(struct bcma_erom *erom,
400    struct bcma_erom_mport *mport)
401{
402	uint32_t	entry;
403	int		error;
404
405	/* Parse the master port descriptor */
406	if ((error = erom_read32(erom, &entry)))
407		return (error);
408
409	if (!BCMA_EROM_ENTRY_IS(entry, MPORT))
410		return (EINVAL);
411
412	mport->port_vid = BCMA_EROM_GET_ATTR(entry, MPORT_ID);
413	mport->port_num = BCMA_EROM_GET_ATTR(entry, MPORT_NUM);
414
415	return (0);
416}
417
418/**
419 * Read the next slave port region descriptor from the EROM table.
420 *
421 * @param erom EROM read state.
422 * @param[out] mport On success, will be populated with the parsed
423 * descriptor data.
424 * @retval 0 success
425 * @retval ENOENT The end of the region descriptor table was reached.
426 * @retval non-zero Reading or parsing the descriptor failed.
427 */
428int
429bcma_erom_parse_sport_region(struct bcma_erom *erom,
430    struct bcma_erom_sport_region *region)
431{
432	uint32_t	entry;
433	uint8_t		size_type;
434	int		error;
435
436	/* Peek at the region descriptor */
437	if (bcma_erom_peek32(erom, &entry))
438		return (EINVAL);
439
440	/* A non-region entry signals the end of the region table */
441	if (!BCMA_EROM_ENTRY_IS(entry, REGION)) {
442		return (ENOENT);
443	} else {
444		erom_skip32(erom);
445	}
446
447	region->base_addr = BCMA_EROM_GET_ATTR(entry, REGION_BASE);
448	region->region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
449	region->region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
450	size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE);
451
452	/* If region address is 64-bit, fetch the high bits. */
453	if (BCMA_EROM_GET_ATTR(entry, REGION_64BIT)) {
454		if ((error = erom_read32(erom, &entry)))
455			return (error);
456
457		region->base_addr |= ((bhnd_addr_t) entry << 32);
458	}
459
460	/* Parse the region size; it's either encoded as the binary logarithm
461	 * of the number of 4K pages (i.e. log2 n), or its encoded as a
462	 * 32-bit/64-bit literal value directly following the current entry. */
463	if (size_type == BCMA_EROM_REGION_SIZE_OTHER) {
464		if ((error = erom_read32(erom, &entry)))
465			return (error);
466
467		region->size = BCMA_EROM_GET_ATTR(entry, RSIZE_VAL);
468
469		if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) {
470			if ((error = erom_read32(erom, &entry)))
471				return (error);
472			region->size |= ((bhnd_size_t) entry << 32);
473		}
474	} else {
475		region->size = BCMA_EROM_REGION_SIZE_BASE << size_type;
476	}
477
478	/* Verify that addr+size does not overflow. */
479	if (region->size != 0 &&
480	    BHND_ADDR_MAX - (region->size - 1) < region->base_addr)
481	{
482		EROM_LOG(erom, "%s%u: invalid address map %llx:%llx\n",
483		    erom_entry_type_name(region->region_type),
484		    region->region_port,
485		    (unsigned long long) region->base_addr,
486		    (unsigned long long) region->size);
487
488		return (EINVAL);
489	}
490
491	return (0);
492}
493
494/**
495 * Parse all cores descriptors from @p erom and return the array
496 * in @p cores and the count in @p num_cores. The current EROM read position
497 * is left unmodified.
498 *
499 * The memory allocated for the table should be freed using
500 * `free(*cores, M_BHND)`. @p cores and @p num_cores are not changed
501 * when an error is returned.
502 *
503 * @param erom EROM read state.
504 * @param[out] cores the table of parsed core descriptors.
505 * @param[out] num_cores the number of core records in @p cores.
506 */
507int
508bcma_erom_get_core_info(struct bcma_erom *erom,
509    struct bhnd_core_info **cores,
510    u_int *num_cores)
511{
512	struct bhnd_core_info	*buffer;
513	bus_size_t		 initial_offset;
514	u_int			 count;
515	int			 error;
516
517	buffer = NULL;
518	initial_offset = bcma_erom_tell(erom);
519
520	/* Determine the core count */
521	bcma_erom_reset(erom);
522	for (count = 0, error = 0; !error; count++) {
523		struct bcma_erom_core core;
524
525		/* Seek to the first readable core entry */
526		error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
527		if (error == ENOENT)
528			break;
529		else if (error)
530			goto cleanup;
531
532		/* Read past the core descriptor */
533		if ((error = bcma_erom_parse_core(erom, &core)))
534			goto cleanup;
535	}
536
537	/* Allocate our output buffer */
538	buffer = malloc(sizeof(struct bhnd_core_info) * count, M_BHND,
539	    M_NOWAIT);
540	if (buffer == NULL) {
541		error = ENOMEM;
542		goto cleanup;
543	}
544
545	/* Parse all core descriptors */
546	bcma_erom_reset(erom);
547	for (u_int i = 0; i < count; i++) {
548		struct bcma_erom_core core;
549
550		/* Parse the core */
551		error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
552		if (error)
553			goto cleanup;
554
555		error = bcma_erom_parse_core(erom, &core);
556		if (error)
557			goto cleanup;
558
559		/* Convert to a bhnd info record */
560		buffer[i].vendor = core.vendor;
561		buffer[i].device = core.device;
562		buffer[i].hwrev = core.rev;
563		buffer[i].core_idx = i;
564		buffer[i].unit = 0;
565
566		/* Determine the unit number */
567		for (u_int j = 0; j < i; j++) {
568			if (buffer[i].vendor == buffer[j].vendor &&
569			    buffer[i].device == buffer[j].device)
570				buffer[i].unit++;
571		}
572	}
573
574cleanup:
575	if (!error) {
576		*cores = buffer;
577		*num_cores = count;
578	} else {
579		if (buffer != NULL)
580			free(buffer, M_BHND);
581	}
582
583	/* Restore the initial position */
584	bcma_erom_seek(erom, initial_offset);
585	return (error);
586}
587
588
589/**
590 * Register all MMIO region descriptors for the given slave port.
591 *
592 * @param erom EROM read state.
593 * @param corecfg Core info to be populated with the scanned port regions.
594 * @param port_num Port index for which regions will be parsed.
595 * @param region_type The region type to be parsed.
596 * @param[out] offset The offset at which to perform parsing. On success, this
597 * will be updated to point to the next EROM table entry.
598 */
599static int
600erom_corecfg_fill_port_regions(struct bcma_erom *erom,
601    struct bcma_corecfg *corecfg, bcma_pid_t port_num,
602    uint8_t region_type)
603{
604	struct bcma_sport	*sport;
605	struct bcma_sport_list	*sports;
606	bus_size_t		 entry_offset;
607	int			 error;
608	bhnd_port_type		 port_type;
609
610	error = 0;
611
612	/* Determine the port type for this region type. */
613	switch (region_type) {
614		case BCMA_EROM_REGION_TYPE_DEVICE:
615			port_type = BHND_PORT_DEVICE;
616			break;
617		case BCMA_EROM_REGION_TYPE_BRIDGE:
618			port_type = BHND_PORT_BRIDGE;
619			break;
620		case BCMA_EROM_REGION_TYPE_MWRAP:
621		case BCMA_EROM_REGION_TYPE_SWRAP:
622			port_type = BHND_PORT_AGENT;
623			break;
624		default:
625			EROM_LOG(erom, "unsupported region type %hhx\n",
626			    region_type);
627			return (EINVAL);
628	}
629
630	/* Fetch the list to be populated */
631	sports = bcma_corecfg_get_port_list(corecfg, port_type);
632
633	/* Allocate a new port descriptor */
634	sport = bcma_alloc_sport(port_num, port_type);
635	if (sport == NULL)
636		return (ENOMEM);
637
638	/* Read all address regions defined for this port */
639	for (bcma_rmid_t region_num = 0;; region_num++) {
640		struct bcma_map			*map;
641		struct bcma_erom_sport_region	 spr;
642
643		/* No valid port definition should come anywhere near
644		 * BCMA_RMID_MAX. */
645		if (region_num == BCMA_RMID_MAX) {
646			EROM_LOG(erom, "core%u %s%u: region count reached "
647			    "upper limit of %u\n",
648			    corecfg->core_info.core_idx,
649			    bhnd_port_type_name(port_type),
650			    port_num, BCMA_RMID_MAX);
651
652			error = EINVAL;
653			goto cleanup;
654		}
655
656		/* Parse the next region entry. */
657		entry_offset = bcma_erom_tell(erom);
658		error = bcma_erom_parse_sport_region(erom, &spr);
659		if (error && error != ENOENT) {
660			EROM_LOG(erom, "core%u %s%u.%u: invalid slave port "
661			    "address region\n",
662			    corecfg->core_info.core_idx,
663			    bhnd_port_type_name(port_type),
664			    port_num, region_num);
665			goto cleanup;
666		}
667
668		/* ENOENT signals no further region entries */
669		if (error == ENOENT) {
670			/* No further entries */
671			error = 0;
672			break;
673		}
674
675		/* A region or type mismatch also signals no further region
676		 * entries */
677		if (spr.region_port != port_num ||
678		    spr.region_type != region_type)
679		{
680			/* We don't want to consume this entry */
681			bcma_erom_seek(erom, entry_offset);
682
683			error = 0;
684			goto cleanup;
685		}
686
687		/*
688		 * Create the map entry.
689		 */
690		map = malloc(sizeof(struct bcma_map), M_BHND, M_NOWAIT);
691		if (map == NULL) {
692			error = ENOMEM;
693			goto cleanup;
694		}
695
696		map->m_region_num = region_num;
697		map->m_base = spr.base_addr;
698		map->m_size = spr.size;
699		map->m_rid = -1;
700
701		/* Add the region map to the port */
702		STAILQ_INSERT_TAIL(&sport->sp_maps, map, m_link);
703		sport->sp_num_maps++;
704	}
705
706cleanup:
707	/* Append the new port descriptor on success, or deallocate the
708	 * partially parsed descriptor on failure. */
709	if (error == 0) {
710		STAILQ_INSERT_TAIL(sports, sport, sp_link);
711	} else if (sport != NULL) {
712		bcma_free_sport(sport);
713	}
714
715	return error;
716}
717
718/**
719 * Parse the next core entry from the EROM table and produce a bcma_corecfg
720 * to be owned by the caller.
721 *
722 * @param erom EROM read state.
723 * @param[out] result On success, the core's device info. The caller inherits
724 * ownership of this allocation.
725 *
726 * @return If successful, returns 0. If the end of the EROM table is hit,
727 * ENOENT will be returned. On error, returns a non-zero error value.
728 */
729int
730bcma_erom_parse_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result)
731{
732	struct bcma_corecfg	*cfg;
733	struct bcma_erom_core	 core;
734	uint8_t			 first_region_type;
735	bus_size_t		 initial_offset;
736	u_int			 core_index;
737	int			 core_unit;
738	int			 error;
739
740	cfg = NULL;
741	initial_offset = bcma_erom_tell(erom);
742
743	/* Parse the next core entry */
744	if ((error = bcma_erom_parse_core(erom, &core)))
745		return (error);
746
747	/* Determine the core's index and unit numbers */
748	bcma_erom_reset(erom);
749	core_unit = 0;
750	core_index = 0;
751	for (; bcma_erom_tell(erom) != initial_offset; core_index++) {
752		struct bcma_erom_core prev_core;
753
754		/* Parse next core */
755		if ((error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE)))
756			return (error);
757
758		if ((error = bcma_erom_parse_core(erom, &prev_core)))
759			return (error);
760
761		/* Is earlier unit? */
762		if (core.vendor == prev_core.vendor &&
763		    core.device == prev_core.device)
764		{
765			core_unit++;
766		}
767
768		/* Seek to next core */
769		if ((error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE)))
770			return (error);
771	}
772
773	/* We already parsed the core descriptor */
774	if ((error = erom_skip_core(erom)))
775		return (error);
776
777	/* Allocate our corecfg */
778	cfg = bcma_alloc_corecfg(core_index, core_unit, core.vendor,
779	    core.device, core.rev);
780	if (cfg == NULL)
781		return (ENOMEM);
782
783	/* These are 5-bit values in the EROM table, and should never be able
784	 * to overflow BCMA_PID_MAX. */
785	KASSERT(core.num_mport <= BCMA_PID_MAX, ("unsupported mport count"));
786	KASSERT(core.num_dport <= BCMA_PID_MAX, ("unsupported dport count"));
787	KASSERT(core.num_mwrap + core.num_swrap <= BCMA_PID_MAX,
788	    ("unsupported wport count"));
789
790	if (bootverbose) {
791		EROM_LOG(erom,
792		    "core%u: %s %s (cid=%hx, rev=%hu, unit=%d)\n",
793		    core_index,
794		    bhnd_vendor_name(core.vendor),
795		    bhnd_find_core_name(core.vendor, core.device),
796		    core.device, core.rev, core_unit);
797	}
798
799	cfg->num_master_ports = core.num_mport;
800	cfg->num_dev_ports = 0;		/* determined below */
801	cfg->num_bridge_ports = 0;	/* determined blow */
802	cfg->num_wrapper_ports = core.num_mwrap + core.num_swrap;
803
804	/* Parse Master Port Descriptors */
805	for (uint8_t i = 0; i < core.num_mport; i++) {
806		struct bcma_mport	*mport;
807		struct bcma_erom_mport	 mpd;
808
809		/* Parse the master port descriptor */
810		error = bcma_erom_parse_mport(erom, &mpd);
811		if (error)
812			goto failed;
813
814		/* Initialize a new bus mport structure */
815		mport = malloc(sizeof(struct bcma_mport), M_BHND, M_NOWAIT);
816		if (mport == NULL) {
817			error = ENOMEM;
818			goto failed;
819		}
820
821		mport->mp_vid = mpd.port_vid;
822		mport->mp_num = mpd.port_num;
823
824		/* Update dinfo */
825		STAILQ_INSERT_TAIL(&cfg->master_ports, mport, mp_link);
826	}
827
828
829	/*
830	 * Determine whether this is a bridge device; if so, we can
831	 * expect the first sequence of address region descriptors to
832	 * be of EROM_REGION_TYPE_BRIDGE instead of
833	 * BCMA_EROM_REGION_TYPE_DEVICE.
834	 *
835	 * It's unclear whether this is the correct mechanism by which we
836	 * should detect/handle bridge devices, but this approach matches
837	 * that of (some of) Broadcom's published drivers.
838	 */
839	if (core.num_dport > 0) {
840		uint32_t entry;
841
842		if ((error = bcma_erom_peek32(erom, &entry)))
843			goto failed;
844
845		if (BCMA_EROM_ENTRY_IS(entry, REGION) &&
846		    BCMA_EROM_GET_ATTR(entry, REGION_TYPE) == BCMA_EROM_REGION_TYPE_BRIDGE)
847		{
848			first_region_type = BCMA_EROM_REGION_TYPE_BRIDGE;
849			cfg->num_dev_ports = 0;
850			cfg->num_bridge_ports = core.num_dport;
851		} else {
852			first_region_type = BCMA_EROM_REGION_TYPE_DEVICE;
853			cfg->num_dev_ports = core.num_dport;
854			cfg->num_bridge_ports = 0;
855		}
856	}
857
858	/* Device/bridge port descriptors */
859	for (uint8_t sp_num = 0; sp_num < core.num_dport; sp_num++) {
860		error = erom_corecfg_fill_port_regions(erom, cfg, sp_num,
861		    first_region_type);
862
863		if (error)
864			goto failed;
865	}
866
867	/* Wrapper (aka device management) descriptors (for master ports). */
868	for (uint8_t sp_num = 0; sp_num < core.num_mwrap; sp_num++) {
869		error = erom_corecfg_fill_port_regions(erom, cfg, sp_num,
870		    BCMA_EROM_REGION_TYPE_MWRAP);
871
872		if (error)
873			goto failed;
874	}
875
876
877	/* Wrapper (aka device management) descriptors (for slave ports). */
878	for (uint8_t i = 0; i < core.num_swrap; i++) {
879		/* Slave wrapper ports are not numbered distinctly from master
880		 * wrapper ports. */
881
882		/*
883		 * Broadcom DDR1/DDR2 Memory Controller
884		 * (cid=82e, rev=1, unit=0, d/mw/sw = 2/0/1 ) ->
885		 * bhnd0: erom[0xdc]: core6 agent0.0: mismatch got: 0x1 (0x2)
886		 *
887		 * ARM BP135 AMBA3 AXI to APB Bridge
888		 * (cid=135, rev=0, unit=0, d/mw/sw = 1/0/1 ) ->
889		 * bhnd0: erom[0x124]: core9 agent1.0: mismatch got: 0x0 (0x2)
890		 *
891		 * core.num_mwrap
892		 * ===>
893		 * (core.num_mwrap > 0) ?
894		 *           core.num_mwrap :
895		 *           ((core.vendor == BHND_MFGID_BCM) ? 1 : 0)
896		 */
897		uint8_t sp_num;
898		sp_num = (core.num_mwrap > 0) ?
899				core.num_mwrap :
900				((core.vendor == BHND_MFGID_BCM) ? 1 : 0) + i;
901		error = erom_corecfg_fill_port_regions(erom, cfg, sp_num,
902		    BCMA_EROM_REGION_TYPE_SWRAP);
903
904		if (error)
905			goto failed;
906	}
907
908	*result = cfg;
909	return (0);
910
911failed:
912	if (cfg != NULL)
913		bcma_free_corecfg(cfg);
914
915	return error;
916}
917