1/* $NetBSD: pckbc.c,v 1.65 2022/11/17 23:57:20 riastradh Exp $ */ 2 3/* 4 * Copyright (c) 2004 Ben Harris. 5 * Copyright (c) 1998 6 * Matthias Drochner. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: pckbc.c,v 1.65 2022/11/17 23:57:20 riastradh Exp $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/callout.h> 35#include <sys/kernel.h> 36#include <sys/proc.h> 37#include <sys/device.h> 38#include <sys/malloc.h> 39#include <sys/errno.h> 40#include <sys/queue.h> 41 42#include <sys/bus.h> 43 44#include <dev/ic/i8042reg.h> 45#include <dev/ic/pckbcvar.h> 46 47#include <dev/pckbport/pckbportvar.h> 48 49#include "locators.h" 50 51#include <sys/rndsource.h> 52 53/* data per slave device */ 54struct pckbc_slotdata { 55 int polling; /* don't process data in interrupt handler */ 56 int poll_data; /* data read from inr handler if polling */ 57 int poll_stat; /* status read from inr handler if polling */ 58 krndsource_t rnd_source; 59}; 60 61static void pckbc_init_slotdata(struct pckbc_slotdata *); 62static int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t); 63 64struct pckbc_internal pckbc_consdata; 65int pckbc_console_attached; 66 67static int pckbc_console; 68static struct pckbc_slotdata pckbc_cons_slotdata; 69 70static int pckbc_xt_translation(void *, pckbport_slot_t, int); 71static int pckbc_send_devcmd(void *, pckbport_slot_t, u_char); 72static void pckbc_slot_enable(void *, pckbport_slot_t, int); 73static void pckbc_intr_establish(void *, pckbport_slot_t); 74static void pckbc_set_poll(void *, pckbc_slot_t, int on); 75 76static int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t); 77 78static int pckbc_get8042cmd(struct pckbc_internal *); 79static int pckbc_put8042cmd(struct pckbc_internal *); 80 81void pckbc_cleanqueue(struct pckbc_slotdata *); 82void pckbc_cleanup(void *); 83int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char); 84void pckbc_start(struct pckbc_internal *, pckbc_slot_t); 85 86const char * const pckbc_slot_names[] = { "kbd", "aux" }; 87 88static const struct pckbport_accessops pckbc_ops = { 89 .t_xt_translation = pckbc_xt_translation, 90 .t_send_devcmd = pckbc_send_devcmd, 91 .t_poll_data1 = pckbc_poll_data1, 92 .t_slot_enable = pckbc_slot_enable, 93 .t_intr_establish = pckbc_intr_establish, 94 .t_set_poll = pckbc_set_poll, 95}; 96 97#define KBD_DELAY DELAY(8) 98 99static inline int 100pckbc_wait_output(bus_space_tag_t iot, bus_space_handle_t ioh_c) 101{ 102 u_int i; 103 104 for (i = 100000; i; i--) 105 if (!(bus_space_read_1(iot, ioh_c, 0) & KBS_IBF)) { 106 KBD_DELAY; 107 return (1); 108 } 109 return (0); 110} 111 112int 113pckbc_send_cmd(bus_space_tag_t iot, bus_space_handle_t ioh_c, u_char val) 114{ 115 if (!pckbc_wait_output(iot, ioh_c)) 116 return (0); 117 bus_space_write_1(iot, ioh_c, 0, val); 118 return (1); 119} 120 121/* 122 * Note: the spl games here are to deal with some strange PC kbd controllers 123 * in some system configurations. 124 * This is not canonical way to handle polling input. 125 */ 126int 127pckbc_poll_data1(void *pt, pckbc_slot_t slot) 128{ 129 struct pckbc_internal *t = pt; 130 struct pckbc_slotdata *q = t->t_slotdata[slot]; 131 int s; 132 u_char stat, c; 133 int i = 100; /* polls for ~100ms */ 134 int checkaux = t->t_haveaux; 135 136 s = splhigh(); 137 138 if (q && q->polling && q->poll_data != -1 && q->poll_stat != -1) { 139 stat = q->poll_stat; 140 c = q->poll_data; 141 q->poll_data = -1; 142 q->poll_stat = -1; 143 goto process; 144 } 145 146 for (; i; i--, delay(1000)) { 147 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0); 148 if (stat & KBS_DIB) { 149 c = bus_space_read_1(t->t_iot, t->t_ioh_d, 0); 150 151 process: 152 if (checkaux && (stat & 0x20)) { /* aux data */ 153 if (slot != PCKBC_AUX_SLOT) { 154#ifdef PCKBCDEBUG 155 printf("pckbc: lost aux 0x%x\n", c); 156#endif 157 continue; 158 } 159 } else { 160 if (slot == PCKBC_AUX_SLOT) { 161#ifdef PCKBCDEBUG 162 printf("pckbc: lost kbd 0x%x\n", c); 163#endif 164 continue; 165 } 166 } 167 splx(s); 168 return (c); 169 } 170 } 171 172 splx(s); 173 return (-1); 174} 175 176/* 177 * Get the current command byte. 178 */ 179static int 180pckbc_get8042cmd(struct pckbc_internal *t) 181{ 182 bus_space_tag_t iot = t->t_iot; 183 bus_space_handle_t ioh_c = t->t_ioh_c; 184 int data; 185 186 if (!pckbc_send_cmd(iot, ioh_c, K_RDCMDBYTE)) 187 return (0); 188 data = pckbc_poll_data1(t, PCKBC_KBD_SLOT); 189 if (data == -1) 190 return (0); 191 t->t_cmdbyte = data; 192 return (1); 193} 194 195/* 196 * Pass command byte to keyboard controller (8042). 197 */ 198static int 199pckbc_put8042cmd(struct pckbc_internal *t) 200{ 201 bus_space_tag_t iot = t->t_iot; 202 bus_space_handle_t ioh_d = t->t_ioh_d; 203 bus_space_handle_t ioh_c = t->t_ioh_c; 204 205 if (!pckbc_send_cmd(iot, ioh_c, K_LDCMDBYTE)) 206 return (0); 207 if (!pckbc_wait_output(iot, ioh_c)) 208 return (0); 209 bus_space_write_1(iot, ioh_d, 0, t->t_cmdbyte); 210 return (1); 211} 212 213static int 214pckbc_send_devcmd(void *pt, pckbc_slot_t slot, u_char val) 215{ 216 struct pckbc_internal *t = pt; 217 bus_space_tag_t iot = t->t_iot; 218 bus_space_handle_t ioh_d = t->t_ioh_d; 219 bus_space_handle_t ioh_c = t->t_ioh_c; 220 221 if (slot == PCKBC_AUX_SLOT) { 222 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE)) 223 return (0); 224 } 225 if (!pckbc_wait_output(iot, ioh_c)) 226 return (0); 227 bus_space_write_1(iot, ioh_d, 0, val); 228 return (1); 229} 230 231int 232pckbc_is_console(bus_space_tag_t iot, bus_addr_t addr) 233{ 234 if (pckbc_console && !pckbc_console_attached && 235 bus_space_is_equal(pckbc_consdata.t_iot, iot) && 236 pckbc_consdata.t_addr == addr) 237 return (1); 238 return (0); 239} 240 241static int 242pckbc_attach_slot(struct pckbc_softc *sc, pckbc_slot_t slot) 243{ 244 struct pckbc_internal *t = sc->id; 245 void *sdata; 246 device_t child; 247 int alloced = 0; 248 249 if (t->t_slotdata[slot] == NULL) { 250 sdata = malloc(sizeof(struct pckbc_slotdata), 251 M_DEVBUF, M_WAITOK); 252 t->t_slotdata[slot] = sdata; 253 pckbc_init_slotdata(t->t_slotdata[slot]); 254 alloced++; 255 } 256 257 child = pckbport_attach_slot(sc->sc_dv, t->t_pt, slot); 258 259 if (child == NULL && alloced) { 260 free(t->t_slotdata[slot], M_DEVBUF); 261 t->t_slotdata[slot] = NULL; 262 } 263 264 if (child != NULL && t->t_slotdata[slot] != NULL) { 265 memset(&t->t_slotdata[slot]->rnd_source, 0, 266 sizeof(t->t_slotdata[slot]->rnd_source)); 267 rnd_attach_source(&t->t_slotdata[slot]->rnd_source, 268 device_xname(child), RND_TYPE_TTY, RND_FLAG_DEFAULT); 269 } 270 271 return child != NULL; 272} 273 274void 275pckbc_attach(struct pckbc_softc *sc) 276{ 277 struct pckbc_internal *t; 278 bus_space_tag_t iot; 279 bus_space_handle_t ioh_d, ioh_c; 280 int res; 281 u_char cmdbits = 0; 282 283 t = sc->id; 284 iot = t->t_iot; 285 ioh_d = t->t_ioh_d; 286 ioh_c = t->t_ioh_c; 287 288 t->t_pt = pckbport_attach(t, &pckbc_ops); 289 if (t->t_pt == NULL) { 290 aprint_error(": attach failed\n"); 291 return; 292 } 293 294 /* flush */ 295 (void) pckbc_poll_data1(t, PCKBC_KBD_SLOT); 296 297 /* set initial cmd byte */ 298 if (!pckbc_put8042cmd(t)) { 299 aprint_error("pckbc: cmd word write error\n"); 300 return; 301 } 302 303/* 304 * XXX Don't check the keyboard port. There are broken keyboard controllers 305 * which don't pass the test but work normally otherwise. 306 */ 307#if 0 308 /* 309 * check kbd port ok 310 */ 311 if (!pckbc_send_cmd(iot, ioh_c, KBC_KBDTEST)) 312 return; 313 res = pckbc_poll_data1(t, PCKBC_KBD_SLOT); 314 315 /* 316 * Normally, we should get a "0" here. 317 * But there are keyboard controllers behaving differently. 318 */ 319 if (!(res == 0 || res == 0xfa || res == 0x01 || res == 0xab)) { 320 printf("pckbc: kbd port test: %x\n", res); 321 return; 322 } 323#ifdef PCKBCDEBUG 324 if (res != 0) 325 printf("pckbc: returned %x on kbd slot test\n", res); 326#endif 327#endif /* 0 */ 328 if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT)) 329 cmdbits |= KC8_KENABLE; 330 331 /* 332 * Check aux port ok. 333 * Avoid KBC_AUXTEST because it hangs some older controllers 334 * (eg UMC880?). 335 */ 336 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXECHO)) { 337 aprint_error("pckbc: aux echo error 1\n"); 338 goto nomouse; 339 } 340 if (!pckbc_wait_output(iot, ioh_c)) { 341 aprint_error("pckbc: aux echo error 2\n"); 342 goto nomouse; 343 } 344 t->t_haveaux = 1; 345 bus_space_write_1(iot, ioh_d, 0, 0x5a); /* a random value */ 346 res = pckbc_poll_data1(t, PCKBC_AUX_SLOT); 347 348 /* 349 * The following is needed to find the aux port on the Tadpole 350 * SPARCle. 351 */ 352 if (res == -1 && ISSET(t->t_flags, PCKBC_NEED_AUXWRITE)) { 353 /* Read of aux echo timed out, try again */ 354 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE)) 355 goto nomouse; 356 if (!pckbc_wait_output(iot, ioh_c)) 357 goto nomouse; 358 bus_space_write_1(iot, ioh_d, 0, 0x5a); 359 res = pckbc_poll_data1(t, PCKBC_AUX_SLOT); 360 } 361 if (res != -1) { 362 /* 363 * In most cases, the 0x5a gets echoed. 364 * Some older controllers (Gateway 2000 circa 1993) 365 * return 0xfe here. 366 * We are satisfied if there is anything in the 367 * aux output buffer. 368 */ 369 if (pckbc_attach_slot(sc, PCKBC_AUX_SLOT)) 370 cmdbits |= KC8_MENABLE; 371 } else { 372 373#ifdef PCKBCDEBUG 374 printf("pckbc: aux echo test failed\n"); 375#endif 376 t->t_haveaux = 0; 377 } 378 379nomouse: 380 /* enable needed interrupts */ 381 t->t_cmdbyte |= cmdbits; 382 if (!pckbc_put8042cmd(t)) 383 aprint_error("pckbc: cmd word write error\n"); 384} 385 386static void 387pckbc_init_slotdata(struct pckbc_slotdata *q) 388{ 389 390 q->polling = 0; 391} 392 393/* 394 * switch scancode translation on / off 395 * return nonzero on success 396 */ 397static int 398pckbc_xt_translation(void *self, pckbc_slot_t slot, int on) 399{ 400 struct pckbc_internal *t = self; 401 int ison; 402 403 if (ISSET(t->t_flags, PCKBC_CANT_TRANSLATE)) 404 return (-1); 405 406 if (slot != PCKBC_KBD_SLOT) { 407 /* translation only for kbd slot */ 408 if (on) 409 return (0); 410 else 411 return (1); 412 } 413 414 ison = t->t_cmdbyte & KC8_TRANS; 415 if ((on && ison) || (!on && !ison)) 416 return (1); 417 418 t->t_cmdbyte ^= KC8_TRANS; 419 if (!pckbc_put8042cmd(t)) 420 return (0); 421 422 /* read back to be sure */ 423 if (!pckbc_get8042cmd(t)) 424 return (0); 425 426 ison = t->t_cmdbyte & KC8_TRANS; 427 if ((on && ison) || (!on && !ison)) 428 return (1); 429 return (0); 430} 431 432static const struct pckbc_portcmd { 433 u_char cmd_en, cmd_dis; 434} pckbc_portcmd[2] = { 435 { 436 KBC_KBDENABLE, KBC_KBDDISABLE, 437 }, { 438 KBC_AUXENABLE, KBC_AUXDISABLE, 439 } 440}; 441 442void 443pckbc_slot_enable(void *self, pckbc_slot_t slot, int on) 444{ 445 struct pckbc_internal *t = (struct pckbc_internal *)self; 446 const struct pckbc_portcmd *cmd; 447 448 cmd = &pckbc_portcmd[slot]; 449 450 if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c, 451 on ? cmd->cmd_en : cmd->cmd_dis)) 452 printf("pckbc: pckbc_slot_enable(%d) failed\n", on); 453} 454 455static void 456pckbc_set_poll(void *self, pckbc_slot_t slot, int on) 457{ 458 struct pckbc_internal *t = (struct pckbc_internal *)self; 459 460 t->t_slotdata[slot]->polling = on; 461 462 if (on) { 463 t->t_slotdata[slot]->poll_data = -1; 464 t->t_slotdata[slot]->poll_stat = -1; 465 } else { 466 int s; 467 468 /* 469 * If disabling polling on a device that's been configured, 470 * make sure there are no bytes left in the FIFO, holding up 471 * the interrupt line. Otherwise we won't get any further 472 * interrupts. 473 */ 474 if (t->t_sc) { 475 s = spltty(); 476 pckbcintr(t->t_sc); 477 splx(s); 478 } 479 } 480} 481 482static void 483pckbc_intr_establish(void *pt, pckbport_slot_t slot) 484{ 485 struct pckbc_internal *t = pt; 486 487 (*t->t_sc->intr_establish)(t->t_sc, slot); 488} 489 490int 491pckbcintr_hard(void *vsc) 492{ 493 struct pckbc_softc *sc = (struct pckbc_softc *)vsc; 494 struct pckbc_internal *t = sc->id; 495 u_char stat; 496 pckbc_slot_t slot; 497 struct pckbc_slotdata *q; 498 int served = 0, data, next, s; 499 500 for(;;) { 501 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0); 502 if (!(stat & KBS_DIB)) 503 break; 504 505 served = 1; 506 507 slot = (t->t_haveaux && (stat & 0x20)) ? 508 PCKBC_AUX_SLOT : PCKBC_KBD_SLOT; 509 q = t->t_slotdata[slot]; 510 511 if (!q) { 512 /* XXX do something for live insertion? */ 513 printf("pckbc: no dev for slot %d\n", slot); 514 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0); 515 continue; 516 } 517 518 data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0); 519 520 rnd_add_uint32(&q->rnd_source, (stat<<8)|data); 521 522 if (q->polling) { 523 q->poll_data = data; 524 q->poll_stat = stat; 525 break; /* pckbc_poll_data() will get it */ 526 } 527 528#if 0 /* XXXBJH */ 529 if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data)) 530 continue; 531#endif 532 533 s = splhigh(); 534 next = (t->rbuf_write+1) % PCKBC_RBUF_SIZE; 535 if (next == t->rbuf_read) { 536 splx(s); 537 break; 538 } 539 t->rbuf[t->rbuf_write].data = data; 540 t->rbuf[t->rbuf_write].slot = slot; 541 t->rbuf_write = next; 542 splx(s); 543 } 544 545 return (served); 546} 547 548void 549pckbcintr_soft(void *vsc) 550{ 551 struct pckbc_softc *sc = vsc; 552 struct pckbc_internal *t = sc->id; 553 int data, slot, s; 554#ifndef __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS 555 int st; 556 557 st = spltty(); 558#endif 559 560 s = splhigh(); 561 while (t->rbuf_read != t->rbuf_write) { 562 slot = t->rbuf[t->rbuf_read].slot; 563 data = t->rbuf[t->rbuf_read].data; 564 t->rbuf_read = (t->rbuf_read+1) % PCKBC_RBUF_SIZE; 565 splx(s); 566 pckbportintr(t->t_pt, slot, data); 567 s = splhigh(); 568 } 569 splx(s); 570 571 572#ifndef __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS 573 splx(st); 574#endif 575} 576 577int 578pckbcintr(void *vsc) 579{ 580 struct pckbc_softc *sc = (struct pckbc_softc *)vsc; 581 struct pckbc_internal *t = sc->id; 582 u_char stat; 583 pckbc_slot_t slot; 584 struct pckbc_slotdata *q; 585 int served = 0, data; 586 587 for(;;) { 588 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0); 589 if (!(stat & KBS_DIB)) 590 break; 591 592 slot = (t->t_haveaux && (stat & 0x20)) ? 593 PCKBC_AUX_SLOT : PCKBC_KBD_SLOT; 594 q = t->t_slotdata[slot]; 595 596 if (q != NULL && q->polling) 597 return 0; 598 599 served = 1; 600 data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0); 601 602 if (q != NULL) 603 rnd_add_uint32(&q->rnd_source, (stat<<8)|data); 604 605 pckbportintr(t->t_pt, slot, data); 606 } 607 608 return (served); 609} 610 611int 612pckbc_cnattach(bus_space_tag_t iot, bus_addr_t addr, 613 bus_size_t cmd_offset, pckbc_slot_t slot, int flags) 614{ 615 bus_space_handle_t ioh_d, ioh_c; 616#ifdef PCKBC_CNATTACH_SELFTEST 617 int reply; 618#endif 619 int res = 0; 620 621 if (bus_space_map(iot, addr + KBDATAP, 1, 0, &ioh_d)) 622 return (ENXIO); 623 if (bus_space_map(iot, addr + cmd_offset, 1, 0, &ioh_c)) { 624 bus_space_unmap(iot, ioh_d, 1); 625 return (ENXIO); 626 } 627 628 memset(&pckbc_consdata, 0, sizeof(pckbc_consdata)); 629 pckbc_consdata.t_iot = iot; 630 pckbc_consdata.t_ioh_d = ioh_d; 631 pckbc_consdata.t_ioh_c = ioh_c; 632 pckbc_consdata.t_addr = addr; 633 pckbc_consdata.t_flags = flags; 634 callout_init(&pckbc_consdata.t_cleanup, 0); 635 636 /* flush */ 637 (void) pckbc_poll_data1(&pckbc_consdata, PCKBC_KBD_SLOT); 638 639#ifdef PCKBC_CNATTACH_SELFTEST 640 /* 641 * In some machines (e.g. netwinder) pckbc refuses to talk at 642 * all until we request a self-test. 643 */ 644 if (!pckbc_send_cmd(iot, ioh_c, KBC_SELFTEST)) { 645 printf("pckbc: unable to request selftest\n"); 646 res = EIO; 647 goto out; 648 } 649 650 reply = pckbc_poll_data1(&pckbc_consdata, PCKBC_KBD_SLOT); 651 if (reply != 0x55) { 652 printf("pckbc: selftest returned 0x%02x\n", reply); 653 res = EIO; 654 goto out; 655 } 656#endif /* PCKBC_CNATTACH_SELFTEST */ 657 658 /* init cmd byte, enable ports */ 659 pckbc_consdata.t_cmdbyte = KC8_CPU; 660 if (!pckbc_put8042cmd(&pckbc_consdata)) { 661 printf("pckbc: cmd word write error\n"); 662 res = EIO; 663 goto out; 664 } 665 666 res = pckbport_cnattach(&pckbc_consdata, &pckbc_ops, slot); 667 668 out: 669 if (res) { 670 bus_space_unmap(iot, pckbc_consdata.t_ioh_d, 1); 671 bus_space_unmap(iot, pckbc_consdata.t_ioh_c, 1); 672 } else { 673 pckbc_consdata.t_slotdata[slot] = &pckbc_cons_slotdata; 674 pckbc_init_slotdata(&pckbc_cons_slotdata); 675 pckbc_console = 1; 676 } 677 678 return (res); 679} 680 681bool 682pckbc_resume(device_t dv, const pmf_qual_t *qual) 683{ 684 struct pckbc_softc *sc = device_private(dv); 685 struct pckbc_internal *t; 686 687 t = sc->id; 688 (void)pckbc_poll_data1(t, PCKBC_KBD_SLOT); 689 if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c, KBC_SELFTEST)) 690 return false; 691 (void)pckbc_poll_data1(t, PCKBC_KBD_SLOT); 692 (void)pckbc_put8042cmd(t); 693 pckbcintr(t->t_sc); 694 695 return true; 696} 697