NetBSD/sys/arch/ews4800mips/sbd/kbms_sbdio.c

526 lines
12 KiB
C

/* $NetBSD: kbms_sbdio.c,v 1.5 2007/03/04 05:59:47 christos Exp $ */
/*-
* Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by UCHIYAMA Yasushi.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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: kbms_sbdio.c,v 1.5 2007/03/04 05:59:47 christos Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsmousevar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
#include <dev/ic/z8530reg.h>
#include <machine/sbdiovar.h>
#include <ews4800mips/dev/ews4800keymap.h>
/* 85C30 keyboard, mouse driver */
struct kbms_reg {
volatile uint8_t *kbd_csr;
volatile uint8_t *kbd_data;
volatile uint8_t *mouse_csr;
volatile uint8_t *mouse_data;
};
enum { MOUSE_PACKET_LEN = 5 };
struct kbms_softc {
struct device sc_dv;
struct device *sc_wskbd;
struct device *sc_wsmouse;
struct kbms_reg sc_reg;
int sc_leds;
int sc_flags;
int sc_mouse_sig;
int sc_mouse_cnt;
int8_t sc_mouse_buf[MOUSE_PACKET_LEN];
};
int kbms_sbdio_match(struct device *, struct cfdata *, void *);
void kbms_sbdio_attach(struct device *, struct device *, void *);
int kbms_sbdio_intr(void *);
CFATTACH_DECL(kbms_sbdio, sizeof(struct kbms_softc),
kbms_sbdio_match, kbms_sbdio_attach, NULL, NULL);
int kbd_enable(void *, int);
void kbd_set_leds(void *, int);
int kbd_ioctl(void *, u_long, void *, int, struct lwp *);
int mouse_enable(void *);
void mouse_disable(void *);
int mouse_ioctl(void *, u_long, void *, int, struct lwp *);
bool kbd_init(struct kbms_softc *);
bool kbd_reset(struct kbms_softc *, int);
void mouse_init(struct kbms_softc *);
#ifdef MOUSE_DEBUG
void mouse_debug_print(u_int, int, int);
#endif
int kbd_sbdio_cnattach(uint32_t, uint32_t);
void kbd_cngetc(void *, u_int *, int *);
void kbd_cnpollc(void *, int);
static struct kbms_reg kbms_consreg;
const struct wskbd_consops kbd_consops = {
kbd_cngetc,
kbd_cnpollc,
};
const struct wskbd_accessops kbd_accessops = {
kbd_enable,
kbd_set_leds,
kbd_ioctl,
};
struct wskbd_mapdata kbd_keymapdata = {
ews4800kbd_keydesctab,
KB_JP,
};
const struct wsmouse_accessops mouse_accessops = {
mouse_enable,
mouse_ioctl,
mouse_disable,
};
#define KBMS_PCLK (9600 * 512)
int
kbms_sbdio_match(struct device *parent, struct cfdata *match, void *aux)
{
struct sbdio_attach_args *sa = aux;
return strcmp(sa->sa_name, "zkbms") ? 0 : 1;
}
void
kbms_sbdio_attach(struct device *parent, struct device *self, void *aux)
{
struct sbdio_attach_args *sa = aux;
struct wsmousedev_attach_args ma;
struct wskbddev_attach_args ka;
struct kbms_softc *sc = (void *)self;
struct kbms_reg *reg = &sc->sc_reg;
uint8_t *base;
printf(" at %p irq %d\n", (void *)sa->sa_addr1, sa->sa_irq);
base = (uint8_t *)MIPS_PHYS_TO_KSEG1(sa->sa_addr1);
reg->kbd_csr = base + 0x00; /* port B */
reg->kbd_data = base + 0x04;
reg->mouse_csr = base + 0x08; /* port A */
reg->mouse_data = base + 0x0c;
if (reg->kbd_csr == kbms_consreg.kbd_csr &&
reg->kbd_data == kbms_consreg.kbd_data)
ka.console = true;
else
ka.console = false;
ka.keymap = &kbd_keymapdata;
ka.accessops = &kbd_accessops;
ka.accesscookie = self;
if (kbd_init(sc) == false) {
printf("keyboard not connected\n");
return;
}
sc->sc_wskbd = config_found(self, &ka, wskbddevprint);
ma.accessops = &mouse_accessops;
ma.accesscookie = self;
if (sa->sa_flags == 0x0001)
sc->sc_flags = 1;
sc->sc_mouse_sig = 0x80;
if (sc->sc_flags == 1) /* ER/TR?? */
sc->sc_mouse_sig = 0x88;
mouse_init(sc);
sc->sc_wsmouse = config_found(self, &ma, wsmousedevprint);
intr_establish(sa->sa_irq, kbms_sbdio_intr, self);
}
int
kbms_sbdio_intr(void *arg)
{
struct kbms_softc *sc = (void *)arg;
struct kbms_reg *reg = &sc->sc_reg;
int v;
if (*reg->kbd_csr & ZSRR0_RX_READY) {
v = *reg->kbd_data;
wskbd_input(sc->sc_wskbd,
v & 0x80 ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN,
v & 0x7f);
}
while (*reg->mouse_csr & ZSRR0_RX_READY) {
int8_t *buf = sc->sc_mouse_buf;
*reg->mouse_csr = 1;
if (((v = *reg->mouse_csr) &
(ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) != 0) {
/* Error occured. re-initialize */
printf("initialize mouse. error=%02x\n", v);
mouse_init(sc);
} else {
v = *reg->mouse_data;
if ((sc->sc_mouse_cnt == 0) &&
(v & 0xf8) != sc->sc_mouse_sig) {
printf("missing first packet. reset. %x\n", v);
mouse_init(sc);
continue;
}
buf[sc->sc_mouse_cnt++] = v;
if (sc->sc_mouse_cnt == MOUSE_PACKET_LEN) {
int x, y;
u_int buttons;
buttons = ~buf[0] & 0x7;
if (sc->sc_flags == 0) {
u_int b1 = (buttons & 0x1) << 2;
u_int b3 = (buttons & 0x4) >> 2;
buttons = (buttons & 0x2) | b1 | b3;
}
x = buf[1] + buf[3];
y = buf[2] + buf[4];
#ifdef MOUSE_DEBUG
mouse_debug_print(buttons, x, y);
#endif
wsmouse_input(sc->sc_wsmouse,
buttons,
x, y, 0, 0,
WSMOUSE_INPUT_DELTA);
sc->sc_mouse_cnt = 0;
}
}
*reg->mouse_csr = ZSWR1_REQ_RX | ZSWR1_RIE | ZSWR1_RIE_FIRST;
(void)*reg->mouse_csr;
}
return 0;
}
#define __REG_WR(r, v) \
do { \
*csr = (r); \
delay(1); \
*csr = (v); \
delay(1); \
} while (/*CONSTCOND*/ 0)
bool
kbd_init(struct kbms_softc *sc)
{
struct kbms_reg *reg = &sc->sc_reg;
volatile uint8_t *csr = reg->kbd_csr;
int retry = 2;
int reset_retry = 100;
do {
__REG_WR(9, ZSWR9_B_RESET);
delay(100);
__REG_WR(9, ZSWR9_MASTER_IE | ZSWR9_NO_VECTOR);
__REG_WR(1, 0);
__REG_WR(4, ZSWR4_CLK_X16 | ZSWR4_ONESB | ZSWR4_PARENB);
__REG_WR(12, BPS_TO_TCONST(KBMS_PCLK / 16, 4800));
__REG_WR(13, 0);
__REG_WR(5, ZSWR5_TX_8 | ZSWR5_RTS);
__REG_WR(3, ZSWR3_RX_8);
__REG_WR(10, 0);
__REG_WR(11, ZSWR11_RXCLK_BAUD | ZSWR11_TXCLK_BAUD |
ZSWR11_TRXC_OUT_ENA | ZSWR11_TRXC_BAUD);
__REG_WR(14, ZSWR14_BAUD_FROM_PCLK | ZSWR14_BAUD_ENA);
__REG_WR(15, 0);
__REG_WR(5, ZSWR5_TX_8 | ZSWR5_TX_ENABLE);
__REG_WR(3, ZSWR3_RX_8 | ZSWR3_RX_ENABLE);
reset_retry *= 2;
} while (!kbd_reset(sc, reset_retry) && --retry > 0);
if (retry == 0) {
printf("keyboard initialize failed.\n");
return false;
}
return true;
}
bool
kbd_reset(struct kbms_softc *sc, int retry)
{
#define __RETRY_LOOP(x, y) \
do { \
for (i = 0; (x) && (i < retry); i++) { \
(void)(y); \
delay(10); \
} \
if (i == retry) \
goto error; \
} while (/*CONSTCOND*/ 0)
int i;
struct kbms_reg *reg = &sc->sc_reg;
volatile uint8_t *csr = reg->kbd_csr;
volatile uint8_t *data = reg->kbd_data;
volatile uint8_t dummy;
__REG_WR(5, ZSWR5_DTR | ZSWR5_TX_8 | ZSWR5_TX_ENABLE);
delay(100);
__RETRY_LOOP(*csr & ZSRR0_RX_READY, dummy = *data);
*csr = 48;
__REG_WR(5, ZSWR5_TX_8 | ZSWR5_TX_ENABLE | ZSWR5_RTS);
__RETRY_LOOP((*csr & ZSRR0_RX_READY) == 0, 0);
*csr = 1;
__RETRY_LOOP((*csr & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) != 0, 0);
__RETRY_LOOP(*data != 0xa0, 0);
__RETRY_LOOP((*csr & ZSRR0_RX_READY) == 0, 0);
*csr = 1;
__RETRY_LOOP((*csr & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) != 0, 0);
__REG_WR(1, ZSWR1_RIE);
/* drain buffer */
(void)*reg->kbd_data;
#undef __RETRY_LOOP
return true;
error:
printf("retry failed.\n");
return false;
}
void
mouse_init(struct kbms_softc *sc)
{
struct kbms_reg *reg = &sc->sc_reg;
volatile uint8_t *csr = reg->mouse_csr;
volatile uint8_t *data = reg->mouse_data;
uint8_t d[] = { 0x02, 0x52, 0x53, 0x3b, 0x4d, 0x54, 0x2c, 0x36, 0x0d };
int i;
__REG_WR(9, ZSWR9_A_RESET);
delay(100);
__REG_WR(9, ZSWR9_MASTER_IE | ZSWR9_NO_VECTOR);
__REG_WR(1, 0);
__REG_WR(4, ZSWR4_CLK_X16 | ZSWR4_ONESB);
if (sc->sc_flags & 0x1) {
__REG_WR(12, BPS_TO_TCONST(KBMS_PCLK / 16, 4800));
__REG_WR(13, 0);
} else {
__REG_WR(12, BPS_TO_TCONST(KBMS_PCLK / 16, 1200));
__REG_WR(13, 0);
}
__REG_WR(5, ZSWR5_DTR | ZSWR5_TX_8 | ZSWR5_RTS);
__REG_WR(3, ZSWR3_RX_8);
__REG_WR(10, 0);
__REG_WR(11, ZSWR11_RXCLK_BAUD | ZSWR11_TXCLK_BAUD |
ZSWR11_TRXC_OUT_ENA | ZSWR11_TRXC_BAUD);
__REG_WR(14, ZSWR14_BAUD_FROM_PCLK);
__REG_WR(15, 0);
__REG_WR(5, ZSWR5_DTR | ZSWR5_TX_8 | ZSWR5_TX_ENABLE);
__REG_WR(3, ZSWR3_RX_8 | ZSWR3_RX_ENABLE);
if (sc->sc_flags & 0x1) {
for (i = 0; i < sizeof d; i++) {
while ((*csr & ZSRR0_TX_READY) == 0)
;
*data = d[i];
}
}
__REG_WR(1, ZSWR1_RIE);
}
int
kbd_enable(void *arg, int on)
{
/* always active */
return 0;
}
void
kbd_set_leds(void *arg, int leds)
{
struct kbms_softc *sc = arg;
struct kbms_reg *reg = &sc->sc_reg;
sc->sc_leds = leds;
if (leds & WSKBD_LED_CAPS)
*reg->kbd_data = 0x92;
else
*reg->kbd_data = 0x90;
}
int
kbd_ioctl(void *arg, u_long cmd, void *data, int flag, struct lwp *l)
{
struct kbms_softc *sc = arg;
switch (cmd) {
case WSKBDIO_GTYPE:
*(int *)data = WSKBD_TYPE_EWS4800;
return 0;
case WSKBDIO_SETLEDS:
kbd_set_leds(arg, *(int *)data);
return 0;
case WSKBDIO_GETLEDS:
*(int *)data = sc->sc_leds;
return 0;
case WSKBDIO_BELL:
case WSKBDIO_COMPLEXBELL:
return 0;
}
return EPASSTHROUGH;
}
int
kbd_sbdio_cnattach(uint32_t csr, uint32_t data)
{
struct kbms_softc __softc, *sc;
struct kbms_reg *reg;
kbms_consreg.kbd_csr = (void *)csr;
kbms_consreg.kbd_data = (void *)data;
/* setup dummy softc for kbd_init() */
sc = &__softc;
memset(sc, 0, sizeof(struct kbms_softc));
reg = &sc->sc_reg;
reg->kbd_csr = (void *)csr;
reg->kbd_data = (void *)data;
if (kbd_init(sc) == false)
return false;
wskbd_cnattach(&kbd_consops, &kbms_consreg, &kbd_keymapdata);
return true;
}
void
kbd_cngetc(void *arg, u_int *type, int *data)
{
struct kbms_reg *reg = (void *)arg;
int v;
while ((*reg->kbd_csr & ZSRR0_RX_READY) == 0)
;
v = *reg->kbd_data;
*type = v & 0x80 ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
*data = v & 0x7f;
}
void
kbd_cnpollc(void *arg, int on)
{
static bool __polling = false;
static int s;
if (on && !__polling) {
s = splhigh(); /* Disable interrupt driven I/O */
} else if (!on && __polling) {
__polling = false;
splx(s); /* Enable interrupt driven I/O */
}
}
int
mouse_enable(void *arg)
{
/* always active */
return 0;
}
void
mouse_disable(void *arg)
{
/* always active */
}
int
mouse_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
{
return EPASSTHROUGH;
}
#ifdef MOUSE_DEBUG
void
mouse_debug_print(u_int buttons, int x, int y)
{
#define MINMAX(x, min, max) \
((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
static int k, __x, __y;
int i, j;
char buf[64];
__x = MINMAX(__x + x, 0, FB_WIDTH);
__y = MINMAX(__y + y, 0, FB_HEIGHT);
*(uint8_t *)(fb.fb_addr + __x + __y * FB_LINEBYTES) = 0xff;
sprintf(buf, "%8d %8d", x, y);
for (i = 0; i < 64 && buf[i]; i++)
fb_drawchar(480 + i * 12, k, buf[i]);
i += 12;
for (j = 0x80; j > 0; j >>= 1, i++) {
fb_drawchar(480 + i * 12, k, buttons & j ? '|' : '.');
}
k += 24;
if (k > 1000)
k = 0;
}
#endif