1 /* $OpenBSD: comkbd_ebus.c,v 1.21 2011/03/18 21:01:17 miod Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Jason L. Wright (jason@thought.net) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Effort sponsored in part by the Defense Advanced Research Projects 29 * Agency (DARPA) and Air Force Research Laboratory, Air Force 30 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 31 * 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/proc.h> 37 #include <sys/device.h> 38 #include <sys/conf.h> 39 #include <sys/file.h> 40 #include <sys/ioctl.h> 41 #include <sys/malloc.h> 42 #include <sys/tty.h> 43 #include <sys/time.h> 44 #include <sys/kernel.h> 45 #include <sys/syslog.h> 46 47 #include <machine/bus.h> 48 #include <machine/autoconf.h> 49 #include <machine/openfirm.h> 50 51 #include <sparc64/dev/ebusreg.h> 52 #include <sparc64/dev/ebusvar.h> 53 54 #include <dev/wscons/wsconsio.h> 55 #include <dev/wscons/wskbdvar.h> 56 57 #include <dev/sun/sunkbdreg.h> 58 #include <dev/sun/sunkbdvar.h> 59 60 #include <dev/ic/comreg.h> 61 #include <dev/ic/comvar.h> 62 #include <dev/ic/ns16550reg.h> 63 64 #include <dev/cons.h> 65 66 #define COMK_RX_RING 64 67 #define COMK_TX_RING 64 68 69 struct comkbd_softc { 70 struct sunkbd_softc sc_base; 71 72 bus_space_tag_t sc_iot; /* bus tag */ 73 bus_space_handle_t sc_ioh; /* bus handle */ 74 void *sc_ih, *sc_si; /* interrupt vectors */ 75 76 u_int sc_rxcnt; 77 u_int8_t sc_rxbuf[COMK_RX_RING]; 78 u_int8_t *sc_rxbeg, *sc_rxend, *sc_rxget, *sc_rxput; 79 80 u_int sc_txcnt; 81 u_int8_t sc_txbuf[COMK_TX_RING]; 82 u_int8_t *sc_txbeg, *sc_txend, *sc_txget, *sc_txput; 83 84 u_int8_t sc_ier; 85 }; 86 87 #define COM_WRITE(sc,r,v) \ 88 bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v)) 89 #define COM_READ(sc,r) \ 90 bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r)) 91 92 int comkbd_match(struct device *, void *, void *); 93 void comkbd_attach(struct device *, struct device *, void *); 94 int comkbd_iskbd(int); 95 96 /* wskbd glue */ 97 void comkbd_cnpollc(void *, int); 98 void comkbd_cngetc(void *, u_int *, int *); 99 100 /* internals */ 101 int comkbd_enqueue(void *, u_int8_t *, u_int); 102 int comkbd_init(struct comkbd_softc *); 103 void comkbd_putc(struct comkbd_softc *, u_int8_t); 104 int comkbd_intr(void *); 105 void comkbd_soft(void *); 106 107 struct cfattach comkbd_ca = { 108 sizeof(struct comkbd_softc), comkbd_match, comkbd_attach 109 }; 110 111 struct cfdriver comkbd_cd = { 112 NULL, "comkbd", DV_DULL 113 }; 114 115 const char *comkbd_names[] = { 116 "su", 117 "su_pnp", 118 NULL 119 }; 120 121 struct wskbd_consops comkbd_consops = { 122 comkbd_cngetc, 123 comkbd_cnpollc 124 }; 125 126 int 127 comkbd_iskbd(node) 128 int node; 129 { 130 if (OF_getproplen(node, "keyboard") == 0) 131 return (10); 132 return (0); 133 } 134 135 int 136 comkbd_match(parent, match, aux) 137 struct device *parent; 138 void *match; 139 void *aux; 140 { 141 struct ebus_attach_args *ea = aux; 142 int i; 143 144 for (i = 0; comkbd_names[i]; i++) 145 if (strcmp(ea->ea_name, comkbd_names[i]) == 0) 146 return (comkbd_iskbd(ea->ea_node)); 147 148 if (strcmp(ea->ea_name, "serial") == 0) { 149 char compat[80]; 150 151 if ((i = OF_getproplen(ea->ea_node, "compatible")) && 152 OF_getprop(ea->ea_node, "compatible", compat, 153 sizeof(compat)) == i) { 154 if (strcmp(compat, "su16550") == 0 || 155 strcmp(compat, "su") == 0) 156 return (comkbd_iskbd(ea->ea_node)); 157 } 158 } 159 return (0); 160 } 161 162 void 163 comkbd_attach(parent, self, aux) 164 struct device *parent, *self; 165 void *aux; 166 { 167 struct comkbd_softc *sc = (void *)self; 168 struct sunkbd_softc *ss = (void *)sc; 169 struct ebus_attach_args *ea = aux; 170 struct wskbddev_attach_args a; 171 int console; 172 173 ss->sc_sendcmd = comkbd_enqueue; 174 timeout_set(&ss->sc_bellto, sunkbd_bellstop, sc); 175 176 sc->sc_iot = ea->ea_memtag; 177 178 sc->sc_rxget = sc->sc_rxput = sc->sc_rxbeg = sc->sc_rxbuf; 179 sc->sc_rxend = sc->sc_rxbuf + COMK_RX_RING; 180 sc->sc_rxcnt = 0; 181 182 sc->sc_txget = sc->sc_txput = sc->sc_txbeg = sc->sc_txbuf; 183 sc->sc_txend = sc->sc_txbuf + COMK_TX_RING; 184 sc->sc_txcnt = 0; 185 186 console = (ea->ea_node == OF_instance_to_package(OF_stdin())); 187 188 sc->sc_si = softintr_establish(IPL_TTY, comkbd_soft, sc); 189 if (sc->sc_si == NULL) { 190 printf(": can't get soft intr\n"); 191 return; 192 } 193 194 /* Use prom address if available, otherwise map it. */ 195 if (ea->ea_nvaddrs && bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0, 196 BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) == 0) { 197 sc->sc_iot = ea->ea_memtag; 198 } else if (ebus_bus_map(ea->ea_memtag, 0, 199 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), 200 ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { 201 sc->sc_iot = ea->ea_memtag; 202 } else if (ebus_bus_map(ea->ea_iotag, 0, 203 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), 204 ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { 205 sc->sc_iot = ea->ea_iotag; 206 } else { 207 printf(": can't map register space\n"); 208 return; 209 } 210 211 sc->sc_ih = bus_intr_establish(sc->sc_iot, 212 ea->ea_intrs[0], IPL_TTY, 0, comkbd_intr, sc, self->dv_xname); 213 if (sc->sc_ih == NULL) { 214 printf(": can't get hard intr\n"); 215 return; 216 } 217 218 if (comkbd_init(sc) == 0) { 219 return; 220 } 221 222 ss->sc_click = 223 strcmp(getpropstring(optionsnode, "keyboard-click?"), "true") == 0; 224 sunkbd_setclick(ss, ss->sc_click); 225 226 a.console = console; 227 if (ISTYPE5(ss->sc_layout)) { 228 a.keymap = &sunkbd5_keymapdata; 229 #ifndef SUNKBD5_LAYOUT 230 if (ss->sc_layout < MAXSUNLAYOUT && 231 sunkbd_layouts[ss->sc_layout] != -1) 232 sunkbd5_keymapdata.layout = 233 sunkbd_layouts[ss->sc_layout]; 234 #endif 235 } else { 236 a.keymap = &sunkbd_keymapdata; 237 #ifndef SUNKBD_LAYOUT 238 if (ss->sc_layout < MAXSUNLAYOUT && 239 sunkbd_layouts[ss->sc_layout] != -1) 240 sunkbd_keymapdata.layout = 241 sunkbd_layouts[ss->sc_layout]; 242 #endif 243 } 244 a.accessops = &sunkbd_accessops; 245 a.accesscookie = sc; 246 247 if (console) { 248 cn_tab->cn_dev = makedev(77, ss->sc_dev.dv_unit); /* XXX */ 249 cn_tab->cn_pollc = wskbd_cnpollc; 250 cn_tab->cn_getc = wskbd_cngetc; 251 wskbd_cnattach(&comkbd_consops, sc, a.keymap); 252 sc->sc_ier = IER_ETXRDY | IER_ERXRDY; 253 COM_WRITE(sc, com_ier, sc->sc_ier); 254 COM_READ(sc, com_iir); 255 COM_WRITE(sc, com_mcr, MCR_IENABLE | MCR_DTR | MCR_RTS); 256 } 257 258 sunkbd_attach(ss, &a); 259 } 260 261 void 262 comkbd_cnpollc(vsc, on) 263 void *vsc; 264 int on; 265 { 266 } 267 268 void 269 comkbd_cngetc(v, type, data) 270 void *v; 271 u_int *type; 272 int *data; 273 { 274 struct comkbd_softc *sc = v; 275 int s; 276 u_int8_t c; 277 278 s = splhigh(); 279 while (1) { 280 if (COM_READ(sc, com_lsr) & LSR_RXRDY) 281 break; 282 } 283 c = COM_READ(sc, com_data); 284 COM_READ(sc, com_iir); 285 splx(s); 286 287 sunkbd_decode(c, type, data); 288 } 289 290 void 291 comkbd_putc(sc, c) 292 struct comkbd_softc *sc; 293 u_int8_t c; 294 { 295 int s, timo; 296 297 s = splhigh(); 298 299 timo = 150000; 300 while (--timo) { 301 if (COM_READ(sc, com_lsr) & LSR_TXRDY) 302 break; 303 } 304 305 COM_WRITE(sc, com_data, c); 306 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, COM_NPORTS, 307 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 308 309 timo = 150000; 310 while (--timo) { 311 if (COM_READ(sc, com_lsr) & LSR_TXRDY) 312 break; 313 } 314 315 splx(s); 316 } 317 318 int 319 comkbd_enqueue(v, buf, buflen) 320 void *v; 321 u_int8_t *buf; 322 u_int buflen; 323 { 324 struct comkbd_softc *sc = v; 325 int s; 326 u_int i; 327 328 s = spltty(); 329 330 /* See if there is room... */ 331 if ((sc->sc_txcnt + buflen) > COMK_TX_RING) { 332 splx(s); 333 return (-1); 334 } 335 336 for (i = 0; i < buflen; i++) { 337 *sc->sc_txget = *buf; 338 buf++; 339 sc->sc_txcnt++; 340 sc->sc_txget++; 341 if (sc->sc_txget == sc->sc_txend) 342 sc->sc_txget = sc->sc_txbeg; 343 } 344 345 comkbd_soft(sc); 346 347 splx(s); 348 return (0); 349 } 350 351 void 352 comkbd_soft(vsc) 353 void *vsc; 354 { 355 struct comkbd_softc *sc = vsc; 356 struct sunkbd_softc *ss = (void *)sc; 357 u_int8_t cbuf[SUNKBD_MAX_INPUT_SIZE], *cptr; 358 u_int8_t c; 359 360 cptr = cbuf; 361 while (sc->sc_rxcnt) { 362 *cptr++ = *sc->sc_rxget; 363 if (++sc->sc_rxget == sc->sc_rxend) 364 sc->sc_rxget = sc->sc_rxbeg; 365 sc->sc_rxcnt--; 366 if (cptr - cbuf == sizeof cbuf) { 367 sunkbd_input(ss, cbuf, cptr - cbuf); 368 cptr = cbuf; 369 } 370 } 371 if (cptr != cbuf) 372 sunkbd_input(ss, cbuf, cptr - cbuf); 373 374 if (sc->sc_txcnt) { 375 c = sc->sc_ier | IER_ETXRDY; 376 if (c != sc->sc_ier) { 377 COM_WRITE(sc, com_ier, c); 378 sc->sc_ier = c; 379 } 380 if (COM_READ(sc, com_lsr) & LSR_TXRDY) { 381 sc->sc_txcnt--; 382 COM_WRITE(sc, com_data, *sc->sc_txput); 383 if (++sc->sc_txput == sc->sc_txend) 384 sc->sc_txput = sc->sc_txbeg; 385 } 386 } 387 } 388 389 int 390 comkbd_intr(vsc) 391 void *vsc; 392 { 393 struct comkbd_softc *sc = vsc; 394 u_int8_t iir, lsr, data; 395 int needsoft = 0; 396 397 /* Nothing to do */ 398 iir = COM_READ(sc, com_iir); 399 if (iir & IIR_NOPEND) 400 return (0); 401 402 for (;;) { 403 lsr = COM_READ(sc, com_lsr); 404 if (lsr & LSR_RXRDY) { 405 needsoft = 1; 406 407 do { 408 data = COM_READ(sc, com_data); 409 if (sc->sc_rxcnt != COMK_RX_RING) { 410 *sc->sc_rxput = data; 411 if (++sc->sc_rxput == sc->sc_rxend) 412 sc->sc_rxput = sc->sc_rxbeg; 413 sc->sc_rxcnt++; 414 } 415 lsr = COM_READ(sc, com_lsr); 416 } while (lsr & LSR_RXRDY); 417 } 418 419 if (lsr & LSR_TXRDY) { 420 if (sc->sc_txcnt == 0) { 421 /* Nothing further to send */ 422 sc->sc_ier &= ~IER_ETXRDY; 423 COM_WRITE(sc, com_ier, sc->sc_ier); 424 } else 425 needsoft = 1; 426 } 427 428 iir = COM_READ(sc, com_iir); 429 if (iir & IIR_NOPEND) 430 break; 431 } 432 433 if (needsoft) 434 softintr_schedule(sc->sc_si); 435 436 return (1); 437 } 438 439 int 440 comkbd_init(sc) 441 struct comkbd_softc *sc; 442 { 443 struct sunkbd_softc *ss = (void *)sc; 444 u_int8_t stat, c; 445 int tries; 446 447 for (tries = 5; tries != 0; tries--) { 448 int ltries; 449 450 ss->sc_leds = 0; 451 ss->sc_layout = -1; 452 453 /* Send reset request */ 454 comkbd_putc(sc, SKBD_CMD_RESET); 455 456 ltries = 1000; 457 while (--ltries > 0) { 458 stat = COM_READ(sc,com_lsr); 459 if (stat & LSR_RXRDY) { 460 c = COM_READ(sc, com_data); 461 462 sunkbd_raw(ss, c); 463 if (ss->sc_kbdstate == SKBD_STATE_RESET) 464 break; 465 } 466 DELAY(1000); 467 } 468 if (ltries == 0) 469 continue; 470 471 /* Wait for reset to finish. */ 472 ltries = 1000; 473 while (--ltries > 0) { 474 stat = COM_READ(sc, com_lsr); 475 if (stat & LSR_RXRDY) { 476 c = COM_READ(sc, com_data); 477 sunkbd_raw(ss, c); 478 if (ss->sc_kbdstate == SKBD_STATE_GETKEY) 479 break; 480 } 481 DELAY(1000); 482 } 483 if (ltries == 0) 484 continue; 485 486 /* Some Sun<=>PS/2 converters need some delay here */ 487 DELAY(5000); 488 489 /* Send layout request */ 490 comkbd_putc(sc, SKBD_CMD_LAYOUT); 491 492 ltries = 1000; 493 while (--ltries > 0) { 494 stat = COM_READ(sc, com_lsr); 495 if (stat & LSR_RXRDY) { 496 c = COM_READ(sc, com_data); 497 sunkbd_raw(ss, c); 498 if (ss->sc_layout != -1) 499 break; 500 } 501 DELAY(1000); 502 } 503 if (ltries != 0) 504 break; 505 } 506 if (tries == 0) 507 printf(": no keyboard\n"); 508 else 509 printf(": layout %d\n", ss->sc_layout); 510 511 return tries; 512 } 513