1/* $NetBSD: ofw_consinit.c,v 1.12 2011/07/01 18:59:19 dyoung Exp $ */
2
3/*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tim Rightnour
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: ofw_consinit.c,v 1.12 2011/07/01 18:59:19 dyoung Exp $");
34
35#include <sys/param.h>
36#include <sys/buf.h>
37#include <sys/tty.h>
38
39#include <prop/proplib.h>
40
41#include <machine/autoconf.h>
42#include <machine/trap.h>
43#include <sys/bus.h>
44
45#include <powerpc/ofw_cons.h>
46
47#include <dev/cons.h>
48#include <dev/ofw/openfirm.h>
49
50#include <dev/wscons/wsksymvar.h>
51#include <dev/wscons/wscons_callbacks.h>
52
53#include "akbd.h"
54#include "adbkbd.h"
55#include "wsdisplay.h"
56#include "ofb.h"
57#include "isa.h"
58
59#include "zsc.h"
60#if NZSC > 0
61#include <machine/z8530var.h>
62#endif
63
64#include "adb.h"
65#if (NADB > 0)
66#include <macppc/dev/adbvar.h>
67#endif
68
69#include "ukbd.h"
70#if (NUKBD > 0)
71#include <dev/usb/ukbdvar.h>
72struct usb_kbd_ihandles {
73	struct usb_kbd_ihandles *next;
74	int ihandle;
75};
76#endif
77
78#include "zstty.h"
79#if (NZSTTY > 0)
80#include <dev/ic/z8530reg.h>
81extern struct consdev consdev_zs;
82#endif
83
84#include "pckbc.h"
85#if (NPCKBC > 0)
86#include <dev/isa/isareg.h>
87#include <dev/ic/i8042reg.h>
88#include <dev/ic/pckbcvar.h>
89#endif
90
91extern int console_node, console_instance;
92
93int chosen, stdin, stdout;
94int ofkbd_ihandle;
95
96static void cninit_kd(void);
97static void ofwoea_bootstrap_console(void);
98static int ofwbootcons_cngetc(dev_t);
99static void ofwbootcons_cnputc(dev_t, int);
100
101/*#define OFDEBUG*/
102
103struct consdev consdev_ofwbootcons = {
104	NULL, NULL,
105	ofwbootcons_cngetc,
106	ofwbootcons_cnputc,
107	nullcnpollc,
108	NULL, NULL, NULL, NODEV, CN_INTERNAL,
109};
110
111#ifdef OFDEBUG
112void ofprint(const char *, ...);
113
114void ofprint(const char *blah, ...)
115{
116	va_list va;
117	char buf[256];
118	int len;
119
120	va_start(va, blah);
121	len = vsnprintf(buf, sizeof(buf), blah, va);
122	OF_write(console_instance, buf, len);
123}
124
125#define OFPRINTF ofprint
126#else
127#define OFPRINTF while(0) printf
128#endif
129
130void
131cninit(void)
132{
133	char name[32];
134
135	ofwoea_bootstrap_console();
136
137	OFPRINTF("console node: %08x\n", console_node);
138
139	if (console_node == -1)
140		goto nocons;
141
142	memset(name, 0, sizeof(name));
143	if (OF_getprop(console_node, "device_type", name, sizeof(name)) == -1)
144		goto nocons;
145
146	OFPRINTF("console type: %s\n", name);
147
148	if (strcmp(name, "serial") == 0) {
149		struct consdev *cp;
150
151#ifdef PMAC_G5
152		/* The MMU hasn't been initialized yet, use failsafe for now */
153		cp = &failsafe_cons;
154		cn_tab = cp;
155		(*cp->cn_probe)(cp);
156		(*cp->cn_init)(cp);
157		aprint_verbose("Early G5 console initialized\n");
158		return;
159#endif /* PMAC_G5 */
160
161#if (NZSTTY > 0) && !defined(MAMBO)
162		OF_getprop(console_node, "name", name, sizeof(name));
163		if (strcmp(name, "ch-a") == 0 || strcmp(name, "ch-b") == 0) {
164			cp = &consdev_zs;
165			(*cp->cn_probe)(cp);
166			(*cp->cn_init)(cp);
167			cn_tab = cp;
168		}
169		return;
170#endif /* NZTTY */
171
172		/* fallback to OFW boot console */
173		cp = &consdev_ofwbootcons;
174		cn_tab = cp;
175		return;
176	}
177	else
178		cninit_kd();
179nocons:
180	return;
181}
182
183
184static void
185cninit_kd(void)
186{
187	int kstdin, node;
188	char name[16];
189#if (NAKBD > 0) || (NADBKBD > 0)
190	int akbd;
191#endif
192#if NUKBD > 0
193	struct usb_kbd_ihandles *ukbds;
194	int ukbd;
195#endif
196
197	/*
198	 * Attach the console output now (so we can see debugging messages,
199	 * if any).
200	 */
201#if NWSDISPLAY > 0
202	rascons_cnattach();
203#endif
204
205	/*
206	 * We must determine which keyboard type we have.
207	 */
208	if (OF_getprop(chosen, "stdin", &kstdin, sizeof(kstdin))
209	    != sizeof(kstdin)) {
210		printf("WARNING: no `stdin' property in /chosen\n");
211		return;
212	}
213
214	node = OF_instance_to_package(kstdin);
215	memset(name, 0, sizeof(name));
216	OF_getprop(node, "name", name, sizeof(name));
217	if (strcmp(name, "keyboard") != 0) {
218		printf("WARNING: stdin is not a keyboard: %s\n", name);
219		return;
220	}
221
222	memset(name, 0, sizeof(name));
223	OF_getprop(OF_parent(node), "name", name, sizeof(name));
224#if NAKBD > 0
225	if (strcmp(name, "adb") == 0) {
226		printf("console keyboard type: ADB\n");
227		akbd_cnattach();
228		goto kbd_found;
229	}
230#endif
231#if NADBKBD > 0
232	if (strcmp(name, "adb") == 0) {
233		printf("console keyboard type: ADB\n");
234		adbkbd_cnattach();
235		goto kbd_found;
236	}
237#endif
238#if NPCKBC > 0
239	if (strcmp(name, "isa") == 0) {
240		printf("console keyboard type: PC Keyboard\n");
241		pckbc_cnattach(&genppc_isa_io_space_tag, IO_KBD, KBCMDP,
242		    PCKBC_KBD_SLOT);
243		goto kbd_found;
244	}
245#endif
246
247	/*
248	 * It is not obviously an ADB/PC keyboard. Could be USB,
249	 * or ADB on some firmware versions (e.g.: iBook G4)
250	 * This is not enough, we have a few more problems:
251	 *
252	 *	(1) The stupid Macintosh firmware uses a
253	 *	    `psuedo-hid' (no typo) or `pseudo-hid',
254	 *	    which apparently merges all keyboards
255	 *	    input into a single input stream.
256	 *	    Because of this, we can't actually
257	 *	    determine which controller or keyboard
258	 *	    is really the console keyboard!
259	 *
260	 *	(2) Even if we could, the keyboard can be USB,
261	 *	    and this requires a lot of the kernel to
262	 *	    be running in order for it to work.
263	 *
264	 *      (3) If the keyboard is behind isa, we don't have enough
265	 * 	    kernel setup to use it yet, so punt to the ofroutines.
266	 *
267	 * So, what we do is this:
268	 *
269	 *	(1) First check for OpenFirmware implementation
270	 *	    that will not let us distinguish between
271	 *	    USB and ADB. In that situation, try attaching
272	 *	    anything as we can, and hope things get better
273	 *	    at autoconfiguration time.
274	 *
275	 *	(2) Assume the keyboard is USB.
276	 *	    Tell the ukbd driver that it is the console.
277	 *	    At autoconfiguration time, it will attach the
278	 *	    first USB keyboard instance as the console
279	 *	    keyboard.
280	 *
281	 *	(3) Until then, so that we have _something_, we
282	 *	    use the OpenFirmware I/O facilities to read
283	 *	    the keyboard.
284	 */
285
286	/*
287	 * stdin is /pseudo-hid/keyboard.  There is no
288	 * `adb-kbd-ihandle or `usb-kbd-ihandles methods
289	 * available. Try attaching as ADB.
290	 * But only if ADB support is actually present.
291	 *
292	 * XXX This must be called before pmap_bootstrap().
293	 */
294	if (strcmp(name, "pseudo-hid") == 0) {
295		int adb_node;
296
297		adb_node = OF_finddevice("/pci/mac-io/via-pmu/adb");
298		if (adb_node > 0) {
299			printf("ADB support found\n");
300#if NAKBD > 0
301			akbd_cnattach();
302#endif
303#if NADBKBD > 0
304			adbkbd_cnattach();
305#endif
306		} else {
307			/* must be USB */
308			printf("No ADB support present, assuming USB "
309			       "keyboard\n");
310#if NUKBD > 0
311			ukbd_cnattach();
312#endif
313		}
314		goto kbd_found;
315	}
316
317	/*
318	 * stdin is /psuedo-hid/keyboard.  Test `adb-kbd-ihandle and
319	 * `usb-kbd-ihandles to figure out the real keyboard(s).
320	 *
321	 * XXX This must be called before pmap_bootstrap().
322	 */
323
324#if NUKBD > 0
325	if (OF_call_method("`usb-kbd-ihandles", stdin, 0, 1, &ukbds) >= 0 &&
326	    ukbds != NULL && ukbds->ihandle != 0 &&
327	    OF_instance_to_package(ukbds->ihandle) != -1) {
328		printf("usb-kbd-ihandles matches\n");
329		printf("console keyboard type: USB\n");
330		ukbd_cnattach();
331		goto kbd_found;
332	}
333	/* Try old method name. */
334	if (OF_call_method("`usb-kbd-ihandle", kstdin, 0, 1, &ukbd) >= 0 &&
335	    ukbd != 0 &&
336	    OF_instance_to_package(ukbd) != -1) {
337		printf("usb-kbd-ihandle matches\n");
338		printf("console keyboard type: USB\n");
339		kstdin = ukbd;
340		ukbd_cnattach();
341		goto kbd_found;
342	}
343#endif
344
345#if (NAKBD > 0) || (NADBKBD > 0)
346	if (OF_call_method("`adb-kbd-ihandle", kstdin, 0, 1, &akbd) >= 0 &&
347	    akbd != 0 &&
348	    OF_instance_to_package(akbd) != -1) {
349		printf("adb-kbd-ihandle matches\n");
350		printf("console keyboard type: ADB\n");
351		kstdin = akbd;
352#if NAKBD > 0
353		akbd_cnattach();
354#endif
355#if NADBKBD > 0
356		adbkbd_cnattach();
357#endif
358		goto kbd_found;
359	}
360#endif
361
362#if NUKBD > 0
363	/*
364	 * XXX Old firmware does not have `usb-kbd-ihandles method.  Assume
365	 * XXX USB keyboard anyway.
366	 */
367	printf("defaulting to USB...");
368	printf("console keyboard type: USB\n");
369	ukbd_cnattach();
370	goto kbd_found;
371#endif
372
373	/*
374	 * No keyboard is found.  Just return.
375	 */
376	printf("no console keyboard\n");
377	return;
378
379kbd_found:;
380#if NAKBD + NUKBD + NADBKBD + NPCKBC > 0
381	/*
382	 * XXX This is a little gross, but we don't get to call
383	 * XXX wskbd_cnattach() twice.
384	 */
385	ofkbd_ihandle = kstdin;
386#if NWSDISPLAY > 0
387	wsdisplay_set_cons_kbd(ofkbd_cngetc, NULL, NULL);
388#endif
389#endif
390}
391
392/*
393 * Bootstrap console keyboard routines, using OpenFirmware I/O.
394 */
395int
396ofkbd_cngetc(dev_t dev)
397{
398	u_char c = '\0';
399	int len;
400
401	do {
402		len = OF_read(ofkbd_ihandle, &c, 1);
403	} while (len != 1);
404
405	return c;
406}
407
408/*
409 * Bootstrap console support functions
410 */
411
412static int
413ofwbootcons_cngetc(dev_t dev)
414{
415	unsigned char ch = '\0';
416	int l;
417
418	while ((l = OF_read(stdin, &ch, 1)) != 1)
419		if (l != -2 && l != 0)
420			return -1;
421	return ch;
422}
423
424static void
425ofwbootcons_cnputc(dev_t dev, int c)
426{
427	char ch = c;
428
429	OF_write(stdout, &ch, 1);
430}
431
432void
433ofwoea_consinit(void)
434{
435	static int initted = 0;
436
437	if (initted)
438		return;
439
440	initted = 1;
441	cninit();
442}
443
444static void
445ofwoea_bootstrap_console(void)
446{
447	int node;
448
449	chosen = OF_finddevice("/chosen");
450	if (chosen == -1)
451		goto nocons;
452
453	if (OF_getprop(chosen, "stdout", &stdout,
454	    sizeof(stdout)) != sizeof(stdout))
455		goto nocons;
456	if (OF_getprop(chosen, "stdin", &stdin,
457	    sizeof(stdin)) != sizeof(stdin))
458		goto nocons;
459	node = OF_instance_to_package(stdout);
460	console_node = node;
461	console_instance = stdout;
462
463	return;
464nocons:
465	panic("No /chosen could be found!\n");
466	console_node = -1;
467	return;
468}
469