1 /* $NetBSD: g42xxeb_kmkbd.c,v 1.9 2008/05/10 15:31:04 martin 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.9 2008/05/10 15:31:04 martin 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(kbctag, kbcslot) 320 pckbc_tag_t kbctag; 321 int kbcslot; 322 { 323 int res; 324 325 res = kmkbd_init(&kmkbd_consdata, kbctag, kbcslot, 1); 326 327 wskbd_cnattach(&kmkbd_consops, &kmkbd_consdata, &kmkbd_keymapdata); 328 329 return (0); 330 } 331 332 void 333 kmkbd_cngetc(void *v, u_int type, int *data) 334 { 335 struct kmkbd_internal *t = v; 336 int val; 337 338 for (;;) { 339 val = pckbc_poll_data(t->t_kbctag, t->t_kbcslot); 340 if ((val != -1) && kmkbd_decode(t, val, type, data)) 341 return; 342 } 343 } 344 345 void 346 kmkbd_cnpollc(void *v, int on) 347 { 348 struct kmkbd_internal *t = v; 349 350 pckbc_set_poll(t->t_kbctag, t->t_kbcslot, on); 351 } 352 353 void 354 kmkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 355 { 356 357 kmkbd_bell(pitch, period, volume, 1); 358 } 359 #endif 360 361 362 /* 363 * low level access to key matrix 364 * 365 * returns bitset of keys being pressed. 366 */ 367 static u_int 368 kmkbd_read_matrix(struct kmkbd_softc *sc) 369 { 370 int i; 371 u_int ret, data; 372 struct obio_softc *osc = (struct obio_softc *)device_parent(&sc->dev); 373 bus_space_tag_t iot = osc->sc_iot; 374 bus_space_handle_t ioh = osc->sc_obioreg_ioh; 375 376 #define KMDELAY() delay(3) 377 378 bus_space_write_2( iot, ioh, G42XXEB_KEYSCAN, 0 ); 379 KMDELAY(); 380 381 data = KEYSCAN_SENSE_IN & 382 bus_space_read_2(iot, ioh, G42XXEB_KEYSCAN); 383 384 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, KEYSCAN_SCAN_OUT); 385 386 if (data == KEYSCAN_SENSE_IN) 387 return 0; 388 389 ret = 0; 390 for( i=0; i<5; ++i ){ 391 /* scan one line */ 392 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, ~(0x0100<<i)); 393 KMDELAY(); 394 data = bus_space_read_2(iot, ioh, G42XXEB_KEYSCAN ); 395 396 data = ~data & KEYSCAN_SENSE_IN; 397 ret |= data << (i*4); 398 } 399 400 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, KEYSCAN_SCAN_OUT); 401 return ret; 402 403 #undef KMDELAY 404 405 } 406 407 /* 408 * report key status change to wskbd subsystem. 409 */ 410 static void 411 kmkbd_report(struct kmkbd_softc *sc, u_int bitset) 412 { 413 u_int changed; 414 int i; 415 416 if (bitset == sc->notified_bits) 417 return; 418 419 if (sc->notified_bits && bitset == 0){ 420 wskbd_input(sc->wskbddev, WSCONS_EVENT_ALL_KEYS_UP, 0); 421 sc->notified_bits = 0; 422 return; 423 } 424 425 changed = bitset ^ sc->notified_bits; 426 for( i=0; changed; ++i){ 427 if ((changed & (1<<i)) == 0) 428 continue; 429 changed &= ~(1<<i); 430 431 wskbd_input(sc->wskbddev, 432 (bitset & (1<<i)) ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP, 433 i); 434 } 435 436 sc->notified_bits = bitset; 437 } 438 439 static int 440 kmkbd_intr(void *arg) 441 { 442 struct kmkbd_softc *sc = arg; 443 struct obio_softc *osc = (struct obio_softc *)device_parent(&sc->dev); 444 445 if ( sc->state != ST_ALL_UP ){ 446 printf("Spurious interrupt from key matrix\n"); 447 obio_intr_mask(osc, sc->ih); 448 return 1; 449 } 450 451 kmkbd_new_state(sc, ST_DEBOUNCE); 452 453 return 1; 454 } 455 456 static void 457 kmkbd_debounce(void *arg) 458 { 459 struct kmkbd_softc *sc = arg; 460 u_int newbits; 461 enum kmkbd_state new_state = ST_DEBOUNCE; 462 int s = spltty(); 463 464 newbits = kmkbd_read_matrix(sc); 465 466 if (newbits != sc->last_bits){ 467 sc->last_bits = newbits; 468 sc->debounce_counter = 0; 469 } 470 else if( ++(sc->debounce_counter) >= DEBOUNCE_COUNT ){ 471 new_state = newbits == 0 ? ST_ALL_UP : ST_KEY_PRESSED; 472 kmkbd_report(sc, newbits); 473 } 474 475 kmkbd_new_state(sc, new_state); 476 splx(s); 477 } 478 479 /* callout routine to watch key release */ 480 static void 481 kmkbd_watch(void *arg) 482 { 483 int s = spltty(); 484 struct kmkbd_softc *sc = arg; 485 u_int newbits; 486 int new_state = ST_KEY_PRESSED; 487 488 newbits = kmkbd_read_matrix(sc); 489 490 if (newbits != sc->last_bits){ 491 /* some keys are released or new keys are pressed. 492 start debounce */ 493 new_state = ST_DEBOUNCE; 494 sc->last_bits = newbits; 495 } 496 497 kmkbd_new_state(sc, new_state); 498 splx(s); 499 } 500 501 static void 502 kmkbd_new_state(struct kmkbd_softc *sc, enum kmkbd_state new_state) 503 { 504 struct obio_softc *osc = (struct obio_softc *)device_parent(&sc->dev); 505 506 switch(new_state){ 507 case ST_DISABLED: 508 if (sc->state != ST_DISABLED){ 509 callout_stop(&sc->callout); 510 obio_intr_mask(osc,sc->ih); 511 } 512 break; 513 case ST_DEBOUNCE: 514 if (sc->state == ST_ALL_UP){ 515 obio_intr_mask(osc, sc->ih); 516 sc->last_bits = kmkbd_read_matrix(sc); 517 } 518 if (sc->state != ST_DEBOUNCE) 519 sc->debounce_counter = 0; 520 521 /* start debounce timer */ 522 callout_reset(&sc->callout, DEBOUNCE_TICKS, kmkbd_debounce, sc); 523 break; 524 case ST_KEY_PRESSED: 525 /* start timer to check key release */ 526 callout_reset(&sc->callout, RELEASE_WATCH_TICKS, kmkbd_watch, sc); 527 break; 528 case ST_ALL_UP: 529 if (sc->state != ST_ALL_UP){ 530 bus_space_tag_t iot = osc->sc_iot; 531 bus_space_handle_t ioh = osc->sc_obioreg_ioh; 532 533 obio_intr_unmask(osc, sc->ih); 534 bus_space_write_2(iot, ioh, G42XXEB_KEYSCAN, 0); 535 } 536 break; 537 case ST_INIT: 538 ; /* Nothing to do */ 539 } 540 541 sc->state = new_state; 542 } 543