summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/hil/hil.c586
1 files changed, 586 insertions, 0 deletions
diff --git a/sys/dev/hil/hil.c b/sys/dev/hil/hil.c
new file mode 100644
index 00000000000..29af438fc52
--- /dev/null
+++ b/sys/dev/hil/hil.c
@@ -0,0 +1,586 @@
+/* $OpenBSD: hil.c,v 1.1 2003/02/11 19:40:20 miod Exp $ */
+/*
+ * Copyright (c) 2003, Miodrag Vallat.
+ * 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.
+ *
+ */
+
+/*
+ * Copyright (c) 1988 University of Utah.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * 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 University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * from: Utah $Hdr: hil.c 1.38 92/01/21$
+ *
+ * @(#)hil.c 8.2 (Berkeley) 1/12/94
+ */
+
+#undef HILDEBUG
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+
+#include <machine/autoconf.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <dev/hil/hilreg.h>
+#include <dev/hil/hilvar.h>
+#include <dev/hil/hildevs.h>
+#include <dev/hil/hildevs_data.h>
+
+#define splhil spltty
+
+struct cfdriver hil_cd = {
+ NULL, "hil", DV_DULL
+};
+
+void hilconfig(struct hil_softc *);
+
+void hil_process_int(struct hil_softc *, u_int8_t, u_int8_t);
+void polloff(struct hil_softc *);
+void pollon(struct hil_softc *);
+
+static void hilwait(struct hil_softc *);
+static void hildatawait(struct hil_softc *);
+
+static __inline void
+hilwait(struct hil_softc *sc)
+{
+ while (bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT) & HIL_BUSY) {
+ /* nothing */
+ }
+}
+
+static __inline void
+hildatawait(struct hil_softc *sc)
+{
+ while (!(bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT) &
+ HIL_DATA_RDY)) {
+ /* nothing */
+ }
+}
+
+/*
+ * Common HIL bus attachment
+ */
+
+void
+hil_attach(struct hil_softc *sc)
+{
+ printf("\n");
+
+ /*
+ * Initialize loop information
+ */
+ sc->sc_cmdending = 0;
+ sc->sc_actdev = sc->sc_cmddev = 0;
+ sc->sc_cmddone = 0;
+ sc->sc_cmdbp = sc->sc_cmdbuf;
+ sc->sc_pollbp = sc->sc_pollbuf;
+ sc->sc_kbddev = 0;
+}
+
+/*
+ * HIL subdevice attachment
+ */
+
+int
+hildevprint(void *aux, const char *pnp)
+{
+ struct hil_attach_args *ha = aux;
+
+ if (pnp != NULL) {
+ printf("\"%s\" at %s id %x",
+ ha->ha_descr, pnp, ha->ha_id);
+ }
+ printf(" code %d", ha->ha_code);
+ if (pnp == NULL) {
+ printf(": %s", ha->ha_descr);
+ }
+
+ return (UNCONF);
+}
+
+void
+hil_attach_deferred(void *v)
+{
+ struct hil_softc *sc = v;
+ struct hil_attach_args ha;
+ int id, s, tries;
+ u_int8_t db;
+
+ /*
+ * Initialize the loop: reconfigure, don't report errors,
+ * put keyboard in cooked mode, and enable autopolling.
+ */
+ db = LPC_RECONF | LPC_KBDCOOK | LPC_NOERROR | LPC_AUTOPOLL;
+ send_hil_cmd(sc, HIL_WRITELPCTRL, &db, 1, NULL);
+
+ /*
+ * Delay one second for reconfiguration and then read the
+ * data to clear the interrupt (if the loop reconfigured).
+ */
+ DELAY(1000000);
+ if (bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT) &
+ HIL_DATA_RDY) {
+ db = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
+ }
+
+ /*
+ * The HIL loop may have reconfigured. If so we proceed on,
+ * if not we loop a few times until a successful reconfiguration
+ * is reported back to us. If the HIL loop is still lost after a
+ * few seconds, give up.
+ */
+ for (tries = 10; tries != 0; tries--) {
+ send_hil_cmd(sc, HIL_READLPSTAT, NULL, 0, &db);
+
+ if (db & (LPS_CONFFAIL | LPS_CONFGOOD))
+ break;
+
+#ifdef HILDEBUG
+ printf("%s: loop not ready, retrying...\n",
+ sc->sc_dev.dv_xname);
+#endif
+
+ DELAY(1000000);
+ }
+
+ if (tries == 0 || (db & LPS_CONFFAIL)) {
+ printf("%s: loop failed to reconfigure\n", sc->sc_dev.dv_xname);
+ return;
+ }
+
+ /*
+ * At this point, the loop should have reconfigured.
+ * The reconfiguration interrupt has already called hilconfig().
+ */
+ send_hil_cmd(sc, HIL_INTON, NULL, 0, NULL);
+
+ /*
+ * Now attach hil devices as they are found.
+ */
+
+ s = splhil();
+
+ /* Attach the remaining devices */
+ for (id = 1; id <= sc->sc_maxdev; id++) {
+ int len;
+ const struct hildevice *hd;
+
+ send_hildev_cmd(sc, id, HIL_IDENTIFY);
+
+ len = sc->sc_cmdbp - sc->sc_cmdbuf;
+ if (len == 0) {
+#ifdef HILDEBUG
+ printf("%s: no device at code %d\n",
+ sc->sc_dev.dv_xname, id);
+#endif
+ continue;
+ }
+
+ /* Attach found devices */
+ for (hd = hildevs; hd->minid >= 0; hd++)
+ if (sc->sc_cmdbuf[0] >= hd->minid &&
+ sc->sc_cmdbuf[0] <= hd->maxid) {
+
+ ha.ha_code = id;
+ ha.ha_type = hd->type;
+ ha.ha_descr = hd->descr;
+ ha.ha_infolen = len;
+ bcopy(sc->sc_cmdbuf, ha.ha_info, len);
+
+ if (ha.ha_type == HIL_DEVICE_KEYBOARD) {
+ /*
+ * Currently, the first found hil keyboard will
+ * be attached as a console keyboard.
+ * This will change when we have hil at wax
+ * attachment.
+ */
+ ha.ha_flags = id == sc->sc_kbddev;
+ } else {
+ ha.ha_flags = 0;
+ }
+
+ config_found(&sc->sc_dev, &ha, hildevprint);
+ }
+ }
+
+ sc->sc_cmdbp = sc->sc_cmdbuf;
+
+ splx(s);
+}
+
+void
+hil_callback_register(struct hil_softc *sc, int hilid,
+ void (*handler)(void *, u_int, u_int8_t *), void *arg)
+{
+#ifdef HILDEBUG
+ if (hilid < 0 || hilid >= NHILD)
+ panic("hil_callback_register invoked with hilid %d", hilid);
+#endif
+
+ if (sc->sc_cb[hilid].cb_fn != NULL)
+ panic("hil_callback_register: invoked twice for hilid %d",
+ hilid);
+
+ sc->sc_cb[hilid].cb_fn = handler;
+ sc->sc_cb[hilid].cb_arg = arg;
+}
+
+/*
+ * Asynchronous event processing
+ */
+
+int
+hil_intr(void *v)
+{
+ struct hil_softc *sc = v;
+ u_int8_t c, stat;
+
+ stat = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT);
+ c = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
+ HILP_DATA); /* clears interrupt */
+ hil_process_int(sc, stat, c);
+
+ return (1);
+}
+
+void
+hil_process_int(struct hil_softc *sc, u_int8_t stat, u_int8_t c)
+{
+ switch ((stat >> HIL_SSHIFT) & HIL_SMASK) {
+ case HIL_STATUS:
+ if (c & HIL_ERROR) {
+ sc->sc_cmddone = 1;
+ if (c == HIL_RECONFIG)
+ hilconfig(sc);
+ break;
+ }
+ if (c & HIL_COMMAND) {
+ if (c & HIL_POLLDATA) { /* End of data */
+ if (sc->sc_cb[sc->sc_actdev].cb_fn != NULL)
+ sc->sc_cb[sc->sc_actdev].cb_fn
+ (sc->sc_cb[sc->sc_actdev].cb_arg,
+ sc->sc_pollbp - sc->sc_pollbuf,
+ sc->sc_pollbuf);
+ } else { /* End of command */
+ sc->sc_cmdending = 1;
+ }
+ sc->sc_actdev = 0;
+ } else {
+ if (c & HIL_POLLDATA) { /* Start of polled data */
+ sc->sc_actdev = (c & HIL_DEVMASK);
+ sc->sc_pollbp = sc->sc_pollbuf;
+ } else { /* Start of command */
+ if (sc->sc_cmddev == (c & HIL_DEVMASK)) {
+ sc->sc_cmdbp = sc->sc_cmdbuf;
+ sc->sc_actdev = 0;
+ }
+ }
+ }
+ break;
+ case HIL_DATA:
+ if (sc->sc_actdev != 0) /* Collecting poll data */
+ *sc->sc_pollbp++ = c;
+ else {
+ if (sc->sc_cmddev != 0) { /* Collecting cmd data */
+ if (sc->sc_cmdending) {
+ sc->sc_cmddone = 1;
+ sc->sc_cmdending = 0;
+ } else
+ *sc->sc_cmdbp++ = c;
+ }
+ }
+ break;
+ }
+
+}
+
+/*
+ * Called after the loop has reconfigured. Here we need to:
+ * - determine how many devices are on the loop
+ * (some may have been added or removed)
+ * - make sure all keyboards are in raw mode (hilkbd depends
+ * on this)
+ * Note that our device state is now potentially invalid as
+ * devices may no longer be where they were. What we should
+ * do here is either track where the devices went and move
+ * state around accordingly...
+ */
+void
+hilconfig(struct hil_softc *sc)
+{
+ u_int8_t db;
+ int s;
+
+ s = splhil();
+
+ /*
+ * Determine how many devices are on the loop.
+ */
+ db = 0;
+ send_hil_cmd(sc, HIL_READLPSTAT, NULL, 0, &db);
+ sc->sc_maxdev = db & LPS_DEVMASK;
+#ifdef HILDEBUG
+ printf("%s: %d devices\n", sc->sc_dev.dv_xname, sc->sc_maxdev);
+#endif
+
+ /*
+ * Locate the main keyboard device, if present.
+ */
+ db = 0;
+ send_hil_cmd(sc, HIL_READKBDSADR, NULL, 0, &db);
+ sc->sc_kbddev = ffs((int)db);
+
+ /*
+ * Put all keyboards in raw mode, in case we ended there after
+ * a reconfigure event.
+ */
+ db = 0;
+ send_hil_cmd(sc, HIL_WRITEKBDSADR, &db, 1, NULL);
+
+ splx(s);
+}
+
+/*
+ * Low level routines which actually talk to the 8042 chip.
+ */
+
+/*
+ * Send a command to the 8042 with zero or more bytes of data.
+ * If rdata is non-null, wait for and return a byte of data.
+ * We run at splvm() to make the transaction as atomic as
+ * possible without blocking the clock (is this necessary?)
+ */
+void
+send_hil_cmd(struct hil_softc *sc, u_int8_t cmd, u_int8_t *data, u_int8_t dlen,
+ u_int8_t *rdata)
+{
+ u_int8_t status;
+ int s = splvm();
+
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, cmd);
+ while (dlen--) {
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, *data++);
+ }
+ if (rdata) {
+ do {
+ hildatawait(sc);
+ status = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
+ HILP_STAT);
+ *rdata = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
+ HILP_DATA);
+ } while (((status >> HIL_SSHIFT) & HIL_SMASK) != HIL_68K);
+ }
+ splx(s);
+}
+
+/*
+ * Send a command to a device on the loop.
+ * Since only one command can be active on the loop at any time,
+ * we must ensure that we are not interrupted during this process.
+ * Hence we mask interrupts to prevent potential access from most
+ * interrupt routines and turn off auto-polling to disable the
+ * internally generated poll commands.
+ *
+ * splhigh is extremely conservative but insures atomic operation,
+ * splvm (clock only interrupts) seems to be good enough in practice.
+ */
+void
+send_hildev_cmd(sc, device, cmd)
+ struct hil_softc *sc;
+ char device, cmd;
+{
+ u_int8_t status, c;
+ int s = splvm();
+
+ polloff(sc);
+
+ sc->sc_cmdbp = sc->sc_cmdbuf;
+ sc->sc_cmddev = device;
+
+ /*
+ * Transfer the command and device info to the chip
+ */
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_STARTCMD);
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, 8 + device);
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, cmd);
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, HIL_TIMEOUT);
+
+ /*
+ * Trigger the command and wait for completion
+ */
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_TRIGGER);
+ sc->sc_cmddone = 0;
+ do {
+ hildatawait(sc);
+ status = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT);
+ c = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
+ hil_process_int(sc, status, c);
+ } while (sc->sc_cmddone == 0);
+
+ sc->sc_cmddev = 0;
+
+ pollon(sc);
+
+ splx(s);
+}
+
+/*
+ * Turn auto-polling off and on.
+ */
+void
+polloff(struct hil_softc *sc)
+{
+ char db;
+
+ /*
+ * Turn off auto repeat
+ */
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_SETARR);
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, 0);
+
+ /*
+ * Turn off auto-polling
+ */
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_READLPCTRL);
+ hildatawait(sc);
+ db = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
+ db &= ~LPC_AUTOPOLL;
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_WRITELPCTRL);
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, db);
+
+ /*
+ * Must wait until polling is really stopped
+ */
+ do {
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_READBUSY);
+ hildatawait(sc);
+ db = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
+ } while (db & BSY_LOOPBUSY);
+}
+
+void
+pollon(struct hil_softc *sc)
+{
+ char db;
+
+ /*
+ * Turn on auto polling
+ */
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_READLPCTRL);
+ hildatawait(sc);
+ db = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
+ db |= LPC_AUTOPOLL;
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_WRITELPCTRL);
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, db);
+
+ /*
+ * Turn off auto repeat - we emulate this through wscons
+ */
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_SETARR);
+ hilwait(sc);
+ bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, 0);
+}
+
+void
+hil_set_poll(struct hil_softc *sc, int on)
+{
+ if (on)
+ pollon(sc);
+ else
+ polloff(sc);
+}
+
+int
+hil_poll_data(struct hil_softc *sc, u_int8_t *stat, u_int8_t *data)
+{
+ u_int8_t s;
+
+ s = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT);
+ if ((s & HIL_DATA_RDY) == 0)
+ return -1;
+
+ *stat = s;
+ *data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
+
+ return 0;
+}