NetBSD/sys/arch/hpcarm/dev/wzero3_keypad.c
chs cbab9cadce split device_t/softc for all remaining drivers.
replace "struct device *" with "device_t".
use device_xname(), device_unit(), etc.
2012-10-27 17:17:22 +00:00

541 lines
12 KiB
C

/* $NetBSD: wzero3_keypad.c,v 1.4 2012/10/27 17:17:52 chs Exp $ */
/*-
* Copyright (C) 2010 NONAKA Kimihiro <nonaka@netbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: wzero3_keypad.c,v 1.4 2012/10/27 17:17:52 chs Exp $");
#include "wzero3lcd.h"
#include "opt_wsdisplay_compat.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/callout.h>
#include <sys/bus.h>
#include <arm/xscale/pxa2x0cpu.h>
#include <arm/xscale/pxa2x0var.h>
#include <arm/xscale/pxa2x0_gpio.h>
#include <machine/bootinfo.h>
#include <machine/config_hook.h>
#include <machine/platid.h>
#include <machine/platid_mask.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/wscons/wsksymdef.h>
#ifdef WSDISPLAY_COMPAT_RAWKBD
#include <dev/hpc/pckbd_encode.h>
#endif
#include <arch/hpcarm/dev/wzero3_reg.h>
#include <arch/hpcarm/dev/wzero3_sspvar.h>
enum {
KD_0,
KD_1,
KD_2,
KD_3,
KD_4,
KD_5,
KD_6,
KD_7,
KD_8,
KD_9,
KD_ASTERISK,
KD_NUMBER,
KD_WINDOWS,
KD_OK,
KD_ONHOOK,
KD_OFFHOOK,
KD_CLEAR,
KD_MOJI,
KD_UP,
KD_DOWN,
KD_LEFT,
KD_RIGHT,
KD_CENTER_BUTTON,
KD_LSOFT,
KD_RSOFT,
KD_NUM,
KD_INVALID = -1
};
static int ws011sh_keyscan2keydown[32] = {
KD_INVALID,
KD_CLEAR,
KD_INVALID,
KD_OK,
KD_INVALID,
KD_LEFT,
KD_INVALID,
KD_ONHOOK,
KD_INVALID,
KD_UP,
KD_DOWN,
KD_MOJI,
KD_INVALID,
KD_WINDOWS,
KD_INVALID,
KD_RIGHT,
KD_INVALID,
KD_1,
KD_4,
KD_7,
KD_ASTERISK,
KD_2,
KD_5,
KD_8,
KD_0,
KD_CENTER_BUTTON,
KD_INVALID,
KD_3,
KD_6,
KD_9,
KD_NUMBER,
KD_INVALID,
};
struct wzero3keypad_softc {
device_t sc_dev;
void *sc_ih;
int sc_intr_pin;
uint32_t sc_okeystat;
struct callout sc_poll_ch;
int sc_poll_interval;
device_t sc_wskbddev;
#ifdef WSDISPLAY_COMPAT_RAWKBD
int sc_rawkbd;
#endif
};
static int wzero3keypad_match(device_t, cfdata_t, void *);
static void wzero3keypad_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(wzero3keypad, sizeof(struct wzero3keypad_softc),
wzero3keypad_match, wzero3keypad_attach, NULL, NULL);
static int wzero3keypad_wskbd_enable(void *, int);
static void wzero3keypad_wskbd_set_leds(void *, int);
static int wzero3keypad_wskbd_ioctl(void *, u_long, void *, int, struct lwp *);
static const int wzero3keypad_wskbd_keys[] = {
82, /* KD_0: 0 */
79, /* KD_1: 1 */
80, /* KD_2: 2 */
81, /* KD_3: 3 */
75, /* KD_4: 4 */
76, /* KD_5: 5 */
77, /* KD_6: 6 */
71, /* KD_7: 7 */
72, /* KD_8: 8 */
73, /* KD_9: 9 */
64, /* KD_ASTERISK: f6 */
65, /* KD_NUMBER: f7 */
221, /* KD_WINDOWS: Menu */
61, /* KD_OK: f3 */
59, /* KD_ONHOOK: f1 */
60, /* KD_OFFHOOK: f2 */
62, /* KD_CLEAR: f4 */
63, /* KD_MOJI: f5 */
200, /* KD_UP: Up */
208, /* KD_DOWN: Down */
203, /* KD_LEFT: Left */
205, /* KD_RIGHT: Right */
156, /* KD_CENTER_BUTTON: KP_Enter */
87, /* KD_LSOFT: f11 */
88, /* KD_RSOFT: f12 */
};
static const keysym_t wzero3keypad_wskbd_keydesc[] = {
KS_KEYCODE(59), KS_f1,
KS_KEYCODE(60), KS_f2,
KS_KEYCODE(61), KS_f3,
KS_KEYCODE(62), KS_f4,
KS_KEYCODE(63), KS_f5,
KS_KEYCODE(64), KS_f6,
KS_KEYCODE(65), KS_f7,
KS_KEYCODE(71), KS_7,
KS_KEYCODE(72), KS_8,
KS_KEYCODE(73), KS_9,
KS_KEYCODE(75), KS_4,
KS_KEYCODE(76), KS_5,
KS_KEYCODE(77), KS_6,
KS_KEYCODE(79), KS_1,
KS_KEYCODE(80), KS_2,
KS_KEYCODE(81), KS_3,
KS_KEYCODE(82), KS_0,
KS_KEYCODE(87), KS_f11,
KS_KEYCODE(88), KS_f12,
KS_KEYCODE(156), KS_KP_Enter,
KS_KEYCODE(200), KS_Up,
KS_KEYCODE(203), KS_Left,
KS_KEYCODE(205), KS_Right,
KS_KEYCODE(208), KS_Down,
KS_KEYCODE(221), KS_Menu,
};
static const struct wscons_keydesc wzero3keypad_wskbd_keydesctab[] = {
{ KB_JP, 0,
sizeof(wzero3keypad_wskbd_keydesc) / sizeof(keysym_t),
wzero3keypad_wskbd_keydesc
},
{ 0, 0, 0, 0 }
};
static const struct wskbd_mapdata wzero3keypad_wskbd_keymapdata = {
wzero3keypad_wskbd_keydesctab, KB_JP
};
static const struct wskbd_accessops wzero3keypad_wskbd_accessops = {
wzero3keypad_wskbd_enable,
wzero3keypad_wskbd_set_leds,
wzero3keypad_wskbd_ioctl,
};
static int wzero3keypad_intr(void *);
static void wzero3keypad_poll(void *);
static void wzero3keypad_poll1(struct wzero3keypad_softc *, int);
static void wzero3keypad_init(struct wzero3keypad_softc *);
static uint32_t wzero3keypad_getkeydown(struct wzero3keypad_softc *, int);
static const struct wzero3keypad_model {
platid_mask_t *platid;
int intr_pin;
} wzero3keypad_table[] = {
#if 0
/* WS007SH */
{
&platid_mask_MACH_SHARP_WZERO3_WS007SH,
-1, /* XXX */
},
#endif
/* WS011SH */
{
&platid_mask_MACH_SHARP_WZERO3_WS011SH,
GPIO_WS011SH_KEYPAD,
},
{ NULL, -1, }
};
static const struct wzero3keypad_model *
wzero3keypad_lookup(void)
{
const struct wzero3keypad_model *model;
for (model = wzero3keypad_table; model->platid != NULL; model++) {
if (platid_match(&platid, model->platid)) {
return model;
}
}
return NULL;
}
static int
wzero3keypad_match(device_t parent, cfdata_t cf, void *aux)
{
if (strcmp(cf->cf_name, "wzero3keypad") != 0)
return 0;
if (wzero3keypad_lookup() == NULL)
return 0;
return 1;
}
static void
wzero3keypad_attach(device_t parent, device_t self, void *aux)
{
struct wzero3keypad_softc *sc = device_private(self);
const struct wzero3keypad_model *model;
struct wskbddev_attach_args wska;
#if NWZERO3LCD > 0
extern int screen_rotate;
#endif
sc->sc_dev = self;
sc->sc_okeystat = 0;
#ifdef WSDISPLAY_COMPAT_RAWKBD
sc->sc_rawkbd = 0;
#endif
model = wzero3keypad_lookup();
if (model == NULL) {
aprint_error(": unknown model\n");
return;
}
aprint_normal(": keypad\n");
aprint_naive("\n");
sc->sc_intr_pin = model->intr_pin;
callout_init(&sc->sc_poll_ch, 0);
callout_setfunc(&sc->sc_poll_ch, wzero3keypad_poll, sc);
sc->sc_poll_interval = hz / 32;
#if NWZERO3LCD > 0
switch (screen_rotate) {
default:
case 0:
break;
case 270: /* counter clock-wise */
ws011sh_keyscan2keydown[5] = KD_UP;
ws011sh_keyscan2keydown[9] = KD_RIGHT;
ws011sh_keyscan2keydown[10] = KD_LEFT;
ws011sh_keyscan2keydown[15] = KD_DOWN;
break;
}
#endif
/* attach wskbd */
wska.console = 0;
wska.keymap = &wzero3keypad_wskbd_keymapdata;
wska.accessops = &wzero3keypad_wskbd_accessops;
wska.accesscookie = sc;
sc->sc_wskbddev = config_found_ia(self, "wskbddev", &wska,
wskbddevprint);
/* setup keypad interrupt */
pxa2x0_gpio_set_function(sc->sc_intr_pin, GPIO_IN);
sc->sc_ih = pxa2x0_gpio_intr_establish(sc->sc_intr_pin,
IST_EDGE_RISING, IPL_TTY, wzero3keypad_intr, sc);
if (sc->sc_ih == NULL) {
aprint_error_dev(sc->sc_dev,
"couldn't establish keypad interrupt\n");
}
/* init hardware */
wzero3keypad_init(sc);
}
static int
wzero3keypad_wskbd_enable(void *arg, int onoff)
{
return 0;
}
static void
wzero3keypad_wskbd_set_leds(void *arg, int leds)
{
/* Nothing to do */
}
static int
wzero3keypad_wskbd_ioctl(void *arg, u_long cmd, void *data, int flags,
struct lwp *l)
{
#ifdef WSDISPLAY_COMPAT_RAWKBD
struct wzero3keypad_softc *sc = (struct wzero3keypad_softc *)arg;
#endif
switch (cmd) {
case WSKBDIO_GTYPE:
*(int *)data = WSKBD_TYPE_HPC_KBD;
return 0;
case WSKBDIO_SETLEDS:
return 0;
case WSKBDIO_GETLEDS:
*(int *)data = 0;
return 0;
#ifdef WSDISPLAY_COMPAT_RAWKBD
case WSKBDIO_SETMODE:
sc->sc_rawkbd = (*(int *)data == WSKBD_RAW);
return 0;
#endif
}
return EPASSTHROUGH;
}
static int
wzero3keypad_intr(void *arg)
{
struct wzero3keypad_softc *sc = (struct wzero3keypad_softc *)arg;
pxa2x0_gpio_clear_intr(sc->sc_intr_pin);
wzero3keypad_poll1(sc, 0);
callout_schedule(&sc->sc_poll_ch, sc->sc_poll_interval);
return 1;
}
static void
wzero3keypad_poll(void *v)
{
struct wzero3keypad_softc *sc = (struct wzero3keypad_softc *)v;
wzero3keypad_poll1(sc, 1);
callout_stop(&sc->sc_poll_ch);
}
static void
wzero3keypad_poll1(struct wzero3keypad_softc *sc, int doscan)
{
uint32_t keydown;
uint32_t diff;
int i;
int s;
s = spltty();
keydown = wzero3keypad_getkeydown(sc, doscan);
diff = keydown ^ sc->sc_okeystat;
if (diff == 0)
goto out;
for (i = 0; i < KD_NUM; i++) {
if (diff & (1 << i)) {
int state = keydown & (1 << i);
int type = state ? WSCONS_EVENT_KEY_DOWN :
WSCONS_EVENT_KEY_UP;
int key = wzero3keypad_wskbd_keys[i];
#ifdef WSDISPLAY_COMPAT_RAWKBD
if (sc->sc_rawkbd) {
int n;
u_char data[16];
n = pckbd_encode(type, key, data);
wskbd_rawinput(sc->sc_wskbddev, data, n);
} else
#endif
wskbd_input(sc->sc_wskbddev, type, key);
}
}
sc->sc_okeystat = keydown;
out:
splx(s);
}
/*----------------------------------------------------------------------------
* AK4184 keypad controller for WS011SH
*/
/* ak4184 command register */
#define AKMCTRL_WR_SH 7
#define AKMCTRL_PAGE_SH 6
#define AKMCTRL_ADDR_SH 0
#define AKMCTRL_WRITE (0<<AKMCTRL_WR_SH)
#define AKMCTRL_READ (1<<AKMCTRL_WR_SH)
#define AKMCTRL_DATA (0<<AKMCTRL_PAGE_SH)
#define AKMCTRL_CTRL (1<<AKMCTRL_PAGE_SH)
static void
wzero3keypad_init(struct wzero3keypad_softc *sc)
{
int s;
s = spltty();
#if 0
/*
* - key interrupt enable
* - key touch scan
* - debounce time: 1ms
* - wait 100us for debounce time
*/
(void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
AKMCTRL_WRITE | AKMCTRL_CTRL | (0<<AKMCTRL_ADDR_SH), 0);
#endif
/* unmask all keys & columns */
(void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
AKMCTRL_WRITE | AKMCTRL_CTRL | (1<<AKMCTRL_ADDR_SH), 0);
(void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
AKMCTRL_WRITE | AKMCTRL_CTRL | (2<<AKMCTRL_ADDR_SH), 0);
(void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
AKMCTRL_WRITE | AKMCTRL_CTRL | (3<<AKMCTRL_ADDR_SH), 0);
/* Enable keypad interrupt (kpdata dummy read) */
(void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
AKMCTRL_READ | AKMCTRL_DATA | (1<<AKMCTRL_ADDR_SH), 0);
splx(s);
}
static uint32_t
wzero3keypad_getkeydown(struct wzero3keypad_softc *sc, int doscan)
{
uint32_t keydown = 0;
uint16_t status;
uint16_t kpdata;
int timo;
if (doscan) {
/* host scan */
(void) wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
AKMCTRL_WRITE | AKMCTRL_CTRL | (4<<AKMCTRL_ADDR_SH), 0);
delay(100);
}
timo = 1000;
do {
status = wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
AKMCTRL_READ | AKMCTRL_CTRL | (0<<AKMCTRL_ADDR_SH), 0);
} while ((status & 0xc000) == 0 && timo-- > 0);
kpdata = wzero3ssp_ic_send(WZERO3_SSP_IC_AK4184_KEYPAD,
AKMCTRL_READ | AKMCTRL_DATA | (1<<AKMCTRL_ADDR_SH), 0);
if ((status & 0xc000) == 0xc000) {
if (!(kpdata & 0x8000)) {
int i;
for (i = 0; i < 3; i++) {
int key, bits;
key = kpdata & 0x1f;
if (key == 0)
break;
bits = ws011sh_keyscan2keydown[key];
if (bits != KD_INVALID)
keydown |= 1 << bits;
kpdata >>= 5;
}
}
}
return keydown;
}