1 /* $NetBSD: smdk2410_kbd.c,v 1.8 2012/10/27 17:17:49 chs Exp $ */ 2 3 /* 4 * Copyright (c) 2004 Genetec Corporation. All rights reserved. 5 * Written by Hiroyuki Bessho for Genetec Corporation. 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 * 3. The name of Genetec Corporation may not be used to endorse or 16 * promote products derived from this software without specific prior 17 * written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Support SMDK2410's keyboard. 34 * 35 * On-board keyboard controller is Semtech SPICoder SA01. 36 * (http://www.semtech.com/pdf/doc5-spi-sa01-ds.pdf) 37 * 38 * The controller is connected to SPI1. 39 * _ATN signal from the SPICoder is connected to EINT1. 40 */ 41 42 #include <sys/cdefs.h> 43 __KERNEL_RCSID(0, "$NetBSD: smdk2410_kbd.c,v 1.8 2012/10/27 17:17:49 chs Exp $"); 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/conf.h> 48 49 #include <sys/bus.h> 50 #include <machine/cpu.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 <arm/s3c2xx0/s3c24x0var.h> 58 #include <arm/s3c2xx0/s3c24x0reg.h> 59 #include <arm/s3c2xx0/s3c2410reg.h> 60 61 #include <arm/s3c2xx0/s3c24x0_spi.h> 62 63 #include "locators.h" 64 65 /* 66 * Keyboard driver for Semtech keyboard controller on SMDK2410. 67 * 68 * There are several keycoder products from Semtech. 69 * This driver supports SPICoder(R) SA01 (UR5HCSPI-SA01) only. 70 * 71 * See http://www.semtech.com/products/ for detail. 72 */ 73 74 /* 75 * Commands/responce 76 */ 77 #define KCDR_INITIALIZE 0xa0 /* Initialize request */ 78 #define KCDR_INITCOMP 0xa1 /* Initialize complete */ 79 #define KCDR_HEARTBEAT 0xa2 /* Heaartbeat request/response */ 80 #define KCDR_IDENTIFY 0xf2 /* Identification request/response */ 81 #define KCDR_LEDSTATUS 0xa3 /* LED status request/report */ 82 #define KCDR_LEDMODIFY 0xa6 /* LED mode modify */ 83 #define KCDR_RESENTREQ 0xa5 /* Re-send request upon error */ 84 #define KCDR_IOMODE 0xa7 /* Input/output mode modify/report */ 85 #define KCDR_OUTPUT 0xa8 /* output to GPIO0 pin */ 86 #define KCDR_SETWAKEUP 0xa9 /* define wake-up keys */ 87 88 #define KCDR_CONTROL 0x80 /* Commands from KeyCorder to Host starts with 89 this code. */ 90 #define KCDR_ESC 0x1b /* Commands from host to KeyCorder starts with 91 this code. */ 92 93 /* 94 * GPIO ports 95 */ 96 #define SSKBD_WUP 0 /* nWUP = GPB0 */ 97 #define SSKBD_SS 6 /* nSS = GPB6 */ 98 99 /* 100 * keymap 101 */ 102 103 #define _(col,row) KS_KEYCODE((col)*8+(row)) 104 105 static const keysym_t sskbd_keydesc_0[] = { 106 /* _(col,row) normal shifted */ 107 _(0,0), KS_Alt_L, KS_Alt_L, 108 109 _(1,0), KS_grave, KS_asciitilde, 110 _(1,1), KS_backslash, KS_bar, 111 _(1,2), KS_Tab, KS_Tab, 112 _(1,3), KS_z, KS_Z, 113 _(1,4), KS_a, KS_A, 114 _(1,5), KS_x, KS_X, 115 116 _(2,1), KS_Shift_L, KS_Shift_L, 117 118 _(3,0), KS_Control_L, KS_Control_L, 119 120 _(4,0), KS_Meta_L, KS_Meta_L, 121 122 _(5,0), KS_Escape, KS_Escape, 123 _(5,1), KS_Delete, KS_Delete, 124 _(5,2), KS_q, KS_Q, 125 _(5,3), KS_Caps_Lock, KS_Caps_Lock, 126 _(5,4), KS_s, KS_S, 127 _(5,5), KS_c, KS_C, 128 _(5,6), KS_3, KS_numbersign, 129 130 _(6,0), KS_1, KS_exclam, 131 _(6,2), KS_w, KS_W, 132 _(6,4), KS_d, KS_D, 133 _(6,5), KS_v, KS_V, 134 _(6,6), KS_4, KS_dollar, 135 136 _(7,0), KS_2, KS_at, 137 _(7,1), KS_t, KS_T, 138 _(7,2), KS_e, KS_E, 139 _(7,4), KS_f, KS_F, 140 _(7,5), KS_b, KS_B, 141 _(7,6), KS_5, KS_percent, 142 143 _(8,0), KS_9, KS_parenleft, 144 _(8,1), KS_y, KS_Y, 145 _(8,2), KS_r, KS_R, 146 _(8,3), KS_k, KS_K, 147 _(8,4), KS_g, KS_G, 148 _(8,5), KS_n, KS_N, 149 _(8,6), KS_6, KS_asciicircum, 150 151 _(9,0), KS_0, KS_parenright, 152 _(9,1), KS_u, KS_U, 153 _(9,2), KS_o, KS_O, 154 _(9,3), KS_l, KS_L, 155 _(9,4), KS_h, KS_H, 156 _(9,5), KS_m, KS_M, 157 _(9,6), KS_7, KS_ampersand, 158 159 _(10,0), KS_minus, KS_underscore, 160 _(10,1), KS_i, KS_I, 161 _(10,2), KS_p, KS_P, 162 _(10,3), KS_l, KS_L, 163 _(10,4), KS_j, KS_J, 164 _(10,5), KS_comma, KS_less, 165 _(10,6), KS_8, KS_asterisk, 166 167 _(11,0), KS_equal, KS_plus, 168 _(11,1), KS_Return, KS_Return, 169 _(11,2), KS_bracketleft, KS_braceleft, 170 _(11,3), KS_apostrophe, KS_quotedbl, 171 _(11,4), KS_slash, KS_question, 172 _(11,5), KS_period, KS_greater, 173 _(11,6), KS_Menu, KS_Menu, /* Prog key */ 174 175 _(12,1), KS_Shift_R, KS_Shift_R, 176 177 _(13,0), KS_BackSpace, KS_BackSpace, 178 _(13,1), KS_Down, KS_Next, 179 _(13,2), KS_bracketright, KS_braceright, 180 _(13,3), KS_Up, KS_Prior, 181 _(13,4), KS_Left, KS_Home, 182 _(13,5), KS_space, KS_space, 183 _(13,6), KS_Right, KS_End, 184 }; 185 186 #define KBD_MAP(name, base, map) \ 187 { name, base, sizeof(map)/sizeof(keysym_t), map } 188 189 static const struct wscons_keydesc sskbd_keydesctab[] = { 190 KBD_MAP(KB_MACHDEP, 0, sskbd_keydesc_0), 191 {0, 0, 0, 0} 192 }; 193 194 const struct wskbd_mapdata sskbd_keymapdata = { 195 sskbd_keydesctab, 196 KB_MACHDEP, 197 }; 198 199 200 /* 201 * SMDK2410 keyboard driver. 202 */ 203 struct sskbd_softc { 204 device_t sc_dev; 205 206 device_t wskbddev; 207 void *atn_ih; /* interrupt handler for nATN */ 208 void *spi_ih; /* interrupt handler for SPI rx */ 209 210 void *soft_ih; /* soft interrupt */ 211 212 bus_space_tag_t iot; 213 bus_space_handle_t ioh; 214 bus_space_handle_t gpioh; 215 216 #define RING_SIZE 16 /* must be power of 2 */ 217 short inptr, outptr; 218 unsigned char ring[RING_SIZE]; 219 #define advance_ring_ptr(p) ((p+1) & ~RING_SIZE) 220 221 short reading, enable; 222 }; 223 224 225 int sskbd_match(device_t, cfdata_t, void *); 226 void sskbd_attach(device_t, device_t, void *); 227 228 CFATTACH_DECL_NEW(sskbd, sizeof(struct sskbd_softc), 229 sskbd_match, sskbd_attach, NULL, NULL); 230 231 static int sskbd_enable(void *, int); 232 static void sskbd_set_leds(void *, int); 233 static int sskbd_ioctl(void *, u_long, void *, int, struct lwp *); 234 static int sskbd_atn_intr(void *); 235 static int sskbd_spi_intr(void *); 236 static void sskbd_soft_intr(void *); 237 238 const struct wskbd_accessops sskbd_accessops = { 239 sskbd_enable, 240 sskbd_set_leds, 241 sskbd_ioctl, 242 }; 243 244 #if 0 245 void sskbd_cngetc(void *, u_int *, int *); 246 void sskbd_cnpollc(void *, int); 247 void sskbd_cnbell(void *, u_int, u_int, u_int); 248 249 const struct wskbd_consops sskbd_consops = { 250 sskbd_cngetc, 251 sskbd_cnpollc, 252 sskbd_cnbell, 253 }; 254 #endif 255 256 int 257 sskbd_match(device_t parent, cfdata_t cf, void *aux) 258 { 259 return 1; 260 } 261 262 void 263 sskbd_attach(device_t parent, device_t self, void *aux) 264 { 265 struct sskbd_softc *sc = device_private(self); 266 struct ssspi_attach_args *spia = aux; 267 uint32_t reg; 268 bus_space_handle_t gpioh; 269 bus_space_tag_t iot; 270 struct wskbddev_attach_args a; 271 272 sc->sc_dev = self; 273 274 aprint_normal("\n"); 275 276 sc->iot = iot = spia->spia_iot; 277 sc->ioh = spia->spia_ioh; 278 sc->gpioh = gpioh = spia->spia_gpioh; 279 280 /* enable pullup register for MISO */ 281 reg = bus_space_read_2(iot, gpioh, GPIO_PGUP); 282 bus_space_write_2(iot, gpioh, GPIO_PGUP, reg & ~(1<<5)); 283 284 /* nSS and wakeup */ 285 bus_space_write_2(iot, gpioh, GPIO_PBDAT, 286 (1<<SSKBD_SS) | (1<<SSKBD_WUP) | 287 bus_space_read_2(iot, gpioh, GPIO_PBDAT)); 288 reg = bus_space_read_4(iot, gpioh, GPIO_PBCON); 289 reg = GPIO_SET_FUNC(reg, SSKBD_WUP, PCON_OUTPUT); 290 reg = GPIO_SET_FUNC(reg, SSKBD_SS, PCON_OUTPUT); 291 bus_space_write_4(iot, gpioh, GPIO_PBCON, reg); 292 293 /* nATN input to EINT1 */ 294 reg = bus_space_read_4(iot, gpioh, GPIO_PFCON); 295 reg = GPIO_SET_FUNC(reg, 1, PCON_ALTFUN); 296 bus_space_write_4(iot, gpioh, GPIO_PFCON, reg); 297 298 #if 0 /* Controller doesn't seem to respond to this. */ 299 300 /* wakeup pulse */ 301 reg = bus_space_read_4(iot, gpioh, GPIO_PBDAT); 302 reg &= ~(1<<SSKBD_WUP); 303 bus_space_write_4(iot, gpioh, GPIO_PBDAT, reg); 304 delay(100); 305 reg |= (1<<SSKBD_WUP); 306 bus_space_write_4(iot, gpioh, GPIO_PBDAT, reg); 307 308 delay(1000); 309 310 /* Send initialize command. */ 311 sskbd_send(sc, KCDR_ESC); 312 sskbd_send(sc, KCDR_INITIALIZE); 313 sskbd_send(sc, 0x7b); 314 #endif 315 316 sc->inptr = sc->outptr = 0; 317 sc->reading = sc->enable = 0; 318 319 sc->atn_ih = s3c24x0_intr_establish(spia->spia_aux_intr, IPL_TTY, 320 IST_EDGE_FALLING, sskbd_atn_intr, sc); 321 322 sc->spi_ih = s3c24x0_intr_establish(spia->spia_intr, IPL_SERIAL, 323 0, sskbd_spi_intr, sc); 324 325 sc->soft_ih = softint_establish(SOFTINT_SERIAL, sskbd_soft_intr, sc); 326 327 if (sc->atn_ih == NULL || sc->spi_ih == NULL) 328 aprint_error_dev(self, "can't establish interrupt handler\n"); 329 330 /* setup SPI control register, and prescaler */ 331 s3c24x0_spi_setup(device_private(device_parent(self)), 332 SPCON_SMOD_INT | SPCON_ENSCK | 333 SPCON_MSTR | SPCON_IDLELOW_RISING, 334 100*1000, 0); 335 336 337 /* Attach the wskbd. */ 338 a.console = 0; 339 a.keymap = &sskbd_keymapdata; 340 a.accessops = &sskbd_accessops; 341 a.accesscookie = sc; 342 343 sc->wskbddev = config_found(self, &a, wskbddevprint); 344 } 345 346 347 /* 348 * Interrupt handler for nATN signal. 349 */ 350 static int 351 sskbd_atn_intr(void *arg) 352 { 353 struct sskbd_softc *sc = arg; 354 int s; 355 uint32_t reg; 356 357 /* make sure SPI transmitter is ready */ 358 if (!(bus_space_read_1(sc->iot, sc->ioh, SPI_SPSTA) & SPSTA_REDY)) 359 return 1; 360 361 362 if (advance_ring_ptr(sc->inptr) == sc->outptr) { 363 /* ring buffer is full. ignore this nATN signale */ 364 softint_schedule(sc->soft_ih); 365 return 1; 366 } 367 368 /* nSS = L */ 369 s = splserial(); 370 sc->reading = 1; 371 reg = bus_space_read_2(sc->iot, sc->gpioh, GPIO_PBDAT); 372 bus_space_write_2(sc->iot, sc->gpioh, GPIO_PBDAT, 373 reg & ~(1<<SSKBD_SS)); 374 375 /* generate clock to receive data from the controller */ 376 bus_space_write_1(sc->iot, sc->ioh, SPI_SPTDAT, 0xff); 377 378 splx(s); 379 380 return 1; 381 } 382 383 /* 384 * Interrupt handler for SPI rx 385 */ 386 static int 387 sskbd_spi_intr(void *arg) 388 { 389 struct sskbd_softc *sc = arg; 390 int data; 391 uint32_t reg; 392 393 if (sc->reading == 0) 394 return 1; /* Ignore garbate input. */ 395 396 sc->reading = 0; 397 398 data = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT); 399 400 /* nSS = H */ 401 reg = bus_space_read_2(sc->iot, sc->gpioh, GPIO_PBDAT); 402 bus_space_write_2(sc->iot, sc->gpioh, GPIO_PBDAT, 403 reg | (1<<SSKBD_SS)); 404 405 if (sc->enable) { 406 sc->ring[sc->inptr] = data; 407 sc->inptr = advance_ring_ptr(sc->inptr); 408 409 softint_schedule(sc->soft_ih); 410 } 411 #ifdef KBD_DEBUG 412 else { 413 printf("discard %x\n", data); 414 } 415 #endif 416 417 return 1; 418 } 419 420 static void 421 sskbd_soft_intr(void *arg) 422 { 423 struct sskbd_softc *sc = arg; 424 int key, up; 425 426 while (sc->outptr != sc->inptr) { 427 key = sc->ring[sc->outptr]; 428 sc->outptr = advance_ring_ptr(sc->outptr); 429 430 up = key & 0x80; 431 key &= ~0x80; 432 433 key -= 1; 434 if (key < 0 || 8*14 < key) 435 continue; 436 437 #ifdef KBD_DEBUG 438 printf("key %d %s\n", key, up ? "up" : "down"); 439 #endif 440 wskbd_input(sc->wskbddev, 441 up ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN, 442 key); 443 } 444 } 445 446 static int 447 sskbd_enable(void *v, int on) 448 { 449 struct sskbd_softc *sc = v; 450 451 #ifdef KBD_DEBUG 452 printf("%s: enable\n", device_xname(sc->sc_dev)); 453 #endif 454 455 #if 0 456 if (!on && isconsole(sc)) 457 return EBUSY; 458 #endif 459 460 sc->enable = on; 461 return (0); 462 } 463 464 465 static void 466 sskbd_set_leds(void *v, int leds) 467 { 468 } 469 470 static int 471 sskbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 472 { 473 /*struct sskbd_softc *sc = v;*/ 474 475 switch (cmd) { 476 case WSKBDIO_GTYPE: 477 *(int *)data = WSKBD_TYPE_HPC_KBD; /* XXX */ 478 return 0; 479 case WSKBDIO_COMPLEXBELL: 480 #ifdef notyet 481 #define d ((struct wskbd_bell_data *)data) 482 /* 483 * Keyboard can't beep directly; we have an 484 * externally-provided global hook to do this. 485 */ 486 sskbd_bell(d->pitch, d->period, d->volume, 0); 487 #undef d 488 #endif 489 return (0); 490 #ifdef WSDISPLAY_COMPAT_RAWKBD 491 case WSKBDIO_SETMODE: 492 sc->rawkbd = (*(int *)data == WSKBD_RAW); 493 return (0); 494 #endif 495 496 #if 0 497 case WSKBDIO_SETLEDS: 498 case WSKBDIO_GETLEDS: 499 /* no LED support */ 500 #endif 501 } 502 return EPASSTHROUGH; 503 } 504