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