1 /* $NetBSD: g42xxeb_kmkbd.c,v 1.10 2009/03/14 15:36:05 dsl 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.10 2009/03/14 15:36:05 dsl 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 <machine/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 61 /*#include "opt_pckbd_layout.h"*/ 62 /*#include "opt_wsdisplay_compat.h"*/ 63 64 #define DEBOUNCE_TICKS ((hz<=50)?1:hz/50) /* 20ms */ 65 #define RELEASE_WATCH_TICKS (hz/10) /* 100ms */ 66 67 struct kmkbd_softc { 68 struct device 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 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(struct device *, struct cfdata *, void *); 90 void kmkbd_attach(struct device *, struct device *, void *); 91 92 CFATTACH_DECL(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 feeper 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(struct device *parent, struct cfdata *cf, void *aux) 187 { 188 return 1; 189 } 190 191 void 192 kmkbd_attach(struct device *parent, struct device *self, void *aux) 193 { 194 struct kmkbd_softc *sc = (void *)self; 195 /*struct obio_attach_args *oa = aux;*/ 196 int state0; 197 struct wskbddev_attach_args a; 198 struct obio_softc *osc = (struct obio_softc *)parent; 199 int s; 200 201 printf("\n"); 202 203 sc->state = ST_INIT; 204 205 if (kmkbd_is_console()){ 206 a.console = 1; 207 state0 = ST_ALL_UP; 208 } else { 209 a.console = 0; 210 state0 = ST_DISABLED; 211 } 212 213 callout_init(&sc->callout, 0); 214 215 s = spltty(); 216 sc->ih = obio_intr_establish(osc, G42XXEB_INT_KEY, IPL_TTY, 217 IST_EDGE_FALLING, kmkbd_intr, (void *)sc); 218 kmkbd_new_state(sc, state0); 219 splx(s); 220 221 a.keymap = &kmkbd_keymapdata; 222 223 a.accessops = &kmkbd_accessops; 224 a.accesscookie = sc; 225 226 227 /* Attach the wskbd. */ 228 sc->wskbddev = config_found(self, &a, wskbddevprint); 229 230 } 231 232 static int 233 kmkbd_enable(void *v, int on) 234 { 235 struct kmkbd_softc *sc = v; 236 237 if (on) { 238 if (sc->state != ST_DISABLED) { 239 #ifdef DIAGNOSTIC 240 printf("kmkbd_enable: bad enable (state=%d)\n", sc->state); 241 #endif 242 return (EBUSY); 243 } 244 245 kmkbd_new_state(sc, ST_ALL_UP); 246 } else { 247 #if 0 248 if (sc->id->t_isconsole) 249 return (EBUSY); 250 #endif 251 252 kmkbd_new_state(sc, ST_DISABLED); 253 } 254 255 return (0); 256 } 257 258 259 260 static void 261 kmkbd_set_leds(void *v, int leds) 262 { 263 } 264 265 static int 266 kmkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 267 { 268 /*struct kmkbd_softc *sc = v;*/ 269 270 switch (cmd) { 271 case WSKBDIO_GTYPE: 272 *(int *)data = WSKBD_TYPE_PC_XT; /* XXX */ 273 return 0; 274 case WSKBDIO_COMPLEXBELL: 275 #define d ((struct wskbd_bell_data *)data) 276 /* 277 * Keyboard can't beep directly; we have an 278 * externally-provided global hook to do this. 279 */ 280 kmkbd_bell(d->pitch, d->period, d->volume, 0); 281 #undef d 282 return (0); 283 #ifdef WSDISPLAY_COMPAT_RAWKBD 284 case WSKBDIO_SETMODE: 285 sc->rawkbd = (*(int *)data == WSKBD_RAW); 286 return (0); 287 #endif 288 289 #if 0 290 case WSKBDIO_SETLEDS: 291 case WSKBDIO_GETLEDS: 292 /* no LED support */ 293 #endif 294 } 295 return EPASSTHROUGH; 296 } 297 298 void 299 kmkbd_bell(u_int pitch, u_int period, u_int volume, int poll) 300 { 301 302 if (kmkbd_bell_fn != NULL) 303 (*kmkbd_bell_fn)(kmkbd_bell_fn_arg, pitch, period, 304 volume, poll); 305 } 306 307 void 308 kmkbd_hookup_bell(void (* fn)(void *, u_int, u_int, u_int, int), void *arg) 309 { 310 311 if (kmkbd_bell_fn == NULL) { 312 kmkbd_bell_fn = fn; 313 kmkbd_bell_fn_arg = arg; 314 } 315 } 316 317 #if 0 318 int 319 kmkbd_cnattach(pckbc_tag_t kbctag, int kbcslot) 320 { 321 int res; 322 323 res = kmkbd_init(&kmkbd_consdata, kbctag, kbcslot, 1); 324 325 wskbd_cnattach(&kmkbd_consops, &kmkbd_consdata, &kmkbd_keymapdata); 326 327 return (0); 328 } 329 330 void 331 kmkbd_cngetc(void *v, u_int type, int *data) 332 { 333 struct kmkbd_internal *t = v; 334 int val; 335 336 for (;;) { 337 val = pckbc_poll_data(t->t_kbctag, t->t_kbcslot); 338 if ((val != -1) && kmkbd_decode(t, val, type, data)) 339 return; 340 } 341 } 342 343 void 344 kmkbd_cnpollc(void *v, int on) 345 { 346 struct kmkbd_internal *t = v; 347 348 pckbc_set_poll(t->t_kbctag, t->t_kbcslot, on); 349 } 350 351 void 352 kmkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 353 { 354 355 kmkbd_bell(pitch, period, volume, 1); 356 } 357 #endif 358 359 360 /* 361 * low level access to key matrix 362 * 363 * returns bitset of keys being pressed. 364 */ 365 static u_int 366 kmkbd_read_matrix(struct kmkbd_softc *sc) 367 { 368 int i; 369 u_int ret, data; 370 struct obio_softc *osc = (struct obio_softc *)device_parent(&sc->dev); 371 bus_space_tag_t iot = osc->sc_iot; 372 bus_space_handle_t ioh = osc->sc_obioreg_ioh; 373 374 #define KMDELAY() delay(3) 375 376 bus_space_write_2( iot, ioh, G42XXEB_KEYSCAN, 0 ); 377 KMDELAY(); 378 379 data = KEYSCAN_SENSE_IN & 380 bus_space_read_2(iot, ioh, G42XXEB_KEYSCAN); 381 382 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, KEYSCAN_SCAN_OUT); 383 384 if (data == KEYSCAN_SENSE_IN) 385 return 0; 386 387 ret = 0; 388 for( i=0; i<5; ++i ){ 389 /* scan one line */ 390 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, ~(0x0100<<i)); 391 KMDELAY(); 392 data = bus_space_read_2(iot, ioh, G42XXEB_KEYSCAN ); 393 394 data = ~data & KEYSCAN_SENSE_IN; 395 ret |= data << (i*4); 396 } 397 398 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, KEYSCAN_SCAN_OUT); 399 return ret; 400 401 #undef KMDELAY 402 403 } 404 405 /* 406 * report key status change to wskbd subsystem. 407 */ 408 static void 409 kmkbd_report(struct kmkbd_softc *sc, u_int bitset) 410 { 411 u_int changed; 412 int i; 413 414 if (bitset == sc->notified_bits) 415 return; 416 417 if (sc->notified_bits && bitset == 0){ 418 wskbd_input(sc->wskbddev, WSCONS_EVENT_ALL_KEYS_UP, 0); 419 sc->notified_bits = 0; 420 return; 421 } 422 423 changed = bitset ^ sc->notified_bits; 424 for( i=0; changed; ++i){ 425 if ((changed & (1<<i)) == 0) 426 continue; 427 changed &= ~(1<<i); 428 429 wskbd_input(sc->wskbddev, 430 (bitset & (1<<i)) ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP, 431 i); 432 } 433 434 sc->notified_bits = bitset; 435 } 436 437 static int 438 kmkbd_intr(void *arg) 439 { 440 struct kmkbd_softc *sc = arg; 441 struct obio_softc *osc = (struct obio_softc *)device_parent(&sc->dev); 442 443 if ( sc->state != ST_ALL_UP ){ 444 printf("Spurious interrupt from key matrix\n"); 445 obio_intr_mask(osc, sc->ih); 446 return 1; 447 } 448 449 kmkbd_new_state(sc, ST_DEBOUNCE); 450 451 return 1; 452 } 453 454 static void 455 kmkbd_debounce(void *arg) 456 { 457 struct kmkbd_softc *sc = arg; 458 u_int newbits; 459 enum kmkbd_state new_state = ST_DEBOUNCE; 460 int s = spltty(); 461 462 newbits = kmkbd_read_matrix(sc); 463 464 if (newbits != sc->last_bits){ 465 sc->last_bits = newbits; 466 sc->debounce_counter = 0; 467 } 468 else if( ++(sc->debounce_counter) >= DEBOUNCE_COUNT ){ 469 new_state = newbits == 0 ? ST_ALL_UP : ST_KEY_PRESSED; 470 kmkbd_report(sc, newbits); 471 } 472 473 kmkbd_new_state(sc, new_state); 474 splx(s); 475 } 476 477 /* callout routine to watch key release */ 478 static void 479 kmkbd_watch(void *arg) 480 { 481 int s = spltty(); 482 struct kmkbd_softc *sc = arg; 483 u_int newbits; 484 int new_state = ST_KEY_PRESSED; 485 486 newbits = kmkbd_read_matrix(sc); 487 488 if (newbits != sc->last_bits){ 489 /* some keys are released or new keys are pressed. 490 start debounce */ 491 new_state = ST_DEBOUNCE; 492 sc->last_bits = newbits; 493 } 494 495 kmkbd_new_state(sc, new_state); 496 splx(s); 497 } 498 499 static void 500 kmkbd_new_state(struct kmkbd_softc *sc, enum kmkbd_state new_state) 501 { 502 struct obio_softc *osc = (struct obio_softc *)device_parent(&sc->dev); 503 504 switch(new_state){ 505 case ST_DISABLED: 506 if (sc->state != ST_DISABLED){ 507 callout_stop(&sc->callout); 508 obio_intr_mask(osc,sc->ih); 509 } 510 break; 511 case ST_DEBOUNCE: 512 if (sc->state == ST_ALL_UP){ 513 obio_intr_mask(osc, sc->ih); 514 sc->last_bits = kmkbd_read_matrix(sc); 515 } 516 if (sc->state != ST_DEBOUNCE) 517 sc->debounce_counter = 0; 518 519 /* start debounce timer */ 520 callout_reset(&sc->callout, DEBOUNCE_TICKS, kmkbd_debounce, sc); 521 break; 522 case ST_KEY_PRESSED: 523 /* start timer to check key release */ 524 callout_reset(&sc->callout, RELEASE_WATCH_TICKS, kmkbd_watch, sc); 525 break; 526 case ST_ALL_UP: 527 if (sc->state != ST_ALL_UP){ 528 bus_space_tag_t iot = osc->sc_iot; 529 bus_space_handle_t ioh = osc->sc_obioreg_ioh; 530 531 obio_intr_unmask(osc, sc->ih); 532 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, 0); 533 } 534 break; 535 case ST_INIT: 536 ; /* Nothing to do */ 537 } 538 539 sc->state = new_state; 540 } 541