[BACK]Return to gsckbc.c CVS log [TXT][DIR] Up to [local] / sys / arch / hppa / gsc

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