root/sys/arch/ews4800mips/sbd/kbms_sbdio.c
/*      $NetBSD: kbms_sbdio.c,v 1.15 2021/12/10 20:36:02 andvar 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.
 *
 * 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.15 2021/12/10 20:36:02 andvar 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 {
        device_t sc_dev;
        device_t sc_wskbd;
        device_t 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(device_t, cfdata_t, void *);
void kbms_sbdio_attach(device_t, device_t, void *);
int kbms_sbdio_intr(void *);

CFATTACH_DECL_NEW(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(device_t parent, cfdata_t cf, void *aux)
{
        struct sbdio_attach_args *sa = aux;

        return strcmp(sa->sa_name, "zkbms") ? 0 : 1;
}

void
kbms_sbdio_attach(device_t parent, device_t self, void *aux)
{
        struct kbms_softc *sc = device_private(self);
        struct sbdio_attach_args *sa = aux;
        struct wsmousedev_attach_args ma;
        struct wskbddev_attach_args ka;
        struct kbms_reg *reg = &sc->sc_reg;
        uint8_t *base;

        sc->sc_dev = self;
        aprint_normal("\n");

        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, CFARGS_NONE);

        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, CFARGS_NONE);

        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 occurred. 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_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 */
                __polling = true;
        } 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;

        snprintf(buf, sizeof(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