diff options
author | Joel Sing <jsing@cvs.openbsd.org> | 2007-10-18 18:59:30 +0000 |
---|---|---|
committer | Joel Sing <jsing@cvs.openbsd.org> | 2007-10-18 18:59:30 +0000 |
commit | 5f5dc8ee699cb0137500182a10819458f4cb5e99 (patch) | |
tree | 709134f01ee741ec277a3d257f202cb70647cb3f /sys/arch/sgi | |
parent | 9f9e1d5e8291af36c167d1fd8a9637ac27f82268 (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/GENERIC | 9 | ||||
-rw-r--r-- | sys/arch/sgi/conf/files.sgi | 8 | ||||
-rw-r--r-- | sys/arch/sgi/dev/mkbc.c | 840 | ||||
-rw-r--r-- | sys/arch/sgi/dev/mkbcreg.h | 49 | ||||
-rw-r--r-- | sys/arch/sgi/localbus/macebus.h | 5 |
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_ */ |