xref: /freebsd-13-stable/sys/mips/broadcom/bcm_machdep.c (revision 3bc80996974a61a4223eae4c1ccd47b6ee32a48a)
1 /*-
2  * Copyright (c) 2007 Bruce M. Simpson.
3  * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com>
4  * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org>
5  * Copyright (c) 2017 The FreeBSD Foundation
6  * All rights reserved.
7  *
8  * Portions of this software were developed by Landon Fuller
9  * under sponsorship from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 #include "opt_ddb.h"
35 
36 #include <sys/param.h>
37 #include <sys/conf.h>
38 #include <sys/kernel.h>
39 #include <sys/systm.h>
40 #include <sys/imgact.h>
41 #include <sys/bio.h>
42 #include <sys/buf.h>
43 #include <sys/bus.h>
44 #include <sys/cpu.h>
45 #include <sys/cons.h>
46 #include <sys/exec.h>
47 #include <sys/ucontext.h>
48 #include <sys/proc.h>
49 #include <sys/kdb.h>
50 #include <sys/ptrace.h>
51 #include <sys/reboot.h>
52 #include <sys/signalvar.h>
53 #include <sys/sysent.h>
54 #include <sys/sysproto.h>
55 #include <sys/user.h>
56 
57 #include <vm/vm.h>
58 #include <vm/vm_param.h>
59 #include <vm/vm_object.h>
60 #include <vm/vm_page.h>
61 #include <vm/vm_phys.h>
62 #include <vm/vm_dumpset.h>
63 
64 #include <machine/cache.h>
65 #include <machine/clock.h>
66 #include <machine/cpu.h>
67 #include <machine/cpuinfo.h>
68 #include <machine/cpufunc.h>
69 #include <machine/cpuregs.h>
70 #include <machine/hwfunc.h>
71 #include <machine/intr_machdep.h>
72 #include <machine/locore.h>
73 #include <machine/md_var.h>
74 #include <machine/pte.h>
75 #include <machine/sigframe.h>
76 #include <machine/trap.h>
77 
78 #include <dev/bhnd/bhnd.h>
79 #include <dev/bhnd/bhndreg.h>
80 #include <dev/bhnd/bhnd_eromvar.h>
81 
82 #include <dev/bhnd/bcma/bcma_eromvar.h>
83 
84 #include <dev/bhnd/siba/sibareg.h>
85 #include <dev/bhnd/siba/sibavar.h>
86 
87 #include <dev/bhnd/cores/chipc/chipcreg.h>
88 #include <dev/bhnd/cores/pmu/bhnd_pmureg.h>
89 
90 #include "bcm_machdep.h"
91 #include "bcm_bmips_exts.h"
92 
93 #ifdef CFE
94 #include <dev/cfe/cfe_api.h>
95 #include <dev/cfe/cfe_error.h>
96 #endif
97 
98 #if 0
99 #define	BCM_TRACE(_fmt, ...)	printf(_fmt, ##__VA_ARGS__)
100 #else
101 #define	BCM_TRACE(_fmt, ...)
102 #endif
103 
104 static int	bcm_init_platform_data(struct bcm_platform *bp);
105 
106 static int	bcm_find_core(struct bcm_platform *bp,
107 		    const struct bhnd_core_match *descs, size_t num_descs,
108 		    struct bhnd_core_info *info, uintptr_t *addr);
109 
110 static int	bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls,
111 		    kobj_ops_t erom_ops, bhnd_erom_t *erom, size_t esize,
112 		    struct bhnd_erom_io *eio, struct bhnd_chipid *cid);
113 
114 extern int	*edata;
115 extern int	*end;
116 
117 static struct bcm_platform	 bcm_platform_data;
118 static bool			 bcm_platform_data_avail = false;
119 
120 #ifdef CFE
121 static struct bcm_nvram_iocfe	 bcm_cfe_nvram;
122 #endif
123 
124 static const struct bhnd_core_match bcm_chipc_cores[] = {
125 	{ BHND_MATCH_CORE(BHND_MFGID_BCM,	BHND_COREID_CC)		},
126 	{ BHND_MATCH_CORE(BHND_MFGID_BCM,	BHND_COREID_4706_CC)	},
127 };
128 
129 static const struct bhnd_core_match bcm_cpu0_cores[] = {
130 	{
131 		BHND_MATCH_CORE_CLASS(BHND_DEVCLASS_CPU),
132 		BHND_MATCH_CORE_UNIT(0)
133 	}
134 };
135 
136 static const struct bhnd_core_match bcm_pmu_cores[] = {
137 	{ BHND_MATCH_CORE(BHND_MFGID_BCM,	BHND_COREID_PMU)	},
138 };
139 
140 struct bcm_platform *
bcm_get_platform(void)141 bcm_get_platform(void)
142 {
143 	if (!bcm_platform_data_avail)
144 		panic("platform data not available");
145 
146 	return (&bcm_platform_data);
147 }
148 
149 static bus_addr_t
bcm_get_bus_addr(void)150 bcm_get_bus_addr(void)
151 {
152 	long maddr;
153 
154 	if (resource_long_value("bhnd", 0, "maddr", &maddr) == 0)
155 		return ((u_long)maddr);
156 
157 	return (BHND_DEFAULT_CHIPC_ADDR);
158 }
159 
160 static bus_size_t
bcm_get_bus_size(void)161 bcm_get_bus_size(void)
162 {
163 	long msize;
164 
165 	if (resource_long_value("bhnd", 0, "msize", &msize) == 0)
166 		return ((u_long)msize);
167 
168 	return (BHND_DEFAULT_ENUM_SIZE);
169 }
170 
171 /**
172  * Search the device enumeration table for a core matching @p descs,
173  *
174  * @param	bp		Platform state containing a valid EROM parser.
175  * @param	descs		The core match descriptor table.
176  * @param	num_descs	The number of match descriptors in @p descs.
177  * @param[out]	info		If non-NULL, will be populated with the core
178  *				info.
179  * @param[out]	addr		If non-NULL, will be populated with the core's
180  *				physical register address.
181  */
182 static int
bcm_find_core(struct bcm_platform * bp,const struct bhnd_core_match * descs,size_t num_descs,struct bhnd_core_info * info,uintptr_t * addr)183 bcm_find_core(struct bcm_platform *bp, const struct bhnd_core_match *descs,
184     size_t num_descs, struct bhnd_core_info *info, uintptr_t *addr)
185 {
186 	bhnd_addr_t		b_addr;
187 	bhnd_size_t		b_size;
188 	int			error;
189 
190 	/* Fetch core info */
191 	for (size_t i = 0; i < num_descs; i++) {
192 		error = bhnd_erom_lookup_core_addr(&bp->erom.obj, &descs[i],
193 		    BHND_PORT_DEVICE, 0, 0, info, &b_addr, &b_size);
194 
195 		/* Terminate search on first match */
196 		if (error == 0)
197 			break;
198 
199 		/* Terminate on first error (other than core not found) */
200 		if (error != ENOENT)
201 			return (error);
202 
203 		/* Continue search ... */
204 	}
205 
206 	/* Provide the core's base address */
207 	if (addr != NULL && b_addr > UINTPTR_MAX) {
208 		BCM_ERR("core address %#jx overflows native address width\n",
209 		    (uintmax_t)b_addr);
210 		return (ERANGE);
211 	}
212 
213 	if (addr != NULL)
214 		*addr = b_addr;
215 
216 	return (0);
217 }
218 
219 /**
220  * Read a variable directly from NVRAM, decoding as @p type.
221  *
222  * @param		bp	Platform state.
223  * @param		name	The raw name of the variable to be fetched,
224  *				including any device path (/pci/1/1/varname) or
225  *				alias prefix (0:varname).
226  * @param[out]		buf	On success, the requested value will be written
227  *				to this buffer. This argment may be NULL if
228  *				the value is not desired.
229  * @param[in,out]	len	The capacity of @p buf. On success, will be set
230  *				to the actual size of the requested value.
231  * @param		type	The data type to be written to @p buf.
232  *
233  * @retval 0		success
234  * @retval ENOMEM	If @p buf is non-NULL and a buffer of @p len is too
235  *			small to hold the requested value.
236  * @retval ENOENT	If @p name is not found.
237  * @retval EFTYPE	If the variable data cannot be coerced to @p type.
238  * @retval ERANGE	If value coercion would overflow @p type.
239  * @retval non-zero	If parsing NVRAM otherwise fails, a regular unix error
240  *			code will be returned.
241  */
242 int
bcm_get_nvram(struct bcm_platform * bp,const char * name,void * buf,size_t * len,bhnd_nvram_type type)243 bcm_get_nvram(struct bcm_platform *bp, const char *name, void *buf, size_t *len,
244     bhnd_nvram_type type)
245 {
246 	if (bp->nvram_io == NULL || bp->nvram_cls == NULL)
247 		return (ENOENT);
248 
249 	return (bhnd_nvram_data_getvar_direct(bp->nvram_cls, bp->nvram_io, name,
250 	    buf, len, type));
251 }
252 
253 /**
254  * Probe and attach a bhnd_erom parser instance for the bhnd bus.
255  *
256  * @param[out]	erom_cls	The probed EROM class.
257  * @param[out]	erom_ops	The storage to be used when compiling
258  *				@p erom_cls.
259  * @param[out]	erom		The storage to be used when initializing the
260  *				static instance of @p erom_cls.
261  * @param	esize		The total available number of bytes allocated
262  *				for @p erom. If this is less than is required
263  *				by @p erom_cls ENOMEM will be returned.
264  * @param	eio		EROM I/O callbacks to be used.
265  * @param[out]	cid		On success, the probed chip identification.
266  */
267 static int
bcm_erom_probe_and_attach(bhnd_erom_class_t ** erom_cls,kobj_ops_t erom_ops,bhnd_erom_t * erom,size_t esize,struct bhnd_erom_io * eio,struct bhnd_chipid * cid)268 bcm_erom_probe_and_attach(bhnd_erom_class_t **erom_cls, kobj_ops_t erom_ops,
269     bhnd_erom_t *erom, size_t esize, struct bhnd_erom_io *eio,
270     struct bhnd_chipid *cid)
271 {
272 	bhnd_erom_class_t	**clsp;
273 	bus_addr_t		  bus_addr;
274 	int			  error, prio, result;
275 
276 	*erom_cls = NULL;
277 	prio = 0;
278 
279 	/* Map our first bus core for the erom probe */
280 	bus_addr = bcm_get_bus_addr();
281 	if ((error = bhnd_erom_io_map(eio, bus_addr, BHND_DEFAULT_CORE_SIZE))) {
282 		BCM_ERR("failed to map first core at %#jx+%#jx: %d\n",
283 		    (uintmax_t)bus_addr, (uintmax_t)BHND_DEFAULT_CORE_SIZE,
284 		    error);
285 
286 		return (error);
287 	}
288 
289 	SET_FOREACH(clsp, bhnd_erom_class_set) {
290 		struct bhnd_chipid	 pcid;
291 		bhnd_erom_class_t	*cls;
292 		struct kobj_ops		 kops;
293 
294 		cls = *clsp;
295 
296 		/* Compile the class' ops table */
297 		kobj_class_compile_static(cls, &kops);
298 
299 		/* Probe the bus address */
300 		result = bhnd_erom_probe(cls, eio, NULL, &pcid);
301 
302 		/* Drop pointer to stack allocated ops table */
303 		cls->ops = NULL;
304 
305 		/* The parser did not match if an error was returned */
306 		if (result > 0)
307 			continue;
308 
309 		/* Check for a new highest priority match */
310 		if (*erom_cls == NULL || result > prio) {
311 			prio = result;
312 
313 			*cid = pcid;
314 			*erom_cls = cls;
315 		}
316 
317 		/* Terminate immediately on BUS_PROBE_SPECIFIC */
318 		if (result == BUS_PROBE_SPECIFIC)
319 			break;
320 	}
321 
322 	/* Valid EROM class probed? */
323 	if (*erom_cls == NULL) {
324 		BCM_ERR("no erom parser found for root bus at %#jx\n",
325 		    (uintmax_t)bus_addr);
326 
327 		return (ENOENT);
328 	}
329 
330 	/* Using the provided storage, recompile the erom class ... */
331 	kobj_class_compile_static(*erom_cls, erom_ops);
332 
333 	/* ... and initialize the erom parser instance */
334 	error = bhnd_erom_init_static(*erom_cls, erom, esize, cid, eio);
335 
336 	return (error);
337 }
338 
339 /**
340  * Populate platform configuration data.
341  */
342 static int
bcm_init_platform_data(struct bcm_platform * bp)343 bcm_init_platform_data(struct bcm_platform *bp)
344 {
345 	bus_addr_t		bus_addr, bus_size;
346 	bus_space_tag_t		erom_bst;
347 	bus_space_handle_t	erom_bsh;
348 	bool			aob, pmu;
349 	int			error;
350 
351 	bus_addr = bcm_get_bus_addr();
352 	bus_size = bcm_get_bus_size();
353 
354 #ifdef CFE
355 	/* Fetch CFE console handle (if any). Must be initialized before
356 	 * any calls to printf/early_putc. */
357 	if ((bp->cfe_console = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE)) < 0)
358 		bp->cfe_console = -1;
359 
360 	/* Probe CFE NVRAM sources */
361 	bp->nvram_io = &bcm_cfe_nvram.io;
362 	error = bcm_nvram_find_cfedev(&bcm_cfe_nvram, &bp->nvram_cls);
363 	if (error) {
364 		bp->nvram_io = NULL;
365 		bp->nvram_cls = NULL;
366 	}
367 #endif /* CFE */
368 
369 	/* Probe and attach device table provider, populating our
370 	 * chip identification */
371 	erom_bst = mips_bus_space_generic;
372 	erom_bsh = BCM_SOC_BSH(bus_addr, 0);
373 
374 	error = bhnd_erom_iobus_init(&bp->erom_io, bus_addr, bus_size, erom_bst,
375 	    erom_bsh);
376 	if (error) {
377 		BCM_ERR("failed to initialize erom I/O callbacks: %d\n", error);
378 		return (error);
379 	}
380 
381 	error = bcm_erom_probe_and_attach(&bp->erom_impl, &bp->erom_ops,
382 	    &bp->erom.obj, sizeof(bp->erom), &bp->erom_io.eio, &bp->cid);
383 	if (error) {
384 		BCM_ERR("error attaching erom parser: %d\n", error);
385 		bhnd_erom_io_fini(&bp->erom_io.eio);
386 		return (error);
387 	}
388 
389 	if (bootverbose)
390 		bhnd_erom_dump(&bp->erom.obj);
391 
392 	/* Fetch chipcommon core info */
393 	error = bcm_find_core(bp, bcm_chipc_cores, nitems(bcm_chipc_cores),
394 	    &bp->cc_id, &bp->cc_addr);
395 	if (error) {
396 		BCM_ERR("error locating chipc core: %d\n", error);
397 		return (error);
398 	}
399 
400 	/* Fetch chipc capability flags */
401 	bp->cc_caps = BCM_SOC_READ_4(bp->cc_addr, CHIPC_CAPABILITIES);
402 	bp->cc_caps_ext = 0x0;
403 
404 	if (CHIPC_HWREV_HAS_CAP_EXT(bp->cc_id.hwrev))
405 		bp->cc_caps_ext = BCM_CHIPC_READ_4(bp, CHIPC_CAPABILITIES_EXT);
406 
407 	/* Fetch PMU info */
408 	pmu = CHIPC_GET_FLAG(bp->cc_caps, CHIPC_CAP_PMU);
409 	aob = CHIPC_GET_FLAG(bp->cc_caps_ext, CHIPC_CAP2_AOB);
410 
411 	if (pmu && aob) {
412 		/* PMU block mapped to a PMU core on the Always-on-Bus (aob) */
413 		error = bcm_find_core(bp, bcm_pmu_cores, nitems(bcm_pmu_cores),
414 		    &bp->pmu_id,  &bp->pmu_addr);
415 		if (error) {
416 			BCM_ERR("error locating pmu core: %d\n", error);
417 			return (error);
418 		}
419 	} else if (pmu) {
420 		/* PMU block mapped to chipc */
421 		bp->pmu_addr = bp->cc_addr;
422 		bp->pmu_id = bp->cc_id;
423 	} else {
424 		/* No PMU */
425 		bp->pmu_addr = 0x0;
426 		memset(&bp->pmu_id, 0, sizeof(bp->pmu_id));
427 	}
428 
429 	/* Initialize PMU query state */
430 	if (pmu) {
431 		error = bhnd_pmu_query_init(&bp->pmu, NULL, bp->cid,
432 		    &bcm_pmu_soc_io, bp);
433 		if (error) {
434 			BCM_ERR("bhnd_pmu_query_init() failed: %d\n", error);
435 			return (error);
436 		}
437 	}
438 
439 	/* Find CPU core info */
440 	error = bcm_find_core(bp, bcm_cpu0_cores, nitems(bcm_cpu0_cores),
441 	    &bp->cpu_id,  &bp->cpu_addr);
442 	if (error) {
443 		BCM_ERR("error locating CPU core: %d\n", error);
444 		return (error);
445 	}
446 
447 	/* Initialize our platform service registry */
448 	if ((error = bhnd_service_registry_init(&bp->services))) {
449 		BCM_ERR("error initializing service registry: %d\n", error);
450 		return (error);
451 	}
452 
453 	bcm_platform_data_avail = true;
454 	return (0);
455 }
456 
457 void
platform_cpu_init()458 platform_cpu_init()
459 {
460 	/* Nothing special */
461 }
462 
463 static void
mips_init(void)464 mips_init(void)
465 {
466 	int i, j;
467 
468 	printf("entry: mips_init()\n");
469 
470 #ifdef CFE
471 	/*
472 	 * Query DRAM memory map from CFE.
473 	 */
474 	physmem = 0;
475 	for (i = 0; i < 10; i += 2) {
476 		int result;
477 		uint64_t addr, len, type;
478 
479 		result = cfe_enummem(i / 2, 0, &addr, &len, &type);
480 		if (result < 0) {
481 			BCM_TRACE("There is no phys memory for: %d\n", i);
482 			phys_avail[i] = phys_avail[i + 1] = 0;
483 			break;
484 		}
485 		if (type != CFE_MI_AVAILABLE) {
486 			BCM_TRACE("phys memory is not available: %d\n", i);
487 			continue;
488 		}
489 
490 		phys_avail[i] = addr;
491 		if (i == 0 && addr == 0) {
492 			/*
493 			 * If this is the first physical memory segment probed
494 			 * from CFE, omit the region at the start of physical
495 			 * memory where the kernel has been loaded.
496 			 */
497 			phys_avail[i] += MIPS_KSEG0_TO_PHYS(kernel_kseg0_end);
498 		}
499 
500 		BCM_TRACE("phys memory is available for: %d\n", i);
501 		BCM_TRACE(" => addr =  %jx\n", addr);
502 		BCM_TRACE(" => len =  %jd\n", len);
503 
504 		phys_avail[i + 1] = addr + len;
505 		physmem += len;
506 	}
507 
508 	BCM_TRACE("Total phys memory is : %ld\n", physmem);
509 	realmem = btoc(physmem);
510 #endif
511 
512 	for (j = 0; j < i; j++)
513 		dump_avail[j] = phys_avail[j];
514 
515 	physmem = realmem;
516 
517 	init_param1();
518 	init_param2(physmem);
519 	mips_cpu_init();
520 	pmap_bootstrap();
521 	mips_proc0_init();
522 	mutex_init();
523 	kdb_init();
524 #ifdef KDB
525 	if (boothowto & RB_KDB)
526 		kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
527 #endif
528 }
529 
530 void
platform_reset(void)531 platform_reset(void)
532 {
533 	struct bcm_platform	*bp;
534 	bool			 bcm4785war;
535 
536 	printf("bcm::platform_reset()\n");
537 	intr_disable();
538 
539 #ifdef CFE
540 	/* Fall back on CFE if reset requested during platform
541 	 * data initialization */
542 	if (!bcm_platform_data_avail) {
543 		cfe_exit(0, 0);
544 		while (1);
545 	}
546 #endif
547 
548 	bp = bcm_get_platform();
549 	bcm4785war = false;
550 
551 	/* Handle BCM4785-specific behavior */
552 	if (bp->cid.chip_id == BHND_CHIPID_BCM4785) {
553 		bcm4785war = true;
554 
555 		/* Switch to async mode */
556 		bcm_bmips_wr_pllcfg3(BMIPS_BCMCFG_PLLCFG3_SM);
557 	}
558 
559 	/* Set watchdog (PMU or ChipCommon) */
560 	if (bp->pmu_addr != 0x0) {
561 		BCM_PMU_WRITE_4(bp, BHND_PMU_WATCHDOG, 1);
562 	} else
563 		BCM_CHIPC_WRITE_4(bp, CHIPC_WATCHDOG, 1);
564 
565 	/* BCM4785 */
566 	if (bcm4785war) {
567 		mips_sync();
568 		__asm __volatile("wait");
569 	}
570 
571 	while (1);
572 }
573 
574 void
platform_start(__register_t a0,__register_t a1,__register_t a2,__register_t a3)575 platform_start(__register_t a0, __register_t a1, __register_t a2,
576 	       __register_t a3)
577 {
578 	vm_offset_t 		 kernend;
579 	uint64_t		 platform_counter_freq;
580 	int			 error;
581 
582 	/* clear the BSS and SBSS segments */
583 	kernend = (vm_offset_t)&end;
584 	memset(&edata, 0, kernend - (vm_offset_t)(&edata));
585 
586 	mips_postboot_fixup();
587 
588 	/* Initialize pcpu stuff */
589 	mips_pcpu0_init();
590 
591 #ifdef CFE
592 	/*
593 	 * Initialize CFE firmware trampolines. This must be done
594 	 * before any CFE APIs are called, including writing
595 	 * to the CFE console.
596 	 *
597 	 * CFE passes the following values in registers:
598 	 * a0: firmware handle
599 	 * a2: firmware entry point
600 	 * a3: entry point seal
601 	 */
602 	if (a3 == CFE_EPTSEAL)
603 		cfe_init(a0, a2);
604 #endif
605 
606 	/* Init BCM platform data */
607 	if ((error = bcm_init_platform_data(&bcm_platform_data)))
608 		panic("bcm_init_platform_data() failed: %d", error);
609 
610 	platform_counter_freq = bcm_get_cpufreq(bcm_get_platform());
611 
612 	/* CP0 ticks every two cycles */
613 	mips_timer_early_init(platform_counter_freq / 2);
614 
615 	cninit();
616 
617 	mips_init();
618 
619 	mips_timer_init_params(platform_counter_freq, 1);
620 }
621 
622 /*
623  * CFE-based EARLY_PRINTF support. To use, add the following to the kernel
624  * config:
625  *	option EARLY_PRINTF
626  *	option CFE
627  *	device cfe
628  */
629 #if defined(EARLY_PRINTF) && defined(CFE)
630 static void
bcm_cfe_eputc(int c)631 bcm_cfe_eputc(int c)
632 {
633 	unsigned char	ch;
634 	int		handle;
635 
636 	ch = (unsigned char) c;
637 
638 	/* bcm_get_platform() cannot be used here, as we may be called
639 	 * from bcm_init_platform_data(). */
640 	if ((handle = bcm_platform_data.cfe_console) < 0)
641 		return;
642 
643 	if (ch == '\n')
644 		early_putc('\r');
645 
646 	while ((cfe_write(handle, &ch, 1)) == 0)
647 		continue;
648 }
649 
650 early_putc_t *early_putc = bcm_cfe_eputc;
651 #endif /* EARLY_PRINTF */
652