Annotation of sys/arch/hppa/gsc/gsckbc.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: gsckbc.c,v 1.9 2006/12/17 21:27:20 miod Exp $ */
! 2: /*
! 3: * Copyright (c) 2003, Miodrag Vallat.
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: * 1. Redistributions of source code must retain the above copyright
! 10: * notice, this list of conditions and the following disclaimer.
! 11: * 2. Redistributions in binary form must reproduce the above copyright
! 12: * notice, this list of conditions and the following disclaimer in the
! 13: * documentation and/or other materials provided with the distribution.
! 14: *
! 15: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 16: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 17: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 18: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 19: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 20: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF MIND,
! 21: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 22: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 23: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 24: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 25: */
! 26:
! 27: /*
! 28: * Derived from /sys/dev/ic/pckbd.c under the following terms:
! 29: * OpenBSD: pckbc.c,v 1.5 2002/06/09 00:58:03 nordin Exp
! 30: * NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp
! 31: */
! 32: /*
! 33: * Copyright (c) 1998
! 34: * Matthias Drochner. All rights reserved.
! 35: *
! 36: * Redistribution and use in source and binary forms, with or without
! 37: * modification, are permitted provided that the following conditions
! 38: * are met:
! 39: * 1. Redistributions of source code must retain the above copyright
! 40: * notice, this list of conditions and the following disclaimer.
! 41: * 2. Redistributions in binary form must reproduce the above copyright
! 42: * notice, this list of conditions and the following disclaimer in the
! 43: * documentation and/or other materials provided with the distribution.
! 44: * 3. All advertising materials mentioning features or use of this software
! 45: * must display the following acknowledgement:
! 46: * This product includes software developed for the NetBSD Project
! 47: * by Matthias Drochner.
! 48: * 4. The name of the author may not be used to endorse or promote products
! 49: * derived from this software without specific prior written permission.
! 50: *
! 51: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 52: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 53: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 54: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 55: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 56: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 57: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 58: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 59: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 60: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 61: */
! 62:
! 63: /*
! 64: * Driver for the PS/2-like keyboard and mouse ports found on 712 and 715
! 65: * models, among others.
! 66: *
! 67: * Contrary to the ``pckbc'' port set found on other arches, the
! 68: * keyboard and mouse port are two separate entities on the snakes, and
! 69: * they are driven by a custom chip not 8042-compatible.
! 70: */
! 71:
! 72: #include <sys/param.h>
! 73: #include <sys/systm.h>
! 74: #include <sys/device.h>
! 75: #include <sys/kernel.h>
! 76: #include <sys/malloc.h>
! 77: #include <sys/proc.h>
! 78:
! 79: #include <machine/autoconf.h>
! 80: #include <machine/bus.h>
! 81: #include <machine/intr.h>
! 82: #include <machine/iomod.h>
! 83:
! 84: #include <hppa/dev/cpudevs.h>
! 85: #include <hppa/gsc/gscbusvar.h>
! 86:
! 87: #include <hppa/gsc/gsckbcreg.h>
! 88: #include <dev/ic/pckbcvar.h>
! 89:
! 90: #include <dev/pckbc/pckbdreg.h> /* constants for probe magic */
! 91: #include <hppa/gsc/gsckbdvar.h>
! 92:
! 93: int gsckbc_match(struct device *, void *, void *);
! 94: void gsckbc_attach(struct device *, struct device *, void *);
! 95:
! 96: struct gsckbc_softc {
! 97: struct pckbc_softc sc_pckbc;
! 98:
! 99: int sc_irq;
! 100: void *sc_ih;
! 101: int sc_type;
! 102: };
! 103:
! 104: struct cfattach gsckbc_ca = {
! 105: sizeof(struct gsckbc_softc), gsckbc_match, gsckbc_attach
! 106: };
! 107:
! 108: struct cfdriver gsckbc_cd = {
! 109: NULL, "gsckbc", DV_DULL
! 110: };
! 111:
! 112: void gsckbc_intr_establish(struct pckbc_softc *, pckbc_slot_t);
! 113:
! 114: #include "gsckbd.h"
! 115: #if (NGSCKBD > 0)
! 116: #include <dev/pckbc/pckbdvar.h>
! 117: #endif
! 118:
! 119: /* descriptor for one device command */
! 120: struct pckbc_devcmd {
! 121: TAILQ_ENTRY(pckbc_devcmd) next;
! 122: int flags;
! 123: #define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
! 124: #define KBC_CMDFLAG_SLOW 2
! 125: u_char cmd[4];
! 126: int cmdlen, cmdidx, retries;
! 127: u_char response[4];
! 128: int status, responselen, responseidx;
! 129: };
! 130:
! 131: /* data per slave device */
! 132: struct pckbc_slotdata {
! 133: int polling; /* don't read data port in interrupt handler */
! 134: TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */
! 135: TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */
! 136: #define NCMD 5
! 137: struct pckbc_devcmd cmds[NCMD];
! 138: };
! 139:
! 140: #define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
! 141: /* Force polling mode behaviour for boot -a XXX */
! 142: #define IS_POLLING(q) ((q)->polling || cold)
! 143:
! 144: void pckbc_init_slotdata(struct pckbc_slotdata *);
! 145: int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t);
! 146: int pckbc_submatch(struct device *, void *, void *);
! 147: int pckbcprint(void *, const char *);
! 148:
! 149: int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t);
! 150: int pckbc_send_devcmd(struct pckbc_internal *, pckbc_slot_t,
! 151: u_char);
! 152: void pckbc_poll_cmd1(struct pckbc_internal *, pckbc_slot_t,
! 153: struct pckbc_devcmd *);
! 154:
! 155: void pckbc_cleanqueue(struct pckbc_slotdata *);
! 156: void pckbc_cleanup(void *);
! 157: int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
! 158: void pckbc_start(struct pckbc_internal *, pckbc_slot_t);
! 159: int gsckbcintr(void *);
! 160:
! 161: const char *pckbc_slot_names[] = { "kbd", "mouse" };
! 162:
! 163: #define KBC_DEVCMD_ACK 0xfa
! 164: #define KBC_DEVCMD_RESEND 0xfe
! 165:
! 166: #define KBD_DELAY DELAY(8)
! 167:
! 168: int
! 169: gsckbc_match(struct device *parent, void *match, void *aux)
! 170: {
! 171: struct gsc_attach_args *ga = aux;
! 172: bus_space_handle_t ioh;
! 173: u_int8_t rv;
! 174:
! 175: if (ga->ga_type.iodc_type != HPPA_TYPE_FIO ||
! 176: ga->ga_type.iodc_sv_model != HPPA_FIO_GPCIO)
! 177: return (0);
! 178:
! 179: /* Map the i/o space. */
! 180: if (bus_space_map(ga->ga_ca.ca_iot, ga->ga_ca.ca_hpa,
! 181: KBMAPSIZE, 0, &ioh))
! 182: return 0;
! 183:
! 184: rv = bus_space_read_1(ga->ga_ca.ca_iot, ioh, KBIDP);
! 185: bus_space_unmap(ga->ga_ca.ca_iot, ioh, KBMAPSIZE);
! 186:
! 187: if (rv == PCKBC_KBD_SLOT || rv == PCKBC_AUX_SLOT)
! 188: return (1); /* keyboard or mouse port */
! 189:
! 190: return (0);
! 191: }
! 192:
! 193: /*
! 194: * Attachment helper functions
! 195: */
! 196:
! 197: /* state machine values */
! 198: #define PROBE_SUCCESS 0
! 199: #define PROBE_TIMEOUT 1
! 200: #define PROBE_RETRANS 2
! 201: #define PROBE_NOACK 3
! 202:
! 203: int probe_readtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply);
! 204: int probe_readretry(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply);
! 205: int probe_sendtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte);
! 206: int probe_sendack(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte);
! 207: int probe_ident(bus_space_tag_t iot, bus_space_handle_t ioh);
! 208:
! 209: #define PROBE_TRIES 1000
! 210:
! 211: int
! 212: probe_readtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply)
! 213: {
! 214: int numtries = PROBE_TRIES;
! 215:
! 216: while (numtries--) {
! 217: if (bus_space_read_1(iot, ioh, KBSTATP) &
! 218: (KBS_DIB | KBS_TERR | KBS_PERR))
! 219: break;
! 220: DELAY(500);
! 221: }
! 222:
! 223: if (numtries <= 0)
! 224: return (PROBE_TIMEOUT);
! 225:
! 226: if (bus_space_read_1(iot, ioh, KBSTATP) & (KBS_PERR | KBS_TERR)) {
! 227: if (!(bus_space_read_1(iot, ioh, KBSTATP) & KBS_DIB)) {
! 228: bus_space_write_1(iot, ioh, KBRESETP, 0xff);
! 229: bus_space_write_1(iot, ioh, KBRESETP, 0x00);
! 230: bus_space_write_1(iot, ioh, KBCMDP,
! 231: bus_space_read_1(iot, ioh, KBCMDP) | KBCP_ENABLE);
! 232: return (PROBE_TIMEOUT);
! 233: }
! 234:
! 235: *reply = bus_space_read_1(iot, ioh, KBDATAP);
! 236: if (!(bus_space_read_1(iot, ioh, KBSTATP) & KBS_DIB)) {
! 237: bus_space_write_1(iot, ioh, KBRESETP, 0xff);
! 238: bus_space_write_1(iot, ioh, KBRESETP, 0x00);
! 239: bus_space_write_1(iot, ioh, KBCMDP,
! 240: bus_space_read_1(iot, ioh, KBCMDP) | KBCP_ENABLE);
! 241: if (probe_sendtmo(iot, ioh, KBR_RESEND))
! 242: return (PROBE_TIMEOUT);
! 243: else
! 244: return (PROBE_RETRANS);
! 245: } else
! 246: return (PROBE_SUCCESS);
! 247: } else {
! 248: *reply = bus_space_read_1(iot, ioh, KBDATAP);
! 249: return (PROBE_SUCCESS);
! 250: }
! 251: }
! 252:
! 253: int
! 254: probe_readretry(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply)
! 255: {
! 256: int read_status;
! 257: int retrans = KB_MAX_RETRANS;
! 258:
! 259: do {
! 260: read_status = probe_readtmo(iot, ioh, reply);
! 261: } while ((read_status == PROBE_RETRANS) && retrans--);
! 262:
! 263: return (read_status);
! 264: }
! 265:
! 266: int
! 267: probe_sendtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte)
! 268: {
! 269: int numtries = PROBE_TRIES;
! 270:
! 271: while (numtries--) {
! 272: if ((bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) == 0)
! 273: break;
! 274: DELAY(500);
! 275: }
! 276:
! 277: if (numtries <= 0)
! 278: return (1);
! 279:
! 280: bus_space_write_1(iot, ioh, KBDATAP, cmdbyte);
! 281: bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
! 282: return (0);
! 283: }
! 284:
! 285: int
! 286: probe_sendack(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte)
! 287: {
! 288: int retranscount;
! 289: int reply;
! 290:
! 291: for (retranscount = 0; retranscount < KB_MAX_RETRANS; retranscount++) {
! 292: if (probe_sendtmo(iot, ioh, cmdbyte))
! 293: return (PROBE_TIMEOUT);
! 294: if (probe_readretry(iot, ioh, &reply))
! 295: return (PROBE_TIMEOUT);
! 296:
! 297: switch (reply) {
! 298: case KBR_ACK:
! 299: return (PROBE_SUCCESS);
! 300: case KBR_RESEND:
! 301: break;
! 302: default:
! 303: return (PROBE_NOACK);
! 304: }
! 305: }
! 306: return (PROBE_TIMEOUT);
! 307:
! 308: }
! 309:
! 310: int
! 311: probe_ident(bus_space_tag_t iot, bus_space_handle_t ioh)
! 312: {
! 313: int status;
! 314:
! 315: bus_space_write_1(iot, ioh, KBRESETP, 0);
! 316: bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
! 317: DELAY(0x20000); /* XXX why 0x? */
! 318: bus_space_write_1(iot, ioh, KBCMDP, 0);
! 319: DELAY(20000);
! 320: bus_space_write_1(iot, ioh, KBRESETP, 0);
! 321: bus_space_write_1(iot, ioh, KBCMDP, KBCP_DIAG);
! 322: DELAY(20000);
! 323:
! 324: status = probe_sendack(iot, ioh, KBC_DISABLE);
! 325: switch (status) {
! 326: case PROBE_TIMEOUT:
! 327: if (bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) {
! 328: bus_space_write_1(iot, ioh, KBRESETP, 0);
! 329: bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
! 330: }
! 331: return (-1);
! 332: case PROBE_NOACK:
! 333: return (-1);
! 334: }
! 335:
! 336: if (probe_sendack(iot, ioh, KBC_ID) != PROBE_SUCCESS)
! 337: return (-1);
! 338:
! 339: if (probe_readretry(iot, ioh, &status))
! 340: return (-1);
! 341:
! 342: switch (status) {
! 343: case KBR_MOUSE_ID:
! 344: return PCKBC_AUX_SLOT;
! 345: case KBR_KBD_ID1:
! 346: if (probe_readretry(iot, ioh, &status))
! 347: return (-1);
! 348: if (status == KBR_KBD_ID2) {
! 349: if (probe_sendack(iot, ioh, KBC_ENABLE) ==
! 350: PROBE_TIMEOUT) {
! 351: bus_space_write_1(iot, ioh, KBRESETP, 0);
! 352: bus_space_write_1(iot, ioh, KBCMDP,
! 353: KBCP_ENABLE);
! 354: }
! 355: return PCKBC_KBD_SLOT;
! 356: }
! 357: }
! 358: return (-1);
! 359: }
! 360:
! 361: void
! 362: gsckbc_attach(struct device *parent, struct device *self, void *aux)
! 363: {
! 364: struct gsc_attach_args *ga = aux;
! 365: struct gsckbc_softc *gsc = (void *)self;
! 366: struct pckbc_softc *sc = &gsc->sc_pckbc;
! 367: struct pckbc_internal *t;
! 368: bus_space_tag_t iot;
! 369: bus_space_handle_t ioh;
! 370: int ident;
! 371:
! 372: iot = ga->ga_ca.ca_iot;
! 373: gsc->sc_irq = ga->ga_ca.ca_irq;
! 374:
! 375: if (bus_space_map(iot, ga->ga_ca.ca_hpa, KBMAPSIZE, 0, &ioh))
! 376: panic("gsckbc_attach: couldn't map port");
! 377:
! 378: gsc->sc_type = bus_space_read_1(iot, ioh, KBIDP);
! 379:
! 380: switch (gsc->sc_type) {
! 381: case PCKBC_KBD_SLOT:
! 382: case PCKBC_AUX_SLOT:
! 383: break;
! 384: default:
! 385: printf(": unknown port type %x\n", gsc->sc_type);
! 386: /* play nice and don't really attach. */
! 387: bus_space_unmap(iot, ioh, KBMAPSIZE);
! 388: return;
! 389: }
! 390:
! 391: printf("\n");
! 392:
! 393: sc->intr_establish = gsckbc_intr_establish;
! 394:
! 395: t = malloc(sizeof(struct pckbc_internal), M_DEVBUF, M_WAITOK);
! 396: bzero(t, sizeof(struct pckbc_internal));
! 397: t->t_iot = iot;
! 398: /* XXX it does not make sense to only map two ports here */
! 399: t->t_ioh_d = t->t_ioh_c = ioh;
! 400: t->t_addr = ga->ga_ca.ca_hpa;
! 401: t->t_sc = sc;
! 402: timeout_set(&t->t_cleanup, pckbc_cleanup, t);
! 403: sc->id = t;
! 404:
! 405: /*
! 406: * Reset port and probe device, if plugged
! 407: */
! 408: ident = probe_ident(iot, ioh);
! 409: if (ident != gsc->sc_type) {
! 410: /* don't whine for unplugged ports */
! 411: if (ident != -1)
! 412: printf("%s: expecting device type %d, got %d\n",
! 413: sc->sc_dv.dv_xname, gsc->sc_type, ident);
! 414: } else {
! 415: #if (NGSCKBD > 0)
! 416: if (gsc->sc_type == PCKBC_KBD_SLOT &&
! 417: ga->ga_dp.dp_mod == PAGE0->mem_kbd.pz_dp.dp_mod &&
! 418: bcmp(ga->ga_dp.dp_bc, PAGE0->mem_kbd.pz_dp.dp_bc, 6) == 0)
! 419: gsckbd_cnattach(t, PCKBC_KBD_SLOT);
! 420: #endif
! 421: pckbc_attach_slot(sc, gsc->sc_type);
! 422: }
! 423: }
! 424:
! 425: void
! 426: gsckbc_intr_establish(struct pckbc_softc *sc, pckbc_slot_t slot)
! 427: {
! 428: struct gsckbc_softc *gsc = (void *)sc;
! 429:
! 430: gsc->sc_ih = gsc_intr_establish((struct gsc_softc *)sc->sc_dv.dv_parent,
! 431: gsc->sc_irq, IPL_TTY, gsckbcintr, sc, sc->sc_dv.dv_xname);
! 432: }
! 433:
! 434: /*
! 435: * pckbc-like interfaces
! 436: */
! 437:
! 438: int
! 439: pckbc_wait_output(iot, ioh)
! 440: bus_space_tag_t iot;
! 441: bus_space_handle_t ioh;
! 442: {
! 443: u_int i;
! 444:
! 445: for (i = 100000; i; i--) {
! 446: if ((bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD)) {
! 447: KBD_DELAY;
! 448: } else
! 449: return (1);
! 450: }
! 451: return (0);
! 452: }
! 453:
! 454: int
! 455: pckbc_send_cmd(iot, ioh, val)
! 456: bus_space_tag_t iot;
! 457: bus_space_handle_t ioh;
! 458: u_char val;
! 459: {
! 460: if (!pckbc_wait_output(iot, ioh))
! 461: return (0);
! 462: bus_space_write_1(iot, ioh, KBOUTP, val);
! 463: bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
! 464: return (1);
! 465: }
! 466:
! 467: /* XXX logic */
! 468: int
! 469: pckbc_poll_data1(iot, ioh, ioh_c, slot, checkaux)
! 470: bus_space_tag_t iot;
! 471: bus_space_handle_t ioh, ioh_c;
! 472: pckbc_slot_t slot;
! 473: int checkaux; /* ignored on hppa */
! 474: {
! 475: int i;
! 476: u_char stat;
! 477:
! 478: /* if 1 port read takes 1us (?), this polls for 100ms */
! 479: for (i = 100000; i; i--) {
! 480: stat = bus_space_read_1(iot, ioh, KBSTATP);
! 481: if (stat & KBS_DIB) {
! 482: KBD_DELAY;
! 483: return bus_space_read_1(iot, ioh, KBDATAP);
! 484: }
! 485: }
! 486: return (-1);
! 487: }
! 488:
! 489: int
! 490: pckbc_send_devcmd(t, slot, val)
! 491: struct pckbc_internal *t;
! 492: pckbc_slot_t slot;
! 493: u_char val;
! 494: {
! 495: return pckbc_send_cmd(t->t_iot, t->t_ioh_d, val);
! 496: }
! 497:
! 498: int
! 499: pckbc_submatch(parent, match, aux)
! 500: struct device *parent;
! 501: void *match;
! 502: void *aux;
! 503: {
! 504: struct cfdata *cf = match;
! 505: struct pckbc_attach_args *pa = aux;
! 506:
! 507: if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
! 508: cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
! 509: return (0);
! 510: return ((*cf->cf_attach->ca_match)(parent, cf, aux));
! 511: }
! 512:
! 513: int
! 514: pckbc_attach_slot(sc, slot)
! 515: struct pckbc_softc *sc;
! 516: pckbc_slot_t slot;
! 517: {
! 518: struct pckbc_internal *t = sc->id;
! 519: struct pckbc_attach_args pa;
! 520: int found;
! 521:
! 522: pa.pa_tag = t;
! 523: pa.pa_slot = slot;
! 524: found = (config_found_sm((struct device *)sc, &pa,
! 525: pckbcprint, pckbc_submatch) != NULL);
! 526:
! 527: if (found && !t->t_slotdata[slot]) {
! 528: t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata),
! 529: M_DEVBUF, M_NOWAIT);
! 530: if (t->t_slotdata[slot] == NULL)
! 531: return 0;
! 532: pckbc_init_slotdata(t->t_slotdata[slot]);
! 533: }
! 534: return (found);
! 535: }
! 536:
! 537: int
! 538: pckbcprint(aux, pnp)
! 539: void *aux;
! 540: const char *pnp;
! 541: {
! 542: #if 0 /* hppa having devices for each slot, this is barely useful */
! 543: struct pckbc_attach_args *pa = aux;
! 544:
! 545: if (!pnp)
! 546: printf(" (%s slot)", pckbc_slot_names[pa->pa_slot]);
! 547: #endif
! 548: return (QUIET);
! 549: }
! 550:
! 551: void
! 552: pckbc_init_slotdata(q)
! 553: struct pckbc_slotdata *q;
! 554: {
! 555: int i;
! 556: TAILQ_INIT(&q->cmdqueue);
! 557: TAILQ_INIT(&q->freequeue);
! 558:
! 559: for (i = 0; i < NCMD; i++) {
! 560: TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
! 561: }
! 562: q->polling = 0;
! 563: }
! 564:
! 565: void
! 566: pckbc_flush(self, slot)
! 567: pckbc_tag_t self;
! 568: pckbc_slot_t slot;
! 569: {
! 570: struct pckbc_internal *t = self;
! 571:
! 572: pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_d, slot, 0);
! 573: }
! 574:
! 575: int
! 576: pckbc_poll_data(self, slot)
! 577: pckbc_tag_t self;
! 578: pckbc_slot_t slot;
! 579: {
! 580: struct pckbc_internal *t = self;
! 581: struct pckbc_slotdata *q = t->t_slotdata[slot];
! 582: int c;
! 583:
! 584: c = pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_d, slot, 0);
! 585: if (c != -1 && q && CMD_IN_QUEUE(q)) {
! 586: /* we jumped into a running command - try to
! 587: deliver the response */
! 588: if (pckbc_cmdresponse(t, slot, c))
! 589: return (-1);
! 590: }
! 591: return (c);
! 592: }
! 593:
! 594: void
! 595: pckbc_slot_enable(self, slot, on)
! 596: pckbc_tag_t self;
! 597: pckbc_slot_t slot;
! 598: int on;
! 599: {
! 600: /* can't enable slots here as they are different devices */
! 601: }
! 602:
! 603: void
! 604: pckbc_set_poll(self, slot, on)
! 605: pckbc_tag_t self;
! 606: pckbc_slot_t slot;
! 607: int on;
! 608: {
! 609: struct pckbc_internal *t = (struct pckbc_internal *)self;
! 610:
! 611: t->t_slotdata[slot]->polling = on;
! 612:
! 613: if (!on) {
! 614: int s;
! 615:
! 616: /*
! 617: * If disabling polling on a device that's been configured,
! 618: * make sure there are no bytes left in the FIFO, holding up
! 619: * the interrupt line. Otherwise we won't get any further
! 620: * interrupts.
! 621: */
! 622: if (t->t_sc) {
! 623: s = spltty();
! 624: gsckbcintr(t->t_sc);
! 625: splx(s);
! 626: }
! 627: }
! 628: }
! 629:
! 630: /*
! 631: * Pass command to device, poll for ACK and data.
! 632: * to be called at spltty()
! 633: */
! 634: void
! 635: pckbc_poll_cmd1(t, slot, cmd)
! 636: struct pckbc_internal *t;
! 637: pckbc_slot_t slot;
! 638: struct pckbc_devcmd *cmd;
! 639: {
! 640: bus_space_tag_t iot = t->t_iot;
! 641: bus_space_handle_t ioh = t->t_ioh_d;
! 642: int i, c = 0;
! 643:
! 644: while (cmd->cmdidx < cmd->cmdlen) {
! 645: if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
! 646: printf("pckbc_cmd: send error\n");
! 647: cmd->status = EIO;
! 648: return;
! 649: }
! 650: for (i = 10; i; i--) { /* 1s ??? */
! 651: c = pckbc_poll_data1(iot, ioh, ioh, slot, 0);
! 652: if (c != -1)
! 653: break;
! 654: }
! 655:
! 656: if (c == KBC_DEVCMD_ACK) {
! 657: cmd->cmdidx++;
! 658: continue;
! 659: }
! 660: if (c == KBC_DEVCMD_RESEND) {
! 661: #ifdef PCKBCDEBUG
! 662: printf("pckbc_cmd: RESEND\n");
! 663: #endif
! 664: if (cmd->retries++ < KB_MAX_RETRANS)
! 665: continue;
! 666: else {
! 667: #ifdef PCKBCDEBUG
! 668: printf("pckbc: cmd failed\n");
! 669: #endif
! 670: cmd->status = EIO;
! 671: return;
! 672: }
! 673: }
! 674: if (c == -1) {
! 675: #ifdef PCKBCDEBUG
! 676: printf("pckbc_cmd: timeout\n");
! 677: #endif
! 678: cmd->status = EIO;
! 679: return;
! 680: }
! 681: #ifdef PCKBCDEBUG
! 682: printf("pckbc_cmd: lost 0x%x\n", c);
! 683: #endif
! 684: }
! 685:
! 686: while (cmd->responseidx < cmd->responselen) {
! 687: if (cmd->flags & KBC_CMDFLAG_SLOW)
! 688: i = 100; /* 10s ??? */
! 689: else
! 690: i = 10; /* 1s ??? */
! 691: while (i--) {
! 692: c = pckbc_poll_data1(iot, ioh, ioh, slot, 0);
! 693: if (c != -1)
! 694: break;
! 695: }
! 696: if (c == -1) {
! 697: #ifdef PCKBCDEBUG
! 698: printf("pckbc_cmd: no data\n");
! 699: #endif
! 700: cmd->status = ETIMEDOUT;
! 701: return;
! 702: } else
! 703: cmd->response[cmd->responseidx++] = c;
! 704: }
! 705: }
! 706:
! 707: /* for use in autoconfiguration */
! 708: int
! 709: pckbc_poll_cmd(self, slot, cmd, len, responselen, respbuf, slow)
! 710: pckbc_tag_t self;
! 711: pckbc_slot_t slot;
! 712: u_char *cmd;
! 713: int len, responselen;
! 714: u_char *respbuf;
! 715: int slow;
! 716: {
! 717: struct pckbc_devcmd nc;
! 718:
! 719: if ((len > 4) || (responselen > 4))
! 720: return (EINVAL);
! 721:
! 722: bzero(&nc, sizeof(nc));
! 723: bcopy(cmd, nc.cmd, len);
! 724: nc.cmdlen = len;
! 725: nc.responselen = responselen;
! 726: nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
! 727:
! 728: pckbc_poll_cmd1(self, slot, &nc);
! 729:
! 730: if (nc.status == 0 && respbuf)
! 731: bcopy(nc.response, respbuf, responselen);
! 732:
! 733: return (nc.status);
! 734: }
! 735:
! 736: /*
! 737: * Clean up a command queue, throw away everything.
! 738: */
! 739: void
! 740: pckbc_cleanqueue(q)
! 741: struct pckbc_slotdata *q;
! 742: {
! 743: struct pckbc_devcmd *cmd;
! 744: #ifdef PCKBCDEBUG
! 745: int i;
! 746: #endif
! 747:
! 748: while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
! 749: TAILQ_REMOVE(&q->cmdqueue, cmd, next);
! 750: #ifdef PCKBCDEBUG
! 751: printf("pckbc_cleanqueue: removing");
! 752: for (i = 0; i < cmd->cmdlen; i++)
! 753: printf(" %02x", cmd->cmd[i]);
! 754: printf("\n");
! 755: #endif
! 756: TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
! 757: }
! 758: }
! 759:
! 760: /*
! 761: * Timeout error handler: clean queues and data port.
! 762: * XXX could be less invasive.
! 763: */
! 764: void
! 765: pckbc_cleanup(self)
! 766: void *self;
! 767: {
! 768: struct pckbc_internal *t = self;
! 769: int s;
! 770:
! 771: printf("pckbc: command timeout\n");
! 772:
! 773: s = spltty();
! 774:
! 775: if (t->t_slotdata[PCKBC_KBD_SLOT])
! 776: pckbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
! 777: if (t->t_slotdata[PCKBC_AUX_SLOT])
! 778: pckbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
! 779:
! 780: while (bus_space_read_1(t->t_iot, t->t_ioh_d, KBSTATP) & KBS_DIB) {
! 781: KBD_DELAY;
! 782: (void) bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);
! 783: }
! 784:
! 785: /* reset KBC? */
! 786:
! 787: splx(s);
! 788: }
! 789:
! 790: /*
! 791: * Pass command to device during normal operation.
! 792: * to be called at spltty()
! 793: */
! 794: void
! 795: pckbc_start(t, slot)
! 796: struct pckbc_internal *t;
! 797: pckbc_slot_t slot;
! 798: {
! 799: struct pckbc_slotdata *q = t->t_slotdata[slot];
! 800: struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
! 801:
! 802: if (IS_POLLING(q)) {
! 803: do {
! 804: pckbc_poll_cmd1(t, slot, cmd);
! 805: if (cmd->status)
! 806: printf("pckbc_start: command error\n");
! 807:
! 808: TAILQ_REMOVE(&q->cmdqueue, cmd, next);
! 809: if (cmd->flags & KBC_CMDFLAG_SYNC)
! 810: wakeup(cmd);
! 811: else {
! 812: timeout_del(&t->t_cleanup);
! 813: TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
! 814: }
! 815: cmd = TAILQ_FIRST(&q->cmdqueue);
! 816: } while (cmd);
! 817: return;
! 818: }
! 819:
! 820: if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
! 821: printf("pckbc_start: send error\n");
! 822: /* XXX what now? */
! 823: return;
! 824: }
! 825: }
! 826:
! 827: /*
! 828: * Handle command responses coming in asynchronously,
! 829: * return nonzero if valid response.
! 830: * to be called at spltty()
! 831: */
! 832: int
! 833: pckbc_cmdresponse(t, slot, data)
! 834: struct pckbc_internal *t;
! 835: pckbc_slot_t slot;
! 836: u_char data;
! 837: {
! 838: struct pckbc_slotdata *q = t->t_slotdata[slot];
! 839: struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
! 840:
! 841: #ifdef DIAGNOSTIC
! 842: if (!cmd)
! 843: panic("pckbc_cmdresponse: no active command");
! 844: #endif
! 845: if (cmd->cmdidx < cmd->cmdlen) {
! 846: if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
! 847: return (0);
! 848:
! 849: if (data == KBC_DEVCMD_RESEND) {
! 850: if (cmd->retries++ < KB_MAX_RETRANS) {
! 851: /* try again last command */
! 852: goto restart;
! 853: } else {
! 854: #ifdef PCKBCDEBUG
! 855: printf("pckbc: cmd failed\n");
! 856: #endif
! 857: cmd->status = EIO;
! 858: /* dequeue */
! 859: }
! 860: } else {
! 861: if (++cmd->cmdidx < cmd->cmdlen)
! 862: goto restart;
! 863: if (cmd->responselen)
! 864: return (1);
! 865: /* else dequeue */
! 866: }
! 867: } else if (cmd->responseidx < cmd->responselen) {
! 868: cmd->response[cmd->responseidx++] = data;
! 869: if (cmd->responseidx < cmd->responselen)
! 870: return (1);
! 871: /* else dequeue */
! 872: } else
! 873: return (0);
! 874:
! 875: /* dequeue: */
! 876: TAILQ_REMOVE(&q->cmdqueue, cmd, next);
! 877: if (cmd->flags & KBC_CMDFLAG_SYNC)
! 878: wakeup(cmd);
! 879: else {
! 880: timeout_del(&t->t_cleanup);
! 881: TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
! 882: }
! 883: if (!CMD_IN_QUEUE(q))
! 884: return (1);
! 885: restart:
! 886: pckbc_start(t, slot);
! 887: return (1);
! 888: }
! 889:
! 890: /*
! 891: * Put command into the device's command queue, return zero or errno.
! 892: */
! 893: int
! 894: pckbc_enqueue_cmd(self, slot, cmd, len, responselen, sync, respbuf)
! 895: pckbc_tag_t self;
! 896: pckbc_slot_t slot;
! 897: u_char *cmd;
! 898: int len, responselen, sync;
! 899: u_char *respbuf;
! 900: {
! 901: struct pckbc_internal *t = self;
! 902: struct pckbc_slotdata *q = t->t_slotdata[slot];
! 903: struct pckbc_devcmd *nc;
! 904: int s, isactive, res = 0;
! 905:
! 906: if ((len > 4) || (responselen > 4))
! 907: return (EINVAL);
! 908: s = spltty();
! 909: nc = TAILQ_FIRST(&q->freequeue);
! 910: if (nc) {
! 911: TAILQ_REMOVE(&q->freequeue, nc, next);
! 912: }
! 913: splx(s);
! 914: if (!nc)
! 915: return (ENOMEM);
! 916:
! 917: bzero(nc, sizeof(*nc));
! 918: bcopy(cmd, nc->cmd, len);
! 919: nc->cmdlen = len;
! 920: nc->responselen = responselen;
! 921: nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
! 922:
! 923: s = spltty();
! 924:
! 925: if (IS_POLLING(q) && sync) {
! 926: /*
! 927: * XXX We should poll until the queue is empty.
! 928: * But we don't come here normally, so make
! 929: * it simple and throw away everything.
! 930: */
! 931: pckbc_cleanqueue(q);
! 932: }
! 933:
! 934: isactive = CMD_IN_QUEUE(q);
! 935: TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
! 936: if (!isactive)
! 937: pckbc_start(t, slot);
! 938:
! 939: if (IS_POLLING(q))
! 940: res = (sync ? nc->status : 0);
! 941: else if (sync) {
! 942: if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
! 943: TAILQ_REMOVE(&q->cmdqueue, nc, next);
! 944: pckbc_cleanup(t);
! 945: } else
! 946: res = nc->status;
! 947: } else
! 948: timeout_add(&t->t_cleanup, hz);
! 949:
! 950: if (sync) {
! 951: if (respbuf)
! 952: bcopy(nc->response, respbuf, responselen);
! 953: TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
! 954: }
! 955:
! 956: splx(s);
! 957:
! 958: return (res);
! 959: }
! 960:
! 961: void
! 962: pckbc_set_inputhandler(self, slot, func, arg, name)
! 963: pckbc_tag_t self;
! 964: pckbc_slot_t slot;
! 965: pckbc_inputfcn func;
! 966: void *arg;
! 967: char *name;
! 968: {
! 969: struct pckbc_internal *t = (struct pckbc_internal *)self;
! 970: struct pckbc_softc *sc = t->t_sc;
! 971:
! 972: if (slot >= PCKBC_NSLOTS)
! 973: panic("pckbc_set_inputhandler: bad slot %d", slot);
! 974:
! 975: (*sc->intr_establish)(sc, slot);
! 976:
! 977: sc->inputhandler[slot] = func;
! 978: sc->inputarg[slot] = arg;
! 979: sc->subname[slot] = name;
! 980: }
! 981:
! 982: int
! 983: gsckbcintr(void *v)
! 984: {
! 985: struct gsckbc_softc *gsc = v;
! 986: struct pckbc_softc *sc = (struct pckbc_softc *)gsc;
! 987: struct pckbc_internal *t = sc->id;
! 988: pckbc_slot_t slot;
! 989: struct pckbc_slotdata *q;
! 990: int served = 0, data;
! 991:
! 992: while (bus_space_read_1(t->t_iot, t->t_ioh_d, KBSTATP) & KBS_DIB) {
! 993: served = 1;
! 994:
! 995: slot = gsc->sc_type;
! 996: q = t->t_slotdata[slot];
! 997:
! 998: if (!q) {
! 999: /* XXX do something for live insertion? */
! 1000: #ifdef PCKBCDEBUG
! 1001: printf("gsckbcintr: no dev for slot %d\n", slot);
! 1002: #endif
! 1003: KBD_DELAY;
! 1004: (void) bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);
! 1005: continue;
! 1006: }
! 1007:
! 1008: if (IS_POLLING(q))
! 1009: break; /* pckbc_poll_data() will get it */
! 1010:
! 1011: KBD_DELAY;
! 1012: data = bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);
! 1013:
! 1014: if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
! 1015: continue;
! 1016:
! 1017: if (sc->inputhandler[slot])
! 1018: (*sc->inputhandler[slot])(sc->inputarg[slot], data);
! 1019: #ifdef PCKBCDEBUG
! 1020: else
! 1021: printf("gsckbcintr: slot %d lost %d\n", slot, data);
! 1022: #endif
! 1023: }
! 1024:
! 1025: return (served);
! 1026: }
CVSweb