xref: /freebsd/sys/dev/bhnd/siba/siba_erom.c (revision f0cfa1b168014f56c02b83e5f28412cc5f78d117)
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
3  * Copyright (c) 2017 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * Portions of this software were developed by Landon Fuller
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer,
14  *    without modification.
15  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17  *    redistribution must be conditioned upon including a substantially
18  *    similar Disclaimer requirement for further binary redistribution.
19  *
20  * NO WARRANTY
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31  * THE POSSIBILITY OF SUCH DAMAGES.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include <sys/param.h>
38 #include <sys/bus.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 
43 #include <machine/bus.h>
44 
45 #include <dev/bhnd/bhnd_erom.h>
46 
47 #include <dev/bhnd/cores/chipc/chipcreg.h>
48 
49 #include "sibareg.h"
50 #include "sibavar.h"
51 
52 struct siba_erom;
53 struct siba_erom_io;
54 
55 
56 static int			siba_eio_init(struct siba_erom_io *io,
57 				    struct bhnd_erom_io *eio, u_int ncores);
58 
59 static uint32_t			siba_eio_read_4(struct siba_erom_io *io,
60 				    u_int core_idx, bus_size_t offset);
61 
62 static struct siba_core_id	siba_eio_read_core_id(struct siba_erom_io *io,
63 				    u_int core_idx, int unit);
64 
65 static int			siba_eio_read_chipid(struct siba_erom_io *io,
66 				    bus_addr_t enum_addr,
67 				    struct bhnd_chipid *cid);
68 
69 /**
70  * SIBA EROM generic I/O context
71  */
72 struct siba_erom_io {
73 	struct bhnd_erom_io	*eio;		/**< erom I/O callbacks */
74 	bhnd_addr_t		 base_addr;	/**< address of first core */
75 	u_int			 ncores;	/**< core count */
76 };
77 
78 /**
79  * SIBA EROM per-instance state.
80  */
81 struct siba_erom {
82 	struct bhnd_erom	obj;
83 	struct siba_erom_io	io;	/**< i/o context */
84 };
85 
86 #define	EROM_LOG(io, fmt, ...)	do {				\
87 	printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__);	\
88 } while(0)
89 
90 /* SIBA implementation of BHND_EROM_PROBE() */
91 static int
92 siba_erom_probe(bhnd_erom_class_t *cls, struct bhnd_erom_io *eio,
93     const struct bhnd_chipid *hint, struct bhnd_chipid *cid)
94 {
95 	struct siba_erom_io	io;
96 	uint32_t		idreg;
97 	int			error;
98 
99 	/* Initialize I/O context, assuming at least the first core is mapped */
100 	if ((error = siba_eio_init(&io, eio, 1)))
101 		return (error);
102 
103 	/* Try using the provided hint. */
104 	if (hint != NULL) {
105 		struct siba_core_id sid;
106 
107 		/* Validate bus type */
108 		if (hint->chip_type != BHND_CHIPTYPE_SIBA)
109 			return (ENXIO);
110 
111 		/*
112 		 * Verify the first core's IDHIGH/IDLOW identification.
113 		 *
114 		 * The core must be a Broadcom core, but must *not* be
115 		 * a chipcommon core; those shouldn't be hinted.
116 		 *
117 		 * The first core on EXTIF-equipped devices varies, but on the
118 		 * BCM4710, it's a SDRAM core (0x803).
119 		 */
120 
121 		sid = siba_eio_read_core_id(&io, 0, 0);
122 
123 		if (sid.core_info.vendor != BHND_MFGID_BCM)
124 			return (ENXIO);
125 
126 		if (sid.core_info.device == BHND_COREID_CC)
127 			return (EINVAL);
128 
129 		*cid = *hint;
130 	} else {
131 		/* Validate bus type */
132 		idreg = siba_eio_read_4(&io, 0, CHIPC_ID);
133 		if (CHIPC_GET_BITS(idreg, CHIPC_ID_BUS) != BHND_CHIPTYPE_SIBA)
134 			return (ENXIO);
135 
136 		/* Identify the chipset */
137 		if ((error = siba_eio_read_chipid(&io, SIBA_ENUM_ADDR, cid)))
138 			return (error);
139 
140 		/* Verify the chip type */
141 		if (cid->chip_type != BHND_CHIPTYPE_SIBA)
142 			return (ENXIO);
143 	}
144 
145 	/*
146 	 * gcc hack: ensure bhnd_chipid.ncores cannot exceed SIBA_MAX_CORES
147 	 * without triggering build failure due to -Wtype-limits
148 	 *
149 	 * if (cid.ncores > SIBA_MAX_CORES)
150 	 *      return (EINVAL)
151 	 */
152 	_Static_assert((2^sizeof(cid->ncores)) <= SIBA_MAX_CORES,
153 	    "ncores could result in over-read of backing resource");
154 
155 	return (0);
156 }
157 
158 /* SIBA implementation of BHND_EROM_INIT() */
159 static int
160 siba_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
161     struct bhnd_erom_io *eio)
162 {
163 	struct siba_erom	*sc;
164 	int			 error;
165 
166 	sc = (struct siba_erom *)erom;
167 
168 	/* Attempt to map the full core enumeration space */
169 	error = bhnd_erom_io_map(eio, cid->enum_addr,
170 	    cid->ncores * SIBA_CORE_SIZE);
171 	if (error) {
172 		printf("%s: failed to map %u cores: %d\n", __FUNCTION__,
173 		    cid->ncores, error);
174 		return (error);
175 	}
176 
177 	/* Initialize I/O context */
178 	return (siba_eio_init(&sc->io, eio, cid->ncores));
179 }
180 
181 /* SIBA implementation of BHND_EROM_FINI() */
182 static void
183 siba_erom_fini(bhnd_erom_t *erom)
184 {
185 	struct siba_erom *sc = (struct siba_erom *)erom;
186 
187 	bhnd_erom_io_fini(sc->io.eio);
188 }
189 
190 /* Initialize siba_erom resource I/O context */
191 static int
192 siba_eio_init(struct siba_erom_io *io, struct bhnd_erom_io *eio, u_int ncores)
193 {
194 	io->eio = eio;
195 	io->ncores = ncores;
196 	return (0);
197 }
198 
199 /**
200  * Read a 32-bit value from @p offset relative to the base address of
201  * the given @p core_idx.
202  *
203  * @param io EROM I/O context.
204  * @param core_idx Core index.
205  * @param offset Core register offset.
206  */
207 static uint32_t
208 siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset)
209 {
210 	/* Sanity check core index and offset */
211 	if (core_idx >= io->ncores)
212 		panic("core index %u out of range (ncores=%u)", core_idx,
213 		    io->ncores);
214 
215 	if (offset > SIBA_CORE_SIZE - sizeof(uint32_t))
216 		panic("invalid core offset %#jx", (uintmax_t)offset);
217 
218 	/* Perform read */
219 	return (bhnd_erom_io_read(io->eio, SIBA_CORE_OFFSET(core_idx) + offset,
220 	    4));
221 }
222 
223 /**
224  * Read and parse identification registers for the given @p core_index.
225  *
226  * @param io EROM I/O context.
227  * @param core_idx The core index.
228  * @param unit The caller-specified unit number to be included in the return
229  * value.
230  */
231 static struct siba_core_id
232 siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit)
233 {
234 	uint32_t idhigh, idlow;
235 
236 	idhigh = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
237 	idlow = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDLOW));
238 
239 	return (siba_parse_core_id(idhigh, idlow, core_idx, unit));
240 }
241 
242 /**
243  * Read and parse the chip identification register from the ChipCommon core.
244  *
245  * @param io EROM I/O context.
246  * @param enum_addr The physical address mapped by @p io.
247  * @param cid On success, the parsed chip identifier.
248  */
249 static int
250 siba_eio_read_chipid(struct siba_erom_io *io, bus_addr_t enum_addr,
251     struct bhnd_chipid *cid)
252 {
253 	struct siba_core_id	ccid;
254 	uint32_t		idreg;
255 
256 	/* Identify the chipcommon core */
257 	ccid = siba_eio_read_core_id(io, 0, 0);
258 	if (ccid.core_info.vendor != BHND_MFGID_BCM ||
259 	    ccid.core_info.device != BHND_COREID_CC)
260 	{
261 		if (bootverbose) {
262 			EROM_LOG(io, "first core not chipcommon "
263 			    "(vendor=%#hx, core=%#hx)\n", ccid.core_info.vendor,
264 			    ccid.core_info.device);
265 		}
266 		return (ENXIO);
267 	}
268 
269 	/* Identify the chipset */
270 	idreg = siba_eio_read_4(io, 0, CHIPC_ID);
271 	*cid = bhnd_parse_chipid(idreg, enum_addr);
272 
273 	/* Fix up the core count in-place */
274 	return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev,
275 	    &cid->ncores));
276 }
277 
278 static int
279 siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
280     struct bhnd_core_info *core)
281 {
282 	struct siba_erom	*sc;
283 	struct bhnd_core_match	 imatch;
284 
285 	sc = (struct siba_erom *)erom;
286 
287 	/* We can't determine a core's unit number during the initial scan. */
288 	imatch = *desc;
289 	imatch.m.match.core_unit = 0;
290 
291 	/* Locate the first matching core */
292 	for (u_int i = 0; i < sc->io.ncores; i++) {
293 		struct siba_core_id	sid;
294 		struct bhnd_core_info	ci;
295 
296 		/* Read the core info */
297 		sid = siba_eio_read_core_id(&sc->io, i, 0);
298 		ci = sid.core_info;
299 
300 		/* Check for initial match */
301 		if (!bhnd_core_matches(&ci, &imatch))
302 			continue;
303 
304 		/* Re-scan preceding cores to determine the unit number. */
305 		for (u_int j = 0; j < i; j++) {
306 			sid = siba_eio_read_core_id(&sc->io, j, 0);
307 
308 			/* Bump the unit number? */
309 			if (sid.core_info.vendor == ci.vendor &&
310 			    sid.core_info.device == ci.device)
311 				ci.unit++;
312 		}
313 
314 		/* Check for full match against now-valid unit number */
315 		if (!bhnd_core_matches(&ci, desc))
316 			continue;
317 
318 		/* Matching core found */
319 		*core = ci;
320 		return (0);
321 	}
322 
323 	/* Not found */
324 	return (ENOENT);
325 }
326 
327 static int
328 siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
329     bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *info,
330     bhnd_addr_t *addr, bhnd_size_t *size)
331 {
332 	struct siba_erom	*sc;
333 	struct bhnd_core_info	 core;
334 	struct siba_core_id	 sid;
335 	uint32_t		 am, am_addr, am_size;
336 	u_int			 am_offset;
337 	u_int			 addrspace, cfg;
338 
339 	int			 error;
340 
341 	sc = (struct siba_erom *)erom;
342 
343 	/* Locate the requested core */
344 	if ((error = siba_erom_lookup_core(erom, desc, &core)))
345 		return (error);
346 
347 	/* Fetch full siba core ident */
348 	sid = siba_eio_read_core_id(&sc->io, core.core_idx, core.unit);
349 
350 	/* Is port valid? */
351 	if (!siba_is_port_valid(&sid, type, port))
352 		return (ENOENT);
353 
354 	/* Is region valid? */
355 	if (region >= siba_port_region_count(&sid, type, port))
356 		return (ENOENT);
357 
358 	/* Is this a siba configuration region? If so, this is mapped to an
359 	 * offset within the device0.0 port */
360 	error = siba_cfg_index(&sid, type, port, region, &cfg);
361 	if (!error) {
362 		bhnd_addr_t	region_addr;
363 		bhnd_addr_t	region_size;
364 		bhnd_size_t	cfg_offset, cfg_size;
365 
366 		cfg_offset = SIBA_CFG_OFFSET(cfg);
367 		cfg_size = SIBA_CFG_SIZE;
368 
369 		/* Fetch the device0.0 addr/size */
370 		error = siba_erom_lookup_core_addr(erom, desc, BHND_PORT_DEVICE,
371 		    0, 0, NULL, &region_addr, &region_size);
372 		if (error)
373 			return (error);
374 
375 		/* Verify that our offset fits within the region */
376 		if (region_size < cfg_size) {
377 			printf("%s%u.%u offset %ju exceeds %s0.0 size %ju\n",
378 			    bhnd_port_type_name(type), port, region, cfg_offset,
379 			    bhnd_port_type_name(BHND_PORT_DEVICE), region_size);
380 
381 			return (ENXIO);
382 		}
383 
384 		if (BHND_ADDR_MAX - region_addr < cfg_offset) {
385 			printf("%s%u.%u offset %ju would overflow %s0.0 addr "
386 			    "%ju\n", bhnd_port_type_name(type), port, region,
387 			    cfg_offset, bhnd_port_type_name(BHND_PORT_DEVICE),
388 			    region_addr);
389 
390 			return (ENXIO);
391 		}
392 
393 		if (info != NULL)
394 			*info = core;
395 
396 		*addr = region_addr + cfg_offset;
397 		*size = cfg_size;
398 		return (0);
399 	}
400 
401 	/*
402 	 * Otherwise, must be a device port.
403 	 *
404 	 * Map the bhnd device port to a siba addrspace index. Unlike siba(4)
405 	 * bus drivers, we do not exclude the siba(4) configuration blocks from
406 	 * the first device port.
407 	 */
408 	error = siba_addrspace_index(&sid, type, port, region, &addrspace);
409 	if (error)
410 		return (error);
411 
412 	/* Determine the register offset */
413 	am_offset = siba_admatch_offset(addrspace);
414 	if (am_offset == 0) {
415 		printf("addrspace %u is unsupported", addrspace);
416 		return (ENODEV);
417 	}
418 
419 	/* Read and parse the address match register */
420 	am = siba_eio_read_4(&sc->io, core.core_idx, am_offset);
421 
422 	if ((error = siba_parse_admatch(am, &am_addr, &am_size))) {
423 		printf("failed to decode address match register value 0x%x\n",
424 		    am);
425 		return (error);
426 	}
427 
428 	if (info != NULL)
429 		*info = core;
430 
431 	*addr = am_addr;
432 	*size = am_size;
433 
434 	return (0);
435 }
436 
437 /* BHND_EROM_GET_CORE_TABLE() */
438 static int
439 siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
440     u_int *num_cores)
441 {
442 	struct siba_erom	*sc;
443 	struct bhnd_core_info	*out;
444 
445 	sc = (struct siba_erom *)erom;
446 
447 	/* Allocate our core array */
448 	out = malloc(sizeof(*out) * sc->io.ncores, M_BHND, M_NOWAIT);
449 	if (out == NULL)
450 		return (ENOMEM);
451 
452 	*cores = out;
453 	*num_cores = sc->io.ncores;
454 
455 	/* Enumerate all cores. */
456 	for (u_int i = 0; i < sc->io.ncores; i++) {
457 		struct siba_core_id sid;
458 
459 		/* Read the core info */
460 		sid = siba_eio_read_core_id(&sc->io, i, 0);
461 		out[i] = sid.core_info;
462 
463 		/* Determine unit number */
464 		for (u_int j = 0; j < i; j++) {
465 			if (out[j].vendor == out[i].vendor &&
466 			    out[j].device == out[i].device)
467 				out[i].unit++;
468 		}
469 	}
470 
471 	return (0);
472 }
473 
474 /* BHND_EROM_FREE_CORE_TABLE() */
475 static void
476 siba_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores)
477 {
478 	free(cores, M_BHND);
479 }
480 
481 /* BHND_EROM_DUMP() */
482 static int
483 siba_erom_dump(bhnd_erom_t *erom)
484 {
485 	struct siba_erom	*sc;
486 	int			 error;
487 
488 	sc = (struct siba_erom *)erom;
489 
490 	/* Enumerate all cores. */
491 	for (u_int i = 0; i < sc->io.ncores; i++) {
492 		uint32_t idhigh, idlow;
493 		uint32_t nraddr;
494 
495 		idhigh = siba_eio_read_4(&sc->io, i,
496 		    SB0_REG_ABS(SIBA_CFG0_IDHIGH));
497 		idlow = siba_eio_read_4(&sc->io, i,
498 		    SB0_REG_ABS(SIBA_CFG0_IDLOW));
499 
500 		printf("siba core %u:\n", i);
501 		printf("\tvendor:\t0x%04x\n", SIBA_REG_GET(idhigh, IDH_VENDOR));
502 		printf("\tdevice:\t0x%04x\n", SIBA_REG_GET(idhigh, IDH_DEVICE));
503 		printf("\trev:\t0x%04x\n", SIBA_IDH_CORE_REV(idhigh));
504 		printf("\tsbrev:\t0x%02x\n", SIBA_REG_GET(idlow, IDL_SBREV));
505 
506 		/* Enumerate the address match registers */
507 		nraddr = SIBA_REG_GET(idlow, IDL_NRADDR);
508 		printf("\tnraddr\t0x%04x\n", nraddr);
509 
510 		for (size_t addrspace = 0; addrspace < nraddr; addrspace++) {
511 			uint32_t	am, am_addr, am_size;
512 			u_int		am_offset;
513 
514 			/* Determine the register offset */
515 			am_offset = siba_admatch_offset(addrspace);
516 			if (am_offset == 0) {
517 				printf("addrspace %zu unsupported",
518 				    addrspace);
519 				break;
520 			}
521 
522 			/* Read and parse the address match register */
523 			am = siba_eio_read_4(&sc->io, i, am_offset);
524 			error = siba_parse_admatch(am, &am_addr, &am_size);
525 			if (error) {
526 				printf("failed to decode address match "
527 				    "register value 0x%x\n", am);
528 				continue;
529 			}
530 
531 			printf("\taddrspace %zu\n", addrspace);
532 			printf("\t\taddr: 0x%08x\n", am_addr);
533 			printf("\t\tsize: 0x%08x\n", am_size);
534 		}
535 	}
536 
537 	return (0);
538 }
539 
540 static kobj_method_t siba_erom_methods[] = {
541 	KOBJMETHOD(bhnd_erom_probe,		siba_erom_probe),
542 	KOBJMETHOD(bhnd_erom_init,		siba_erom_init),
543 	KOBJMETHOD(bhnd_erom_fini,		siba_erom_fini),
544 	KOBJMETHOD(bhnd_erom_get_core_table,	siba_erom_get_core_table),
545 	KOBJMETHOD(bhnd_erom_free_core_table,	siba_erom_free_core_table),
546 	KOBJMETHOD(bhnd_erom_lookup_core,	siba_erom_lookup_core),
547 	KOBJMETHOD(bhnd_erom_lookup_core_addr,	siba_erom_lookup_core_addr),
548 	KOBJMETHOD(bhnd_erom_dump,		siba_erom_dump),
549 
550 	KOBJMETHOD_END
551 };
552 
553 BHND_EROM_DEFINE_CLASS(siba_erom, siba_erom_parser, siba_erom_methods, sizeof(struct siba_erom));
554