xref: /freebsd/sys/dev/bhnd/siba/siba_erom.c (revision 273c26a3c3bea87a241d6879abd4f991db180bf0)
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.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/malloc.h>
37 #include <sys/module.h>
38 
39 #include <machine/bus.h>
40 
41 #include <dev/bhnd/bhnd_erom.h>
42 
43 #include <dev/bhnd/cores/chipc/chipcreg.h>
44 
45 #include "sibareg.h"
46 #include "sibavar.h"
47 
48 struct siba_erom;
49 struct siba_erom_io;
50 
51 
52 static int			siba_eio_init(struct siba_erom_io *io,
53 				    device_t parent, struct bhnd_resource *res,
54 				    int rid, bus_size_t offset, u_int ncores);
55 
56 static int			siba_eio_init_static(struct siba_erom_io *io,
57 				    bus_space_tag_t bst, bus_space_handle_t bsh,
58 				    bus_size_t offset, u_int ncores);
59 
60 static uint32_t			siba_eio_read_4(struct siba_erom_io *io,
61 				    u_int core_idx, bus_size_t offset);
62 
63 static struct siba_core_id	siba_eio_read_core_id(struct siba_erom_io *io,
64 				    u_int core_idx, int unit);
65 
66 static int			siba_eio_read_chipid(struct siba_erom_io *io,
67 				    bus_addr_t enum_addr,
68 				    struct bhnd_chipid *cid);
69 
70 /**
71  * SIBA EROM generic I/O context
72  */
73 struct siba_erom_io {
74 	u_int			 ncores;	/**< core count */
75 	bus_size_t	 	 offset;	/**< base read offset */
76 
77 	/* resource state */
78 	device_t	 	 dev;		/**< parent dev to use for resource allocations,
79 						     or NULL if unavailable. */
80 	struct bhnd_resource	*res;		/**< memory resource, or NULL */
81 	int			 rid;		/**< memory resource ID */
82 
83 	/* bus tag state */
84 	bus_space_tag_t		 bst;		/**< bus space tag */
85 	bus_space_handle_t	 bsh;		/**< bus space handle */
86 };
87 
88 /**
89  * SIBA EROM per-instance state.
90  */
91 struct siba_erom {
92 	struct bhnd_erom	obj;
93 	struct siba_erom_io	io;	/**< i/o context */
94 };
95 
96 #define	EROM_LOG(io, fmt, ...)	do {					\
97 	if (io->dev != NULL) {						\
98 		device_printf(io->dev, "%s: " fmt, __FUNCTION__,	\
99 		    ##__VA_ARGS__);					\
100 	} else {							\
101 		printf("%s: " fmt, __FUNCTION__, ##__VA_ARGS__);	\
102 	}								\
103 } while(0)
104 
105 static int
106 siba_erom_probe_common(struct siba_erom_io *io, const struct bhnd_chipid *hint,
107     struct bhnd_chipid *cid)
108 {
109 	uint32_t		idreg;
110 	int			error;
111 
112 	/* Try using the provided hint. */
113 	if (hint != NULL) {
114 		struct siba_core_id sid;
115 
116 		/* Validate bus type */
117 		if (hint->chip_type != BHND_CHIPTYPE_SIBA)
118 			return (ENXIO);
119 
120 		/*
121 		 * Verify the first core's IDHIGH/IDLOW identification.
122 		 *
123 		 * The core must be a Broadcom core, but must *not* be
124 		 * a chipcommon core; those shouldn't be hinted.
125 		 *
126 		 * The first core on EXTIF-equipped devices varies, but on the
127 		 * BCM4710, it's a SDRAM core (0x803).
128 		 */
129 
130 		sid = siba_eio_read_core_id(io, 0, 0);
131 
132 		if (sid.core_info.vendor != BHND_MFGID_BCM)
133 			return (ENXIO);
134 
135 		if (sid.core_info.device == BHND_COREID_CC)
136 			return (EINVAL);
137 
138 		*cid = *hint;
139 	} else {
140 		/* Validate bus type */
141 		idreg = siba_eio_read_4(io, 0, CHIPC_ID);
142 		if (CHIPC_GET_BITS(idreg, CHIPC_ID_BUS) != BHND_CHIPTYPE_SIBA)
143 			return (ENXIO);
144 
145 		/* Identify the chipset */
146 		if ((error = siba_eio_read_chipid(io, SIBA_ENUM_ADDR, cid)))
147 			return (error);
148 
149 		/* Verify the chip type */
150 		if (cid->chip_type != BHND_CHIPTYPE_SIBA)
151 			return (ENXIO);
152 	}
153 
154 	/*
155 	 * gcc hack: ensure bhnd_chipid.ncores cannot exceed SIBA_MAX_CORES
156 	 * without triggering build failure due to -Wtype-limits
157 	 *
158 	 * if (cid.ncores > SIBA_MAX_CORES)
159 	 *      return (EINVAL)
160 	 */
161 	_Static_assert((2^sizeof(cid->ncores)) <= SIBA_MAX_CORES,
162 	    "ncores could result in over-read of backing resource");
163 
164 	return (0);
165 }
166 
167 /* SIBA implementation of BHND_EROM_PROBE() */
168 static int
169 siba_erom_probe(bhnd_erom_class_t *cls, struct bhnd_resource *res,
170     bus_size_t offset, const struct bhnd_chipid *hint,
171     struct bhnd_chipid *cid)
172 {
173 	struct siba_erom_io	io;
174 	int			error, rid;
175 
176 	rid = rman_get_rid(res->res);
177 
178 	/* Initialize I/O context, assuming at least 1 core exists.  */
179 	if ((error = siba_eio_init(&io, NULL, res, rid, offset, 1)))
180 		return (error);
181 
182 	return (siba_erom_probe_common(&io, hint, cid));
183 }
184 
185 /* SIBA implementation of BHND_EROM_PROBE_STATIC() */
186 static int
187 siba_erom_probe_static(bhnd_erom_class_t *cls, bus_space_tag_t bst,
188      bus_space_handle_t bsh, bus_addr_t paddr, const struct bhnd_chipid *hint,
189      struct bhnd_chipid *cid)
190 {
191 	struct siba_erom_io	io;
192 	int			error;
193 
194 	/* Initialize I/O context, assuming at least 1 core exists.  */
195 	if ((error = siba_eio_init_static(&io, bst, bsh, 0, 1)))
196 		return (error);
197 
198 	return (siba_erom_probe_common(&io, hint, cid));
199 }
200 
201 /* SIBA implementation of BHND_EROM_INIT() */
202 static int
203 siba_erom_init(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
204     device_t parent, int rid)
205 {
206 	struct siba_erom	*sc;
207 	struct bhnd_resource	*res;
208 	int			 error;
209 
210 	sc = (struct siba_erom *)erom;
211 
212 	/* Allocate backing resource */
213 	res = bhnd_alloc_resource(parent, SYS_RES_MEMORY, &rid,
214 	    cid->enum_addr, cid->enum_addr + SIBA_ENUM_SIZE -1, SIBA_ENUM_SIZE,
215 	    RF_ACTIVE|RF_SHAREABLE);
216 	if (res == NULL)
217 		return (ENOMEM);
218 
219 	/* Initialize I/O context */
220 	error = siba_eio_init(&sc->io, parent, res, rid, 0x0, cid->ncores);
221 	if (error)
222 		bhnd_release_resource(parent, SYS_RES_MEMORY, rid, res);
223 
224 	return (error);
225 }
226 
227 /* SIBA implementation of BHND_EROM_INIT_STATIC() */
228 static int
229 siba_erom_init_static(bhnd_erom_t *erom, const struct bhnd_chipid *cid,
230     bus_space_tag_t bst, bus_space_handle_t bsh)
231 {
232 	struct siba_erom	*sc;
233 
234 	sc = (struct siba_erom *)erom;
235 
236 	/* Initialize I/O context */
237 	return (siba_eio_init_static(&sc->io, bst, bsh, 0x0, cid->ncores));
238 }
239 
240 /* SIBA implementation of BHND_EROM_FINI() */
241 static void
242 siba_erom_fini(bhnd_erom_t *erom)
243 {
244 	struct siba_erom *sc = (struct siba_erom *)erom;
245 
246 	if (sc->io.res != NULL) {
247 		bhnd_release_resource(sc->io.dev, SYS_RES_MEMORY, sc->io.rid,
248 		    sc->io.res);
249 
250 		sc->io.res = NULL;
251 		sc->io.rid = -1;
252 	}
253 }
254 
255 /* Initialize siba_erom resource I/O context */
256 static int
257 siba_eio_init(struct siba_erom_io *io, device_t parent,
258     struct bhnd_resource *res, int rid, bus_size_t offset, u_int ncores)
259 {
260 	io->dev = parent;
261 	io->res = res;
262 	io->rid = rid;
263 	io->offset = offset;
264 	io->ncores = ncores;
265 
266 	return (0);
267 }
268 
269 /* Initialize siba_erom bus space I/O context */
270 static int
271 siba_eio_init_static(struct siba_erom_io *io, bus_space_tag_t bst,
272     bus_space_handle_t bsh, bus_size_t offset, u_int ncores)
273 {
274 	io->res = NULL;
275 	io->rid = -1;
276 	io->bst = bst;
277 	io->bsh = bsh;
278 	io->offset = offset;
279 	io->ncores = ncores;
280 
281 	return (0);
282 }
283 
284 /**
285  * Read a 32-bit value from @p offset relative to the base address of
286  * the given @p core_idx.
287  *
288  * @param io EROM I/O context.
289  * @param core_idx Core index.
290  * @param offset Core register offset.
291  */
292 static uint32_t
293 siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset)
294 {
295 	bus_size_t core_offset;
296 
297 	/* Sanity check core index and offset */
298 	if (core_idx >= io->ncores)
299 		panic("core index %u out of range (ncores=%u)", core_idx,
300 		    io->ncores);
301 
302 	if (offset > SIBA_CORE_SIZE - sizeof(uint32_t))
303 		panic("invalid core offset %#jx", (uintmax_t)offset);
304 
305 	/* Perform read */
306 	core_offset = io->offset + SIBA_CORE_OFFSET(core_idx) + offset;
307 	if (io->res != NULL)
308 		return (bhnd_bus_read_4(io->res, core_offset));
309 	else
310 		return (bus_space_read_4(io->bst, io->bsh, core_offset));
311 }
312 
313 /**
314  * Read and parse identification registers for the given @p core_index.
315  *
316  * @param io EROM I/O context.
317  * @param core_idx The core index.
318  * @param unit The caller-specified unit number to be included in the return
319  * value.
320  */
321 static struct siba_core_id
322 siba_eio_read_core_id(struct siba_erom_io *io, u_int core_idx, int unit)
323 {
324 	uint32_t idhigh, idlow;
325 
326 	idhigh = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDHIGH));
327 	idlow = siba_eio_read_4(io, core_idx, SB0_REG_ABS(SIBA_CFG0_IDLOW));
328 
329 	return (siba_parse_core_id(idhigh, idlow, core_idx, unit));
330 }
331 
332 /**
333  * Read and parse the chip identification register from the ChipCommon core.
334  *
335  * @param io EROM I/O context.
336  * @param enum_addr The physical address mapped by @p io.
337  * @param cid On success, the parsed chip identifier.
338  */
339 static int
340 siba_eio_read_chipid(struct siba_erom_io *io, bus_addr_t enum_addr,
341     struct bhnd_chipid *cid)
342 {
343 	struct siba_core_id	ccid;
344 	uint32_t		idreg;
345 
346 	/* Identify the chipcommon core */
347 	ccid = siba_eio_read_core_id(io, 0, 0);
348 	if (ccid.core_info.vendor != BHND_MFGID_BCM ||
349 	    ccid.core_info.device != BHND_COREID_CC)
350 	{
351 		if (bootverbose) {
352 			EROM_LOG(io, "first core not chipcommon "
353 			    "(vendor=%#hx, core=%#hx)\n", ccid.core_info.vendor,
354 			    ccid.core_info.device);
355 		}
356 		return (ENXIO);
357 	}
358 
359 	/* Identify the chipset */
360 	idreg = siba_eio_read_4(io, 0, CHIPC_ID);
361 	*cid = bhnd_parse_chipid(idreg, enum_addr);
362 
363 	/* Fix up the core count in-place */
364 	return (bhnd_chipid_fixed_ncores(cid, ccid.core_info.hwrev,
365 	    &cid->ncores));
366 }
367 
368 static int
369 siba_erom_lookup_core(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
370     struct bhnd_core_info *core)
371 {
372 	struct siba_erom	*sc;
373 	struct bhnd_core_match	 imatch;
374 
375 	sc = (struct siba_erom *)erom;
376 
377 	/* We can't determine a core's unit number during the initial scan. */
378 	imatch = *desc;
379 	imatch.m.match.core_unit = 0;
380 
381 	/* Locate the first matching core */
382 	for (u_int i = 0; i < sc->io.ncores; i++) {
383 		struct siba_core_id	sid;
384 		struct bhnd_core_info	ci;
385 
386 		/* Read the core info */
387 		sid = siba_eio_read_core_id(&sc->io, i, 0);
388 		ci = sid.core_info;
389 
390 		/* Check for initial match */
391 		if (!bhnd_core_matches(&ci, &imatch))
392 			continue;
393 
394 		/* Re-scan preceding cores to determine the unit number. */
395 		for (u_int j = 0; j < i; j++) {
396 			sid = siba_eio_read_core_id(&sc->io, i, 0);
397 
398 			/* Bump the unit number? */
399 			if (sid.core_info.vendor == ci.vendor &&
400 			    sid.core_info.device == ci.device)
401 				ci.unit++;
402 		}
403 
404 		/* Check for full match against now-valid unit number */
405 		if (!bhnd_core_matches(&ci, desc))
406 			continue;
407 
408 		/* Matching core found */
409 		*core = ci;
410 		return (0);
411 	}
412 
413 	/* Not found */
414 	return (ENOENT);
415 }
416 
417 static int
418 siba_erom_lookup_core_addr(bhnd_erom_t *erom, const struct bhnd_core_match *desc,
419     bhnd_port_type type, u_int port, u_int region, struct bhnd_core_info *info,
420     bhnd_addr_t *addr, bhnd_size_t *size)
421 {
422 	struct siba_erom	*sc;
423 	struct bhnd_core_info	 core;
424 	struct siba_core_id	 sid;
425 	uint32_t		 am, am_addr, am_size;
426 	u_int			 am_offset;
427 	u_int			 addrspace;
428 	int			 error;
429 
430 	sc = (struct siba_erom *)erom;
431 
432 	/* Locate the requested core */
433 	if ((error = siba_erom_lookup_core(erom, desc, &core)))
434 		return (error);
435 
436 	/* Fetch full siba core ident */
437 	sid = siba_eio_read_core_id(&sc->io, core.core_idx, core.unit);
438 
439 	/* Is port valid? */
440 	if (!siba_is_port_valid(sid.num_addrspace, type, port))
441 		return (ENOENT);
442 
443 	/* Is region valid? */
444 	if (region >= siba_addrspace_region_count(sid.num_addrspace, port))
445 		return (ENOENT);
446 
447 	/* Map the bhnd port values to a siba addrspace index */
448 	error = siba_addrspace_index(sid.num_addrspace, type, port, region,
449 	   &addrspace);
450 	if (error)
451 		return (error);
452 
453 	/* Determine the register offset */
454 	am_offset = siba_admatch_offset(addrspace);
455 	if (am_offset == 0) {
456 		printf("addrspace %u is unsupported", addrspace);
457 		return (ENODEV);
458 	}
459 
460 	/* Read and parse the address match register */
461 	am = siba_eio_read_4(&sc->io, core.core_idx, am_offset);
462 
463 	if ((error = siba_parse_admatch(am, &am_addr, &am_size))) {
464 		printf("failed to decode address match register value 0x%x\n",
465 		    am);
466 		return (error);
467 	}
468 
469 	if (info != NULL)
470 		*info = core;
471 
472 	*addr = am_addr;
473 	*size = am_size;
474 
475 	return (0);
476 }
477 
478 /* BHND_EROM_GET_CORE_TABLE() */
479 static int
480 siba_erom_get_core_table(bhnd_erom_t *erom, struct bhnd_core_info **cores,
481     u_int *num_cores)
482 {
483 	struct siba_erom	*sc;
484 	struct bhnd_core_info	*out;
485 
486 	sc = (struct siba_erom *)erom;
487 
488 	/* Allocate our core array */
489 	out = malloc(sizeof(*out) * sc->io.ncores, M_BHND, M_NOWAIT);
490 	if (out == NULL)
491 		return (ENOMEM);
492 
493 	*cores = out;
494 	*num_cores = sc->io.ncores;
495 
496 	/* Enumerate all cores. */
497 	for (u_int i = 0; i < sc->io.ncores; i++) {
498 		struct siba_core_id sid;
499 
500 		/* Read the core info */
501 		sid = siba_eio_read_core_id(&sc->io, i, 0);
502 		out[i] = sid.core_info;
503 
504 		/* Determine unit number */
505 		for (u_int j = 0; j < i; j++) {
506 			if (out[j].vendor == out[i].vendor &&
507 			    out[j].device == out[i].device)
508 				out[i].unit++;
509 		}
510 	}
511 
512 	return (0);
513 }
514 
515 /* BHND_EROM_FREE_CORE_TABLE() */
516 static void
517 siba_erom_free_core_table(bhnd_erom_t *erom, struct bhnd_core_info *cores)
518 {
519 	free(cores, M_BHND);
520 }
521 
522 static kobj_method_t siba_erom_methods[] = {
523 	KOBJMETHOD(bhnd_erom_probe,		siba_erom_probe),
524 	KOBJMETHOD(bhnd_erom_probe_static,	siba_erom_probe_static),
525 	KOBJMETHOD(bhnd_erom_init,		siba_erom_init),
526 	KOBJMETHOD(bhnd_erom_init_static,	siba_erom_init_static),
527 	KOBJMETHOD(bhnd_erom_fini,		siba_erom_fini),
528 	KOBJMETHOD(bhnd_erom_get_core_table,	siba_erom_get_core_table),
529 	KOBJMETHOD(bhnd_erom_free_core_table,	siba_erom_free_core_table),
530 	KOBJMETHOD(bhnd_erom_lookup_core,	siba_erom_lookup_core),
531 	KOBJMETHOD(bhnd_erom_lookup_core_addr,	siba_erom_lookup_core_addr),
532 
533 	KOBJMETHOD_END
534 };
535 
536 BHND_EROM_DEFINE_CLASS(siba_erom, siba_erom_parser, siba_erom_methods, sizeof(struct siba_erom));
537