123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278 |
- /* $OpenBSD: iockbc.c,v 1.11 2015/02/11 07:05:39 dlg Exp $ */
- /*
- * Copyright (c) 2013, Miodrag Vallat
- * Copyright (c) 2006, 2007, 2009 Joel Sing <jsing@openbsd.org>
- *
- * 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.
- */
- /*
- * Derived from sys/dev/ic/pckbc.c under the following terms:
- * $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 IOC3 and IOC4 PS/2 Controllers (iockbc)
- */
- #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 <sys/errno.h>
- #include <sys/queue.h>
- #include <machine/autoconf.h>
- #include <machine/bus.h>
- #include <mips64/archtype.h>
- #include <sgi/pci/iocreg.h>
- #include <sgi/pci/iocvar.h>
- #include <sgi/pci/iofreg.h>
- #include <sgi/pci/iofvar.h>
- #include <dev/ic/i8042reg.h>
- #include <dev/ic/pckbcvar.h>
- #include <dev/pckbc/pckbdreg.h>
- #define KBC_DEVCMD_ACK KBR_ACK
- #define KBC_DEVCMD_RESEND KBR_RESEND
- #include <dev/pckbc/pckbdvar.h>
- #include <dev/pci/pcireg.h>
- #include <dev/pci/pcidevs.h>
- #include <sgi/dev/iockbcreg.h>
- #include <sgi/dev/iockbcvar.h>
- #include "iockbc.h"
- const char *iockbc_slot_names[] = { "kbd", "mouse" };
- /* #define IOCKBC_DEBUG */
- #ifdef IOCKBC_DEBUG
- #define DPRINTF(x...) do { printf(x); } while(0)
- #else
- #define DPRINTF(x...)
- #endif
- struct iockbc_reginfo {
- bus_addr_t rx;
- bus_addr_t tx;
- bus_addr_t cs;
- uint32_t busy;
- };
- struct iockbc_softc {
- struct pckbc_softc sc_pckbc;
- bus_space_tag_t iot;
- bus_space_handle_t ioh;
- const struct iockbc_reginfo *reginfo;
- int console;
- };
- int iockbc_match(struct device *, void *, void *);
- void iockbc_ioc_attach(struct device *, struct device *, void *);
- void iockbc_iof_attach(struct device *, struct device *, void *);
- #if NIOCKBC_IOC > 0
- struct cfattach iockbc_ioc_ca = {
- sizeof(struct iockbc_softc), iockbc_match, iockbc_ioc_attach
- };
- #endif
- #if NIOCKBC_IOF > 0
- struct cfattach iockbc_iof_ca = {
- sizeof(struct iockbc_softc), iockbc_match, iockbc_iof_attach
- };
- #endif
- struct cfdriver iockbc_cd = {
- NULL, "iockbc", 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];
- int rx_queue[3];
- int rx_index;
- const struct iockbc_reginfo *reginfo;
- };
- #define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
- enum iockbc_slottype { EMPTY, KBD, MOUSE };
- static struct pckbc_internal iockbc_consdata;
- static struct pckbc_slotdata iockbc_cons_slotdata;
- int iockbc_is_ioc_console(struct ioc_attach_args *);
- int iockbc_is_iof_console(struct iof_attach_args *);
- void iockbc_attach_common(struct iockbc_softc *, bus_addr_t, int,
- const struct iockbc_reginfo *, const struct iockbc_reginfo *);
- void iockbc_start(struct pckbc_internal *, pckbc_slot_t);
- int iockbc_attach_slot(struct iockbc_softc *, pckbc_slot_t);
- void iockbc_init_slotdata(struct pckbc_slotdata *,
- const struct iockbc_reginfo *);
- int iockbc_submatch(struct device *, void *, void *);
- int iockbcprint(void *, const char *);
- int iockbcintr(void *);
- int iockbcintr_internal(struct pckbc_internal *, struct pckbc_softc *);
- void iockbc_cleanqueue(struct pckbc_slotdata *);
- void iockbc_cleanup(void *);
- void iockbc_poll(void *);
- int iockbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
- int iockbc_poll_read(struct pckbc_internal *, pckbc_slot_t);
- int iockbc_poll_write(struct pckbc_internal *, pckbc_slot_t, int);
- void iockbc_process_input(struct pckbc_softc *, struct pckbc_internal *,
- int, uint);
- enum iockbc_slottype
- iockbc_probe_slot(struct pckbc_internal *, pckbc_slot_t);
- int
- iockbc_match(struct device *parent, void *cf, void *aux)
- {
- /*
- * We expect ioc and iof NOT to attach us if there are no PS/2 ports.
- */
- return 1;
- }
- #if NIOCKBC_IOC > 0
- /*
- * Register assignments
- */
- const struct iockbc_reginfo iockbc_ioc[PCKBC_NSLOTS] = {
- [PCKBC_KBD_SLOT] = {
- .rx = IOC3_KBC_KBD_RX,
- .tx = IOC3_KBC_KBD_TX,
- .cs = IOC3_KBC_CTRL_STATUS,
- .busy = IOC3_KBC_STATUS_KBD_WRITE_PENDING
- },
- [PCKBC_AUX_SLOT] = {
- .rx = IOC3_KBC_AUX_RX,
- .tx = IOC3_KBC_AUX_TX,
- .cs = IOC3_KBC_CTRL_STATUS,
- .busy = IOC3_KBC_STATUS_AUX_WRITE_PENDING
- }
- };
- const struct iockbc_reginfo iockbc_ioc_inverted[PCKBC_NSLOTS] = {
- [PCKBC_KBD_SLOT] = {
- .rx = IOC3_KBC_AUX_RX,
- .tx = IOC3_KBC_AUX_TX,
- .cs = IOC3_KBC_CTRL_STATUS,
- .busy = IOC3_KBC_STATUS_AUX_WRITE_PENDING
- },
- [PCKBC_AUX_SLOT] = {
- .rx = IOC3_KBC_KBD_RX,
- .tx = IOC3_KBC_KBD_TX,
- .cs = IOC3_KBC_CTRL_STATUS,
- .busy = IOC3_KBC_STATUS_KBD_WRITE_PENDING
- }
- };
- void
- iockbc_ioc_attach(struct device *parent, struct device *self, void *aux)
- {
- struct iockbc_softc *isc = (void*)self;
- struct ioc_attach_args *iaa = aux;
- /* Setup bus space mapping. */
- isc->iot = iaa->iaa_memt;
- isc->ioh = iaa->iaa_memh;
- /* Establish interrupt handler. */
- if (ioc_intr_establish(parent, iaa->iaa_dev, IPL_TTY, iockbcintr,
- (void *)isc, self->dv_xname))
- printf("\n");
- else
- printf(": unable to establish interrupt\n");
- iockbc_attach_common(isc, iaa->iaa_base, iockbc_is_ioc_console(iaa),
- iockbc_ioc, iockbc_ioc_inverted);
- }
- #endif
- #if NIOCKBC_IOF > 0
- /*
- * Register assignments
- */
- const struct iockbc_reginfo iockbc_iof[PCKBC_NSLOTS] = {
- [PCKBC_KBD_SLOT] = {
- .rx = IOC4_KBC_KBD_RX,
- .tx = IOC4_KBC_KBD_TX,
- .cs = IOC4_KBC_CTRL_STATUS,
- .busy = IOC3_KBC_STATUS_KBD_WRITE_PENDING
- },
- [PCKBC_AUX_SLOT] = {
- .rx = IOC4_KBC_AUX_RX,
- .tx = IOC4_KBC_AUX_TX,
- .cs = IOC4_KBC_CTRL_STATUS,
- .busy = IOC3_KBC_STATUS_AUX_WRITE_PENDING
- }
- };
- const struct iockbc_reginfo iockbc_iof_inverted[PCKBC_NSLOTS] = {
- [PCKBC_KBD_SLOT] = {
- .rx = IOC4_KBC_AUX_RX,
- .tx = IOC4_KBC_AUX_TX,
- .cs = IOC4_KBC_CTRL_STATUS,
- .busy = IOC3_KBC_STATUS_AUX_WRITE_PENDING
- },
- [PCKBC_AUX_SLOT] = {
- .rx = IOC4_KBC_KBD_RX,
- .tx = IOC4_KBC_KBD_TX,
- .cs = IOC4_KBC_CTRL_STATUS,
- .busy = IOC3_KBC_STATUS_KBD_WRITE_PENDING
- }
- };
- void
- iockbc_iof_attach(struct device *parent, struct device *self, void *aux)
- {
- struct iockbc_softc *isc = (void*)self;
- struct iof_attach_args *iaa = aux;
- /* Setup bus space mapping. */
- isc->iot = iaa->iaa_memt;
- isc->ioh = iaa->iaa_memh;
- /* Establish interrupt handler. */
- if (iof_intr_establish(parent, iaa->iaa_dev, IPL_TTY, iockbcintr,
- (void *)isc, self->dv_xname))
- printf("\n");
- else
- printf(": unable to establish interrupt\n");
- iockbc_attach_common(isc, iaa->iaa_base, iockbc_is_iof_console(iaa),
- iockbc_iof, iockbc_iof_inverted);
- }
- #endif
- void
- iockbc_init_slotdata(struct pckbc_slotdata *q,
- const struct iockbc_reginfo *reginfo)
- {
- 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;
- q->rx_index = -1;
- q->reginfo = reginfo;
- }
- int
- iockbcprint(void *aux, const char *pnp)
- {
- struct pckbc_attach_args *pa = aux;
- if (!pnp)
- printf(" (%s slot)", iockbc_slot_names[pa->pa_slot]);
- return (QUIET);
- }
- int
- iockbc_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, pa));
- }
- /*
- * Figure out what kind of device is connected to the given slot, if any.
- */
- enum iockbc_slottype
- iockbc_probe_slot(struct pckbc_internal *t, pckbc_slot_t slot)
- {
- int rc, i, tries;
- /* reset device */
- pckbc_flush(t, slot);
- for (tries = 0; tries < 5; tries++) {
- rc = iockbc_poll_write(t, slot, KBC_RESET);
- if (rc < 0) {
- DPRINTF(("%s: slot %d write failed\n", __func__, slot));
- return EMPTY;
- }
- for (i = 10; i != 0; i--) {
- rc = iockbc_poll_read(t, slot);
- if (rc >= 0)
- break;
- }
- if (rc < 0) {
- DPRINTF(("%s: slot %d no answer to reset\n",
- __func__, slot));
- return EMPTY;
- }
- if (rc == KBC_DEVCMD_ACK)
- break;
- if (rc == KBC_DEVCMD_RESEND)
- continue;
- DPRINTF(("%s: slot %d bogus reset ack %02x\n",
- __func__, slot, rc));
- return EMPTY;
- }
- /* get answer byte */
- for (i = 10; i != 0; i--) {
- rc = iockbc_poll_read(t, slot);
- if (rc >= 0)
- break;
- }
- if (rc < 0) {
- DPRINTF(("%s: slot %d no answer to reset after ack\n",
- __func__, slot));
- return EMPTY;
- }
- if (rc != KBR_RSTDONE) {
- DPRINTF(("%s: slot %d bogus reset answer %02x\n",
- __func__, slot, rc));
- return EMPTY;
- }
- /* mice send an extra byte */
- (void)iockbc_poll_read(t, slot);
- /* ask for device id */
- for (tries = 0; tries < 5; tries++) {
- rc = iockbc_poll_write(t, slot, KBC_READID);
- if (rc == -1) {
- DPRINTF(("%s: slot %d write failed\n", __func__, slot));
- return EMPTY;
- }
- for (i = 10; i != 0; i--) {
- rc = iockbc_poll_read(t, slot);
- if (rc >= 0)
- break;
- }
- if (rc < 0) {
- DPRINTF(("%s: slot %d no answer to command\n",
- __func__, slot));
- return EMPTY;
- }
- if (rc == KBC_DEVCMD_ACK)
- break;
- if (rc == KBC_DEVCMD_RESEND)
- continue;
- DPRINTF(("%s: slot %d bogus command ack %02x\n",
- __func__, slot, rc));
- return EMPTY;
- }
- /* get first answer byte */
- for (i = 10; i != 0; i--) {
- rc = iockbc_poll_read(t, slot);
- if (rc >= 0)
- break;
- }
- if (rc < 0) {
- DPRINTF(("%s: slot %d no answer to command after ack\n",
- __func__, slot));
- return EMPTY;
- }
- switch (rc) {
- case KCID_KBD1: /* keyboard */
- /* get second answer byte */
- rc = iockbc_poll_read(t, slot);
- if (rc < 0) {
- DPRINTF(("%s: slot %d truncated keyboard answer\n",
- __func__, slot));
- return EMPTY;
- }
- if (rc != KCID_KBD2) {
- DPRINTF(("%s: slot %d unexpected keyboard answer"
- " 0x%02x 0x%02x\n", __func__, slot, KCID_KBD1, rc));
- /* return EMPTY; */
- }
- return KBD;
- case KCID_MOUSE: /* mouse */
- return MOUSE;
- default:
- DPRINTF(("%s: slot %d unknown device answer 0x%02x\n",
- __func__, slot, rc));
- return EMPTY;
- }
- }
- int
- iockbc_attach_slot(struct iockbc_softc *isc, pckbc_slot_t slot)
- {
- struct pckbc_softc *sc = &isc->sc_pckbc;
- struct pckbc_internal *t = sc->id;
- struct pckbc_attach_args pa;
- int found;
- iockbc_init_slotdata(t->t_slotdata[slot], &isc->reginfo[slot]);
- pa.pa_tag = t;
- pa.pa_slot = slot;
- found = (config_found_sm((struct device *)sc, &pa,
- iockbcprint, iockbc_submatch) != NULL);
- return (found);
- }
- void
- iockbc_attach_common(struct iockbc_softc *isc, bus_addr_t addr, int console,
- const struct iockbc_reginfo *reginfo,
- const struct iockbc_reginfo *reginfo_inverted)
- {
- struct pckbc_softc *sc = &isc->sc_pckbc;
- struct pckbc_internal *t;
- bus_addr_t cs;
- uint32_t csr;
- pckbc_slot_t slot;
- if (console) {
- iockbc_consdata.t_sc = sc;
- sc->id = t = &iockbc_consdata;
- isc->console = 1;
- if (®info[PCKBC_KBD_SLOT] == iockbc_cons_slotdata.reginfo)
- isc->reginfo = reginfo;
- else
- isc->reginfo = reginfo_inverted;
- } else {
- /*
- * Setup up controller: do not force pull clock and data lines
- * low, clamp clocks after one byte received.
- */
- cs = reginfo[PCKBC_KBD_SLOT].cs;
- csr = bus_space_read_4(isc->iot, isc->ioh, cs);
- csr &= ~(IOC3_KBC_CTRL_KBD_PULL_DATA_LOW |
- IOC3_KBC_CTRL_KBD_PULL_CLOCK_LOW |
- IOC3_KBC_CTRL_AUX_PULL_DATA_LOW |
- IOC3_KBC_CTRL_AUX_PULL_CLOCK_LOW |
- IOC3_KBC_CTRL_KBD_CLAMP_3 | IOC3_KBC_CTRL_AUX_CLAMP_3);
- csr |= IOC3_KBC_CTRL_KBD_CLAMP_1 | IOC3_KBC_CTRL_AUX_CLAMP_1;
- bus_space_write_4(isc->iot, isc->ioh, cs, csr);
- /* Setup pckbc_internal structure. */
- t = malloc(sizeof(struct pckbc_internal), M_DEVBUF,
- M_WAITOK | M_ZERO);
- t->t_iot = isc->iot;
- t->t_ioh_d = isc->ioh;
- t->t_ioh_c = isc->ioh;
- t->t_addr = addr;
- t->t_sc = sc;
- sc->id = t;
- timeout_set(&t->t_cleanup, iockbc_cleanup, t);
- timeout_set(&t->t_poll, iockbc_poll, t);
- isc->reginfo = reginfo;
- }
- for (slot = 0; slot < PCKBC_NSLOTS; slot++) {
- if (t->t_slotdata[slot] == NULL) {
- t->t_slotdata[slot] =
- malloc(sizeof(struct pckbc_slotdata),
- M_DEVBUF, M_WAITOK);
- }
- }
- if (!console) {
- enum iockbc_slottype slottype;
- int mouse_on_main = 0;
- /*
- * Probe for a keyboard. If none is found at the regular
- * keyboard port, but one is found at the mouse port, then
- * it is likely that this particular system has both ports
- * inverted (or incorrect labels on the chassis), unless
- * this is a human error. In any case, try to get the
- * keyboard to attach to the `keyboard' port and the
- * pointing device to the `mouse' port.
- */
- for (slot = 0; slot < PCKBC_NSLOTS; slot++) {
- iockbc_init_slotdata(t->t_slotdata[slot],
- &isc->reginfo[slot]);
- slottype = iockbc_probe_slot(t, slot);
- if (slottype == KBD)
- break;
- if (slottype == MOUSE)
- mouse_on_main = slot == PCKBC_KBD_SLOT;
- }
- if (slot == PCKBC_NSLOTS) {
- /*
- * We could not identify a keyboard. Let's assume
- * none is connected; if a mouse has been found on
- * the keyboard port and none on the aux port, the
- * ports are likely to be inverted.
- */
- if (mouse_on_main)
- slot = PCKBC_AUX_SLOT;
- else
- slot = PCKBC_KBD_SLOT;
- }
- if (slot == PCKBC_AUX_SLOT) {
- /*
- * Either human error or inverted wiring; use
- * the inverted port settings.
- * iockbc_attach_slot() below will call
- * iockbc_init_slotdata() again.
- */
- isc->reginfo = reginfo_inverted;
- }
- }
- /*
- * Attach "slots".
- */
- iockbc_attach_slot(isc, PCKBC_KBD_SLOT);
- iockbc_attach_slot(isc, PCKBC_AUX_SLOT);
- }
- void
- iockbc_poll(void *self)
- {
- struct pckbc_internal *t = self;
- int s;
- s = spltty();
- (void)iockbcintr_internal(t, t->t_sc);
- timeout_add_sec(&t->t_poll, 1);
- splx(s);
- }
- int
- iockbcintr(void *vsc)
- {
- struct iockbc_softc *isc = (struct iockbc_softc *)vsc;
- struct pckbc_softc *sc = &isc->sc_pckbc;
- struct pckbc_internal *t = sc->id;
- return iockbcintr_internal(t, sc);
- }
- int
- iockbcintr_internal(struct pckbc_internal *t, struct pckbc_softc *sc)
- {
- pckbc_slot_t slot;
- struct pckbc_slotdata *q;
- int served = 0;
- uint32_t data;
- uint32_t val;
- /* Reschedule timeout further into the idle times. */
- if (timeout_pending(&t->t_poll))
- timeout_add_sec(&t->t_poll, 1);
- /*
- * Need to check both "slots" since interrupt could be from
- * either controller.
- */
- for (slot = 0; slot < PCKBC_NSLOTS; slot++) {
- q = t->t_slotdata[slot];
- for (;;) {
- if (!q) {
- DPRINTF("iockbcintr: no slot%d data!\n", slot);
- break;
- }
- if (q->polling) {
- served = 1;
- break; /* pckbc_poll_data() will get it */
- }
- val = bus_space_read_4(t->t_iot, t->t_ioh_d,
- q->reginfo->rx);
- if ((val & IOC3_KBC_DATA_VALID) == 0)
- break;
- served = 1;
- /* Process received data. */
- if (val & IOC3_KBC_DATA_0_VALID) {
- data = (val & IOC3_KBC_DATA_0_MASK) >>
- IOC3_KBC_DATA_0_SHIFT;
- iockbc_process_input(sc, t, slot, data);
- }
- if (val & IOC3_KBC_DATA_1_VALID) {
- data = (val & IOC3_KBC_DATA_1_MASK) >>
- IOC3_KBC_DATA_1_SHIFT;
- iockbc_process_input(sc, t, slot, data);
- }
- if (val & IOC3_KBC_DATA_2_VALID) {
- data = (val & IOC3_KBC_DATA_2_MASK) >>
- IOC3_KBC_DATA_2_SHIFT;
- iockbc_process_input(sc, t, slot, data);
- }
- }
- }
-
- return (served);
- }
- void
- iockbc_process_input(struct pckbc_softc *sc, struct pckbc_internal *t,
- int slot, uint data)
- {
- struct pckbc_slotdata *q;
- q = t->t_slotdata[slot];
- if (CMD_IN_QUEUE(q) && iockbc_cmdresponse(t, slot, data))
- return;
- if (sc->inputhandler[slot])
- (*sc->inputhandler[slot])(sc->inputarg[slot], data);
- else
- DPRINTF("iockbcintr: slot %d lost %d\n", slot, data);
- }
- int
- iockbc_poll_write(struct pckbc_internal *t, pckbc_slot_t slot, int val)
- {
- struct pckbc_slotdata *q = t->t_slotdata[slot];
- bus_space_tag_t iot = t->t_iot;
- bus_space_handle_t ioh = t->t_ioh_d;
- u_int64_t stat;
- int timeout = 10000;
- /* Attempt to write a value to the controller. */
- while (timeout--) {
- stat = bus_space_read_4(iot, ioh, q->reginfo->cs);
- if ((stat & q->reginfo->busy) == 0) {
- bus_space_write_4(iot, ioh, q->reginfo->tx, val & 0xff);
- return 0;
- }
- delay(50);
- }
- DPRINTF("iockbc_poll_write: timeout, sts %08x\n", stat);
- return -1;
- }
- int
- iockbc_poll_read(struct pckbc_internal *t, pckbc_slot_t slot)
- {
- struct pckbc_slotdata *q = t->t_slotdata[slot];
- int timeout = 10000;
- u_int32_t val;
- /* See if we already have bytes queued. */
- if (q->rx_index >= 0)
- return q->rx_queue[q->rx_index--];
- /* Poll input from controller. */
- while (timeout--) {
- val = bus_space_read_4(t->t_iot, t->t_ioh_d, q->reginfo->rx);
- if (val & IOC3_KBC_DATA_VALID)
- break;
- delay(50);
- }
- if ((val & IOC3_KBC_DATA_VALID) == 0) {
- DPRINTF("iockbc_poll_read: timeout, wx %08x\n", val);
- return -1;
- }
- /* Process received data. */
- if (val & IOC3_KBC_DATA_2_VALID)
- q->rx_queue[++q->rx_index] =
- (val & IOC3_KBC_DATA_2_MASK) >> IOC3_KBC_DATA_2_SHIFT;
- if (val & IOC3_KBC_DATA_1_VALID)
- q->rx_queue[++q->rx_index] =
- (val & IOC3_KBC_DATA_1_MASK) >> IOC3_KBC_DATA_1_SHIFT;
- if (val & IOC3_KBC_DATA_0_VALID)
- q->rx_queue[++q->rx_index] =
- (val & IOC3_KBC_DATA_0_MASK) >> IOC3_KBC_DATA_0_SHIFT;
- if (q->rx_index >= 0)
- return q->rx_queue[q->rx_index--];
- else
- return -1;
- }
- /*
- * Pass command to device, poll for ACK and data.
- * to be called at spltty()
- */
- static void
- iockbc_poll_cmd(struct pckbc_internal *t, pckbc_slot_t slot,
- struct pckbc_devcmd *cmd)
- {
- int i, c = 0;
- while (cmd->cmdidx < cmd->cmdlen) {
- if (iockbc_poll_write(t, slot, cmd->cmd[cmd->cmdidx]) == -1) {
- DPRINTF("iockbc_poll_cmd: send error\n");
- cmd->status = EIO;
- return;
- }
- for (i = 10; i; i--) { /* 1s ??? */
- c = iockbc_poll_read(t, slot);
- if (c != -1)
- break;
- }
- if (c == KBC_DEVCMD_ACK) {
- cmd->cmdidx++;
- continue;
- }
- if (c == KBC_DEVCMD_RESEND) {
- DPRINTF("iockbc_cmd: RESEND\n");
- if (cmd->retries++ < 5)
- continue;
- else {
- DPRINTF("iockbc: cmd failed\n");
- cmd->status = EIO;
- return;
- }
- }
- if (c == -1) {
- DPRINTF("iockbc_cmd: timeout\n");
- cmd->status = EIO;
- return;
- }
- DPRINTF("iockbc_cmd: lost 0x%x\n", c);
- }
- while (cmd->responseidx < cmd->responselen) {
- if (cmd->flags & KBC_CMDFLAG_SLOW)
- i = 100; /* 10s ??? */
- else
- i = 10; /* 1s ??? */
- while (i--) {
- c = iockbc_poll_read(t, slot);
- if (c != -1)
- break;
- }
- if (c == -1) {
- DPRINTF("iockbc_poll_cmd: no data\n");
- cmd->status = ETIMEDOUT;
- return;
- } else
- cmd->response[cmd->responseidx++] = c;
- }
- }
- /*
- * Clean up a command queue, throw away everything.
- */
- void
- iockbc_cleanqueue(struct pckbc_slotdata *q)
- {
- struct pckbc_devcmd *cmd;
- #ifdef IOCKBC_DEBUG
- int i;
- #endif
- while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
- TAILQ_REMOVE(&q->cmdqueue, cmd, next);
- #ifdef IOCKBC_DEBUG
- printf("iockbc_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
- iockbc_cleanup(void *self)
- {
- struct pckbc_internal *t = self;
- int s;
- printf("iockbc: command timeout\n");
- s = spltty();
- if (t->t_slotdata[PCKBC_KBD_SLOT])
- iockbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
- if (t->t_slotdata[PCKBC_AUX_SLOT])
- iockbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
- while (iockbc_poll_read(t, PCKBC_KBD_SLOT)
- != -1) ;
- while (iockbc_poll_read(t, PCKBC_AUX_SLOT)
- != -1) ;
- /* Reset KBC? */
- splx(s);
- }
- /*
- * Pass command to device during normal operation.
- * to be called at spltty()
- */
- void
- iockbc_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 {
- iockbc_poll_cmd(t, slot, cmd);
- if (cmd->status)
- printf("iockbc_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 (iockbc_poll_write(t, slot, cmd->cmd[cmd->cmdidx])) {
- printf("iockbc_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
- iockbc_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("iockbc_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 {
- DPRINTF("iockbc: cmd failed\n");
- 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:
- iockbc_start(t, slot);
- return (1);
- }
- /*
- * Interfaces to act like pckbc(4).
- */
- int
- pckbc_xt_translation(pckbc_tag_t self)
- {
- /* Translation isn't supported... */
- return (-1);
- }
- /* 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();
- iockbc_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) iockbc_poll_read(t, slot);
- }
- /*
- * 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.
- */
- iockbc_cleanqueue(q);
- }
- isactive = CMD_IN_QUEUE(q);
- TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
- if (!isactive)
- iockbc_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);
- iockbc_cleanup(t);
- } else
- res = nc->status;
- } else
- timeout_add_sec(&t->t_cleanup, 1);
- 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 = iockbc_poll_read(t, slot);
- if (c != -1 && q && CMD_IN_QUEUE(q)) {
- /* We jumped into a running command - try to deliver the
- response. */
- if (iockbc_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;
- struct iockbc_softc *isc = (struct iockbc_softc *)sc;
- if (slot >= PCKBC_NSLOTS)
- panic("iockbc_set_inputhandler: bad slot %d", slot);
- sc->inputhandler[slot] = func;
- sc->inputarg[slot] = arg;
- sc->subname[slot] = name;
- if ((isc == NULL || isc->console) && slot == PCKBC_KBD_SLOT)
- timeout_add_sec(&t->t_poll, 1);
- }
- void
- pckbc_slot_enable(pckbc_tag_t self, pckbc_slot_t slot, int on)
- {
- struct pckbc_internal *t = (struct pckbc_internal *)self;
- if (slot == PCKBC_KBD_SLOT) {
- if (on)
- timeout_add_sec(&t->t_poll, 1);
- else
- timeout_del(&t->t_poll);
- }
- }
- 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();
- iockbcintr(t->t_sc);
- splx(s);
- }
- }
- }
- /*
- * Console support.
- */
- static int iockbc_console;
- int
- iockbc_cnattach()
- {
- bus_space_tag_t iot = &sys_config.console_io;
- bus_space_handle_t ioh = (bus_space_handle_t)iot->bus_base;
- struct pckbc_internal *t = &iockbc_consdata;
- const struct iockbc_reginfo *reginfo = NULL, *reginfo_inverted;
- enum iockbc_slottype slottype;
- pckbc_slot_t slot;
- uint32_t csr;
- int is_ioc;
- int rc;
- is_ioc = console_input.specific ==
- PCI_ID_CODE(PCI_VENDOR_SGI, PCI_PRODUCT_SGI_IOC3);
- if (is_ioc) {
- #if NIOCKBC_IOC > 0
- reginfo = iockbc_ioc;
- reginfo_inverted = iockbc_ioc_inverted;
- #endif
- } else {
- #if NIOCKBC_IOF > 0
- reginfo = iockbc_iof;
- reginfo_inverted = iockbc_iof_inverted;
- #endif
- }
- if (reginfo == NULL)
- return ENXIO;
- /*
- * Setup up controller: do not force pull clock and data lines
- * low, clamp clocks after one byte received.
- */
- csr = bus_space_read_4(iot, ioh, reginfo->cs);
- csr &= ~(IOC3_KBC_CTRL_KBD_PULL_DATA_LOW |
- IOC3_KBC_CTRL_KBD_PULL_CLOCK_LOW |
- IOC3_KBC_CTRL_AUX_PULL_DATA_LOW |
- IOC3_KBC_CTRL_AUX_PULL_CLOCK_LOW |
- IOC3_KBC_CTRL_KBD_CLAMP_3 | IOC3_KBC_CTRL_AUX_CLAMP_3);
- csr |= IOC3_KBC_CTRL_KBD_CLAMP_1 | IOC3_KBC_CTRL_AUX_CLAMP_1;
- bus_space_write_4(iot, ioh, reginfo->cs, csr);
- /* Setup pckbc_internal structure. */
- t->t_iot = iot;
- t->t_ioh_d = (bus_space_handle_t)iot->bus_base;
- t->t_addr = 0; /* unused */
- timeout_set(&t->t_cleanup, iockbc_cleanup, t);
- timeout_set(&t->t_poll, iockbc_poll, t);
- /*
- * Probe for a keyboard. There must be one connected, for the PROM
- * would not have advertized glass console if none had been
- * detected.
- */
- for (slot = 0; slot < PCKBC_NSLOTS; slot++) {
- iockbc_init_slotdata(&iockbc_cons_slotdata, ®info[slot]);
- t->t_slotdata[slot] = &iockbc_cons_slotdata;
- slottype = iockbc_probe_slot(t, slot);
- t->t_slotdata[slot] = NULL;
- if (slottype == KBD)
- break;
- }
- if (slot == PCKBC_NSLOTS) {
- /*
- * We could not identify a keyboard, but the PROM did;
- * let's assume it's a fluke and assume it exists and
- * is connected to the first connector.
- */
- slot = PCKBC_KBD_SLOT;
- /*
- * For some reason keyboard and mouse ports are inverted on
- * Fuel. They also are inverted on some IO9 boards, but
- * we can't tell both IO9 flavour apart, yet.
- */
- if (is_ioc && sys_config.system_type == SGI_IP35)
- slot = PCKBC_AUX_SLOT;
- }
- if (slot == PCKBC_AUX_SLOT) {
- /*
- * Either human error when plugging the keyboard, or the
- * physical connectors on the chassis are inverted.
- * Compensate by switching in software (pckbd relies upon
- * being at PCKBC_KBD_SLOT).
- */
- reginfo = reginfo_inverted;
- }
- iockbc_init_slotdata(&iockbc_cons_slotdata, ®info[PCKBC_KBD_SLOT]);
- t->t_slotdata[PCKBC_KBD_SLOT] = &iockbc_cons_slotdata;
- rc = pckbd_cnattach(t);
- if (rc == 0)
- iockbc_console = 1;
- return rc;
- }
- #if NIOCKBC_IOC > 0
- int
- iockbc_is_ioc_console(struct ioc_attach_args *iaa)
- {
- if (iockbc_console == 0)
- return 0;
- return location_match(&iaa->iaa_location, &console_input);
- }
- #endif
- #if NIOCKBC_IOF > 0
- int
- iockbc_is_iof_console(struct iof_attach_args *iaa)
- {
- if (iockbc_console == 0)
- return 0;
- return location_match(&iaa->iaa_location, &console_input);
- }
- #endif
|