1 /* $NetBSD: g42xxeb_kmkbd.c,v 1.9 2008/05/10 15:31:04 martin Exp $ */
4 * Copyright (c) 2002, 2003, 2005 Genetec corp.
7 * 4x5 matrix key switch driver for TWINTAIL.
8 * Written by Hiroyuki Bessho for Genetec corp.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
36 * Use on-board matrix switches as wskbd.
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: g42xxeb_kmkbd.c,v 1.9 2008/05/10 15:31:04 martin Exp $" );
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 */
50 #include <machine/bus.h>
52 #include <dev/wscons/wsconsio.h>
53 #include <dev/wscons/wskbdvar.h>
54 #include <dev/wscons/wsksymdef.h>
55 #include <dev/wscons/wsksymvar.h>
57 #include <arch/evbarm/g42xxeb/g42xxeb_var.h>
61 /*#include "opt_pckbd_layout.h"*/
62 /*#include "opt_wsdisplay_compat.h"*/
64 #define DEBOUNCE_TICKS ((hz<=50)?1:hz/50) /* 20ms */
65 #define RELEASE_WATCH_TICKS (hz/10) /* 100ms */
70 struct device
*wskbddev
;
71 void *ih
; /* interrupt handler */
72 struct callout callout
;
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
82 ST_ALL_UP
, /* waiting for interrupt */
83 ST_DEBOUNCE
, /* doing debounce */
84 ST_KEY_PRESSED
/* some keys are pressed */
89 int kmkbd_match(struct device
*, struct cfdata
*, void *);
90 void kmkbd_attach(struct device
*, struct device
*, void *);
92 CFATTACH_DECL(kmkbd
, sizeof(struct kmkbd_softc
),
93 kmkbd_match
, kmkbd_attach
, NULL
, NULL
);
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
*);
99 const struct wskbd_accessops kmkbd_accessops
= {
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
);
110 const struct wskbd_consops kmkbd_consops
= {
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
,
141 #define KBD_MAP(name, base, map) \
142 { name, base, sizeof(map)/sizeof(keysym_t), map }
144 static const struct wscons_keydesc kmkbd_keydesctab
[] = {
145 KBD_MAP(KB_MACHDEP
, 0, kmkbd_keydesc_0
),
149 const struct wskbd_mapdata kmkbd_keymapdata
= {
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.
162 void (*kmkbd_bell_fn
)(void *, u_int
, u_int
, u_int
, int);
163 void *kmkbd_bell_fn_arg
;
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
);
168 static int kmkbd_intr(void *);
169 static void kmkbd_new_state(struct kmkbd_softc
*, enum kmkbd_state
);
171 /*struct kmkbd_internal kmkbd_consdata;*/
174 kmkbd_is_console(void)
177 return (kmkbd_consdata
.t_isconsole
&&
178 (tag
== kmkbd_consdata
.t_kbctag
) &&
179 (slot
== kmkbd_consdata
.t_kbcslot
));
186 kmkbd_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
192 kmkbd_attach(struct device
*parent
, struct device
*self
, void *aux
)
194 struct kmkbd_softc
*sc
= (void *)self
;
195 /*struct obio_attach_args *oa = aux;*/
197 struct wskbddev_attach_args a
;
198 struct obio_softc
*osc
= (struct obio_softc
*)parent
;
205 if (kmkbd_is_console()){
210 state0
= ST_DISABLED
;
213 callout_init(&sc
->callout
, 0);
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
);
221 a
.keymap
= &kmkbd_keymapdata
;
223 a
.accessops
= &kmkbd_accessops
;
227 /* Attach the wskbd. */
228 sc
->wskbddev
= config_found(self
, &a
, wskbddevprint
);
233 kmkbd_enable(void *v
, int on
)
235 struct kmkbd_softc
*sc
= v
;
238 if (sc
->state
!= ST_DISABLED
) {
240 printf("kmkbd_enable: bad enable (state=%d)\n", sc
->state
);
245 kmkbd_new_state(sc
, ST_ALL_UP
);
248 if (sc
->id
->t_isconsole
)
252 kmkbd_new_state(sc
, ST_DISABLED
);
261 kmkbd_set_leds(void *v
, int leds
)
266 kmkbd_ioctl(void *v
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
268 /*struct kmkbd_softc *sc = v;*/
272 *(int *)data
= WSKBD_TYPE_PC_XT
; /* XXX */
274 case WSKBDIO_COMPLEXBELL
:
275 #define d ((struct wskbd_bell_data *)data)
277 * Keyboard can't beep directly; we have an
278 * externally-provided global hook to do this.
280 kmkbd_bell(d
->pitch
, d
->period
, d
->volume
, 0);
283 #ifdef WSDISPLAY_COMPAT_RAWKBD
284 case WSKBDIO_SETMODE
:
285 sc
->rawkbd
= (*(int *)data
== WSKBD_RAW
);
290 case WSKBDIO_SETLEDS
:
291 case WSKBDIO_GETLEDS
:
299 kmkbd_bell(u_int pitch
, u_int period
, u_int volume
, int poll
)
302 if (kmkbd_bell_fn
!= NULL
)
303 (*kmkbd_bell_fn
)(kmkbd_bell_fn_arg
, pitch
, period
,
308 kmkbd_hookup_bell(void (* fn
)(void *, u_int
, u_int
, u_int
, int), void *arg
)
311 if (kmkbd_bell_fn
== NULL
) {
313 kmkbd_bell_fn_arg
= arg
;
319 kmkbd_cnattach(pckbc_tag_t kbctag
, int kbcslot
)
323 res
= kmkbd_init(&kmkbd_consdata
, kbctag
, kbcslot
, 1);
325 wskbd_cnattach(&kmkbd_consops
, &kmkbd_consdata
, &kmkbd_keymapdata
);
331 kmkbd_cngetc(void *v
, u_int type
, int *data
)
333 struct kmkbd_internal
*t
= v
;
337 val
= pckbc_poll_data(t
->t_kbctag
, t
->t_kbcslot
);
338 if ((val
!= -1) && kmkbd_decode(t
, val
, type
, data
))
344 kmkbd_cnpollc(void *v
, int on
)
346 struct kmkbd_internal
*t
= v
;
348 pckbc_set_poll(t
->t_kbctag
, t
->t_kbcslot
, on
);
352 kmkbd_cnbell(void *v
, u_int pitch
, u_int period
, u_int volume
)
355 kmkbd_bell(pitch
, period
, volume
, 1);
361 * low level access to key matrix
363 * returns bitset of keys being pressed.
366 kmkbd_read_matrix(struct kmkbd_softc
*sc
)
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
;
374 #define KMDELAY() delay(3)
376 bus_space_write_2( iot
, ioh
, G42XXEB_KEYSCAN
, 0 );
379 data
= KEYSCAN_SENSE_IN
&
380 bus_space_read_2(iot
, ioh
, G42XXEB_KEYSCAN
);
382 bus_space_write_2(iot
, ioh
, G42XXEB_KEYSCAN
, KEYSCAN_SCAN_OUT
);
384 if (data
== KEYSCAN_SENSE_IN
)
388 for( i
=0; i
<5; ++i
){
390 bus_space_write_2(iot
, ioh
, G42XXEB_KEYSCAN
, ~(0x0100<<i
));
392 data
= bus_space_read_2(iot
, ioh
, G42XXEB_KEYSCAN
);
394 data
= ~data
& KEYSCAN_SENSE_IN
;
395 ret
|= data
<< (i
*4);
398 bus_space_write_2(iot
, ioh
, G42XXEB_KEYSCAN
, KEYSCAN_SCAN_OUT
);
406 * report key status change to wskbd subsystem.
409 kmkbd_report(struct kmkbd_softc
*sc
, u_int bitset
)
414 if (bitset
== sc
->notified_bits
)
417 if (sc
->notified_bits
&& bitset
== 0){
418 wskbd_input(sc
->wskbddev
, WSCONS_EVENT_ALL_KEYS_UP
, 0);
419 sc
->notified_bits
= 0;
423 changed
= bitset
^ sc
->notified_bits
;
424 for( i
=0; changed
; ++i
){
425 if ((changed
& (1<<i
)) == 0)
429 wskbd_input(sc
->wskbddev
,
430 (bitset
& (1<<i
)) ? WSCONS_EVENT_KEY_DOWN
: WSCONS_EVENT_KEY_UP
,
434 sc
->notified_bits
= bitset
;
438 kmkbd_intr(void *arg
)
440 struct kmkbd_softc
*sc
= arg
;
441 struct obio_softc
*osc
= (struct obio_softc
*)device_parent(&sc
->dev
);
443 if ( sc
->state
!= ST_ALL_UP
){
444 printf("Spurious interrupt from key matrix\n");
445 obio_intr_mask(osc
, sc
->ih
);
449 kmkbd_new_state(sc
, ST_DEBOUNCE
);
455 kmkbd_debounce(void *arg
)
457 struct kmkbd_softc
*sc
= arg
;
459 enum kmkbd_state new_state
= ST_DEBOUNCE
;
462 newbits
= kmkbd_read_matrix(sc
);
464 if (newbits
!= sc
->last_bits
){
465 sc
->last_bits
= newbits
;
466 sc
->debounce_counter
= 0;
468 else if( ++(sc
->debounce_counter
) >= DEBOUNCE_COUNT
){
469 new_state
= newbits
== 0 ? ST_ALL_UP
: ST_KEY_PRESSED
;
470 kmkbd_report(sc
, newbits
);
473 kmkbd_new_state(sc
, new_state
);
477 /* callout routine to watch key release */
479 kmkbd_watch(void *arg
)
482 struct kmkbd_softc
*sc
= arg
;
484 int new_state
= ST_KEY_PRESSED
;
486 newbits
= kmkbd_read_matrix(sc
);
488 if (newbits
!= sc
->last_bits
){
489 /* some keys are released or new keys are pressed.
491 new_state
= ST_DEBOUNCE
;
492 sc
->last_bits
= newbits
;
495 kmkbd_new_state(sc
, new_state
);
500 kmkbd_new_state(struct kmkbd_softc
*sc
, enum kmkbd_state new_state
)
502 struct obio_softc
*osc
= (struct obio_softc
*)device_parent(&sc
->dev
);
506 if (sc
->state
!= ST_DISABLED
){
507 callout_stop(&sc
->callout
);
508 obio_intr_mask(osc
,sc
->ih
);
512 if (sc
->state
== ST_ALL_UP
){
513 obio_intr_mask(osc
, sc
->ih
);
514 sc
->last_bits
= kmkbd_read_matrix(sc
);
516 if (sc
->state
!= ST_DEBOUNCE
)
517 sc
->debounce_counter
= 0;
519 /* start debounce timer */
520 callout_reset(&sc
->callout
, DEBOUNCE_TICKS
, kmkbd_debounce
, sc
);
523 /* start timer to check key release */
524 callout_reset(&sc
->callout
, RELEASE_WATCH_TICKS
, kmkbd_watch
, sc
);
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
;
531 obio_intr_unmask(osc
, sc
->ih
);
532 bus_space_write_2(iot
, ioh
, G42XXEB_KEYSCAN
, 0);
536 ; /* Nothing to do */
539 sc
->state
= new_state
;