1 /* $NetBSD: smdk2410_kbd.c,v 1.6 2010/06/07 15:17:24 bsh 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.6 2010/06/07 15:17:24 bsh Exp $"); 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/conf.h> 48 49 #include <machine/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 struct device dev; 205 206 struct device *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(struct device *, struct cfdata *, void *); 226 void sskbd_attach(struct device *, struct device *, void *); 227 228 CFATTACH_DECL(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(struct device *parent, struct cfdata *cf, void *aux) 258 { 259 return 1; 260 } 261 262 void 263 sskbd_attach(struct device *parent, struct device *self, void *aux) 264 { 265 struct sskbd_softc *sc = (void *)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 aprint_normal("\n"); 273 274 sc->iot = iot = spia->spia_iot; 275 sc->ioh = spia->spia_ioh; 276 sc->gpioh = gpioh = spia->spia_gpioh; 277 278 /* enable pullup register for MISO */ 279 reg = bus_space_read_2(iot, gpioh, GPIO_PGUP); 280 bus_space_write_2(iot, gpioh, GPIO_PGUP, reg & ~(1<<5)); 281 282 /* nSS and wakeup */ 283 bus_space_write_2(iot, gpioh, GPIO_PBDAT, 284 (1<<SSKBD_SS) | (1<<SSKBD_WUP) | 285 bus_space_read_2(iot, gpioh, GPIO_PBDAT)); 286 reg = bus_space_read_4(iot, gpioh, GPIO_PBCON); 287 reg = GPIO_SET_FUNC(reg, SSKBD_WUP, PCON_OUTPUT); 288 reg = GPIO_SET_FUNC(reg, SSKBD_SS, PCON_OUTPUT); 289 bus_space_write_4(iot, gpioh, GPIO_PBCON, reg); 290 291 /* nATN input to EINT1 */ 292 reg = bus_space_read_4(iot, gpioh, GPIO_PFCON); 293 reg = GPIO_SET_FUNC(reg, 1, PCON_ALTFUN); 294 bus_space_write_4(iot, gpioh, GPIO_PFCON, reg); 295 296 #if 0 /* Controller doesn't seem to respond to this. */ 297 298 /* wakeup pulse */ 299 reg = bus_space_read_4(iot, gpioh, GPIO_PBDAT); 300 reg &= ~(1<<SSKBD_WUP); 301 bus_space_write_4(iot, gpioh, GPIO_PBDAT, reg); 302 delay(100); 303 reg |= (1<<SSKBD_WUP); 304 bus_space_write_4(iot, gpioh, GPIO_PBDAT, reg); 305 306 delay(1000); 307 308 /* Send initialize command. */ 309 sskbd_send(sc, KCDR_ESC); 310 sskbd_send(sc, KCDR_INITIALIZE); 311 sskbd_send(sc, 0x7b); 312 #endif 313 314 sc->inptr = sc->outptr = 0; 315 sc->reading = sc->enable = 0; 316 317 sc->atn_ih = s3c24x0_intr_establish(spia->spia_aux_intr, IPL_TTY, 318 IST_EDGE_FALLING, sskbd_atn_intr, sc); 319 320 sc->spi_ih = s3c24x0_intr_establish(spia->spia_intr, IPL_SERIAL, 321 0, sskbd_spi_intr, sc); 322 323 sc->soft_ih = softint_establish(SOFTINT_SERIAL, sskbd_soft_intr, sc); 324 325 if (sc->atn_ih == NULL || sc->spi_ih == NULL) 326 aprint_error_dev(self, "can't establish interrupt handler\n"); 327 328 /* setup SPI control register, and prescaler */ 329 s3c24x0_spi_setup((struct ssspi_softc *)device_parent(self), 330 SPCON_SMOD_INT | SPCON_ENSCK | 331 SPCON_MSTR | SPCON_IDLELOW_RISING, 332 100*1000, 0); 333 334 335 /* Attach the wskbd. */ 336 a.console = 0; 337 a.keymap = &sskbd_keymapdata; 338 a.accessops = &sskbd_accessops; 339 a.accesscookie = sc; 340 341 sc->wskbddev = config_found(self, &a, wskbddevprint); 342 } 343 344 345 /* 346 * Interrupt handler for nATN signal. 347 */ 348 static int 349 sskbd_atn_intr(void *arg) 350 { 351 struct sskbd_softc *sc = arg; 352 int s; 353 uint32_t reg; 354 355 /* make sure SPI transmitter is ready */ 356 if (!(bus_space_read_1(sc->iot, sc->ioh, SPI_SPSTA) & SPSTA_REDY)) 357 return 1; 358 359 360 if (advance_ring_ptr(sc->inptr) == sc->outptr) { 361 /* ring buffer is full. ignore this nATN signale */ 362 softint_schedule(sc->soft_ih); 363 return 1; 364 } 365 366 /* nSS = L */ 367 s = splserial(); 368 sc->reading = 1; 369 reg = bus_space_read_2(sc->iot, sc->gpioh, GPIO_PBDAT); 370 bus_space_write_2(sc->iot, sc->gpioh, GPIO_PBDAT, 371 reg & ~(1<<SSKBD_SS)); 372 373 /* generate clock to receive data from the controller */ 374 bus_space_write_1(sc->iot, sc->ioh, SPI_SPTDAT, 0xff); 375 376 splx(s); 377 378 return 1; 379 } 380 381 /* 382 * Interrupt handler for SPI rx 383 */ 384 static int 385 sskbd_spi_intr(void *arg) 386 { 387 struct sskbd_softc *sc = arg; 388 int data; 389 uint32_t reg; 390 391 if (sc->reading == 0) 392 return 1; /* Ignore garbate input. */ 393 394 sc->reading = 0; 395 396 data = bus_space_read_1(sc->iot, sc->ioh, SPI_SPRDAT); 397 398 /* nSS = H */ 399 reg = bus_space_read_2(sc->iot, sc->gpioh, GPIO_PBDAT); 400 bus_space_write_2(sc->iot, sc->gpioh, GPIO_PBDAT, 401 reg | (1<<SSKBD_SS)); 402 403 if (sc->enable) { 404 sc->ring[sc->inptr] = data; 405 sc->inptr = advance_ring_ptr(sc->inptr); 406 407 softint_schedule(sc->soft_ih); 408 } 409 #ifdef KBD_DEBUG 410 else { 411 printf("discard %x\n", data); 412 } 413 #endif 414 415 return 1; 416 } 417 418 static void 419 sskbd_soft_intr(void *arg) 420 { 421 struct sskbd_softc *sc = arg; 422 int key, up; 423 424 while (sc->outptr != sc->inptr) { 425 key = sc->ring[sc->outptr]; 426 sc->outptr = advance_ring_ptr(sc->outptr); 427 428 up = key & 0x80; 429 key &= ~0x80; 430 431 key -= 1; 432 if (key < 0 || 8*14 < key) 433 continue; 434 435 #ifdef KBD_DEBUG 436 printf("key %d %s\n", key, up ? "up" : "down"); 437 #endif 438 wskbd_input(sc->wskbddev, 439 up ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN, 440 key); 441 } 442 } 443 444 static int 445 sskbd_enable(void *v, int on) 446 { 447 struct sskbd_softc *sc = v; 448 449 #ifdef KBD_DEBUG 450 printf("%s: enable\n", device_xname(sc->dev)); 451 #endif 452 453 #if 0 454 if (!on && isconsole(sc)) 455 return EBUSY; 456 #endif 457 458 sc->enable = on; 459 return (0); 460 } 461 462 463 static void 464 sskbd_set_leds(void *v, int leds) 465 { 466 } 467 468 static int 469 sskbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 470 { 471 /*struct sskbd_softc *sc = v;*/ 472 473 switch (cmd) { 474 case WSKBDIO_GTYPE: 475 *(int *)data = WSKBD_TYPE_HPC_KBD; /* XXX */ 476 return 0; 477 case WSKBDIO_COMPLEXBELL: 478 #ifdef notyet 479 #define d ((struct wskbd_bell_data *)data) 480 /* 481 * Keyboard can't beep directly; we have an 482 * externally-provided global hook to do this. 483 */ 484 sskbd_bell(d->pitch, d->period, d->volume, 0); 485 #undef d 486 #endif 487 return (0); 488 #ifdef WSDISPLAY_COMPAT_RAWKBD 489 case WSKBDIO_SETMODE: 490 sc->rawkbd = (*(int *)data == WSKBD_RAW); 491 return (0); 492 #endif 493 494 #if 0 495 case WSKBDIO_SETLEDS: 496 case WSKBDIO_GETLEDS: 497 /* no LED support */ 498 #endif 499 } 500 return EPASSTHROUGH; 501 } 502