1296077Sadrian/*-
2296077Sadrian * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3296077Sadrian * All rights reserved.
4296077Sadrian *
5296077Sadrian * Redistribution and use in source and binary forms, with or without
6296077Sadrian * modification, are permitted provided that the following conditions
7296077Sadrian * are met:
8296077Sadrian * 1. Redistributions of source code must retain the above copyright
9296077Sadrian *    notice, this list of conditions and the following disclaimer,
10296077Sadrian *    without modification.
11296077Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12296077Sadrian *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13296077Sadrian *    redistribution must be conditioned upon including a substantially
14296077Sadrian *    similar Disclaimer requirement for further binary redistribution.
15296077Sadrian *
16296077Sadrian * NO WARRANTY
17296077Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18296077Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19296077Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20296077Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21296077Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22296077Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23296077Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24296077Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25296077Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26296077Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27296077Sadrian * THE POSSIBILITY OF SUCH DAMAGES.
28296077Sadrian */
29296077Sadrian
30296077Sadrian#include <sys/cdefs.h>
31296077Sadrian__FBSDID("$FreeBSD: releng/11.0/sys/dev/bhnd/bcma/bcma_erom.c 299314 2016-05-10 04:55:57Z adrian $");
32296077Sadrian
33296077Sadrian#include <sys/param.h>
34296077Sadrian#include <sys/bus.h>
35296077Sadrian#include <sys/kernel.h>
36296077Sadrian#include <sys/limits.h>
37296077Sadrian#include <sys/systm.h>
38296077Sadrian
39296077Sadrian#include <machine/bus.h>
40296077Sadrian#include <machine/resource.h>
41296077Sadrian
42296077Sadrian#include "bcma_eromreg.h"
43296077Sadrian#include "bcma_eromvar.h"
44296077Sadrian
45296077Sadrian/*
46296077Sadrian * BCMA Enumeration ROM (EROM) Table
47296077Sadrian *
48296077Sadrian * Provides auto-discovery of BCMA cores on Broadcom's HND SoC.
49296077Sadrian *
50296077Sadrian * The EROM core address can be found at BCMA_CC_EROM_ADDR within the
51296077Sadrian * ChipCommon registers. The table itself is comprised of 32-bit
52296077Sadrian * type-tagged entries, organized into an array of variable-length
53296077Sadrian * core descriptor records.
54296077Sadrian *
55296077Sadrian * The final core descriptor is followed by a 32-bit BCMA_EROM_TABLE_EOF (0xF)
56296077Sadrian * marker.
57296077Sadrian */
58296077Sadrian
59296077Sadrianstatic const char	*erom_entry_type_name (uint8_t entry);
60296077Sadrianstatic int		 erom_read32(struct bcma_erom *erom, uint32_t *entry);
61296077Sadrianstatic int		 erom_skip32(struct bcma_erom *erom);
62296077Sadrian
63296077Sadrianstatic int		 erom_skip_core(struct bcma_erom *erom);
64296077Sadrianstatic int		 erom_skip_mport(struct bcma_erom *erom);
65296077Sadrianstatic int		 erom_skip_sport_region(struct bcma_erom *erom);
66296077Sadrian
67296077Sadrianstatic int		 erom_seek_next(struct bcma_erom *erom, uint8_t etype);
68296077Sadrian
69296077Sadrian#define	EROM_LOG(erom, fmt, ...)	\
70296077Sadrian	device_printf(erom->dev, "erom[0x%llx]: " fmt, \
71296077Sadrian	    (unsigned long long) (erom->offset), ##__VA_ARGS__);
72296077Sadrian
73296077Sadrian/**
74296077Sadrian * Open an EROM table for reading.
75296077Sadrian *
76296077Sadrian * @param[out] erom On success, will be populated with a valid EROM
77296077Sadrian * read state.
78296077Sadrian * @param r An active resource mapping the EROM core.
79296077Sadrian * @param offset Offset of the EROM core within @p resource.
80296077Sadrian *
81296077Sadrian * @retval 0 success
82296077Sadrian * @retval non-zero if the erom table could not be opened.
83296077Sadrian */
84296077Sadrianint
85296077Sadrianbcma_erom_open(struct bcma_erom *erom, struct resource *r, bus_size_t offset)
86296077Sadrian{
87296077Sadrian	/* Initialize the EROM reader */
88296077Sadrian	erom->dev = rman_get_device(r);
89296077Sadrian	erom->r = r;
90296077Sadrian	erom->start = offset + BCMA_EROM_TABLE_START;
91296077Sadrian	erom->offset = 0;
92296077Sadrian
93296077Sadrian	return (0);
94296077Sadrian}
95296077Sadrian
96296077Sadrian/** Return the type name for an EROM entry */
97296077Sadrianstatic const char *
98296077Sadrianerom_entry_type_name (uint8_t entry)
99296077Sadrian{
100296077Sadrian	switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
101296077Sadrian	case BCMA_EROM_ENTRY_TYPE_CORE:
102296077Sadrian		return "core";
103296077Sadrian	case BCMA_EROM_ENTRY_TYPE_MPORT:
104296077Sadrian		return "mport";
105296077Sadrian	case BCMA_EROM_ENTRY_TYPE_REGION:
106296077Sadrian		return "region";
107296077Sadrian	default:
108296077Sadrian		return "unknown";
109296077Sadrian	}
110296077Sadrian}
111296077Sadrian
112296077Sadrian/**
113296077Sadrian * Return the current read position.
114296077Sadrian */
115296077Sadrianbus_size_t
116296077Sadrianbcma_erom_tell(struct bcma_erom *erom)
117296077Sadrian{
118296077Sadrian	return (erom->offset);
119296077Sadrian}
120296077Sadrian
121296077Sadrian/**
122296077Sadrian * Seek to an absolute read position.
123296077Sadrian */
124296077Sadrianvoid
125296077Sadrianbcma_erom_seek(struct bcma_erom *erom, bus_size_t offset)
126296077Sadrian{
127296077Sadrian	erom->offset = offset;
128296077Sadrian}
129296077Sadrian
130296077Sadrian/**
131296077Sadrian * Read a 32-bit entry value from the EROM table without advancing the
132296077Sadrian * read position.
133296077Sadrian *
134296077Sadrian * @param erom EROM read state.
135296077Sadrian * @param entry Will contain the read result on success.
136296077Sadrian * @retval 0 success
137296077Sadrian * @retval ENOENT The end of the EROM table was reached.
138296077Sadrian * @retval non-zero The read could not be completed.
139296077Sadrian */
140296077Sadrianint
141296077Sadrianbcma_erom_peek32(struct bcma_erom *erom, uint32_t *entry)
142296077Sadrian{
143296077Sadrian	if (erom->offset >= BCMA_EROM_TABLE_SIZE) {
144296077Sadrian		EROM_LOG(erom, "BCMA EROM table missing terminating EOF\n");
145296077Sadrian		return (EINVAL);
146296077Sadrian	}
147296077Sadrian
148296077Sadrian	*entry = bus_read_4(erom->r, erom->start + erom->offset);
149296077Sadrian	return (0);
150296077Sadrian}
151296077Sadrian
152296077Sadrian/**
153296077Sadrian * Read a 32-bit entry value from the EROM table.
154296077Sadrian *
155296077Sadrian * @param erom EROM read state.
156296077Sadrian * @param entry Will contain the read result on success.
157296077Sadrian * @retval 0 success
158296077Sadrian * @retval ENOENT The end of the EROM table was reached.
159296077Sadrian * @retval non-zero The read could not be completed.
160296077Sadrian */
161296077Sadrianstatic int
162296077Sadrianerom_read32(struct bcma_erom *erom, uint32_t *entry)
163296077Sadrian{
164296077Sadrian	int error;
165296077Sadrian
166296077Sadrian	if ((error = bcma_erom_peek32(erom, entry)) == 0)
167296077Sadrian		erom->offset += 4;
168296077Sadrian
169296077Sadrian	return (error);
170296077Sadrian}
171296077Sadrian
172296077Sadrian/**
173296077Sadrian * Read and discard 32-bit entry value from the EROM table.
174296077Sadrian *
175296077Sadrian * @param erom EROM read state.
176296077Sadrian * @retval 0 success
177296077Sadrian * @retval ENOENT The end of the EROM table was reached.
178296077Sadrian * @retval non-zero The read could not be completed.
179296077Sadrian */
180296077Sadrianstatic int
181296077Sadrianerom_skip32(struct bcma_erom *erom)
182296077Sadrian{
183296077Sadrian	uint32_t	entry;
184296077Sadrian
185296077Sadrian	return erom_read32(erom, &entry);
186296077Sadrian}
187296077Sadrian
188296077Sadrian/**
189296077Sadrian * Read and discard a core descriptor from the EROM table.
190296077Sadrian *
191296077Sadrian * @param erom EROM read state.
192296077Sadrian * @retval 0 success
193296077Sadrian * @retval ENOENT The end of the EROM table was reached.
194296077Sadrian * @retval non-zero The read could not be completed.
195296077Sadrian */
196296077Sadrianstatic int
197296077Sadrianerom_skip_core(struct bcma_erom *erom)
198296077Sadrian{
199296077Sadrian	struct bcma_erom_core core;
200296077Sadrian	return (bcma_erom_parse_core(erom, &core));
201296077Sadrian}
202296077Sadrian
203296077Sadrian/**
204296077Sadrian * Read and discard a master port descriptor from the EROM table.
205296077Sadrian *
206296077Sadrian * @param erom EROM read state.
207296077Sadrian * @retval 0 success
208296077Sadrian * @retval ENOENT The end of the EROM table was reached.
209296077Sadrian * @retval non-zero The read could not be completed.
210296077Sadrian */
211296077Sadrianstatic int
212296077Sadrianerom_skip_mport(struct bcma_erom *erom)
213296077Sadrian{
214296077Sadrian	struct bcma_erom_mport mp;
215296077Sadrian	return (bcma_erom_parse_mport(erom, &mp));
216296077Sadrian}
217296077Sadrian
218296077Sadrian/**
219296077Sadrian * Read and discard a port region descriptor from the EROM table.
220296077Sadrian *
221296077Sadrian * @param erom EROM read state.
222296077Sadrian * @retval 0 success
223296077Sadrian * @retval ENOENT The end of the EROM table was reached.
224296077Sadrian * @retval non-zero The read could not be completed.
225296077Sadrian */
226296077Sadrianstatic int
227296077Sadrianerom_skip_sport_region(struct bcma_erom *erom)
228296077Sadrian{
229296077Sadrian	struct bcma_erom_sport_region r;
230296077Sadrian	return (bcma_erom_parse_sport_region(erom, &r));
231296077Sadrian}
232296077Sadrian
233296077Sadrian/**
234296077Sadrian * Seek to the next entry matching the given EROM entry type.
235296077Sadrian *
236296077Sadrian * @param erom EROM read state.
237296077Sadrian * @param etype  One of BCMA_EROM_ENTRY_TYPE_CORE,
238296077Sadrian * BCMA_EROM_ENTRY_TYPE_MPORT, or BCMA_EROM_ENTRY_TYPE_REGION.
239296077Sadrian * @retval 0 success
240296077Sadrian * @retval ENOENT The end of the EROM table was reached.
241296077Sadrian * @retval non-zero Reading or parsing the descriptor failed.
242296077Sadrian */
243296077Sadrianstatic int
244296077Sadrianerom_seek_next(struct bcma_erom *erom, uint8_t etype)
245296077Sadrian{
246296077Sadrian	uint32_t			entry;
247296077Sadrian	int				error;
248296077Sadrian
249296077Sadrian	/* Iterate until we hit an entry matching the requested type. */
250296077Sadrian	while (!(error = bcma_erom_peek32(erom, &entry))) {
251296077Sadrian		/* Handle EOF */
252296077Sadrian		if (entry == BCMA_EROM_TABLE_EOF)
253296077Sadrian			return (ENOENT);
254296077Sadrian
255296077Sadrian		/* Invalid entry */
256296077Sadrian		if (!BCMA_EROM_GET_ATTR(entry, ENTRY_ISVALID))
257296077Sadrian			return (EINVAL);
258296077Sadrian
259296077Sadrian		/* Entry type matches? */
260296077Sadrian		if (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE) == etype)
261296077Sadrian			return (0);
262296077Sadrian
263296077Sadrian		/* Skip non-matching entry types. */
264296077Sadrian		switch (BCMA_EROM_GET_ATTR(entry, ENTRY_TYPE)) {
265296077Sadrian		case BCMA_EROM_ENTRY_TYPE_CORE:
266296077Sadrian			if ((error = erom_skip_core(erom)))
267296077Sadrian				return (error);
268296077Sadrian
269296077Sadrian			break;
270296077Sadrian
271296077Sadrian		case BCMA_EROM_ENTRY_TYPE_MPORT:
272296077Sadrian			if ((error = erom_skip_mport(erom)))
273296077Sadrian				return (error);
274296077Sadrian
275296077Sadrian			break;
276296077Sadrian
277296077Sadrian		case BCMA_EROM_ENTRY_TYPE_REGION:
278296077Sadrian			if ((error = erom_skip_sport_region(erom)))
279296077Sadrian				return (error);
280296077Sadrian			break;
281296077Sadrian
282296077Sadrian		default:
283296077Sadrian			/* Unknown entry type! */
284296077Sadrian			return (EINVAL);
285296077Sadrian		}
286296077Sadrian	}
287296077Sadrian
288296077Sadrian	return (error);
289296077Sadrian}
290296077Sadrian
291296077Sadrian/**
292296077Sadrian * Return the read position to the start of the EROM table.
293296077Sadrian *
294296077Sadrian * @param erom EROM read state.
295296077Sadrian */
296296077Sadrianvoid
297296077Sadrianbcma_erom_reset(struct bcma_erom *erom)
298296077Sadrian{
299296077Sadrian	erom->offset = 0;
300296077Sadrian}
301296077Sadrian
302296077Sadrian/**
303296077Sadrian * Seek to the requested core entry.
304296077Sadrian *
305296077Sadrian * @param erom EROM read state.
306296077Sadrian * @param core_index Index of the core to seek to.
307296077Sadrian * @retval 0 success
308296077Sadrian * @retval ENOENT The end of the EROM table was reached before @p index was
309296077Sadrian * found.
310296077Sadrian * @retval non-zero Reading or parsing failed.
311296077Sadrian */
312296077Sadrianint
313296077Sadrianbcma_erom_seek_core_index(struct bcma_erom *erom, u_int core_index)
314296077Sadrian{
315296077Sadrian	int error;
316296077Sadrian
317296077Sadrian	/* Start search at top of EROM */
318296077Sadrian	bcma_erom_reset(erom);
319296077Sadrian
320296077Sadrian	/* Skip core descriptors till we hit the requested entry */
321296077Sadrian	for (u_int i = 0; i < core_index; i++) {
322296077Sadrian		struct bcma_erom_core core;
323296077Sadrian
324296077Sadrian		/* Read past the core descriptor */
325296077Sadrian		if ((error = bcma_erom_parse_core(erom, &core)))
326296077Sadrian			return (error);
327296077Sadrian
328296077Sadrian		/* Seek to the next readable core entry */
329296077Sadrian		error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
330296077Sadrian		if (error)
331296077Sadrian			return (error);
332296077Sadrian	}
333296077Sadrian
334296077Sadrian	return (0);
335296077Sadrian}
336296077Sadrian
337296077Sadrian
338296077Sadrian/**
339296077Sadrian * Read the next core descriptor from the EROM table.
340296077Sadrian *
341296077Sadrian * @param erom EROM read state.
342296077Sadrian * @param[out] core On success, will be populated with the parsed core
343296077Sadrian * descriptor data.
344296077Sadrian * @retval 0 success
345296077Sadrian * @retval ENOENT The end of the EROM table was reached.
346296077Sadrian * @retval non-zero Reading or parsing the core descriptor failed.
347296077Sadrian */
348296077Sadrianint
349296077Sadrianbcma_erom_parse_core(struct bcma_erom *erom, struct bcma_erom_core *core)
350296077Sadrian{
351296077Sadrian	uint32_t	entry;
352296077Sadrian	int		error;
353296077Sadrian
354296077Sadrian	/* Parse CoreDescA */
355296077Sadrian	if ((error = erom_read32(erom, &entry)))
356296077Sadrian		return (error);
357296077Sadrian
358296077Sadrian	/* Handle EOF */
359296077Sadrian	if (entry == BCMA_EROM_TABLE_EOF)
360296077Sadrian		return (ENOENT);
361296077Sadrian
362296077Sadrian	if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
363296077Sadrian		EROM_LOG(erom, "Unexpected EROM entry 0x%x (type=%s)\n",
364296077Sadrian                   entry, erom_entry_type_name(entry));
365296077Sadrian
366296077Sadrian		return (EINVAL);
367296077Sadrian	}
368296077Sadrian
369296077Sadrian	core->vendor = BCMA_EROM_GET_ATTR(entry, COREA_DESIGNER);
370296077Sadrian	core->device = BCMA_EROM_GET_ATTR(entry, COREA_ID);
371296077Sadrian
372296077Sadrian	/* Parse CoreDescB */
373296077Sadrian	if ((error = erom_read32(erom, &entry)))
374296077Sadrian		return (error);
375296077Sadrian
376296077Sadrian	if (!BCMA_EROM_ENTRY_IS(entry, CORE)) {
377296077Sadrian		return (EINVAL);
378296077Sadrian	}
379296077Sadrian
380296077Sadrian	core->rev = BCMA_EROM_GET_ATTR(entry, COREB_REV);
381296077Sadrian	core->num_mport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_MP);
382296077Sadrian	core->num_dport = BCMA_EROM_GET_ATTR(entry, COREB_NUM_DP);
383296077Sadrian	core->num_mwrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WMP);
384296077Sadrian	core->num_swrap = BCMA_EROM_GET_ATTR(entry, COREB_NUM_WSP);
385296077Sadrian
386296077Sadrian	return (0);
387296077Sadrian}
388296077Sadrian
389296077Sadrian/**
390296077Sadrian * Read the next master port descriptor from the EROM table.
391296077Sadrian *
392296077Sadrian * @param erom EROM read state.
393296077Sadrian * @param[out] mport On success, will be populated with the parsed
394296077Sadrian * descriptor data.
395296077Sadrian * @retval 0 success
396296077Sadrian * @retval non-zero Reading or parsing the descriptor failed.
397296077Sadrian */
398296077Sadrianint
399296077Sadrianbcma_erom_parse_mport(struct bcma_erom *erom,
400296077Sadrian    struct bcma_erom_mport *mport)
401296077Sadrian{
402296077Sadrian	uint32_t	entry;
403296077Sadrian	int		error;
404296077Sadrian
405296077Sadrian	/* Parse the master port descriptor */
406296077Sadrian	if ((error = erom_read32(erom, &entry)))
407296077Sadrian		return (error);
408296077Sadrian
409296077Sadrian	if (!BCMA_EROM_ENTRY_IS(entry, MPORT))
410296077Sadrian		return (EINVAL);
411296077Sadrian
412296077Sadrian	mport->port_vid = BCMA_EROM_GET_ATTR(entry, MPORT_ID);
413296077Sadrian	mport->port_num = BCMA_EROM_GET_ATTR(entry, MPORT_NUM);
414296077Sadrian
415296077Sadrian	return (0);
416296077Sadrian}
417296077Sadrian
418296077Sadrian/**
419296077Sadrian * Read the next slave port region descriptor from the EROM table.
420296077Sadrian *
421296077Sadrian * @param erom EROM read state.
422296077Sadrian * @param[out] mport On success, will be populated with the parsed
423296077Sadrian * descriptor data.
424296077Sadrian * @retval 0 success
425296077Sadrian * @retval ENOENT The end of the region descriptor table was reached.
426296077Sadrian * @retval non-zero Reading or parsing the descriptor failed.
427296077Sadrian */
428296077Sadrianint
429296077Sadrianbcma_erom_parse_sport_region(struct bcma_erom *erom,
430296077Sadrian    struct bcma_erom_sport_region *region)
431296077Sadrian{
432296077Sadrian	uint32_t	entry;
433296077Sadrian	uint8_t		size_type;
434296077Sadrian	int		error;
435296077Sadrian
436296077Sadrian	/* Peek at the region descriptor */
437296077Sadrian	if (bcma_erom_peek32(erom, &entry))
438296077Sadrian		return (EINVAL);
439296077Sadrian
440296077Sadrian	/* A non-region entry signals the end of the region table */
441296077Sadrian	if (!BCMA_EROM_ENTRY_IS(entry, REGION)) {
442296077Sadrian		return (ENOENT);
443296077Sadrian	} else {
444296077Sadrian		erom_skip32(erom);
445296077Sadrian	}
446296077Sadrian
447296077Sadrian	region->base_addr = BCMA_EROM_GET_ATTR(entry, REGION_BASE);
448296077Sadrian	region->region_type = BCMA_EROM_GET_ATTR(entry, REGION_TYPE);
449296077Sadrian	region->region_port = BCMA_EROM_GET_ATTR(entry, REGION_PORT);
450296077Sadrian	size_type = BCMA_EROM_GET_ATTR(entry, REGION_SIZE);
451296077Sadrian
452296077Sadrian	/* If region address is 64-bit, fetch the high bits. */
453296077Sadrian	if (BCMA_EROM_GET_ATTR(entry, REGION_64BIT)) {
454296077Sadrian		if ((error = erom_read32(erom, &entry)))
455296077Sadrian			return (error);
456296077Sadrian
457296077Sadrian		region->base_addr |= ((bhnd_addr_t) entry << 32);
458296077Sadrian	}
459296077Sadrian
460296077Sadrian	/* Parse the region size; it's either encoded as the binary logarithm
461296077Sadrian	 * of the number of 4K pages (i.e. log2 n), or its encoded as a
462296077Sadrian	 * 32-bit/64-bit literal value directly following the current entry. */
463296077Sadrian	if (size_type == BCMA_EROM_REGION_SIZE_OTHER) {
464296077Sadrian		if ((error = erom_read32(erom, &entry)))
465296077Sadrian			return (error);
466296077Sadrian
467296077Sadrian		region->size = BCMA_EROM_GET_ATTR(entry, RSIZE_VAL);
468296077Sadrian
469296077Sadrian		if (BCMA_EROM_GET_ATTR(entry, RSIZE_64BIT)) {
470296077Sadrian			if ((error = erom_read32(erom, &entry)))
471296077Sadrian				return (error);
472296077Sadrian			region->size |= ((bhnd_size_t) entry << 32);
473296077Sadrian		}
474296077Sadrian	} else {
475296077Sadrian		region->size = BCMA_EROM_REGION_SIZE_BASE << size_type;
476296077Sadrian	}
477296077Sadrian
478296077Sadrian	/* Verify that addr+size does not overflow. */
479296077Sadrian	if (region->size != 0 &&
480296077Sadrian	    BHND_ADDR_MAX - (region->size - 1) < region->base_addr)
481296077Sadrian	{
482296077Sadrian		EROM_LOG(erom, "%s%u: invalid address map %llx:%llx\n",
483296077Sadrian		    erom_entry_type_name(region->region_type),
484296077Sadrian		    region->region_port,
485296077Sadrian		    (unsigned long long) region->base_addr,
486296077Sadrian		    (unsigned long long) region->size);
487296077Sadrian
488296077Sadrian		return (EINVAL);
489296077Sadrian	}
490296077Sadrian
491296077Sadrian	return (0);
492296077Sadrian}
493296077Sadrian
494296077Sadrian/**
495296077Sadrian * Parse all cores descriptors from @p erom and return the array
496296077Sadrian * in @p cores and the count in @p num_cores. The current EROM read position
497296077Sadrian * is left unmodified.
498296077Sadrian *
499296077Sadrian * The memory allocated for the table should be freed using
500296077Sadrian * `free(*cores, M_BHND)`. @p cores and @p num_cores are not changed
501296077Sadrian * when an error is returned.
502296077Sadrian *
503296077Sadrian * @param erom EROM read state.
504296077Sadrian * @param[out] cores the table of parsed core descriptors.
505296077Sadrian * @param[out] num_cores the number of core records in @p cores.
506296077Sadrian */
507296077Sadrianint
508296077Sadrianbcma_erom_get_core_info(struct bcma_erom *erom,
509296077Sadrian    struct bhnd_core_info **cores,
510296077Sadrian    u_int *num_cores)
511296077Sadrian{
512296077Sadrian	struct bhnd_core_info	*buffer;
513296077Sadrian	bus_size_t		 initial_offset;
514296077Sadrian	u_int			 count;
515296077Sadrian	int			 error;
516296077Sadrian
517296077Sadrian	buffer = NULL;
518296077Sadrian	initial_offset = bcma_erom_tell(erom);
519296077Sadrian
520296077Sadrian	/* Determine the core count */
521296077Sadrian	bcma_erom_reset(erom);
522296077Sadrian	for (count = 0, error = 0; !error; count++) {
523296077Sadrian		struct bcma_erom_core core;
524296077Sadrian
525296077Sadrian		/* Seek to the first readable core entry */
526296077Sadrian		error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
527296077Sadrian		if (error == ENOENT)
528296077Sadrian			break;
529296077Sadrian		else if (error)
530296077Sadrian			goto cleanup;
531296077Sadrian
532296077Sadrian		/* Read past the core descriptor */
533296077Sadrian		if ((error = bcma_erom_parse_core(erom, &core)))
534296077Sadrian			goto cleanup;
535296077Sadrian	}
536296077Sadrian
537296077Sadrian	/* Allocate our output buffer */
538296077Sadrian	buffer = malloc(sizeof(struct bhnd_core_info) * count, M_BHND,
539296077Sadrian	    M_NOWAIT);
540296077Sadrian	if (buffer == NULL) {
541296077Sadrian		error = ENOMEM;
542296077Sadrian		goto cleanup;
543296077Sadrian	}
544296077Sadrian
545296077Sadrian	/* Parse all core descriptors */
546296077Sadrian	bcma_erom_reset(erom);
547296077Sadrian	for (u_int i = 0; i < count; i++) {
548296077Sadrian		struct bcma_erom_core core;
549296077Sadrian
550296077Sadrian		/* Parse the core */
551296077Sadrian		error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE);
552296077Sadrian		if (error)
553296077Sadrian			goto cleanup;
554296077Sadrian
555296077Sadrian		error = bcma_erom_parse_core(erom, &core);
556296077Sadrian		if (error)
557296077Sadrian			goto cleanup;
558296077Sadrian
559296077Sadrian		/* Convert to a bhnd info record */
560296077Sadrian		buffer[i].vendor = core.vendor;
561296077Sadrian		buffer[i].device = core.device;
562296077Sadrian		buffer[i].hwrev = core.rev;
563296077Sadrian		buffer[i].core_idx = i;
564296077Sadrian		buffer[i].unit = 0;
565296077Sadrian
566296077Sadrian		/* Determine the unit number */
567296077Sadrian		for (u_int j = 0; j < i; j++) {
568296077Sadrian			if (buffer[i].vendor == buffer[j].vendor &&
569296077Sadrian			    buffer[i].device == buffer[j].device)
570297793Spfg				buffer[i].unit++;
571296077Sadrian		}
572296077Sadrian	}
573296077Sadrian
574296077Sadriancleanup:
575296077Sadrian	if (!error) {
576296077Sadrian		*cores = buffer;
577296077Sadrian		*num_cores = count;
578296077Sadrian	} else {
579296077Sadrian		if (buffer != NULL)
580296077Sadrian			free(buffer, M_BHND);
581296077Sadrian	}
582296077Sadrian
583296077Sadrian	/* Restore the initial position */
584296077Sadrian	bcma_erom_seek(erom, initial_offset);
585296077Sadrian	return (error);
586296077Sadrian}
587296077Sadrian
588296077Sadrian
589296077Sadrian/**
590296077Sadrian * Register all MMIO region descriptors for the given slave port.
591296077Sadrian *
592296077Sadrian * @param erom EROM read state.
593296077Sadrian * @param corecfg Core info to be populated with the scanned port regions.
594296077Sadrian * @param port_num Port index for which regions will be parsed.
595296077Sadrian * @param region_type The region type to be parsed.
596296077Sadrian * @param[out] offset The offset at which to perform parsing. On success, this
597296077Sadrian * will be updated to point to the next EROM table entry.
598296077Sadrian */
599296077Sadrianstatic int
600296077Sadrianerom_corecfg_fill_port_regions(struct bcma_erom *erom,
601296077Sadrian    struct bcma_corecfg *corecfg, bcma_pid_t port_num,
602296077Sadrian    uint8_t region_type)
603296077Sadrian{
604296077Sadrian	struct bcma_sport	*sport;
605296077Sadrian	struct bcma_sport_list	*sports;
606296077Sadrian	bus_size_t		 entry_offset;
607296077Sadrian	int			 error;
608296077Sadrian	bhnd_port_type		 port_type;
609296077Sadrian
610296077Sadrian	error = 0;
611296077Sadrian
612296077Sadrian	/* Determine the port type for this region type. */
613296077Sadrian	switch (region_type) {
614296077Sadrian		case BCMA_EROM_REGION_TYPE_DEVICE:
615296077Sadrian			port_type = BHND_PORT_DEVICE;
616296077Sadrian			break;
617296077Sadrian		case BCMA_EROM_REGION_TYPE_BRIDGE:
618296077Sadrian			port_type = BHND_PORT_BRIDGE;
619296077Sadrian			break;
620296077Sadrian		case BCMA_EROM_REGION_TYPE_MWRAP:
621296077Sadrian		case BCMA_EROM_REGION_TYPE_SWRAP:
622296077Sadrian			port_type = BHND_PORT_AGENT;
623296077Sadrian			break;
624296077Sadrian		default:
625296077Sadrian			EROM_LOG(erom, "unsupported region type %hhx\n",
626296077Sadrian			    region_type);
627296077Sadrian			return (EINVAL);
628297793Spfg	}
629296077Sadrian
630296077Sadrian	/* Fetch the list to be populated */
631296077Sadrian	sports = bcma_corecfg_get_port_list(corecfg, port_type);
632296077Sadrian
633296077Sadrian	/* Allocate a new port descriptor */
634296077Sadrian	sport = bcma_alloc_sport(port_num, port_type);
635296077Sadrian	if (sport == NULL)
636296077Sadrian		return (ENOMEM);
637296077Sadrian
638296077Sadrian	/* Read all address regions defined for this port */
639296077Sadrian	for (bcma_rmid_t region_num = 0;; region_num++) {
640296077Sadrian		struct bcma_map			*map;
641296077Sadrian		struct bcma_erom_sport_region	 spr;
642296077Sadrian
643296077Sadrian		/* No valid port definition should come anywhere near
644296077Sadrian		 * BCMA_RMID_MAX. */
645296077Sadrian		if (region_num == BCMA_RMID_MAX) {
646296077Sadrian			EROM_LOG(erom, "core%u %s%u: region count reached "
647296077Sadrian			    "upper limit of %u\n",
648296077Sadrian			    corecfg->core_info.core_idx,
649296077Sadrian			    bhnd_port_type_name(port_type),
650296077Sadrian			    port_num, BCMA_RMID_MAX);
651296077Sadrian
652296077Sadrian			error = EINVAL;
653296077Sadrian			goto cleanup;
654296077Sadrian		}
655296077Sadrian
656296077Sadrian		/* Parse the next region entry. */
657296077Sadrian		entry_offset = bcma_erom_tell(erom);
658296077Sadrian		error = bcma_erom_parse_sport_region(erom, &spr);
659296077Sadrian		if (error && error != ENOENT) {
660296077Sadrian			EROM_LOG(erom, "core%u %s%u.%u: invalid slave port "
661296077Sadrian			    "address region\n",
662296077Sadrian			    corecfg->core_info.core_idx,
663296077Sadrian			    bhnd_port_type_name(port_type),
664296077Sadrian			    port_num, region_num);
665296077Sadrian			goto cleanup;
666296077Sadrian		}
667296077Sadrian
668296077Sadrian		/* ENOENT signals no further region entries */
669296077Sadrian		if (error == ENOENT) {
670296077Sadrian			/* No further entries */
671296077Sadrian			error = 0;
672296077Sadrian			break;
673296077Sadrian		}
674296077Sadrian
675296077Sadrian		/* A region or type mismatch also signals no further region
676296077Sadrian		 * entries */
677296077Sadrian		if (spr.region_port != port_num ||
678296077Sadrian		    spr.region_type != region_type)
679296077Sadrian		{
680296077Sadrian			/* We don't want to consume this entry */
681296077Sadrian			bcma_erom_seek(erom, entry_offset);
682296077Sadrian
683296077Sadrian			error = 0;
684296077Sadrian			goto cleanup;
685296077Sadrian		}
686296077Sadrian
687296077Sadrian		/*
688296077Sadrian		 * Create the map entry.
689296077Sadrian		 */
690296077Sadrian		map = malloc(sizeof(struct bcma_map), M_BHND, M_NOWAIT);
691296077Sadrian		if (map == NULL) {
692296077Sadrian			error = ENOMEM;
693296077Sadrian			goto cleanup;
694296077Sadrian		}
695296077Sadrian
696296077Sadrian		map->m_region_num = region_num;
697296077Sadrian		map->m_base = spr.base_addr;
698296077Sadrian		map->m_size = spr.size;
699296077Sadrian		map->m_rid = -1;
700296077Sadrian
701296077Sadrian		/* Add the region map to the port */
702296077Sadrian		STAILQ_INSERT_TAIL(&sport->sp_maps, map, m_link);
703296077Sadrian		sport->sp_num_maps++;
704296077Sadrian	}
705296077Sadrian
706296077Sadriancleanup:
707296077Sadrian	/* Append the new port descriptor on success, or deallocate the
708296077Sadrian	 * partially parsed descriptor on failure. */
709296077Sadrian	if (error == 0) {
710296077Sadrian		STAILQ_INSERT_TAIL(sports, sport, sp_link);
711296077Sadrian	} else if (sport != NULL) {
712296077Sadrian		bcma_free_sport(sport);
713296077Sadrian	}
714296077Sadrian
715296077Sadrian	return error;
716296077Sadrian}
717296077Sadrian
718296077Sadrian/**
719296077Sadrian * Parse the next core entry from the EROM table and produce a bcma_corecfg
720296077Sadrian * to be owned by the caller.
721296077Sadrian *
722296077Sadrian * @param erom EROM read state.
723296077Sadrian * @param[out] result On success, the core's device info. The caller inherits
724296077Sadrian * ownership of this allocation.
725296077Sadrian *
726296077Sadrian * @return If successful, returns 0. If the end of the EROM table is hit,
727296077Sadrian * ENOENT will be returned. On error, returns a non-zero error value.
728296077Sadrian */
729296077Sadrianint
730296077Sadrianbcma_erom_parse_corecfg(struct bcma_erom *erom, struct bcma_corecfg **result)
731296077Sadrian{
732296077Sadrian	struct bcma_corecfg	*cfg;
733296077Sadrian	struct bcma_erom_core	 core;
734296077Sadrian	uint8_t			 first_region_type;
735296077Sadrian	bus_size_t		 initial_offset;
736296077Sadrian	u_int			 core_index;
737296077Sadrian	int			 core_unit;
738296077Sadrian	int			 error;
739296077Sadrian
740296077Sadrian	cfg = NULL;
741296077Sadrian	initial_offset = bcma_erom_tell(erom);
742296077Sadrian
743296077Sadrian	/* Parse the next core entry */
744296077Sadrian	if ((error = bcma_erom_parse_core(erom, &core)))
745296077Sadrian		return (error);
746296077Sadrian
747296077Sadrian	/* Determine the core's index and unit numbers */
748296077Sadrian	bcma_erom_reset(erom);
749296077Sadrian	core_unit = 0;
750296077Sadrian	core_index = 0;
751296077Sadrian	for (; bcma_erom_tell(erom) != initial_offset; core_index++) {
752296077Sadrian		struct bcma_erom_core prev_core;
753296077Sadrian
754296077Sadrian		/* Parse next core */
755296077Sadrian		if ((error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE)))
756296077Sadrian			return (error);
757296077Sadrian
758296077Sadrian		if ((error = bcma_erom_parse_core(erom, &prev_core)))
759296077Sadrian			return (error);
760296077Sadrian
761296077Sadrian		/* Is earlier unit? */
762296077Sadrian		if (core.vendor == prev_core.vendor &&
763296077Sadrian		    core.device == prev_core.device)
764296077Sadrian		{
765296077Sadrian			core_unit++;
766296077Sadrian		}
767296077Sadrian
768296077Sadrian		/* Seek to next core */
769296077Sadrian		if ((error = erom_seek_next(erom, BCMA_EROM_ENTRY_TYPE_CORE)))
770296077Sadrian			return (error);
771296077Sadrian	}
772296077Sadrian
773296077Sadrian	/* We already parsed the core descriptor */
774296077Sadrian	if ((error = erom_skip_core(erom)))
775296077Sadrian		return (error);
776296077Sadrian
777296077Sadrian	/* Allocate our corecfg */
778296077Sadrian	cfg = bcma_alloc_corecfg(core_index, core_unit, core.vendor,
779296077Sadrian	    core.device, core.rev);
780296077Sadrian	if (cfg == NULL)
781296077Sadrian		return (ENOMEM);
782296077Sadrian
783296077Sadrian	/* These are 5-bit values in the EROM table, and should never be able
784296077Sadrian	 * to overflow BCMA_PID_MAX. */
785296077Sadrian	KASSERT(core.num_mport <= BCMA_PID_MAX, ("unsupported mport count"));
786296077Sadrian	KASSERT(core.num_dport <= BCMA_PID_MAX, ("unsupported dport count"));
787296077Sadrian	KASSERT(core.num_mwrap + core.num_swrap <= BCMA_PID_MAX,
788296077Sadrian	    ("unsupported wport count"));
789296077Sadrian
790296077Sadrian	if (bootverbose) {
791296077Sadrian		EROM_LOG(erom,
792296077Sadrian		    "core%u: %s %s (cid=%hx, rev=%hu, unit=%d)\n",
793296077Sadrian		    core_index,
794296077Sadrian		    bhnd_vendor_name(core.vendor),
795296077Sadrian		    bhnd_find_core_name(core.vendor, core.device),
796296077Sadrian		    core.device, core.rev, core_unit);
797296077Sadrian	}
798296077Sadrian
799296077Sadrian	cfg->num_master_ports = core.num_mport;
800296077Sadrian	cfg->num_dev_ports = 0;		/* determined below */
801296077Sadrian	cfg->num_bridge_ports = 0;	/* determined blow */
802296077Sadrian	cfg->num_wrapper_ports = core.num_mwrap + core.num_swrap;
803296077Sadrian
804296077Sadrian	/* Parse Master Port Descriptors */
805296077Sadrian	for (uint8_t i = 0; i < core.num_mport; i++) {
806296077Sadrian		struct bcma_mport	*mport;
807296077Sadrian		struct bcma_erom_mport	 mpd;
808296077Sadrian
809296077Sadrian		/* Parse the master port descriptor */
810296077Sadrian		error = bcma_erom_parse_mport(erom, &mpd);
811296077Sadrian		if (error)
812296077Sadrian			goto failed;
813296077Sadrian
814296077Sadrian		/* Initialize a new bus mport structure */
815296077Sadrian		mport = malloc(sizeof(struct bcma_mport), M_BHND, M_NOWAIT);
816296077Sadrian		if (mport == NULL) {
817296077Sadrian			error = ENOMEM;
818296077Sadrian			goto failed;
819296077Sadrian		}
820296077Sadrian
821296077Sadrian		mport->mp_vid = mpd.port_vid;
822296077Sadrian		mport->mp_num = mpd.port_num;
823296077Sadrian
824296077Sadrian		/* Update dinfo */
825296077Sadrian		STAILQ_INSERT_TAIL(&cfg->master_ports, mport, mp_link);
826296077Sadrian	}
827296077Sadrian
828296077Sadrian
829296077Sadrian	/*
830296077Sadrian	 * Determine whether this is a bridge device; if so, we can
831296077Sadrian	 * expect the first sequence of address region descriptors to
832296077Sadrian	 * be of EROM_REGION_TYPE_BRIDGE instead of
833296077Sadrian	 * BCMA_EROM_REGION_TYPE_DEVICE.
834296077Sadrian	 *
835296077Sadrian	 * It's unclear whether this is the correct mechanism by which we
836296077Sadrian	 * should detect/handle bridge devices, but this approach matches
837296077Sadrian	 * that of (some of) Broadcom's published drivers.
838296077Sadrian	 */
839296077Sadrian	if (core.num_dport > 0) {
840296077Sadrian		uint32_t entry;
841296077Sadrian
842296077Sadrian		if ((error = bcma_erom_peek32(erom, &entry)))
843296077Sadrian			goto failed;
844296077Sadrian
845296077Sadrian		if (BCMA_EROM_ENTRY_IS(entry, REGION) &&
846296077Sadrian		    BCMA_EROM_GET_ATTR(entry, REGION_TYPE) == BCMA_EROM_REGION_TYPE_BRIDGE)
847296077Sadrian		{
848296077Sadrian			first_region_type = BCMA_EROM_REGION_TYPE_BRIDGE;
849296077Sadrian			cfg->num_dev_ports = 0;
850296077Sadrian			cfg->num_bridge_ports = core.num_dport;
851296077Sadrian		} else {
852296077Sadrian			first_region_type = BCMA_EROM_REGION_TYPE_DEVICE;
853296077Sadrian			cfg->num_dev_ports = core.num_dport;
854296077Sadrian			cfg->num_bridge_ports = 0;
855296077Sadrian		}
856296077Sadrian	}
857296077Sadrian
858296077Sadrian	/* Device/bridge port descriptors */
859296077Sadrian	for (uint8_t sp_num = 0; sp_num < core.num_dport; sp_num++) {
860296077Sadrian		error = erom_corecfg_fill_port_regions(erom, cfg, sp_num,
861296077Sadrian		    first_region_type);
862296077Sadrian
863296077Sadrian		if (error)
864296077Sadrian			goto failed;
865296077Sadrian	}
866296077Sadrian
867296077Sadrian	/* Wrapper (aka device management) descriptors (for master ports). */
868296077Sadrian	for (uint8_t sp_num = 0; sp_num < core.num_mwrap; sp_num++) {
869296077Sadrian		error = erom_corecfg_fill_port_regions(erom, cfg, sp_num,
870296077Sadrian		    BCMA_EROM_REGION_TYPE_MWRAP);
871296077Sadrian
872296077Sadrian		if (error)
873296077Sadrian			goto failed;
874296077Sadrian	}
875296077Sadrian
876296077Sadrian
877296077Sadrian	/* Wrapper (aka device management) descriptors (for slave ports). */
878296077Sadrian	for (uint8_t i = 0; i < core.num_swrap; i++) {
879296077Sadrian		/* Slave wrapper ports are not numbered distinctly from master
880296077Sadrian		 * wrapper ports. */
881299314Sadrian
882299314Sadrian		/*
883299314Sadrian		 * Broadcom DDR1/DDR2 Memory Controller
884299314Sadrian		 * (cid=82e, rev=1, unit=0, d/mw/sw = 2/0/1 ) ->
885299314Sadrian		 * bhnd0: erom[0xdc]: core6 agent0.0: mismatch got: 0x1 (0x2)
886299314Sadrian		 *
887299314Sadrian		 * ARM BP135 AMBA3 AXI to APB Bridge
888299314Sadrian		 * (cid=135, rev=0, unit=0, d/mw/sw = 1/0/1 ) ->
889299314Sadrian		 * bhnd0: erom[0x124]: core9 agent1.0: mismatch got: 0x0 (0x2)
890299314Sadrian		 *
891299314Sadrian		 * core.num_mwrap
892299314Sadrian		 * ===>
893299314Sadrian		 * (core.num_mwrap > 0) ?
894299314Sadrian		 *           core.num_mwrap :
895299314Sadrian		 *           ((core.vendor == BHND_MFGID_BCM) ? 1 : 0)
896299314Sadrian		 */
897299314Sadrian		uint8_t sp_num;
898299314Sadrian		sp_num = (core.num_mwrap > 0) ?
899299314Sadrian				core.num_mwrap :
900299314Sadrian				((core.vendor == BHND_MFGID_BCM) ? 1 : 0) + i;
901296077Sadrian		error = erom_corecfg_fill_port_regions(erom, cfg, sp_num,
902296077Sadrian		    BCMA_EROM_REGION_TYPE_SWRAP);
903296077Sadrian
904296077Sadrian		if (error)
905296077Sadrian			goto failed;
906296077Sadrian	}
907296077Sadrian
908296077Sadrian	*result = cfg;
909296077Sadrian	return (0);
910296077Sadrian
911296077Sadrianfailed:
912296077Sadrian	if (cfg != NULL)
913296077Sadrian		bcma_free_corecfg(cfg);
914296077Sadrian
915296077Sadrian	return error;
916296077Sadrian}
917