xref: /netbsd-src/sys/arch/evbarm/g42xxeb/g42xxeb_kmkbd.c (revision 7e30e94394d0994ab9534f68a8f91665045c91ce)
1 /* $NetBSD: g42xxeb_kmkbd.c,v 1.14 2012/10/27 17:17:47 chs 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.14 2012/10/27 17:17:47 chs 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 	device_t 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(device_t parent, cfdata_t 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