date and time created 86/11/29 12:09:52 by sam
authorSam Leffler <sam@ucbvax.Berkeley.EDU>
Sun, 30 Nov 1986 04:09:52 +0000 (20:09 -0800)
committerSam Leffler <sam@ucbvax.Berkeley.EDU>
Sun, 30 Nov 1986 04:09:52 +0000 (20:09 -0800)
SCCS-vsn: sys/tahoe/vba/ik.c 1.1

usr/src/sys/tahoe/vba/ik.c [new file with mode: 0644]

diff --git a/usr/src/sys/tahoe/vba/ik.c b/usr/src/sys/tahoe/vba/ik.c
new file mode 100644 (file)
index 0000000..4204207
--- /dev/null
@@ -0,0 +1,698 @@
+/*     ik.c    1.1     86/11/29        */
+
+#include "ik.h"
+#if NIK > 0
+/*
+ * PS300/IKON DR-11W Device Driver.
+ */
+#include "param.h"
+#include "buf.h"
+#include "cmap.h"
+#include "conf.h"
+#include "dir.h"
+#include "dkstat.h"
+#include "map.h"
+#include "systm.h"
+#include "user.h"
+#include "vmmac.h"
+#include "proc.h"
+#include "uio.h"
+#include "kernel.h"
+
+#include "../tahoe/mtpr.h"
+#include "../tahoe/pte.h"
+
+#include "../tahoevba/vbavar.h"
+#include "../tahoevba/ikreg.h"
+#include "../tahoevba/psreg.h"
+#include "../tahoevba/psproto.h"
+
+int     ikprobe(), ikattach(), iktimer();
+struct  vba_device *ikinfo[NIK];
+long    ikstd[] = { 0 };
+struct  vba_driver ikdriver = { ikprobe, 0, ikattach, 0, ikstd, "ik", ikinfo };
+
+#define splik()         spl4()
+/*
+ * Devices are organized in pairs with the odd valued
+ * device being used for ``diagnostic'' purposes.  That
+ * is diagnostic devices don't get auto-attach'd and
+ * detach'd on open-close.
+ */
+#define IKUNIT(dev)     (minor(dev) >> 1)
+#define IKDIAG(dev)     (minor(dev) & 01)       /* is a diagnostic unit */
+
+struct  ik_softc {
+        uid_t   is_uid;         /* uid of open processes */
+        u_short is_timeout;     /* current timeout (seconds) */
+        u_short is_error;       /* internal error codes */
+        u_short is_flags;
+#define IKF_ATTACHED    0x1     /* unit is attached (not used yet) */
+        union {
+                u_short w[2];
+                u_long  l;
+        } is_nameaddr;          /* address of last symbol lookup */
+       caddr_t is_buf;         /* i/o buffer XXX */
+} ik_softc[NIK];
+
+struct  buf iktab[NIK];         /* unit command queue headers */
+struct  buf rikbuf[NIK];        /* buffers for read/write operations */
+struct  buf cikbuf[NIK];        /* buffers for control operations */
+
+/* buf overlay definitions */
+#define b_command       b_resid
+
+int     ikdiotimo = PS_DIOTIMO; /* dio polling timeout */
+int     iktimeout = PS_TIMEOUT; /* attention/dma timeout (in hz) */
+
+ikprobe(reg, vi)
+        caddr_t reg;
+       struct vba_device *vi;
+{
+        register int br, cvec;          /* r12, r11 */
+       register struct ikdevice *ik;
+
+        if (badaddr(reg, 2))
+                return (0);
+       ik = (struct ikdevice *)reg;
+       ik->ik_vec = --vi->ui_hd->vh_lastiv;
+        /*
+         * Try and reset the PS300.  Since this
+         * won't work if it's powered off, we
+         * can't use sucess/failure to decide
+         * if the device is present.
+         */
+       br = 0;
+        if (!psreset(ik, IKCSR_IENA) || br == 0)
+               br = 0x18, cvec = ik->ik_vec;   /* XXX */
+        return (sizeof (struct ikdevice));
+}
+
+/*
+ * Perform a ``hard'' reset.
+ */
+psreset(ik, iena)
+        register struct ikdevice *ik;
+{
+
+        ik->ik_csr = IKCSR_MCLR|iena;
+        DELAY(10000);
+        ik->ik_csr = IKCSR_FNC3;
+        return (dioread(ik) == PS_RESET);
+}
+
+ikattach(vi)
+        struct vba_device *vi;
+{
+        register struct ikdevice *ik;
+
+        ikinfo[vi->ui_unit] = vi;
+        ik = (struct ikdevice *)vi->ui_addr;
+        ik->ik_vec = IKVEC_BASE + vi->ui_unit;  /* interrupt vector */
+        ik->ik_mod = IKMOD_STD;                 /* address modifier */
+        ik_softc[vi->ui_unit].is_uid = -1;
+}
+
+/*
+ * Open a PS300 and attach.  We allow multiple
+ * processes with the same uid to share a unit.
+ */
+/*ARGSUSED*/
+ikopen(dev, flag)
+        dev_t dev;
+        int flag;
+{
+        register int unit = IKUNIT(dev);
+        register struct ik_softc *sc;
+        struct vba_device *vi;
+        struct ikdevice *ik;
+        int reset;
+
+        if (unit >= NIK || (vi = ikinfo[unit]) == 0 || vi->ui_alive == 0)
+                return (ENXIO);
+        sc = &ik_softc[unit];
+        if (sc->is_uid != -1 && sc->is_uid != u.u_uid)
+                return (EBUSY);
+        if (sc->is_uid == -1) {
+               sc->is_buf = (caddr_t)wmemall(vmemall, PS_MAXDMA);
+               if (sc->is_buf == 0)
+                       return (ENOMEM);
+                sc->is_timeout = 0;
+                timeout(iktimer, unit, hz);
+                /*
+                 * Perform PS300 attach for first process.
+                 */
+                if (!IKDIAG(dev)) {
+                        reset = 0;
+                again:
+                        if (ikcommand(dev, PS_ATTACH, 1)) {
+                                /*
+                                 * If attach fails, perform a hard
+                                 * reset once, then retry the command.
+                                 */
+                                ik = (struct ikdevice *)ikinfo[unit]->ui_addr;
+                                if (!reset++ && psreset(ik, 0))
+                                        goto again;
+                                untimeout(iktimer, unit);
+                               wmemfree(sc->is_buf, PS_MAXDMA);
+                               sc->is_buf = 0;
+                                return (EIO);
+                        }
+                }
+                sc->is_uid = u.u_uid;
+        }
+        return (0);
+}
+
+/*ARGSUSED*/
+ikclose(dev, flag)
+        dev_t dev;
+        int flag;
+{
+        int unit = IKUNIT(dev);
+       register struct ik_softc *sc = &ik_softc[unit];
+
+        if (!IKDIAG(dev))
+                (void) ikcommand(dev, PS_DETACH, 1);    /* auto detach */
+        sc->is_uid = -1;
+       if (sc->is_buf) {
+               wmemfree(sc->is_buf, PS_MAXDMA);
+               sc->is_buf = 0;
+       }
+        untimeout(iktimer, unit);
+}
+
+ikread(dev, uio)
+        dev_t dev;
+        struct uio *uio;
+{
+
+        return (ikrw(dev, uio, B_READ));
+}
+
+ikwrite(dev, uio)
+        dev_t dev;
+        struct uio *uio;
+{
+
+        return (ikrw(dev, uio, B_WRITE));
+}
+
+/*
+ * Take read/write request and perform physical i/o
+ * transaction with PS300.  This involves constructing
+ * a physical i/o request vector based on the uio
+ * vector, performing the dma, and, finally, moving
+ * the data to it's final destination (because of CCI
+ * VERSAbus bogosities).
+ */
+ikrw(dev, uio, rw)
+        dev_t dev;
+        register struct uio *uio;
+        int rw;
+{
+        int error, unit = IKUNIT(dev), s, wrcmd;
+        register struct buf *bp;
+        register struct iovec *iov;
+        register struct psalist *ap;
+        struct ik_softc *sc = &ik_softc[unit];
+
+        if (unit >= NIK)
+                return (ENXIO);
+        bp = &rikbuf[unit];
+        error = 0, iov = uio->uio_iov, wrcmd = PS_WRPHY;
+        for (; !error && uio->uio_iovcnt; iov++, uio->uio_iovcnt--) { 
+                /*
+                 * Hack way to set PS300 address w/o doing an lseek
+                 * and specify write physical w/ refresh synchronization.
+                 */
+                if (iov->iov_len == 0) {
+                        if ((int)iov->iov_base&PSIO_SYNC)
+                                wrcmd = PS_WRPHY_SYNC;
+                        uio->uio_offset = (int)iov->iov_base & ~PSIO_SYNC;
+                        continue;
+                }
+                if (iov->iov_len > PS_MAXDMA) {
+                        sc->is_error = PSERROR_INVALBC, error = EINVAL;
+                        continue;
+                }
+                if ((int)uio->uio_offset&01) {
+                        sc->is_error = PSERROR_BADADDR, error = EINVAL;
+                        continue;
+                }
+                s = splbio();
+                while (bp->b_flags&B_BUSY) {
+                        bp->b_flags |= B_WANTED;
+                        sleep((caddr_t)bp, PRIBIO+1);
+                }
+                splx(s);
+                bp->b_flags = B_BUSY | rw;
+                /*
+                 * Construct address descriptor in buffer.
+                 */
+                ap = (struct psalist *)sc->is_buf;
+                ap->nblocks = 1;
+                /* work-around dr300 word swapping */
+                ap->addr[0] = uio->uio_offset & 0xffff;
+                ap->addr[1] = uio->uio_offset >> 16;
+                ap->wc = (iov->iov_len + 1) >> 1;
+                if (rw == B_WRITE) {
+                        error = copyin(iov->iov_base, (caddr_t)&ap[1],
+                            iov->iov_len);
+                        if (!error)
+                                error = ikcommand(dev, wrcmd,
+                                    iov->iov_len + sizeof (*ap));
+                } else {
+                        caddr_t cp;
+                        int len;
+
+                        error = ikcommand(dev, PS_RDPHY, sizeof (*ap));
+                        cp = (caddr_t)&ap[1], len = iov->iov_len;
+                        for (; len > 0; len -= NBPG, cp += NBPG)
+                                mtpr(cp, P1DC);
+                        if (!error)
+                                error = copyout((caddr_t)&ap[1], iov->iov_base,
+                                    iov->iov_len);
+                }
+                (void) splbio();
+                if (bp->b_flags&B_WANTED)
+                        wakeup((caddr_t)bp);
+                splx(s);
+                uio->uio_resid -= iov->iov_len;
+                uio->uio_offset += iov->iov_len;
+                bp->b_flags &= ~(B_BUSY|B_WANTED);
+        }
+        return (error);
+}
+
+/*
+ * Perform a PS300 command.
+ */
+ikcommand(dev, com, count)
+        dev_t dev;
+        int com, count;
+{
+        register struct buf *bp;
+        register int s;
+
+        bp = &cikbuf[IKUNIT(dev)];
+        s = splik();
+        while (bp->b_flags&B_BUSY) {
+                if (bp->b_flags&B_DONE)
+                        break;
+                bp->b_flags |= B_WANTED;
+                sleep((caddr_t)bp, PRIBIO);
+        }
+        bp->b_flags = B_BUSY|B_READ;
+        splx(s);
+        bp->b_dev = dev;
+        bp->b_command = com;
+        bp->b_bcount = count;
+        ikstrategy(bp);
+        biowait(bp);
+        if (bp->b_flags&B_WANTED)
+                wakeup((caddr_t)bp);
+        bp->b_flags &= B_ERROR;
+        return (geterror(bp));
+}
+
+/*
+ * Physio strategy routine
+ */
+ikstrategy(bp)
+        register struct buf *bp;
+{
+        register struct buf *dp;
+
+        /*
+         * Put request at end of controller queue.
+         */
+        dp = &iktab[IKUNIT(bp->b_dev)];
+        bp->av_forw = NULL;
+        (void) splik();
+        if (dp->b_actf != NULL) {
+                dp->b_actl->av_forw = bp;
+                dp->b_actl = bp;
+        } else
+                dp->b_actf = dp->b_actl = bp;
+        if (!dp->b_active)
+                ikstart(dp);
+        (void) spl0();
+}
+
+/*
+ * Start the next command on the controller's queue.
+ */
+ikstart(dp)
+        register struct buf *dp;
+{
+        register struct buf *bp;
+        register struct ikdevice *ik;
+        register struct ik_softc *sc;
+        register struct psalist *ap;
+        u_short bc, csr;
+        u_int addr;
+        int unit;
+
+loop:
+        /*
+         * Pull a request off the controller queue
+         */
+        if ((bp = dp->b_actf) == NULL) {
+                dp->b_active = 0;
+                return;
+        }
+        /*
+         * Mark controller busy and process this request.
+         */
+        dp->b_active = 1;
+        unit = IKUNIT(bp->b_dev);
+        sc = &ik_softc[unit];
+        ik = (struct ikdevice *)ikinfo[unit]->ui_addr;
+        switch (bp->b_command) {
+
+        case PS_ATTACH:         /* logical unit attach */
+        case PS_DETACH:         /* logical unit detach */
+        case PS_LOOKUP:         /* name lookup */
+        case PS_RDPHY:          /* physical i/o read */
+        case PS_WRPHY:          /* physical i/o write */
+        case PS_WRPHY_SYNC:     /* physical i/o write w/ sync */
+                /*
+                 * Handshake command and, optionally,
+                 * byte count and byte swap flag.
+                 */
+                if (sc->is_error = diowrite(ik, bp->b_command))
+                        goto bad;
+                if (bp->b_command < PS_DETACH) {
+                        if (sc->is_error = diowrite(ik, bp->b_bcount))
+                                goto bad;
+                        if (sc->is_error = diowrite(ik, 0 /* !swab */))
+                                goto bad;
+                }
+                /*
+                 * Set timeout and wait for an attention interrupt.
+                 */
+                sc->is_timeout = iktimeout;
+                return;
+
+        case PS_DMAOUT:         /* dma data host->PS300 */
+                bc = bp->b_bcount;
+                csr = IKCSR_CYCLE;
+                break;
+
+        case PS_DMAIN:          /* dma data PS300->host */
+                bc = bp->b_bcount;
+                csr = IKCSR_CYCLE|IKCSR_FNC1;
+                break;
+
+        default:
+                log(LOG_ERR, "ik%d: bad cmd %x\n", unit, bp->b_command);
+                sc->is_error = PSERROR_BADCMD;
+                goto bad;
+        }
+        /* initiate dma transfer */
+        addr = vtoph((struct proc *)0, sc->is_buf);
+        ik->ik_bahi = addr >> 17;
+        ik->ik_balo = (addr >> 1) & 0xffff;
+        ik->ik_wc = ((bc + 1) >> 1) - 1;        /* round & convert */
+        ik->ik_pulse = IKPULSE_RATTF|IKPULSE_RDMAF;
+        sc->is_timeout = iktimeout;
+        ik->ik_csr = IKCSR_IENA|IKCSR_GO|csr;
+        return;
+bad:
+        bp->b_flags |= B_ERROR;
+        dp->b_actf = bp->av_forw;               /* remove from queue */
+        biodone(bp);
+        goto loop;
+}
+
+#define FETCHWORD(i) { \
+        int v; \
+\
+        v = dioread(ik); \
+        if (v == -1) { \
+                sc->is_error = PSERROR_NAMETIMO; \
+                goto bad; \
+        } \
+        sc->is_nameaddr.w[i] = v; \
+}
+
+/*
+ * Process a device interrupt.
+ */
+ikintr(ikon)
+        int ikon;
+{
+        register struct ikdevice *ik;
+        register struct buf *bp, *dp;
+        struct ik_softc *sc;
+        register u_short data;
+        u_short i, v;
+
+        /* should go by controller, but for now... */
+        if (ikinfo[ikon] == 0)
+                return;
+        ik = (struct ikdevice *)ikinfo[ikon]->ui_addr;
+        /*
+         * Discard all non-attention interrupts.  The
+         * interrupts we're throwing away should all be
+         * associated with DMA completion.
+         */
+        data = ik->ik_data;
+        if ((ik->ik_csr&(IKCSR_ATTF|IKCSR_STATC)) != IKCSR_ATTF) {
+                ik->ik_pulse = IKPULSE_RATTF|IKPULSE_RDMAF|IKPULSE_SIENA;
+                return;
+        }
+        /*
+         * Fetch attention code immediately.
+         */
+        ik->ik_csr = IKCSR_RATTF|IKCSR_RDMAF|IKCSR_FNC1;
+        ik->ik_pulse = IKPULSE_FNC2;
+        /*
+         * Get device and block structures, and a pointer
+         * to the vba_device for the device.  We receive an
+         * unsolicited interrupt whenever the PS300 is power
+         * cycled (so ignore it in that case).
+         */
+        dp = &iktab[ikon];
+        if ((bp = dp->b_actf) == NULL) {
+                if (PS_CODE(data) != PS_RESET)          /* power failure */
+                        log(LOG_WARNING, "ik%d: spurious interrupt, code %x\n",
+                            ikon, data);
+                goto enable;
+        }
+        sc = &ik_softc[IKUNIT(bp->b_dev)];
+        sc->is_timeout = 0;                     /* disable timer */
+        switch (PS_CODE(data)) {
+
+        case PS_LOOKUP:                         /* name lookup */
+                if (data == PS_LOOKUP) {        /* dma name */
+                        bp->b_command = PS_DMAOUT;
+                        goto opcont;
+                }
+                if (data == PS_DMAOK(PS_LOOKUP)) {
+                        /* reenable interrupt and wait for address */
+                        sc->is_timeout = iktimeout;
+                        goto enable;
+                }
+                /*
+                 * Address should be present, extract it one
+                 * word at a time from the PS300 (yech).
+                 */
+                if (data != PS_ADROK(PS_LOOKUP))
+                        goto bad;
+                FETCHWORD(0);
+                FETCHWORD(1);
+                goto opdone;
+
+        case PS_WRPHY_SYNC:                     /* physical i/o write w/ sync */
+                if (data == PS_WRPHY_SYNC) {    /* start dma transfer */
+                        bp->b_command = PS_DMAOUT;
+                        goto opcont;
+                }
+                if (data != PS_DMAOK(PS_WRPHY_SYNC))
+                        goto bad;
+                goto opdone;
+
+        case PS_WRPHY:                          /* physical i/o write */
+                if (data == PS_WRPHY) { /* start dma transfer */
+                        bp->b_command = PS_DMAOUT;
+                        goto opcont;
+                }
+                if (data != PS_DMAOK(PS_WRPHY))
+                        goto bad;
+                goto opdone;
+
+        case PS_ATTACH:                         /* attach unit */
+        case PS_DETACH:                         /* detach unit */
+        case PS_ABORT:                          /* abort code from ps300 */
+                if (data != bp->b_command)
+                        goto bad;
+                goto opdone;
+
+        case PS_RDPHY:                          /* physical i/o read */
+                if (data == PS_RDPHY) {         /* dma address list */
+                        bp->b_command = PS_DMAOUT;
+                        goto opcont;
+                }
+                if (data == PS_ADROK(PS_RDPHY)) {
+                        /* collect read byte count and start dma */
+                        bp->b_bcount = dioread(ik);
+                        if (bp->b_bcount == -1)
+                                goto bad;
+                        bp->b_command = PS_DMAIN;
+                        goto opcont;
+                }
+                if (data == PS_DMAOK(PS_RDPHY))
+                        goto opdone;
+                goto bad;
+        }
+bad:
+        sc->is_error = data;
+        bp->b_flags |= B_ERROR;
+opdone:
+        dp->b_actf = bp->av_forw;               /* remove from queue */
+        biodone(bp);
+opcont:
+        ikstart(dp);
+enable:
+        ik->ik_pulse = IKPULSE_SIENA;           /* explicitly reenable */
+}
+
+/*
+ * Watchdog timer.
+ */
+iktimer(unit)
+        int unit;
+{
+        register struct ik_softc *sc = &ik_softc[unit];
+
+        if (sc->is_timeout && --sc->is_timeout == 0) {
+                register struct buf *dp, *bp;
+                int s;
+
+                log(LOG_ERROR, "ik%d: timeout\n", unit);
+                s = splik();
+                /* should abort current command */
+                dp = &iktab[unit];
+                if (bp = dp->b_actf) {
+                        sc->is_error = PSERROR_CMDTIMO;
+                        bp->b_flags |= B_ERROR;
+                        dp->b_actf = bp->av_forw;       /* remove from queue */
+                        biodone(bp);
+                        ikstart(dp);
+                }
+                splx(s);
+        }
+        timeout(iktimer, unit, hz);
+}
+
+/*
+ * Handshake read from DR300.
+ */
+dioread(ik)
+        register struct ikdevice *ik;
+{
+        register int timeout;
+        u_short data;
+
+        for (timeout = ikdiotimo; timeout > 0; timeout--)
+                if ((ik->ik_csr&(IKCSR_ATTF|IKCSR_STATC)) == IKCSR_ATTF) {
+                        data = ik->ik_data;
+                        ik->ik_csr = IKCSR_RATTF|IKCSR_RDMAF|IKCSR_FNC1;
+                        ik->ik_pulse = IKPULSE_FNC2;
+                        return (data);
+                }
+        return (-1);
+}
+
+/*
+ * Handshake write to DR300. 
+ *
+ * Interrupts are enabled before completing the work
+ * so the caller should either be at splik or be
+ * prepared to take the interrupt immediately.
+ */
+diowrite(ik, v)
+        register struct ikdevice *ik;
+        u_short v;
+{
+        register int timeout;
+        register u_short csr;
+
+top:
+        /*
+         * Deposit data and generate dr300 attention
+         */
+        ik->ik_data = v;
+        ik->ik_csr = IKCSR_RDMAF|IKCSR_RATTF;
+        ik->ik_pulse = IKPULSE_FNC2;
+        for (timeout = ikdiotimo; timeout > 0; timeout--) {
+                csr = ik->ik_csr;
+#define IKCSR_DONE      (IKCSR_STATA|IKCSR_STATC)
+                if ((csr&IKCSR_DONE) == IKCSR_DONE) {
+                        /* 
+                         * Done, complete handshake by notifying dr300.
+                         */
+                        ik->ik_csr = IKCSR_IENA;        /* ~IKCSR_FNC1 */
+                        ik->ik_pulse = IKPULSE_FNC2;
+                        return (0);
+                }
+                /* beware of potential deadlock with dioread */
+                if ((csr&(IKCSR_ATTF|IKCSR_STATC)) == IKCSR_ATTF)
+                        goto top;
+        }
+        ik->ik_csr = IKCSR_IENA;
+        return (PSERROR_DIOTIMO);
+}
+
+/*ARGSUSED*/
+ikioctl(dev, cmd, data, flag)
+        dev_t dev;
+        int cmd;
+        caddr_t data;
+        int flag;
+{
+        int error = 0, unit = IKUNIT(dev), s;
+        register struct ik_softc *sc = &ik_softc[unit];
+
+        switch (cmd) {
+
+        case PSIOGETERROR:              /* get error code for last operation */
+                *(int *)data = sc->is_error;
+                break;
+
+        case PSIOLOOKUP: {              /* PS300 name lookup */
+                register struct pslookup *lp = (struct pslookup *)data;
+                register struct buf *bp;
+
+                if (lp->pl_len > PS_MAXNAMELEN)
+                        return (EINVAL);
+                bp = &rikbuf[unit];
+                s = splbio();
+                while (bp->b_flags&B_BUSY) {
+                        bp->b_flags |= B_WANTED;
+                        sleep((caddr_t)bp, PRIBIO+1);
+                }
+                splx(s);
+                bp->b_flags = B_BUSY | B_WRITE;
+                error = copyin(lp->pl_name, sc->is_buf, lp->pl_len);
+                if (error == 0) {
+                        if (lp->pl_len&1)
+                                sc->is_buf[lp->pl_len] = '\0';
+                        error = ikcommand(dev, PS_LOOKUP, lp->pl_len);
+                }
+                s = splbio();
+                if (bp->b_flags&B_WANTED)
+                        wakeup((caddr_t)bp);
+                splx(s);
+                bp->b_flags &= ~(B_BUSY|B_WANTED);
+                lp->pl_addr = sc->is_nameaddr.l;
+                break;
+        }
+        default:
+                return (ENOTTY);
+        }
+        return (error);
+}
+#endif