1 /* $NetBSD: g42xxeb_kmkbd.c,v 1.18 2024/02/09 22:08:32 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2002, 2003, 2005 Genetec corp. 5 * All rights reserved. 6 * 7 * 4x5 matrix key switch driver for TWINTAIL. 8 * Written by Hiroyuki Bessho for Genetec corp. 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 * 3. Neither the name of The NetBSD Foundation nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* 36 * Use on-board matrix switches as wskbd. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: g42xxeb_kmkbd.c,v 1.18 2024/02/09 22:08:32 andvar Exp $" ); 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/device.h> 45 #include <sys/ioctl.h> 46 #include <sys/callout.h> 47 #include <sys/kernel.h> /* for hz */ 48 49 #include <sys/bus.h> 50 51 #include <dev/wscons/wsconsio.h> 52 #include <dev/wscons/wskbdvar.h> 53 #include <dev/wscons/wsksymdef.h> 54 #include <dev/wscons/wsksymvar.h> 55 56 #include <arch/evbarm/g42xxeb/g42xxeb_var.h> 57 58 #include "locators.h" 59 #include "opt_wsdisplay_compat.h" 60 61 #define DEBOUNCE_TICKS ((hz<=50)?1:hz/50) /* 20ms */ 62 #define RELEASE_WATCH_TICKS (hz/10) /* 100ms */ 63 64 #define NUMKEYS (4*5) /* the number of keys */ 65 66 struct kmkbd_softc { 67 device_t dev; 68 69 device_t wskbddev; 70 void *ih; /* interrupt handler */ 71 struct callout callout; 72 73 uint32_t notified_bits; /* reported state of keys */ 74 uint32_t last_bits; /* used for debounce */ 75 u_char debounce_counter; 76 #define DEBOUNCE_COUNT 3 77 u_char polling; 78 u_char rawkbd; 79 enum kmkbd_state { 80 ST_INIT, 81 ST_DISABLED, 82 ST_ALL_UP, /* waiting for interrupt */ 83 ST_DEBOUNCE, /* doing debounce */ 84 ST_KEY_PRESSED /* some keys are pressed */ 85 } state; 86 }; 87 88 89 int kmkbd_match(device_t, cfdata_t, void *); 90 void kmkbd_attach(device_t, device_t, void *); 91 92 CFATTACH_DECL_NEW(kmkbd, sizeof(struct kmkbd_softc), 93 kmkbd_match, kmkbd_attach, NULL, NULL); 94 95 static int kmkbd_enable(void *, int); 96 static void kmkbd_set_leds(void *, int); 97 static int kmkbd_ioctl(void *, u_long, void *, int, struct lwp *); 98 99 const struct wskbd_accessops kmkbd_accessops = { 100 kmkbd_enable, 101 kmkbd_set_leds, 102 kmkbd_ioctl, 103 }; 104 105 #if 0 106 void kmkbd_cngetc(void *, u_int *, int *); 107 void kmkbd_cnpollc(void *, int); 108 void kmkbd_cnbell(void *, u_int, u_int, u_int); 109 110 const struct wskbd_consops kmkbd_consops = { 111 kmkbd_cngetc, 112 kmkbd_cnpollc, 113 kmkbd_cnbell, 114 }; 115 #endif 116 117 static const keysym_t kmkbd_keydesc_0[] = { 118 /* pos normal shifted */ 119 KS_KEYCODE(0), KS_a, KS_A, 120 KS_KEYCODE(1), KS_b, KS_B, 121 KS_KEYCODE(2), KS_c, KS_C, 122 KS_KEYCODE(3), KS_d, KS_D, 123 KS_KEYCODE(4), KS_e, KS_E, 124 KS_KEYCODE(5), KS_f, KS_F, 125 KS_KEYCODE(6), KS_g, KS_G, 126 KS_KEYCODE(7), KS_h, KS_H, 127 KS_KEYCODE(8), KS_i, KS_I, 128 KS_KEYCODE(9), KS_j, KS_J, 129 KS_KEYCODE(10), KS_k, KS_K, 130 KS_KEYCODE(11), KS_l, KS_L, 131 KS_KEYCODE(12), KS_m, KS_M, 132 KS_KEYCODE(13), KS_n, KS_N, 133 KS_KEYCODE(14), KS_o, KS_O, 134 KS_KEYCODE(15), KS_p, KS_P, 135 KS_KEYCODE(16), KS_q, KS_Q, 136 KS_KEYCODE(17), KS_r, KS_R, 137 KS_KEYCODE(18), '\003', '\003', 138 KS_KEYCODE(19), KS_Return, KS_Linefeed, 139 }; 140 141 #define KBD_MAP(name, base, map) \ 142 { name, base, sizeof(map)/sizeof(keysym_t), map } 143 144 static const struct wscons_keydesc kmkbd_keydesctab[] = { 145 KBD_MAP(KB_MACHDEP, 0, kmkbd_keydesc_0), 146 {0, 0, 0, 0} 147 }; 148 149 const struct wskbd_mapdata kmkbd_keymapdata = { 150 kmkbd_keydesctab, 151 #ifdef KMKBD_LAYOUT 152 KMKBD_LAYOUT, 153 #else 154 KB_MACHDEP, 155 #endif 156 }; 157 158 /* 159 * Hackish support for a bell on the PC Keyboard; when a suitable beeper 160 * is found, it attaches itself into the pckbd driver here. 161 */ 162 void (*kmkbd_bell_fn)(void *, u_int, u_int, u_int, int); 163 void *kmkbd_bell_fn_arg; 164 165 void kmkbd_bell(u_int, u_int, u_int, int); 166 void kmkbd_hookup_bell(void (* fn)(void *, u_int, u_int, u_int, int), void *arg); 167 168 static int kmkbd_intr(void *); 169 static void kmkbd_new_state(struct kmkbd_softc *, enum kmkbd_state); 170 171 /*struct kmkbd_internal kmkbd_consdata;*/ 172 173 static int 174 kmkbd_is_console(void) 175 { 176 #if 0 177 return (kmkbd_consdata.t_isconsole && 178 (tag == kmkbd_consdata.t_kbctag) && 179 (slot == kmkbd_consdata.t_kbcslot)); 180 #else 181 return 0; 182 #endif 183 } 184 185 int 186 kmkbd_match(device_t parent, cfdata_t cf, void *aux) 187 { 188 return 1; 189 } 190 191 void 192 kmkbd_attach(device_t parent, device_t self, void *aux) 193 { 194 struct kmkbd_softc *sc = device_private(self); 195 /*struct obio_attach_args *oa = aux;*/ 196 int state0; 197 struct wskbddev_attach_args a; 198 struct obio_softc *osc = device_private(parent); 199 int s; 200 201 printf("\n"); 202 203 sc->dev = self; 204 sc->state = ST_INIT; 205 206 if (kmkbd_is_console()){ 207 a.console = 1; 208 state0 = ST_ALL_UP; 209 } else { 210 a.console = 0; 211 state0 = ST_DISABLED; 212 } 213 214 #ifdef WSDISPLAY_COMPAT_RAWKBD 215 sc->rawkbd = 0; 216 #endif 217 callout_init(&sc->callout, 0); 218 219 s = spltty(); 220 sc->ih = obio_intr_establish(osc, G42XXEB_INT_KEY, IPL_TTY, 221 IST_EDGE_FALLING, kmkbd_intr, (void *)sc); 222 kmkbd_new_state(sc, state0); 223 splx(s); 224 225 a.keymap = &kmkbd_keymapdata; 226 227 a.accessops = &kmkbd_accessops; 228 a.accesscookie = sc; 229 230 231 /* Attach the wskbd. */ 232 sc->wskbddev = config_found(self, &a, wskbddevprint, CFARGS_NONE); 233 234 } 235 236 static int 237 kmkbd_enable(void *v, int on) 238 { 239 struct kmkbd_softc *sc = v; 240 241 if (on) { 242 if (sc->state != ST_DISABLED) { 243 #ifdef DIAGNOSTIC 244 printf("kmkbd_enable: bad enable (state=%d)\n", sc->state); 245 #endif 246 return (EBUSY); 247 } 248 249 kmkbd_new_state(sc, ST_ALL_UP); 250 } else { 251 #if 0 252 if (sc->id->t_isconsole) 253 return (EBUSY); 254 #endif 255 256 kmkbd_new_state(sc, ST_DISABLED); 257 } 258 259 return (0); 260 } 261 262 263 264 static void 265 kmkbd_set_leds(void *v, int leds) 266 { 267 } 268 269 static int 270 kmkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 271 { 272 #ifdef WSDISPLAY_COMPAT_RAWKBD 273 struct kmkbd_softc *sc = v; 274 #endif 275 276 switch (cmd) { 277 case WSKBDIO_GTYPE: 278 *(int *)data = WSKBD_TYPE_PC_XT; /* XXX */ 279 return 0; 280 case WSKBDIO_COMPLEXBELL: 281 #define d ((struct wskbd_bell_data *)data) 282 /* 283 * Keyboard can't beep directly; we have an 284 * externally-provided global hook to do this. 285 */ 286 kmkbd_bell(d->pitch, d->period, d->volume, 0); 287 #undef d 288 return (0); 289 #ifdef WSDISPLAY_COMPAT_RAWKBD 290 case WSKBDIO_SETMODE: 291 sc->rawkbd = (*(int *)data == WSKBD_RAW); 292 return (0); 293 #endif 294 295 #if 0 296 case WSKBDIO_SETLEDS: 297 case WSKBDIO_GETLEDS: 298 /* no LED support */ 299 #endif 300 } 301 return EPASSTHROUGH; 302 } 303 304 void 305 kmkbd_bell(u_int pitch, u_int period, u_int volume, int poll) 306 { 307 308 if (kmkbd_bell_fn != NULL) 309 (*kmkbd_bell_fn)(kmkbd_bell_fn_arg, pitch, period, 310 volume, poll); 311 } 312 313 void 314 kmkbd_hookup_bell(void (* fn)(void *, u_int, u_int, u_int, int), void *arg) 315 { 316 317 if (kmkbd_bell_fn == NULL) { 318 kmkbd_bell_fn = fn; 319 kmkbd_bell_fn_arg = arg; 320 } 321 } 322 323 #if 0 324 int 325 kmkbd_cnattach(pckbc_tag_t kbctag, int kbcslot) 326 { 327 int res; 328 329 res = kmkbd_init(&kmkbd_consdata, kbctag, kbcslot, 1); 330 331 wskbd_cnattach(&kmkbd_consops, &kmkbd_consdata, &kmkbd_keymapdata); 332 333 return (0); 334 } 335 336 void 337 kmkbd_cngetc(void *v, u_int type, int *data) 338 { 339 struct kmkbd_internal *t = v; 340 int val; 341 342 for (;;) { 343 val = pckbc_poll_data(t->t_kbctag, t->t_kbcslot); 344 if ((val != -1) && kmkbd_decode(t, val, type, data)) 345 return; 346 } 347 } 348 349 void 350 kmkbd_cnpollc(void *v, int on) 351 { 352 struct kmkbd_internal *t = v; 353 354 pckbc_set_poll(t->t_kbctag, t->t_kbcslot, on); 355 } 356 357 void 358 kmkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 359 { 360 361 kmkbd_bell(pitch, period, volume, 1); 362 } 363 #endif 364 365 366 /* 367 * low level access to key matrix 368 * 369 * returns bitset of keys being pressed. 370 */ 371 static u_int 372 kmkbd_read_matrix(struct kmkbd_softc *sc) 373 { 374 int i; 375 u_int ret, data; 376 struct obio_softc *osc = device_private(device_parent(sc->dev)); 377 bus_space_tag_t iot = osc->sc_iot; 378 bus_space_handle_t ioh = osc->sc_obioreg_ioh; 379 380 #define KMDELAY() delay(3) 381 382 bus_space_write_2( iot, ioh, G42XXEB_KEYSCAN, 0 ); 383 KMDELAY(); 384 385 data = KEYSCAN_SENSE_IN & 386 bus_space_read_2(iot, ioh, G42XXEB_KEYSCAN); 387 388 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, KEYSCAN_SCAN_OUT); 389 390 if (data == KEYSCAN_SENSE_IN) 391 return 0; 392 393 ret = 0; 394 for( i=0; i<5; ++i ){ 395 /* scan one line */ 396 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, ~(0x0100<<i)); 397 KMDELAY(); 398 data = bus_space_read_2(iot, ioh, G42XXEB_KEYSCAN ); 399 400 data = ~data & KEYSCAN_SENSE_IN; 401 ret |= data << (i*4); 402 } 403 404 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, KEYSCAN_SCAN_OUT); 405 return ret; 406 407 #undef KMDELAY 408 409 } 410 411 /* 412 * report key status change to wskbd subsystem. 413 */ 414 static void 415 kmkbd_report(struct kmkbd_softc *sc, u_int bitset) 416 { 417 u_int changed; 418 int i; 419 420 if (bitset == sc->notified_bits) 421 return; 422 423 if (sc->notified_bits && bitset == 0){ 424 wskbd_input(sc->wskbddev, WSCONS_EVENT_ALL_KEYS_UP, 0); 425 sc->notified_bits = 0; 426 return; 427 } 428 429 changed = bitset ^ sc->notified_bits; 430 for( i=0; changed; ++i){ 431 if ((changed & (1<<i)) == 0) 432 continue; 433 changed &= ~(1<<i); 434 435 wskbd_input(sc->wskbddev, 436 (bitset & (1<<i)) ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP, 437 i); 438 } 439 440 sc->notified_bits = bitset; 441 } 442 443 #ifdef WSDISPLAY_COMPAT_RAWKBD 444 static void 445 kmkbd_report_raw(struct kmkbd_softc *sc, u_int bitset) 446 { 447 int i, nc; 448 char cbuf[NUMKEYS]; 449 u_int changed; 450 451 if (bitset == sc->notified_bits) 452 return; 453 454 nc = 0; 455 changed = bitset ^ sc->notified_bits; 456 457 while (changed) { 458 i = ffs(changed) - 1; 459 if (nc < NUMKEYS) { 460 cbuf[nc] = i + 1; 461 if (0 == (bitset & (1<<i))) { 462 /* the key is released */ 463 cbuf[nc] |= 0x80; 464 } 465 ++nc; 466 } 467 468 changed &= ~(1<<i); 469 } 470 471 wskbd_rawinput(sc->wskbddev, cbuf, nc); 472 sc->notified_bits = bitset; 473 } 474 #endif 475 476 static int 477 kmkbd_intr(void *arg) 478 { 479 struct kmkbd_softc *sc = arg; 480 struct obio_softc *osc = device_private(device_parent(sc->dev)); 481 482 if ( sc->state != ST_ALL_UP ){ 483 printf("Spurious interrupt from key matrix\n"); 484 obio_intr_mask(osc, sc->ih); 485 return 1; 486 } 487 488 kmkbd_new_state(sc, ST_DEBOUNCE); 489 490 return 1; 491 } 492 493 static void 494 kmkbd_debounce(void *arg) 495 { 496 struct kmkbd_softc *sc = arg; 497 u_int newbits; 498 enum kmkbd_state new_state = ST_DEBOUNCE; 499 int s = spltty(); 500 501 newbits = kmkbd_read_matrix(sc); 502 503 if (newbits != sc->last_bits){ 504 sc->last_bits = newbits; 505 sc->debounce_counter = 0; 506 } 507 else if( ++(sc->debounce_counter) >= DEBOUNCE_COUNT ){ 508 new_state = newbits == 0 ? ST_ALL_UP : ST_KEY_PRESSED; 509 #ifdef WSDISPLAY_COMPAT_RAWKBD 510 if (sc->rawkbd) 511 kmkbd_report_raw(sc, newbits); 512 else 513 #endif 514 kmkbd_report(sc, newbits); 515 } 516 517 kmkbd_new_state(sc, new_state); 518 splx(s); 519 } 520 521 /* callout routine to watch key release */ 522 static void 523 kmkbd_watch(void *arg) 524 { 525 int s = spltty(); 526 struct kmkbd_softc *sc = arg; 527 u_int newbits; 528 int new_state = ST_KEY_PRESSED; 529 530 newbits = kmkbd_read_matrix(sc); 531 532 if (newbits != sc->last_bits){ 533 /* some keys are released or new keys are pressed. 534 start debounce */ 535 new_state = ST_DEBOUNCE; 536 sc->last_bits = newbits; 537 } 538 539 kmkbd_new_state(sc, new_state); 540 splx(s); 541 } 542 543 static void 544 kmkbd_new_state(struct kmkbd_softc *sc, enum kmkbd_state new_state) 545 { 546 struct obio_softc *osc = device_private(device_parent(sc->dev)); 547 548 switch(new_state){ 549 case ST_DISABLED: 550 if (sc->state != ST_DISABLED){ 551 callout_stop(&sc->callout); 552 obio_intr_mask(osc,sc->ih); 553 } 554 break; 555 case ST_DEBOUNCE: 556 if (sc->state == ST_ALL_UP){ 557 obio_intr_mask(osc, sc->ih); 558 sc->last_bits = kmkbd_read_matrix(sc); 559 } 560 if (sc->state != ST_DEBOUNCE) 561 sc->debounce_counter = 0; 562 563 /* start debounce timer */ 564 callout_reset(&sc->callout, DEBOUNCE_TICKS, kmkbd_debounce, sc); 565 break; 566 case ST_KEY_PRESSED: 567 /* start timer to check key release */ 568 callout_reset(&sc->callout, RELEASE_WATCH_TICKS, kmkbd_watch, sc); 569 break; 570 case ST_ALL_UP: 571 if (sc->state != ST_ALL_UP){ 572 bus_space_tag_t iot = osc->sc_iot; 573 bus_space_handle_t ioh = osc->sc_obioreg_ioh; 574 575 obio_intr_unmask(osc, sc->ih); 576 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, 0); 577 } 578 break; 579 case ST_INIT: 580 ; /* Nothing to do */ 581 } 582 583 sc->state = new_state; 584 } 585