xref: /netbsd/sys/arch/hpcarm/dev/wzero3_keypad.c (revision 6550d01e)
1 /*	$NetBSD: wzero3_keypad.c,v 1.1 2010/05/30 10:00:27 nonaka Exp $	*/
2 
3 /*
4  * Copyright (c) 2010 NONAKA Kimihiro <nonaka@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 2010/05/30 10:00:27 nonaka 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