wzero3_keypad.c revision 1.1.6.2 1 /* $NetBSD: wzero3_keypad.c,v 1.1.6.2 2010/08/17 06:44:26 uebayasi Exp $ */
2
3 /*
4 * Copyright (c) 2010 NONAKA Kimihiro <nonaka (at) netbsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: wzero3_keypad.c,v 1.1.6.2 2010/08/17 06:44:26 uebayasi Exp $");
31
32 #include "wzero3lcd.h"
33 #include "opt_wsdisplay_compat.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/kernel.h>
39 #include <sys/callout.h>
40
41 #include <arm/xscale/pxa2x0cpu.h>
42 #include <arm/xscale/pxa2x0var.h>
43 #include <arm/xscale/pxa2x0_gpio.h>
44
45 #include <machine/bus.h>
46 #include <machine/bootinfo.h>
47 #include <machine/config_hook.h>
48 #include <machine/platid.h>
49 #include <machine/platid_mask.h>
50
51 #include <dev/wscons/wsconsio.h>
52 #include <dev/wscons/wskbdvar.h>
53 #include <dev/wscons/wsksymvar.h>
54 #include <dev/wscons/wsksymdef.h>
55
56 #ifdef WSDISPLAY_COMPAT_RAWKBD
57 #include <dev/hpc/pckbd_encode.h>
58 #endif
59
60 #include <arch/hpcarm/dev/wzero3_reg.h>
61 #include <arch/hpcarm/dev/wzero3_sspvar.h>
62
63 enum {
64 KD_0,
65 KD_1,
66 KD_2,
67 KD_3,
68 KD_4,
69 KD_5,
70 KD_6,
71 KD_7,
72 KD_8,
73 KD_9,
74 KD_ASTERISK,
75 KD_NUMBER,
76 KD_WINDOWS,
77 KD_OK,
78 KD_ONHOOK,
79 KD_OFFHOOK,
80 KD_CLEAR,
81 KD_MOJI,
82 KD_UP,
83 KD_DOWN,
84 KD_LEFT,
85 KD_RIGHT,
86 KD_CENTER_BUTTON,
87 KD_LSOFT,
88 KD_RSOFT,
89 KD_NUM,
90
91 KD_INVALID = -1
92 };
93
94 static int ws011sh_keyscan2keydown[32] = {
95 KD_INVALID,
96 KD_CLEAR,
97 KD_INVALID,
98 KD_OK,
99 KD_INVALID,
100 KD_LEFT,
101 KD_INVALID,
102 KD_ONHOOK,
103 KD_INVALID,
104 KD_UP,
105 KD_DOWN,
106 KD_MOJI,
107 KD_INVALID,
108 KD_WINDOWS,
109 KD_INVALID,
110 KD_RIGHT,
111 KD_INVALID,
112 KD_1,
113 KD_4,
114 KD_7,
115 KD_ASTERISK,
116 KD_2,
117 KD_5,
118 KD_8,
119 KD_0,
120 KD_CENTER_BUTTON,
121 KD_INVALID,
122 KD_3,
123 KD_6,
124 KD_9,
125 KD_NUMBER,
126 KD_INVALID,
127 };
128
129 struct wzero3keypad_softc {
130 device_t sc_dev;
131
132 void *sc_ih;
133 int sc_intr_pin;
134
135 uint32_t sc_okeystat;
136
137 struct callout sc_poll_ch;
138 int sc_poll_interval;
139
140 device_t sc_wskbddev;
141
142 #ifdef WSDISPLAY_COMPAT_RAWKBD
143 int sc_rawkbd;
144 #endif
145 };
146
147 static int wzero3keypad_match(device_t, cfdata_t, void *);
148 static void wzero3keypad_attach(device_t, device_t, void *);
149
150 CFATTACH_DECL_NEW(wzero3keypad, sizeof(struct wzero3keypad_softc),
151 wzero3keypad_match, wzero3keypad_attach, NULL, NULL);
152
153 static int wzero3keypad_wskbd_enable(void *, int);
154 static void wzero3keypad_wskbd_set_leds(void *, int);
155 static int wzero3keypad_wskbd_ioctl(void *, u_long, void *, int, struct lwp *);
156
157 static const int wzero3keypad_wskbd_keys[] = {
158 82, /* KD_0: 0 */
159 79, /* KD_1: 1 */
160 80, /* KD_2: 2 */
161 81, /* KD_3: 3 */
162 75, /* KD_4: 4 */
163 76, /* KD_5: 5 */
164 77, /* KD_6: 6 */
165 71, /* KD_7: 7 */
166 72, /* KD_8: 8 */
167 73, /* KD_9: 9 */
168 64, /* KD_ASTERISK: f6 */
169 65, /* KD_NUMBER: f7 */
170 221, /* KD_WINDOWS: Menu */
171 61, /* KD_OK: f3 */
172 59, /* KD_ONHOOK: f1 */
173 60, /* KD_OFFHOOK: f2 */
174 62, /* KD_CLEAR: f4 */
175 63, /* KD_MOJI: f5 */
176 200, /* KD_UP: Up */
177 208, /* KD_DOWN: Down */
178 203, /* KD_LEFT: Left */
179 205, /* KD_RIGHT: Right */
180 156, /* KD_CENTER_BUTTON: KP_Enter */
181 87, /* KD_LSOFT: f11 */
182 88, /* KD_RSOFT: f12 */
183 };
184
185 static const keysym_t wzero3keypad_wskbd_keydesc[] = {
186 KS_KEYCODE(59), KS_f1,
187 KS_KEYCODE(60), KS_f2,
188 KS_KEYCODE(61), KS_f3,
189 KS_KEYCODE(62), KS_f4,
190 KS_KEYCODE(63), KS_f5,
191 KS_KEYCODE(64), KS_f6,
192 KS_KEYCODE(65), KS_f7,
193 KS_KEYCODE(71), KS_7,
194 KS_KEYCODE(72), KS_8,
195 KS_KEYCODE(73), KS_9,
196 KS_KEYCODE(75), KS_4,
197 KS_KEYCODE(76), KS_5,
198 KS_KEYCODE(77), KS_6,
199 KS_KEYCODE(79), KS_1,
200 KS_KEYCODE(80), KS_2,
201 KS_KEYCODE(81), KS_3,
202 KS_KEYCODE(82), KS_0,
203 KS_KEYCODE(87), KS_f11,
204 KS_KEYCODE(88), KS_f12,
205 KS_KEYCODE(156), KS_KP_Enter,
206 KS_KEYCODE(200), KS_Up,
207 KS_KEYCODE(203), KS_Left,
208 KS_KEYCODE(205), KS_Right,
209 KS_KEYCODE(208), KS_Down,
210 KS_KEYCODE(221), KS_Menu,
211 };
212
213 static const struct wscons_keydesc wzero3keypad_wskbd_keydesctab[] = {
214 { KB_JP, 0,
215 sizeof(wzero3keypad_wskbd_keydesc) / sizeof(keysym_t),
216 wzero3keypad_wskbd_keydesc
217 },
218
219 { 0, 0, 0, 0 }
220 };
221
222 static const struct wskbd_mapdata wzero3keypad_wskbd_keymapdata = {
223 wzero3keypad_wskbd_keydesctab, KB_JP
224 };
225
226 static const struct wskbd_accessops wzero3keypad_wskbd_accessops = {
227 wzero3keypad_wskbd_enable,
228 wzero3keypad_wskbd_set_leds,
229 wzero3keypad_wskbd_ioctl,
230 };
231
232 static int wzero3keypad_intr(void *);
233 static void wzero3keypad_poll(void *);
234 static void wzero3keypad_poll1(struct wzero3keypad_softc *, int);
235
236 static void wzero3keypad_init(struct wzero3keypad_softc *);
237 static uint32_t wzero3keypad_getkeydown(struct wzero3keypad_softc *, int);
238
239 static const struct wzero3keypad_model {
240 platid_mask_t *platid;
241 int intr_pin;
242 } wzero3keypad_table[] = {
243 #if 0
244 /* WS007SH */
245 {
246 &platid_mask_MACH_SHARP_WZERO3_WS007SH,
247 -1, /* XXX */
248 },
249 #endif
250 /* WS011SH */
251 {
252 &platid_mask_MACH_SHARP_WZERO3_WS011SH,
253 GPIO_WS011SH_KEYPAD,
254 },
255
256 { NULL, -1, }
257 };
258
259 static const struct wzero3keypad_model *
260 wzero3keypad_lookup(void)
261 {
262 const struct wzero3keypad_model *model;
263
264 for (model = wzero3keypad_table; model->platid != NULL; model++) {
265 if (platid_match(&platid, model->platid)) {
266 return model;
267 }
268 }
269 return NULL;
270 }
271
272 static int
273 wzero3keypad_match(struct device *parent, struct cfdata *cf, void *aux)
274 {
275
276 if (strcmp(cf->cf_name, "wzero3keypad") != 0)
277 return 0;
278 if (wzero3keypad_lookup() == NULL)
279 return 0;
280 return 1;
281 }
282
283 static void
284 wzero3keypad_attach(struct device *parent, struct device *self, void *aux)
285 {
286 struct wzero3keypad_softc *sc = device_private(self);
287 const struct wzero3keypad_model *model;
288 struct wskbddev_attach_args wska;
289 #if NWZERO3LCD > 0
290 extern int screen_rotate;
291 #endif
292
293 sc->sc_dev = self;
294 sc->sc_okeystat = 0;
295 #ifdef WSDISPLAY_COMPAT_RAWKBD
296 sc->sc_rawkbd = 0;
297 #endif
298
299 model = wzero3keypad_lookup();
300 if (model == NULL) {
301 aprint_error(": unknown model\n");
302 return;
303 }
304
305 aprint_normal(": keypad\n");
306 aprint_naive("\n");
307
308 sc->sc_intr_pin = model->intr_pin;
309
310 callout_init(&sc->sc_poll_ch, 0);
311 callout_setfunc(&sc->sc_poll_ch, wzero3keypad_poll, sc);
312 sc->sc_poll_interval = hz / 32;
313
314 #if NWZERO3LCD > 0
315 switch (screen_rotate) {
316 default:
317 case 0:
318 break;
319
320 case 270: /* counter clock-wise */
321 ws011sh_keyscan2keydown[5] = KD_UP;
322 ws011sh_keyscan2keydown[9] = KD_RIGHT;
323 ws011sh_keyscan2keydown[10] = KD_LEFT;
324 ws011sh_keyscan2keydown[15] = KD_DOWN;
325 break;
326 }
327 #endif
328
329 /* attach wskbd */
330 wska.console = 0;
331 wska.keymap = &wzero3keypad_wskbd_keymapdata;
332 wska.accessops = &wzero3keypad_wskbd_accessops;
333 wska.accesscookie = sc;
334 sc->sc_wskbddev = config_found_ia(self, "wskbddev", &wska,
335 wskbddevprint);
336
337 /* setup keypad interrupt */
338 pxa2x0_gpio_set_function(sc->sc_intr_pin, GPIO_IN);
339 sc->sc_ih = pxa2x0_gpio_intr_establish(sc->sc_intr_pin,
340 IST_EDGE_RISING, IPL_TTY, wzero3keypad_intr, sc);
341 if (sc->sc_ih == NULL) {
342 aprint_error_dev(sc->sc_dev,
343 "couldn't establish keypad interrupt\n");
344 }
345
346 /* init hardware */
347 wzero3keypad_init(sc);
348 }
349
350 static int
351 wzero3keypad_wskbd_enable(void *arg, int onoff)
352 {
353
354 return 0;
355 }
356
357 static void
358 wzero3keypad_wskbd_set_leds(void *arg, int leds)
359 {
360
361 /* Nothing to do */
362 }
363
364 static int
365 wzero3keypad_wskbd_ioctl(void *arg, u_long cmd, void *data, int flags,
366 struct lwp *l)
367 {
368 #ifdef WSDISPLAY_COMPAT_RAWKBD
369 struct wzero3keypad_softc *sc = (struct wzero3keypad_softc *)arg;
370 #endif
371
372 switch (cmd) {
373 case WSKBDIO_GTYPE:
374 *(int *)data = WSKBD_TYPE_HPC_KBD;
375 return 0;
376 case WSKBDIO_SETLEDS:
377 return 0;
378 case WSKBDIO_GETLEDS:
379 *(int *)data = 0;
380 return 0;
381 #ifdef WSDISPLAY_COMPAT_RAWKBD
382 case WSKBDIO_SETMODE:
383 sc->sc_rawkbd = (*(int *)data == WSKBD_RAW);
384 return 0;
385 #endif
386 }
387
388 return EPASSTHROUGH;
389 }
390
391 static int
392 wzero3keypad_intr(void *arg)
393 {
394 struct wzero3keypad_softc *sc = (struct wzero3keypad_softc *)arg;
395
396 pxa2x0_gpio_clear_intr(sc->sc_intr_pin);
397
398 wzero3keypad_poll1(sc, 0);
399
400 callout_schedule(&sc->sc_poll_ch, sc->sc_poll_interval);
401
402 return 1;
403 }
404
405 static void
406 wzero3keypad_poll(void *v)
407 {
408 struct wzero3keypad_softc *sc = (struct wzero3keypad_softc *)v;
409
410 wzero3keypad_poll1(sc, 1);
411
412 callout_stop(&sc->sc_poll_ch);
413 }
414
415 static void
416 wzero3keypad_poll1(struct wzero3keypad_softc *sc, int doscan)
417 {
418 uint32_t keydown;
419 uint32_t diff;
420 int i;
421 int s;
422
423 s = spltty();
424
425 keydown = wzero3keypad_getkeydown(sc, doscan);
426 diff = keydown ^ sc->sc_okeystat;
427 if (diff == 0)
428 goto out;
429
430 for (i = 0; i < KD_NUM; i++) {
431 if (diff & (1 << i)) {
432 int state = keydown & (1 << i);
433 int type = state ? WSCONS_EVENT_KEY_DOWN :
434 WSCONS_EVENT_KEY_UP;
435 int key = wzero3keypad_wskbd_keys[i];
436 #ifdef WSDISPLAY_COMPAT_RAWKBD
437 if (sc->sc_rawkbd) {
438 int n;
439 u_char data[16];
440
441 n = pckbd_encode(type, key, data);
442 wskbd_rawinput(sc->sc_wskbddev, data, n);
443 } else
444 #endif
445 wskbd_input(sc->sc_wskbddev, type, key);
446 }
447 }
448 sc->sc_okeystat = keydown;
449
450 out:
451 splx(s);
452 }
453
454 /*----------------------------------------------------------------------------
455 * AK4184 keypad controller for WS011SH
456 */
457 /* ak4184 command register */
458 #define AKMCTRL_WR_SH 7
459 #define AKMCTRL_PAGE_SH 6
460 #define AKMCTRL_ADDR_SH 0
461 #define AKMCTRL_WRITE (0<<AKMCTRL_WR_SH)
462 #define AKMCTRL_READ (1<<AKMCTRL_WR_SH)
463 #define AKMCTRL_DATA (0<<AKMCTRL_PAGE_SH)
464 #define AKMCTRL_CTRL (1<<AKMCTRL_PAGE_SH)
465
466 static void
467 wzero3keypad_init(struct wzero3keypad_softc *sc)
468 {
469 int s;
470
471 s = spltty();
472
473 #if 0
474 /*
475 * - key interrupt enable
476 * - key touch scan
477 * - debounce time: 1ms
478 * - wait 100us for debounce time
479 */
480 (void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
481 AKMCTRL_WRITE | AKMCTRL_CTRL | (0<<AKMCTRL_ADDR_SH), 0);
482 #endif
483
484 /* unmask all keys & columns */
485 (void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
486 AKMCTRL_WRITE | AKMCTRL_CTRL | (1<<AKMCTRL_ADDR_SH), 0);
487 (void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
488 AKMCTRL_WRITE | AKMCTRL_CTRL | (2<<AKMCTRL_ADDR_SH), 0);
489 (void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
490 AKMCTRL_WRITE | AKMCTRL_CTRL | (3<<AKMCTRL_ADDR_SH), 0);
491
492 /* Enable keypad interrupt (kpdata dummy read) */
493 (void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
494 AKMCTRL_READ | AKMCTRL_DATA | (1<<AKMCTRL_ADDR_SH), 0);
495
496 splx(s);
497 }
498
499 static uint32_t
500 wzero3keypad_getkeydown(struct wzero3keypad_softc *sc, int doscan)
501 {
502 uint32_t keydown = 0;
503 uint16_t status;
504 uint16_t kpdata;
505 int timo;
506
507 if (doscan) {
508 /* host scan */
509 (void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
510 AKMCTRL_WRITE | AKMCTRL_CTRL | (4<<AKMCTRL_ADDR_SH), 0);
511 delay(100);
512 }
513
514 timo = 1000;
515 do {
516 status = wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
517 AKMCTRL_READ | AKMCTRL_CTRL | (0<<AKMCTRL_ADDR_SH), 0);
518 } while ((status & 0xc000) == 0 && timo-- > 0);
519
520 kpdata = wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
521 AKMCTRL_READ | AKMCTRL_DATA | (1<<AKMCTRL_ADDR_SH), 0);
522 if ((status & 0xc000) == 0xc000) {
523 if (!(kpdata & 0x8000)) {
524 int i;
525
526 for (i = 0; i < 3; i++) {
527 int key, bits;
528
529 key = kpdata & 0x1f;
530 if (key == 0)
531 break;
532 bits = ws011sh_keyscan2keydown[key];
533 if (bits != KD_INVALID)
534 keydown |= 1 << bits;
535 kpdata >>= 5;
536 }
537 }
538 }
539
540 return keydown;
541 }
542