summaryrefslogtreecommitdiff
path: root/sys/arch/sgi
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2007-10-18 18:59:30 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2007-10-18 18:59:30 +0000
commit5f5dc8ee699cb0137500182a10819458f4cb5e99 (patch)
tree709134f01ee741ec277a3d257f202cb70647cb3f /sys/arch/sgi
parent9f9e1d5e8291af36c167d1fd8a9637ac27f82268 (diff)
Add support for the Moosehead PS/2 controller as found on SGI O2 workstations.
ok miod@ deraadt@
Diffstat (limited to 'sys/arch/sgi')
-rw-r--r--sys/arch/sgi/conf/GENERIC9
-rw-r--r--sys/arch/sgi/conf/files.sgi8
-rw-r--r--sys/arch/sgi/dev/mkbc.c840
-rw-r--r--sys/arch/sgi/dev/mkbcreg.h49
-rw-r--r--sys/arch/sgi/localbus/macebus.h5
5 files changed, 907 insertions, 4 deletions
diff --git a/sys/arch/sgi/conf/GENERIC b/sys/arch/sgi/conf/GENERIC
index 5dff9cc042b..27ec1c40fee 100644
--- a/sys/arch/sgi/conf/GENERIC
+++ b/sys/arch/sgi/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.21 2007/07/17 21:28:48 jakemsr Exp $
+# $OpenBSD: GENERIC,v 1.22 2007/10/18 18:59:29 jsing Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -51,6 +51,7 @@ macebus0 at mainbus0 # MACE controller localbus.
clock0 at macebus0
mec0 at macebus0 sys 0x18 base 0x00280000 irq 4
mavb0 at macebus0 sys 0x18 base 0x00300000 irq 7
+mkbc0 at macebus0 disable sys 0x18 base 0x00320000 irq 6
com0 at macebus0 sys 0x18 base 0x00390000 irq 5
com1 at macebus0 sys 0x18 base 0x00398000 irq 5
@@ -110,6 +111,12 @@ audio* at emu?
#wsdisplay* at vga?
#wskbd* at ukbd? mux 1
+#### Keyboard and Mouse
+pckbd* at mkbc?
+pms* at mkbc?
+wskbd* at pckbd? console ?
+wsmouse* at pms? mux 0
+
#### SCSI Bus devices
sd* at scsibus?
st* at scsibus?
diff --git a/sys/arch/sgi/conf/files.sgi b/sys/arch/sgi/conf/files.sgi
index 0b9fd20ada1..100cc12e849 100644
--- a/sys/arch/sgi/conf/files.sgi
+++ b/sys/arch/sgi/conf/files.sgi
@@ -1,4 +1,4 @@
-# $OpenBSD: files.sgi,v 1.13 2007/06/21 20:17:12 miod Exp $
+# $OpenBSD: files.sgi,v 1.14 2007/10/18 18:59:29 jsing Exp $
#
# maxpartitions must be first item in files.${ARCH}
#
@@ -103,6 +103,12 @@ device mavb: audio
attach mavb at macebus
file arch/sgi/dev/mavb.c mavb
+# MACE PS/2 Controller
+include "dev/pckbc/files.pckbc"
+device mkbc: pckbcslot
+attach mkbc at macebus
+file arch/sgi/dev/mkbc.c mkbc
+
# Raster operations
include "dev/rasops/files.rasops"
include "dev/wsfont/files.wsfont"
diff --git a/sys/arch/sgi/dev/mkbc.c b/sys/arch/sgi/dev/mkbc.c
new file mode 100644
index 00000000000..53400011f31
--- /dev/null
+++ b/sys/arch/sgi/dev/mkbc.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) 2006-2007, Joel Sing
+ * 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 MIND,
+ * 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.
+ */
+
+/*
+ * Derived from sys/dev/ic/pckbc.c under the following terms:
+ * $OpenBSD: mkbc.c,v 1.1 2007/10/18 18:59:29 jsing Exp $
+ * $NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp $ */
+
+/*
+ * Copyright (c) 1998
+ * Matthias Drochner. 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.
+ */
+
+/*
+ * Driver for Moosehead PS/2 Controllers (mkbc)
+ *
+ * There are actually two separate controllers attached to the macebus.
+ * However in the interest of reusing code, we want to act like a pckbc
+ * so that we can directly attach pckbd and pms. As a result, we make
+ * each controller look like a "slot" and combine them into a single device.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/timeout.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/signalvar.h>
+
+#include <machine/autoconf.h>
+
+#include <mips64/archtype.h>
+
+#include <sgi/localbus/macebus.h>
+#include <sgi/localbus/crimebus.h>
+
+#include <dev/ic/pckbcvar.h>
+
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <sys/lock.h>
+
+#include <machine/bus.h>
+
+#include "mkbcreg.h"
+
+const char *mkbc_slot_names[] = { "kbd", "mouse" };
+
+#define KBC_DEVCMD_ACK 0xfa
+#define KBC_DEVCMD_RESEND 0xfe
+
+#define KBD_DELAY DELAY(8)
+
+struct mkbc_softc {
+ struct pckbc_softc sc_pckbc;
+ int sc_irq;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+};
+
+int mkbc_match(struct device *, void *, void *);
+void mkbc_attach(struct device *, struct device *, void *);
+
+struct cfattach mkbc_ca = {
+ sizeof(struct mkbc_softc), mkbc_match, mkbc_attach
+};
+
+struct cfdriver mkbc_cd = {
+ NULL, "mkbc", DV_DULL
+};
+
+/* Descriptor for one device command. */
+struct pckbc_devcmd {
+ TAILQ_ENTRY(pckbc_devcmd) next;
+ int flags;
+#define KBC_CMDFLAG_SYNC 1 /* Give descriptor back to caller. */
+#define KBC_CMDFLAG_SLOW 2
+ u_char cmd[4];
+ int cmdlen, cmdidx, retries;
+ u_char response[4];
+ int status, responselen, responseidx;
+};
+
+/* Data per slave device. */
+struct pckbc_slotdata {
+ int polling; /* Don't read data port in interrupt handler. */
+ TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* Active commands. */
+ TAILQ_HEAD(, pckbc_devcmd) freequeue; /* Free commands. */
+#define NCMD 5
+ struct pckbc_devcmd cmds[NCMD];
+ bus_space_handle_t ioh;
+};
+
+#define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
+
+void mkbc_start(struct pckbc_internal *, pckbc_slot_t);
+int mkbc_attach_slot(struct mkbc_softc *, pckbc_slot_t);
+void mkbc_init_slotdata(struct pckbc_slotdata *);
+int mkbc_submatch(struct device *, void *, void *);
+int mkbcprint(void *, const char *);
+int mkbcintr(void *);
+void mkbc_cleanqueue(struct pckbc_slotdata *);
+void mkbc_cleanup(void *self);
+int mkbc_cmdresponse(struct pckbc_internal *t, pckbc_slot_t slot, u_char data);
+int mkbc_poll_read(bus_space_tag_t, bus_space_handle_t);
+int mkbc_poll_write(bus_space_tag_t, bus_space_handle_t, int);
+
+int
+mkbc_match(struct device *parent, void *cf, void *aux)
+{
+ struct confargs *ca = aux;
+
+ /* MACE PS/2 controller only on SGI O2 */
+ if (ca->ca_sys == SGI_O2)
+ return 1;
+
+ return 0;
+}
+
+void
+mkbc_init_slotdata(struct pckbc_slotdata *q)
+{
+ int i;
+ TAILQ_INIT(&q->cmdqueue);
+ TAILQ_INIT(&q->freequeue);
+
+ for (i = 0; i < NCMD; i++) {
+ TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
+ }
+ q->polling = 0;
+}
+
+int
+mkbcprint(void *aux, const char *pnp)
+{
+ struct pckbc_attach_args *pa = aux;
+
+ if (!pnp)
+ printf(" (%s slot)", mkbc_slot_names[pa->pa_slot]);
+ return (QUIET);
+}
+
+int
+mkbc_submatch(struct device *parent, void *match, void *aux)
+{
+ struct cfdata *cf = match;
+ struct pckbc_attach_args *pa = aux;
+
+ if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
+ cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
+ return (0);
+ return ((*cf->cf_attach->ca_match)(parent, cf, aux));
+}
+
+int
+mkbc_attach_slot(struct mkbc_softc *msc, pckbc_slot_t slot)
+{
+ struct pckbc_softc *sc = &msc->sc_pckbc;
+ struct pckbc_internal *t = sc->id;
+ struct pckbc_attach_args pa;
+ bus_space_handle_t ioh;
+ int found;
+
+ if (!t->t_slotdata[slot]) {
+
+ t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata),
+ M_DEVBUF, M_NOWAIT);
+
+ if (t->t_slotdata[slot] == NULL) {
+ printf("Failed to allocate slot data!\n");
+ return 0;
+ }
+ mkbc_init_slotdata(t->t_slotdata[slot]);
+
+ /* Map subregion of bus space for this "slot". */
+ if (bus_space_subregion(msc->iot, msc->ioh,
+ MKBC_PORTSIZE * slot,
+ MKBC_PORTSIZE, &ioh)) {
+ printf("Unable to map slot subregion!\n");
+ return 0;
+ }
+ t->t_slotdata[slot]->ioh = ioh;
+
+ /* Initialise controller. */
+ bus_space_write_8(msc->iot, ioh, MKBC_CONTROL,
+ MKBC_CONTROL_TX_CLOCK_DISABLE | MKBC_CONTROL_RESET);
+ delay(100); /* 100us */
+
+ /* Enable controller. */
+ bus_space_write_8(t->t_iot, ioh, MKBC_CONTROL,
+ MKBC_CONTROL_RX_CLOCK_ENABLE | MKBC_CONTROL_TX_ENABLE);
+
+ }
+
+ pa.pa_tag = t;
+ pa.pa_slot = slot;
+ found = (config_found_sm((struct device *)msc, &pa,
+ mkbcprint, mkbc_submatch) != NULL);
+
+ return (found);
+}
+
+void
+mkbc_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct mkbc_softc *msc = (void*)self;
+ struct confargs *ca = aux;
+ struct pckbc_softc *sc = &msc->sc_pckbc;
+ struct pckbc_internal *t;
+ void *rv = NULL;
+
+ /* Setup bus space mapping. */
+ msc->iot = ca->ca_iot;
+ if (bus_space_map(msc->iot, ca->ca_baseaddr, MKBC_PORTSIZE * 2, 0,
+ &msc->ioh)) {
+ printf(": unable to map bus space!\n");
+ return;
+ }
+
+ /* Setup pckbc_internal structure (from pckbc_isa.c). */
+ t = malloc(sizeof(struct pckbc_internal), M_DEVBUF, M_WAITOK);
+ bzero(t, sizeof(struct pckbc_internal));
+ t->t_iot = msc->iot;
+ t->t_ioh_d = NULL;
+ t->t_ioh_c = NULL;
+ t->t_addr = ca->ca_baseaddr;
+ t->t_cmdbyte = 0;
+ t->t_sc = (struct pckbc_softc *)msc;
+ sc->id = t;
+
+ /* Establish interrupt handler. */
+ msc->sc_irq = ca->ca_intr;
+ rv = macebus_intr_establish(NULL, msc->sc_irq, IST_EDGE,
+ IPL_TTY, mkbcintr, msc, sc->sc_dv.dv_xname);
+ if (rv == NULL) {
+ printf(": unable to establish interrupt\n");
+ } else {
+ printf(": using irq %d\n", msc->sc_irq);
+ }
+
+ /*
+ * Attach "slots" - technically these are separate controllers
+ * in the same bus space, however we want to act like pckbc so
+ * that we can attach pckbd and pms.
+ */
+ mkbc_attach_slot(msc, PCKBC_KBD_SLOT);
+ mkbc_attach_slot(msc, PCKBC_AUX_SLOT);
+
+ return;
+
+}
+
+int
+mkbcintr(void *vsc)
+{
+ struct mkbc_softc *msc = (struct mkbc_softc *)vsc;
+ struct pckbc_softc *sc = &msc->sc_pckbc;
+ struct pckbc_internal *t = sc->id;
+ pckbc_slot_t slot;
+ struct pckbc_slotdata *q;
+ int served = 0;
+ u_int64_t stat;
+ u_int64_t data;
+
+ /*
+ * Need to check both "slots" since interrupt could be from
+ * either controller.
+ */
+ slot = PCKBC_KBD_SLOT;
+ q = t->t_slotdata[slot];
+ for(;;) {
+
+ if (!q) {
+ printf("mkbcintr: no kbd slot data!\n");
+ break;
+ }
+
+ stat = bus_space_read_8(t->t_iot, q->ioh, MKBC_STATUS);
+ if (!(stat & MKBC_STATUS_RX_FULL))
+ break;
+
+ served = 1;
+
+ if (q->polling)
+ break; /* pckbc_poll_data() will get it */
+
+ KBD_DELAY;
+ data = bus_space_read_8(t->t_iot, q->ioh, MKBC_RX_PORT);
+ if (CMD_IN_QUEUE(q) && mkbc_cmdresponse(t, slot, data))
+ continue;
+
+ if (sc->inputhandler[slot])
+ (*sc->inputhandler[slot])(sc->inputarg[slot], data);
+#ifdef MKBCDEBUG
+ else
+ printf("mkbcintr: slot %d lost %d\n", slot, data);
+#endif
+ }
+
+ /* Mouse controller/slot. */
+ slot = PCKBC_AUX_SLOT;
+ q = t->t_slotdata[slot];
+ for(;;) {
+
+ if (!q) {
+ printf("mkbcintr: no mouse slot data!\n");
+ break;
+ }
+
+ stat = bus_space_read_8(t->t_iot, q->ioh, MKBC_STATUS);
+ if (!(stat & MKBC_STATUS_RX_FULL))
+ break;
+
+ served = 1;
+
+ if (q->polling)
+ break; /* pckbc_poll_data() will get it. */
+
+ KBD_DELAY;
+ data = bus_space_read_8(t->t_iot, q->ioh, MKBC_RX_PORT);
+ if (CMD_IN_QUEUE(q) && mkbc_cmdresponse(t, slot, data))
+ continue;
+
+ if (sc->inputhandler[slot])
+ (*sc->inputhandler[slot])(sc->inputarg[slot], data);
+#ifdef MKBCDEBUG
+ else
+ printf("mkbcintr: slot %d lost %d\n", slot, data);
+#endif
+ }
+
+ return (served);
+
+}
+
+int
+mkbc_poll_write(bus_space_tag_t iot, bus_space_handle_t ioh, int val)
+{
+
+ int timeout = 10000;
+ u_int64_t stat;
+
+ /* Attempt to write a value to the controller. */
+ while (timeout--) {
+ stat = bus_space_read_8(iot, ioh, MKBC_STATUS);
+ if (stat & MKBC_STATUS_TX_EMPTY) {
+ bus_space_write_8(iot, ioh, MKBC_TX_PORT, val & 0xff);
+ return 0;
+ }
+ delay(50);
+ }
+ return -1;
+
+}
+
+int
+mkbc_poll_read(bus_space_tag_t iot, bus_space_handle_t ioh)
+{
+
+ int timeout = 10000;
+ u_int64_t stat, val;
+
+ /* Poll input from controller. */
+ while (timeout--) {
+ stat = bus_space_read_8(iot, ioh, MKBC_STATUS);
+ if (stat & MKBC_STATUS_RX_FULL) {
+ val = bus_space_read_8(iot, ioh, MKBC_RX_PORT);
+ return val & 0xff;
+ }
+ delay(50);
+ }
+ return -1;
+
+}
+
+/*
+ * Pass command to device, poll for ACK and data.
+ * to be called at spltty()
+ */
+static void
+mkbc_poll_cmd(struct pckbc_internal *t, pckbc_slot_t slot,
+ struct pckbc_devcmd *cmd)
+{
+ bus_space_tag_t iot = t->t_iot;
+ bus_space_handle_t ioh = t->t_slotdata[slot]->ioh;
+
+ int i, c = 0;
+
+ while (cmd->cmdidx < cmd->cmdlen) {
+ if (mkbc_poll_write(iot, ioh, cmd->cmd[cmd->cmdidx]) == -1) {
+ printf("mkbc_poll_cmd: send error\n");
+ cmd->status = EIO;
+ return;
+ }
+ for (i = 10; i; i--) { /* 1s ??? */
+ c = mkbc_poll_read(iot, ioh);
+ if (c != -1)
+ break;
+ }
+
+ if (c == KBC_DEVCMD_ACK) {
+ cmd->cmdidx++;
+ continue;
+ }
+ if (c == KBC_DEVCMD_RESEND) {
+#ifdef MKBCDEBUG
+ printf("mkbc_cmd: RESEND\n");
+#endif
+ if (cmd->retries++ < 5)
+ continue;
+ else {
+#ifdef MKBCDEBUG
+ printf("mkbc: cmd failed\n");
+#endif
+ cmd->status = EIO;
+ return;
+ }
+ }
+ if (c == -1) {
+#ifdef MKBCDEBUG
+ printf("mkbc_cmd: timeout\n");
+#endif
+ cmd->status = EIO;
+ return;
+ }
+#ifdef MKBCDEBUG
+ printf("mkbc_cmd: lost 0x%x\n", c);
+#endif
+ }
+
+ while (cmd->responseidx < cmd->responselen) {
+ if (cmd->flags & KBC_CMDFLAG_SLOW)
+ i = 100; /* 10s ??? */
+ else
+ i = 10; /* 1s ??? */
+ while (i--) {
+ c = mkbc_poll_read(iot, ioh);
+ if (c != -1)
+ break;
+ }
+ if (c == -1) {
+#ifdef MKBCDEBUG
+ printf("mkbc_poll_cmd: no data\n");
+#endif
+ cmd->status = ETIMEDOUT;
+ return;
+ } else
+ cmd->response[cmd->responseidx++] = c;
+ }
+}
+
+/*
+ * Clean up a command queue, throw away everything.
+ */
+void
+mkbc_cleanqueue(struct pckbc_slotdata *q)
+{
+ struct pckbc_devcmd *cmd;
+#ifdef MKBCDEBUG
+ int i;
+#endif
+
+ while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
+ TAILQ_REMOVE(&q->cmdqueue, cmd, next);
+#ifdef MKBCDEBUG
+ printf("mkbc_cleanqueue: removing");
+ for (i = 0; i < cmd->cmdlen; i++)
+ printf(" %02x", cmd->cmd[i]);
+ printf("\n");
+#endif
+ TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
+ }
+}
+
+/*
+ * Timeout error handler: clean queues and data port.
+ * XXX could be less invasive.
+ */
+void
+mkbc_cleanup(void *self)
+{
+ struct pckbc_internal *t = self;
+ int s;
+
+ printf("mkbc: command timeout\n");
+
+ s = spltty();
+
+ if (t->t_slotdata[PCKBC_KBD_SLOT])
+ mkbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
+ if (t->t_slotdata[PCKBC_AUX_SLOT])
+ mkbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
+
+ while (mkbc_poll_read(t->t_iot, t->t_slotdata[PCKBC_KBD_SLOT]->ioh)
+ != -1) ;
+ while (mkbc_poll_read(t->t_iot, t->t_slotdata[PCKBC_AUX_SLOT]->ioh)
+ != -1) ;
+
+ /* Reset KBC? */
+
+ splx(s);
+}
+
+/*
+ * Pass command to device during normal operation.
+ * to be called at spltty()
+ */
+void
+mkbc_start(struct pckbc_internal *t, pckbc_slot_t slot)
+{
+ struct pckbc_slotdata *q = t->t_slotdata[slot];
+ struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
+
+ if (q->polling) {
+ do {
+ mkbc_poll_cmd(t, slot, cmd);
+ if (cmd->status)
+ printf("mkbc_start: command error\n");
+
+ TAILQ_REMOVE(&q->cmdqueue, cmd, next);
+ if (cmd->flags & KBC_CMDFLAG_SYNC)
+ wakeup(cmd);
+ else {
+ timeout_del(&t->t_cleanup);
+ TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
+ }
+ cmd = TAILQ_FIRST(&q->cmdqueue);
+ } while (cmd);
+ return;
+ }
+
+ if (mkbc_poll_write(t->t_iot, t->t_slotdata[slot]->ioh,
+ cmd->cmd[cmd->cmdidx])) {
+ printf("mkbc_start: send error\n");
+ /* XXX what now? */
+ return;
+ }
+}
+
+/*
+ * Handle command responses coming in asynchronously,
+ * return nonzero if valid response.
+ * to be called at spltty()
+ */
+int
+mkbc_cmdresponse(struct pckbc_internal *t, pckbc_slot_t slot, u_char data)
+{
+ struct pckbc_slotdata *q = t->t_slotdata[slot];
+ struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
+#ifdef DIAGNOSTIC
+ if (!cmd)
+ panic("mkbc_cmdresponse: no active command");
+#endif
+ if (cmd->cmdidx < cmd->cmdlen) {
+ if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
+ return (0);
+
+ if (data == KBC_DEVCMD_RESEND) {
+ if (cmd->retries++ < 5) {
+ /* try again last command */
+ goto restart;
+ } else {
+#ifdef MKBCDEBUG
+ printf("mkbc: cmd failed\n");
+#endif
+ cmd->status = EIO;
+ /* dequeue */
+ }
+ } else {
+ if (++cmd->cmdidx < cmd->cmdlen)
+ goto restart;
+ if (cmd->responselen)
+ return (1);
+ /* else dequeue */
+ }
+ } else if (cmd->responseidx < cmd->responselen) {
+ cmd->response[cmd->responseidx++] = data;
+ if (cmd->responseidx < cmd->responselen)
+ return (1);
+ /* else dequeue */
+ } else
+ return (0);
+
+ /* dequeue: */
+ TAILQ_REMOVE(&q->cmdqueue, cmd, next);
+ if (cmd->flags & KBC_CMDFLAG_SYNC)
+ wakeup(cmd);
+ else {
+ timeout_del(&t->t_cleanup);
+ TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
+ }
+ if (!CMD_IN_QUEUE(q))
+ return (1);
+restart:
+ mkbc_start(t, slot);
+ return (1);
+}
+
+/*
+ * Interfaces to act like a pckbc(4).
+ */
+
+int
+pckbc_xt_translation(pckbc_tag_t self, pckbc_slot_t slot, int on)
+{
+ /* Translation isn't supported... */
+ return 0;
+}
+
+/* For use in autoconfiguration. */
+int
+pckbc_poll_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
+ int responselen, u_char *respbuf, int slow)
+{
+ struct pckbc_devcmd nc;
+ int s;
+
+ if ((len > 4) || (responselen > 4))
+ return (EINVAL);
+
+ bzero(&nc, sizeof(nc));
+ bcopy(cmd, nc.cmd, len);
+ nc.cmdlen = len;
+ nc.responselen = responselen;
+ nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
+
+ s = spltty();
+ mkbc_poll_cmd(self, slot, &nc);
+ splx(s);
+
+ if (nc.status == 0 && respbuf)
+ bcopy(nc.response, respbuf, responselen);
+
+ return (nc.status);
+}
+
+void
+pckbc_flush(pckbc_tag_t self, pckbc_slot_t slot)
+{
+ /* Read any data and discard */
+ struct pckbc_internal *t = self;
+ (void) mkbc_poll_read(t->t_iot, t->t_slotdata[slot]->ioh);
+}
+
+/*
+ * Put command into the device's command queue, return zero or errno.
+ */
+int
+pckbc_enqueue_cmd(pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd, int len,
+ int responselen, int sync, u_char *respbuf)
+{
+ struct pckbc_internal *t = self;
+ struct pckbc_slotdata *q = t->t_slotdata[slot];
+ struct pckbc_devcmd *nc;
+ int s, isactive, res = 0;
+
+ if ((len > 4) || (responselen > 4))
+ return (EINVAL);
+ s = spltty();
+ nc = TAILQ_FIRST(&q->freequeue);
+ if (nc) {
+ TAILQ_REMOVE(&q->freequeue, nc, next);
+ }
+ splx(s);
+ if (!nc)
+ return (ENOMEM);
+
+ bzero(nc, sizeof(*nc));
+ bcopy(cmd, nc->cmd, len);
+ nc->cmdlen = len;
+ nc->responselen = responselen;
+ nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
+
+ s = spltty();
+
+ if (q->polling && sync) {
+ /*
+ * XXX We should poll until the queue is empty.
+ * But we don't come here normally, so make
+ * it simple and throw away everything.
+ */
+ mkbc_cleanqueue(q);
+ }
+
+ isactive = CMD_IN_QUEUE(q);
+ TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
+ if (!isactive)
+ mkbc_start(t, slot);
+
+ if (q->polling)
+ res = (sync ? nc->status : 0);
+ else if (sync) {
+ if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
+ TAILQ_REMOVE(&q->cmdqueue, nc, next);
+ mkbc_cleanup(t);
+ } else
+ res = nc->status;
+ } else
+ timeout_add(&t->t_cleanup, hz);
+
+ if (sync) {
+ if (respbuf)
+ bcopy(nc->response, respbuf, responselen);
+ TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
+ }
+
+ splx(s);
+
+ return (res);
+}
+
+int
+pckbc_poll_data(pckbc_tag_t self, pckbc_slot_t slot)
+{
+ struct pckbc_internal *t = self;
+ struct pckbc_slotdata *q = t->t_slotdata[slot];
+ int c;
+
+ c = mkbc_poll_read(t->t_iot, q->ioh);
+ if (c != -1 && q && CMD_IN_QUEUE(q)) {
+ /* we jumped into a running command - try to
+ deliver the response */
+ if (mkbc_cmdresponse(t, slot, c))
+ return (-1);
+ }
+ return (c);
+}
+
+void
+pckbc_set_inputhandler(pckbc_tag_t self, pckbc_slot_t slot, pckbc_inputfcn func,
+ void *arg, char *name)
+{
+ struct pckbc_internal *t = (struct pckbc_internal *)self;
+ struct pckbc_softc *sc = t->t_sc;
+
+ if (slot >= PCKBC_NSLOTS)
+ panic("mkbc_set_inputhandler: bad slot %d", slot);
+
+ sc->inputhandler[slot] = func;
+ sc->inputarg[slot] = arg;
+ sc->subname[slot] = name;
+}
+
+void
+pckbc_slot_enable(pckbc_tag_t self, pckbc_slot_t slot, int on)
+{
+ struct pckbc_internal *t = (struct pckbc_internal *)self;
+
+ /*
+ * Should we also enable/disable the controller??
+ * If we did then none of the poll_ functions would work...
+ */
+
+ if (on) {
+
+ /* Enable controller interrupts. */
+ bus_space_write_8(t->t_iot, t->t_slotdata[slot]->ioh,
+ MKBC_CONTROL,
+ MKBC_CONTROL_RX_CLOCK_ENABLE | MKBC_CONTROL_TX_ENABLE
+ | MKBC_CONTROL_RX_INT_ENABLE);
+
+ } else {
+
+ /* Disable controller interrupts. */
+ bus_space_write_8(t->t_iot, t->t_slotdata[slot]->ioh,
+ MKBC_CONTROL,
+ MKBC_CONTROL_RX_CLOCK_ENABLE | MKBC_CONTROL_TX_ENABLE);
+
+ }
+
+}
+
+void
+pckbc_set_poll(pckbc_tag_t self, pckbc_slot_t slot, int on)
+{
+ struct pckbc_internal *t = (struct pckbc_internal *)self;
+
+ t->t_slotdata[slot]->polling = on;
+
+ if (!on) {
+ int s;
+
+ /*
+ * If disabling polling on a device that's been configured,
+ * make sure there are no bytes left in the FIFO, holding up
+ * the interrupt line. Otherwise we won't get any further
+ * interrupts.
+ */
+ if (t->t_sc) {
+ s = spltty();
+ mkbcintr(t->t_sc);
+ splx(s);
+ }
+ }
+}
diff --git a/sys/arch/sgi/dev/mkbcreg.h b/sys/arch/sgi/dev/mkbcreg.h
new file mode 100644
index 00000000000..d071f96071d
--- /dev/null
+++ b/sys/arch/sgi/dev/mkbcreg.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2006-2007, Joel Sing
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * MACE PS/2 controller register definitions.
+ */
+
+#define MKBC_PORTSIZE 0x20
+
+#define MKBC_TX_PORT 0x00
+#define MKBC_RX_PORT 0x08
+#define MKBC_CONTROL 0x10
+#define MKBC_STATUS 0x18
+
+/*
+ * Controller status flags
+ */
+#define MKBC_STATUS_CLOCK_SIGNAL 0x01
+#define MKBC_STATUS_CLOCK_INHIBIT 0x02
+#define MKBC_STATUS_TX_INPROGRESS 0x04
+#define MKBC_STATUS_TX_EMPTY 0x08
+#define MKBC_STATUS_RX_FULL 0x10
+#define MKBC_STATUS_RX_INPROGRESS 0x20
+#define MKBC_STATUS_ERROR_PARITY 0x40
+#define MKBC_STATUS_ERROR_FRAMING 0x80
+
+/*
+ * Control bits
+ */
+#define MKBC_CONTROL_TX_CLOCK_DISABLE 0x01
+#define MKBC_CONTROL_TX_ENABLE 0x02
+#define MKBC_CONTROL_TX_INT_ENABLE 0x04
+#define MKBC_CONTROL_RX_INT_ENABLE 0x08
+#define MKBC_CONTROL_RX_CLOCK_ENABLE 0x10
+#define MKBC_CONTROL_RESET 0x20
+
diff --git a/sys/arch/sgi/localbus/macebus.h b/sys/arch/sgi/localbus/macebus.h
index 249465c287f..d55e6508b9c 100644
--- a/sys/arch/sgi/localbus/macebus.h
+++ b/sys/arch/sgi/localbus/macebus.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: macebus.h,v 1.7 2005/12/19 21:37:48 miod Exp $ */
+/* $OpenBSD: macebus.h,v 1.8 2007/10/18 18:59:29 jsing Exp $ */
/*
* Copyright (c) 2003-2004 Opsycon AB (www.opsycon.com).
@@ -119,7 +119,6 @@
#define MACE_ISA_RTC_OFFS (MACE_ISAX_OFFS+0x00020000)
#define MACE_ISA_GAME_OFFS (MACE_ISAX_OFFS+0x00030000)
-
extern bus_space_t macebus_tag;
u_int8_t mace_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
@@ -136,4 +135,6 @@ int mace_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, bus_space_handl
void mace_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
int mace_space_region(bus_space_tag_t, bus_space_handle_t, bus_size_t, bus_size_t, bus_space_handle_t *);
+void *macebus_intr_establish(void *, u_long, int, int, int (*)(void *), void *, char *);
+
#endif /* _MACEBUS_H_ */