#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: gsckbc.c,v 1.3 2022/06/13 17:26:34 andvar Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <machine/iomod.h>
#include <machine/autoconf.h>
#include <hppa/dev/cpudevs.h>
#include <hppa/gsc/gscbusvar.h>
#include <hppa/hppa/machdep.h>
#include "locators.h"
#include "ioconf.h"
#include <sys/bus.h>
#include <machine/intr.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/ioccom.h>
#include <sys/types.h>
#include <dev/pckbport/pckbportvar.h>
#define REG_ID 0x0
#define REG_RESET 0x0
#define REG_RCVDATA 0x4
#define REG_XMITDATA 0x4
#define REG_CONTROL 0x8
#define REG_STATUS 0xc
#define REG_SZ 0xc
#define REG_OFFSET 0x100
#define ID_KBD 0
#define ID_AUX 1
#define CTRL_ENBL 0x01
#define CTRL_LPBXR 0x02
#define CTRL_DIAG 0x20
#define CTRL_DATDIR 0x40
#define CTRL_CLKDIR 0x80
#define STAT_RBNE 0x01
#define STAT_TBNE 0x02
#define STAT_TERR 0x04
#define STAT_PERR 0x08
#define STAT_CMPINTR 0x10
#define STAT_DATSHD 0x40
#define STAT_CLKSHD 0x80
static int gsckbc_match(device_t, cfdata_t, void *);
static void gsckbc_attach(device_t, device_t, void *);
static int gsckbc_xt_translation(void *, pckbport_slot_t, int);
static int gsckbc_send_devcmd(void *, pckbport_slot_t, u_char);
static int gsckbc_poll_data1(void *, pckbport_slot_t);
static void gsckbc_slot_enable(void *, pckbport_slot_t, int);
static void gsckbc_intr_establish(void *, pckbport_slot_t);
static void gsckbc_set_poll(void *, pckbport_slot_t, int);
static int gsckbc_intr(void *);
struct gsckbc_softc {
device_t sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
struct gsckbc_softc *sc_op;
void *sc_ih;
pckbport_slot_t sc_slot;
pckbport_tag_t sc_pckbport;
device_t sc_child;
int sc_poll;
int sc_enable;
};
CFATTACH_DECL_NEW(gsckbc, sizeof(struct gsckbc_softc), gsckbc_match, gsckbc_attach,
NULL, NULL);
const struct pckbport_accessops gsckbc_accessops = {
gsckbc_xt_translation,
gsckbc_send_devcmd,
gsckbc_poll_data1,
gsckbc_slot_enable,
gsckbc_intr_establish,
gsckbc_set_poll
};
static int
gsckbc_xt_translation(void *cookie, pckbport_slot_t slot, int on)
{
return 0;
}
static int
gsckbc_send_devcmd(void *cookie, pckbport_slot_t slot, u_char byte)
{
struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_STATUS) & STAT_TBNE)
!= 0)
DELAY(100);
if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_STATUS) & STAT_TBNE)
!= 0)
return 0;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, REG_XMITDATA, byte);
return 1;
}
static int
gsckbc_poll_data1(void *cookie, pckbport_slot_t slot)
{
struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
int i;
for (i = 0; i < 1000; i++) {
if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_STATUS) &
STAT_RBNE) != 0 || sc->sc_poll == 0) {
return bus_space_read_1(sc->sc_iot, sc->sc_ioh,
REG_RCVDATA);
}
DELAY(100);
}
return -1;
}
static void
gsckbc_slot_enable(void *cookie, pckbport_slot_t slot, int on)
{
struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
sc->sc_enable = on;
}
static void
gsckbc_intr_establish(void *cookie, pckbport_slot_t slot)
{
return;
}
static void
gsckbc_set_poll(void *cookie, pckbport_slot_t slot, int on)
{
struct gsckbc_softc *sc = (struct gsckbc_softc *) cookie;
sc->sc_poll = on;
}
static int
gsckbc_intr(void *arg)
{
struct gsckbc_softc *sc = (struct gsckbc_softc *) arg;
int data;
while ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_STATUS)
& STAT_RBNE) != 0 && sc->sc_poll == 0) {
data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_RCVDATA);
if (sc->sc_enable != 0)
pckbportintr(sc->sc_pckbport, sc->sc_slot, data);
}
while ((bus_space_read_1(sc->sc_op->sc_iot, sc->sc_op->sc_ioh,
REG_STATUS) & STAT_RBNE) != 0 && sc->sc_op->sc_poll == 0) {
data = bus_space_read_1(sc->sc_op->sc_iot, sc->sc_op->sc_ioh,
REG_RCVDATA);
if (sc->sc_op->sc_enable != 0)
pckbportintr(sc->sc_op->sc_pckbport, sc->sc_op->sc_slot,
data);
}
return 1;
}
static int
gsckbc_match(device_t parent, cfdata_t match, void *aux)
{
struct gsc_attach_args *ga = aux;
if (ga->ga_type.iodc_type == HPPA_TYPE_FIO
&& ga->ga_type.iodc_sv_model == HPPA_FIO_GPCIO)
return(1);
return(0);
}
static void
gsckbc_attach(device_t parent, device_t self, void *aux)
{
struct gsckbc_softc *sc = device_private(self);
struct gsc_attach_args *ga = aux;
static struct gsckbc_softc *master_sc;
int pagezero_cookie;
int i;
sc->sc_dev = self;
sc->sc_iot = ga->ga_iot;
if (ga->ga_irq >= 0) {
if (bus_space_map(sc->sc_iot, ga->ga_hpa, REG_SZ + REG_OFFSET,
0, &sc->sc_ioh)) {
aprint_normal(": gsckbc_attach: can't map I/O space\n");
return;
}
aprint_debug(" (master)");
sc->sc_ih = hppa_intr_establish(IPL_TTY, gsckbc_intr, sc,
ga->ga_ir, ga->ga_irq);
master_sc = sc;
} else {
if (master_sc == NULL) {
aprint_normal(": can't find master device\n");
return;
}
sc->sc_op = master_sc;
master_sc->sc_op = sc;
sc->sc_ioh = sc->sc_op->sc_ioh + REG_OFFSET;
aprint_debug(" (slave)");
}
sc->sc_poll = 1;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, REG_RESET, 0);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, REG_CONTROL, CTRL_ENBL);
for (i = 0; i < 5; i++)
bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_RCVDATA);
if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, REG_ID) == ID_KBD) {
aprint_normal(": keyboard\n");
sc->sc_slot = PCKBPORT_KBD_SLOT;
pagezero_cookie = hppa_pagezero_map();
if ((hppa_hpa_t)PAGE0->mem_kbd.pz_hpa == ga->ga_hpa) {
if (pckbport_cnattach(sc, &gsckbc_accessops,
sc->sc_slot) != 0)
aprint_normal("Failed to attach console "
"keyboard!\n");
else
sc->sc_enable = 1;
}
hppa_pagezero_unmap(pagezero_cookie);
} else {
aprint_normal(": mouse\n");
sc->sc_slot = PCKBPORT_AUX_SLOT;
}
sc->sc_pckbport = pckbport_attach(sc, &gsckbc_accessops);
if (sc->sc_pckbport != NULL)
sc->sc_child = pckbport_attach_slot(self, sc->sc_pckbport,
sc->sc_slot);
sc->sc_poll = 0;
}