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