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