1 /* $OpenBSD: comkbd_ebus.c,v 1.24 2021/10/24 17:05:03 mpi 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/ioctl.h> 40 #include <sys/malloc.h> 41 #include <sys/tty.h> 42 #include <sys/time.h> 43 #include <sys/kernel.h> 44 #include <sys/syslog.h> 45 46 #include <machine/bus.h> 47 #include <machine/autoconf.h> 48 #include <machine/openfirm.h> 49 50 #include <sparc64/dev/ebusreg.h> 51 #include <sparc64/dev/ebusvar.h> 52 53 #include <dev/wscons/wsconsio.h> 54 #include <dev/wscons/wskbdvar.h> 55 56 #include <dev/sun/sunkbdreg.h> 57 #include <dev/sun/sunkbdvar.h> 58 59 #include <dev/ic/comreg.h> 60 #include <dev/ic/comvar.h> 61 #include <dev/ic/ns16550reg.h> 62 63 #include <dev/cons.h> 64 65 #define COMK_RX_RING 64 66 #define COMK_TX_RING 64 67 68 struct comkbd_softc { 69 struct sunkbd_softc sc_base; 70 71 bus_space_tag_t sc_iot; /* bus tag */ 72 bus_space_handle_t sc_ioh; /* bus handle */ 73 void *sc_ih, *sc_si; /* interrupt vectors */ 74 75 u_int sc_rxcnt; 76 u_int8_t sc_rxbuf[COMK_RX_RING]; 77 u_int8_t *sc_rxbeg, *sc_rxend, *sc_rxget, *sc_rxput; 78 79 u_int sc_txcnt; 80 u_int8_t sc_txbuf[COMK_TX_RING]; 81 u_int8_t *sc_txbeg, *sc_txend, *sc_txget, *sc_txput; 82 83 u_int8_t sc_ier; 84 }; 85 86 #define COM_WRITE(sc,r,v) \ 87 bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v)) 88 #define COM_READ(sc,r) \ 89 bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r)) 90 91 int comkbd_match(struct device *, void *, void *); 92 void comkbd_attach(struct device *, struct device *, void *); 93 int comkbd_iskbd(int); 94 95 /* wskbd glue */ 96 void comkbd_cnpollc(void *, int); 97 void comkbd_cngetc(void *, u_int *, int *); 98 99 /* internals */ 100 int comkbd_enqueue(void *, u_int8_t *, u_int); 101 int comkbd_init(struct comkbd_softc *); 102 void comkbd_putc(struct comkbd_softc *, u_int8_t); 103 int comkbd_intr(void *); 104 void comkbd_soft(void *); 105 106 const struct cfattach comkbd_ca = { 107 sizeof(struct comkbd_softc), comkbd_match, comkbd_attach 108 }; 109 110 struct cfdriver comkbd_cd = { 111 NULL, "comkbd", DV_DULL 112 }; 113 114 const char *comkbd_names[] = { 115 "su", 116 "su_pnp", 117 NULL 118 }; 119 120 struct wskbd_consops comkbd_consops = { 121 comkbd_cngetc, 122 comkbd_cnpollc 123 }; 124 125 int 126 comkbd_iskbd(int node) 127 { 128 if (OF_getproplen(node, "keyboard") == 0) 129 return (10); 130 return (0); 131 } 132 133 int 134 comkbd_match(struct device *parent, void *match, void *aux) 135 { 136 struct ebus_attach_args *ea = aux; 137 int i; 138 139 for (i = 0; comkbd_names[i]; i++) 140 if (strcmp(ea->ea_name, comkbd_names[i]) == 0) 141 return (comkbd_iskbd(ea->ea_node)); 142 143 if (strcmp(ea->ea_name, "serial") == 0) { 144 char compat[80]; 145 146 if ((i = OF_getproplen(ea->ea_node, "compatible")) && 147 OF_getprop(ea->ea_node, "compatible", compat, 148 sizeof(compat)) == i) { 149 if (strcmp(compat, "su16550") == 0 || 150 strcmp(compat, "su") == 0) 151 return (comkbd_iskbd(ea->ea_node)); 152 } 153 } 154 return (0); 155 } 156 157 void 158 comkbd_attach(struct device *parent, struct device *self, void *aux) 159 { 160 struct comkbd_softc *sc = (void *)self; 161 struct sunkbd_softc *ss = (void *)sc; 162 struct ebus_attach_args *ea = aux; 163 struct wskbddev_attach_args a; 164 int console; 165 166 ss->sc_sendcmd = comkbd_enqueue; 167 timeout_set(&ss->sc_bellto, sunkbd_bellstop, sc); 168 169 sc->sc_iot = ea->ea_memtag; 170 171 sc->sc_rxget = sc->sc_rxput = sc->sc_rxbeg = sc->sc_rxbuf; 172 sc->sc_rxend = sc->sc_rxbuf + COMK_RX_RING; 173 sc->sc_rxcnt = 0; 174 175 sc->sc_txget = sc->sc_txput = sc->sc_txbeg = sc->sc_txbuf; 176 sc->sc_txend = sc->sc_txbuf + COMK_TX_RING; 177 sc->sc_txcnt = 0; 178 179 console = (ea->ea_node == OF_instance_to_package(OF_stdin())); 180 181 sc->sc_si = softintr_establish(IPL_TTY, comkbd_soft, sc); 182 if (sc->sc_si == NULL) { 183 printf(": can't get soft intr\n"); 184 return; 185 } 186 187 /* Use prom address if available, otherwise map it. */ 188 if (ea->ea_nvaddrs && bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0, 189 BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) == 0) { 190 sc->sc_iot = ea->ea_memtag; 191 } else if (ebus_bus_map(ea->ea_memtag, 0, 192 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), 193 ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { 194 sc->sc_iot = ea->ea_memtag; 195 } else if (ebus_bus_map(ea->ea_iotag, 0, 196 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), 197 ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { 198 sc->sc_iot = ea->ea_iotag; 199 } else { 200 printf(": can't map register space\n"); 201 return; 202 } 203 204 sc->sc_ih = bus_intr_establish(sc->sc_iot, 205 ea->ea_intrs[0], IPL_TTY, 0, comkbd_intr, sc, self->dv_xname); 206 if (sc->sc_ih == NULL) { 207 printf(": can't get hard intr\n"); 208 return; 209 } 210 211 if (comkbd_init(sc) == 0) { 212 return; 213 } 214 215 ss->sc_click = 216 strcmp(getpropstring(optionsnode, "keyboard-click?"), "true") == 0; 217 sunkbd_setclick(ss, ss->sc_click); 218 219 a.console = console; 220 if (ISTYPE5(ss->sc_layout)) { 221 a.keymap = &sunkbd5_keymapdata; 222 #ifndef SUNKBD5_LAYOUT 223 if (ss->sc_layout < MAXSUNLAYOUT && 224 sunkbd_layouts[ss->sc_layout] != -1) 225 sunkbd5_keymapdata.layout = 226 sunkbd_layouts[ss->sc_layout]; 227 #endif 228 } else { 229 a.keymap = &sunkbd_keymapdata; 230 #ifndef SUNKBD_LAYOUT 231 if (ss->sc_layout < MAXSUNLAYOUT && 232 sunkbd_layouts[ss->sc_layout] != -1) 233 sunkbd_keymapdata.layout = 234 sunkbd_layouts[ss->sc_layout]; 235 #endif 236 } 237 a.accessops = &sunkbd_accessops; 238 a.accesscookie = sc; 239 240 if (console) { 241 cn_tab->cn_dev = makedev(77, ss->sc_dev.dv_unit); /* XXX */ 242 cn_tab->cn_pollc = wskbd_cnpollc; 243 cn_tab->cn_getc = wskbd_cngetc; 244 wskbd_cnattach(&comkbd_consops, sc, a.keymap); 245 sc->sc_ier = IER_ETXRDY | IER_ERXRDY; 246 COM_WRITE(sc, com_ier, sc->sc_ier); 247 COM_READ(sc, com_iir); 248 COM_WRITE(sc, com_mcr, MCR_IENABLE | MCR_DTR | MCR_RTS); 249 } 250 251 sunkbd_attach(ss, &a); 252 } 253 254 void 255 comkbd_cnpollc(void *vsc, int on) 256 { 257 } 258 259 void 260 comkbd_cngetc(void *v, u_int *type, int *data) 261 { 262 struct comkbd_softc *sc = v; 263 int s; 264 u_int8_t c; 265 266 s = splhigh(); 267 while (1) { 268 if (COM_READ(sc, com_lsr) & LSR_RXRDY) 269 break; 270 } 271 c = COM_READ(sc, com_data); 272 COM_READ(sc, com_iir); 273 splx(s); 274 275 sunkbd_decode(c, type, data); 276 } 277 278 void 279 comkbd_putc(struct comkbd_softc *sc, u_int8_t c) 280 { 281 int s, timo; 282 283 s = splhigh(); 284 285 timo = 150000; 286 while (--timo) { 287 if (COM_READ(sc, com_lsr) & LSR_TXRDY) 288 break; 289 } 290 291 COM_WRITE(sc, com_data, c); 292 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, COM_NPORTS, 293 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 294 295 timo = 150000; 296 while (--timo) { 297 if (COM_READ(sc, com_lsr) & LSR_TXRDY) 298 break; 299 } 300 301 splx(s); 302 } 303 304 int 305 comkbd_enqueue(void *v, u_int8_t *buf, u_int buflen) 306 { 307 struct comkbd_softc *sc = v; 308 int s; 309 u_int i; 310 311 s = spltty(); 312 313 /* See if there is room... */ 314 if ((sc->sc_txcnt + buflen) > COMK_TX_RING) { 315 splx(s); 316 return (-1); 317 } 318 319 for (i = 0; i < buflen; i++) { 320 *sc->sc_txget = *buf; 321 buf++; 322 sc->sc_txcnt++; 323 sc->sc_txget++; 324 if (sc->sc_txget == sc->sc_txend) 325 sc->sc_txget = sc->sc_txbeg; 326 } 327 328 comkbd_soft(sc); 329 330 splx(s); 331 return (0); 332 } 333 334 void 335 comkbd_soft(void *vsc) 336 { 337 struct comkbd_softc *sc = vsc; 338 struct sunkbd_softc *ss = (void *)sc; 339 u_int8_t cbuf[SUNKBD_MAX_INPUT_SIZE], *cptr; 340 u_int8_t c; 341 342 cptr = cbuf; 343 while (sc->sc_rxcnt) { 344 *cptr++ = *sc->sc_rxget; 345 if (++sc->sc_rxget == sc->sc_rxend) 346 sc->sc_rxget = sc->sc_rxbeg; 347 sc->sc_rxcnt--; 348 if (cptr - cbuf == sizeof cbuf) { 349 sunkbd_input(ss, cbuf, cptr - cbuf); 350 cptr = cbuf; 351 } 352 } 353 if (cptr != cbuf) 354 sunkbd_input(ss, cbuf, cptr - cbuf); 355 356 if (sc->sc_txcnt) { 357 c = sc->sc_ier | IER_ETXRDY; 358 if (c != sc->sc_ier) { 359 COM_WRITE(sc, com_ier, c); 360 sc->sc_ier = c; 361 } 362 if (COM_READ(sc, com_lsr) & LSR_TXRDY) { 363 sc->sc_txcnt--; 364 COM_WRITE(sc, com_data, *sc->sc_txput); 365 if (++sc->sc_txput == sc->sc_txend) 366 sc->sc_txput = sc->sc_txbeg; 367 } 368 } 369 } 370 371 int 372 comkbd_intr(void *vsc) 373 { 374 struct comkbd_softc *sc = vsc; 375 u_int8_t iir, lsr, data; 376 int needsoft = 0; 377 378 /* Nothing to do */ 379 iir = COM_READ(sc, com_iir); 380 if (iir & IIR_NOPEND) 381 return (0); 382 383 for (;;) { 384 lsr = COM_READ(sc, com_lsr); 385 if (lsr & LSR_RXRDY) { 386 needsoft = 1; 387 388 do { 389 data = COM_READ(sc, com_data); 390 if (sc->sc_rxcnt != COMK_RX_RING) { 391 *sc->sc_rxput = data; 392 if (++sc->sc_rxput == sc->sc_rxend) 393 sc->sc_rxput = sc->sc_rxbeg; 394 sc->sc_rxcnt++; 395 } 396 lsr = COM_READ(sc, com_lsr); 397 } while (lsr & LSR_RXRDY); 398 } 399 400 if (lsr & LSR_TXRDY) { 401 if (sc->sc_txcnt == 0) { 402 /* Nothing further to send */ 403 sc->sc_ier &= ~IER_ETXRDY; 404 COM_WRITE(sc, com_ier, sc->sc_ier); 405 } else 406 needsoft = 1; 407 } 408 409 iir = COM_READ(sc, com_iir); 410 if (iir & IIR_NOPEND) 411 break; 412 } 413 414 if (needsoft) 415 softintr_schedule(sc->sc_si); 416 417 return (1); 418 } 419 420 int 421 comkbd_init(struct comkbd_softc *sc) 422 { 423 struct sunkbd_softc *ss = (void *)sc; 424 u_int8_t stat, c; 425 int tries; 426 427 for (tries = 5; tries != 0; tries--) { 428 int ltries; 429 430 ss->sc_leds = 0; 431 ss->sc_layout = -1; 432 433 /* Send reset request */ 434 comkbd_putc(sc, SKBD_CMD_RESET); 435 436 ltries = 1000; 437 while (--ltries > 0) { 438 stat = COM_READ(sc,com_lsr); 439 if (stat & LSR_RXRDY) { 440 c = COM_READ(sc, com_data); 441 442 sunkbd_raw(ss, c); 443 if (ss->sc_kbdstate == SKBD_STATE_RESET) 444 break; 445 } 446 DELAY(1000); 447 } 448 if (ltries == 0) 449 continue; 450 451 /* Wait for reset to finish. */ 452 ltries = 1000; 453 while (--ltries > 0) { 454 stat = COM_READ(sc, com_lsr); 455 if (stat & LSR_RXRDY) { 456 c = COM_READ(sc, com_data); 457 sunkbd_raw(ss, c); 458 if (ss->sc_kbdstate == SKBD_STATE_GETKEY) 459 break; 460 } 461 DELAY(1000); 462 } 463 if (ltries == 0) 464 continue; 465 466 /* Some Sun<=>PS/2 converters need some delay here */ 467 DELAY(5000); 468 469 /* Send layout request */ 470 comkbd_putc(sc, SKBD_CMD_LAYOUT); 471 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_layout != -1) 479 break; 480 } 481 DELAY(1000); 482 } 483 if (ltries != 0) 484 break; 485 } 486 if (tries == 0) 487 printf(": no keyboard\n"); 488 else 489 printf(": layout %d\n", ss->sc_layout); 490 491 return tries; 492 } 493