1 /* $NetBSD: smdk2410_kbd.c,v 1.4 2007/03/04 05:59:45 christos Exp $ */
4 * Copyright (c) 2004 Genetec Corporation. All rights reserved.
5 * Written by Hiroyuki Bessho for Genetec Corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
33 * Support SMDK2410's keyboard.
35 * On-board keyboard controller is Semtech SPICoder SA01.
36 * (http://www.semtech.com/pdf/doc5-spi-sa01-ds.pdf)
38 * The controller is connected to SPI1.
39 * _ATN signal from the SPICoder is connected to EINT1.
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: smdk2410_kbd.c,v 1.4 2007/03/04 05:59:45 christos Exp $");
45 #include <sys/param.h>
46 #include <sys/systm.h>
49 #include <machine/bus.h>
50 #include <machine/cpu.h>
52 #include <dev/wscons/wsconsio.h>
53 #include <dev/wscons/wskbdvar.h>
54 #include <dev/wscons/wsksymdef.h>
55 #include <dev/wscons/wsksymvar.h>
57 #include <arm/s3c2xx0/s3c24x0var.h>
58 #include <arm/s3c2xx0/s3c24x0reg.h>
59 #include <arm/s3c2xx0/s3c2410reg.h>
61 #include <arm/s3c2xx0/s3c24x0_spi.h>
66 * Keyboard driver for Semtech keyboard controller on SMDK2410.
68 * There are several keycoder products from Semtech.
69 * This driver supports SPICoder(R) SA01 (UR5HCSPI-SA01) only.
71 * See http://www.semtech.com/products/ for detail.
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 */
88 #define KCDR_CONTROL 0x80 /* Commands from KeyCorder to Host starts with
90 #define KCDR_ESC 0x1b /* Commands from host to KeyCorder starts with
96 #define SSKBD_WUP 0 /* nWUP = GPB0 */
97 #define SSKBD_SS 6 /* nSS = GPB6 */
103 #define _(col,row) KS_KEYCODE((col)*8+(row))
105 static const keysym_t sskbd_keydesc_0
[] = {
106 /* _(col,row) normal shifted */
107 _(0,0), KS_Alt_L
, KS_Alt_L
,
109 _(1,0), KS_grave
, KS_asciitilde
,
110 _(1,1), KS_backslash
, KS_bar
,
111 _(1,2), KS_Tab
, KS_Tab
,
116 _(2,1), KS_Shift_L
, KS_Shift_L
,
118 _(3,0), KS_Control_L
, KS_Control_L
,
120 _(4,0), KS_Meta_L
, KS_Meta_L
,
122 _(5,0), KS_Escape
, KS_Escape
,
123 _(5,1), KS_Delete
, KS_Delete
,
125 _(5,3), KS_Caps_Lock
, KS_Caps_Lock
,
128 _(5,6), KS_3
, KS_numbersign
,
130 _(6,0), KS_1
, KS_exclam
,
134 _(6,6), KS_4
, KS_dollar
,
141 _(7,6), KS_5
, KS_percent
,
143 _(8,0), KS_9
, KS_parenleft
,
149 _(8,6), KS_6
, KS_asciicircum
,
151 _(9,0), KS_0
, KS_parenright
,
157 _(9,6), KS_7
, KS_ampersand
,
159 _(10,0), KS_minus
, KS_underscore
,
164 _(10,5), KS_comma
, KS_less
,
165 _(10,6), KS_8
, KS_asterisk
,
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 */
175 _(12,1), KS_Shift_R
, KS_Shift_R
,
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
,
186 #define KBD_MAP(name, base, map) \
187 { name, base, sizeof(map)/sizeof(keysym_t), map }
189 static const struct wscons_keydesc sskbd_keydesctab
[] = {
190 KBD_MAP(KB_MACHDEP
, 0, sskbd_keydesc_0
),
194 const struct wskbd_mapdata sskbd_keymapdata
= {
201 * SMDK2410 keyboard driver.
206 struct device
*wskbddev
;
207 void *atn_ih
; /* interrupt handler for nATN */
208 void *spi_ih
; /* interrupt handler for SPI rx */
210 void *soft_ih
; /* soft interrupt */
213 bus_space_handle_t ioh
;
214 bus_space_handle_t gpioh
;
216 #define RING_SIZE 16 /* must be power of 2 */
218 unsigned char ring
[RING_SIZE
];
219 #define advance_ring_ptr(p) ((p+1) & ~RING_SIZE)
221 short reading
, enable
;
225 int sskbd_match(struct device
*, struct cfdata
*, void *);
226 void sskbd_attach(struct device
*, struct device
*, void *);
228 CFATTACH_DECL(sskbd
, sizeof(struct sskbd_softc
),
229 sskbd_match
, sskbd_attach
, NULL
, NULL
);
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 *);
238 const struct wskbd_accessops sskbd_accessops
= {
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
);
249 const struct wskbd_consops sskbd_consops
= {
257 sskbd_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
263 sskbd_attach(struct device
*parent
, struct device
*self
, void *aux
)
265 struct sskbd_softc
*sc
= (void *)self
;
266 struct ssspi_attach_args
*spia
= aux
;
268 bus_space_handle_t gpioh
;
270 struct wskbddev_attach_args a
;
274 sc
->iot
= iot
= spia
->spia_iot
;
275 sc
->ioh
= spia
->spia_ioh
;
276 sc
->gpioh
= gpioh
= spia
->spia_gpioh
;
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));
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
);
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
);
296 #if 0 /* Controller doesn't seem to respond to this. */
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
);
303 reg
|= (1<<SSKBD_WUP
);
304 bus_space_write_4(iot
, gpioh
, GPIO_PBDAT
, reg
);
308 /* Send initialize command. */
309 sskbd_send(sc
, KCDR_ESC
);
310 sskbd_send(sc
, KCDR_INITIALIZE
);
311 sskbd_send(sc
, 0x7b);
314 sc
->inptr
= sc
->outptr
= 0;
315 sc
->reading
= sc
->enable
= 0;
317 sc
->atn_ih
= s3c24x0_intr_establish(spia
->spia_aux_intr
, IPL_TTY
,
318 IST_EDGE_FALLING
, sskbd_atn_intr
, sc
);
320 sc
->spi_ih
= s3c24x0_intr_establish(spia
->spia_intr
, IPL_SERIAL
,
321 0, sskbd_spi_intr
, sc
);
323 sc
->soft_ih
= softint_establish(SOFTINT_SERIAL
, sskbd_soft_intr
, sc
);
325 if (sc
->atn_ih
== NULL
|| sc
->spi_ih
== NULL
)
326 aprint_error("%s: can't establish interrupt handler\n",
329 /* setup SPI control register, and prescaler */
330 s3c24x0_spi_setup((struct ssspi_softc
*)device_parent(self
),
331 SPCON_SMOD_INT
| SPCON_ENSCK
|
332 SPCON_MSTR
| SPCON_IDLELOW_RISING
,
336 /* Attach the wskbd. */
338 a
.keymap
= &sskbd_keymapdata
;
339 a
.accessops
= &sskbd_accessops
;
342 sc
->wskbddev
= config_found(self
, &a
, wskbddevprint
);
347 * Interrupt handler for nATN signal.
350 sskbd_atn_intr(void *arg
)
352 struct sskbd_softc
*sc
= arg
;
356 /* make sure SPI transmitter is ready */
357 if (!(bus_space_read_1(sc
->iot
, sc
->ioh
, SPI_SPSTA
) & SPSTA_REDY
))
361 if (advance_ring_ptr(sc
->inptr
) == sc
->outptr
) {
362 /* ring buffer is full. ignore this nATN signale */
363 softint_schedule(sc
->soft_ih
);
370 reg
= bus_space_read_2(sc
->iot
, sc
->gpioh
, GPIO_PBDAT
);
371 bus_space_write_2(sc
->iot
, sc
->gpioh
, GPIO_PBDAT
,
372 reg
& ~(1<<SSKBD_SS
));
374 /* generate clock to receive data from the controller */
375 bus_space_write_1(sc
->iot
, sc
->ioh
, SPI_SPTDAT
, 0xff);
383 * Interrupt handler for SPI rx
386 sskbd_spi_intr(void *arg
)
388 struct sskbd_softc
*sc
= arg
;
392 if (sc
->reading
== 0)
393 return 1; /* Ignore garbate input. */
397 data
= bus_space_read_1(sc
->iot
, sc
->ioh
, SPI_SPRDAT
);
400 reg
= bus_space_read_2(sc
->iot
, sc
->gpioh
, GPIO_PBDAT
);
401 bus_space_write_2(sc
->iot
, sc
->gpioh
, GPIO_PBDAT
,
402 reg
| (1<<SSKBD_SS
));
405 sc
->ring
[sc
->inptr
] = data
;
406 sc
->inptr
= advance_ring_ptr(sc
->inptr
);
408 softint_schedule(sc
->soft_ih
);
412 printf("discard %x\n", data
);
420 sskbd_soft_intr(void *arg
)
422 struct sskbd_softc
*sc
= arg
;
425 while (sc
->outptr
!= sc
->inptr
) {
426 key
= sc
->ring
[sc
->outptr
];
427 sc
->outptr
= advance_ring_ptr(sc
->outptr
);
433 if (key
< 0 || 8*14 < key
)
437 printf("key %d %s\n", key
, up
? "up" : "down");
439 wskbd_input(sc
->wskbddev
,
440 up
? WSCONS_EVENT_KEY_UP
: WSCONS_EVENT_KEY_DOWN
,
446 sskbd_enable(void *v
, int on
)
448 struct sskbd_softc
*sc
= v
;
451 printf("%s: enable\n", sc
->dev
.dv_xname
);
455 if (!on
&& isconsole(sc
))
465 sskbd_set_leds(void *v
, int leds
)
470 sskbd_ioctl(void *v
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
472 /*struct sskbd_softc *sc = v;*/
476 *(int *)data
= WSKBD_TYPE_HPC_KBD
; /* XXX */
478 case WSKBDIO_COMPLEXBELL
:
480 #define d ((struct wskbd_bell_data *)data)
482 * Keyboard can't beep directly; we have an
483 * externally-provided global hook to do this.
485 sskbd_bell(d
->pitch
, d
->period
, d
->volume
, 0);
489 #ifdef WSDISPLAY_COMPAT_RAWKBD
490 case WSKBDIO_SETMODE
:
491 sc
->rawkbd
= (*(int *)data
== WSKBD_RAW
);
496 case WSKBDIO_SETLEDS
:
497 case WSKBDIO_GETLEDS
: