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