fix comments; check cs1&TRE
[unix-history] / usr / src / sys / vax / uba / up.c
index a6933a4..871777f 100644 (file)
@@ -1,17 +1,18 @@
-int    cs1del;
-int    printsw;
-/*     %H%     3.2     %G%     */
+/*     up.c    3.21    %G%     */
 
 /*
 
 /*
- * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery.
+ * UNIBUS disk driver with overlapped seeks and ECC recovery.
  *
  *
- * NB: This device is very sensitive: be aware that the code is the way
- *     it is for good reason and that there are delay loops here which may
- *     have to be lengthened if your processor is faster and which should
- *     probably be shortened if your processor is slower.
+ * This driver works marginally on an Emulex SC-11B controller with rev
+ * level J microcode, defining:
+ *     int     olducode = 1;
+ * to force CPU stalling delays.
  *
  *
- * This driver has been tested on a SC-11B Controller, configured
- * with the following internal switch settings:
+ * It has worked with no delays and no problems on a prototype
+ * SC-21 controller.  Emulex intends to upgrade all SC-11s on VAXes to SC-21s.
+ * You should get a SC-21 to replace any SC-11 on a VAX.
+ *
+ * SC-11B Controller switch settings:
  *     SW1-1   5/19 surfaces   (off, 19 surfaces on Ampex 9300)
  *     SW1-2   chksum enable   (off, checksum disabled)
  *     SW1-3   volume select   (off, 815 cylinders)
  *     SW1-1   5/19 surfaces   (off, 19 surfaces on Ampex 9300)
  *     SW1-2   chksum enable   (off, checksum disabled)
  *     SW1-3   volume select   (off, 815 cylinders)
@@ -23,26 +24,21 @@ int printsw;
  * and top mounted switches:
  *     SW2-1   extend opcodes  (off=open, disable)
  *     SW2-2   extend diag     (off=open, disable)
  * and top mounted switches:
  *     SW2-1   extend opcodes  (off=open, disable)
  *     SW2-2   extend diag     (off=open, disable)
- *     SW2-3   4 wd dma burst  (off=open, disable)
+ *     SW2-3   4 wd dma burst  (on=closed, enable)
  *     SW2-4   unused          (off=open)
  *     SW2-4   unused          (off=open)
- *
- * The controller transfers data much more rapidly with SW2-3 set,
- * but we have previously experienced problems with it set this way.
- * We intend to try this again in the near future.
- *
- *     wnj     June 14, 1980
  */
 
 #include "../h/param.h"
 #include "../h/systm.h"
  */
 
 #include "../h/param.h"
 #include "../h/systm.h"
+#include "../h/dk.h"
 #include "../h/buf.h"
 #include "../h/conf.h"
 #include "../h/dir.h"
 #include "../h/user.h"
 #include "../h/map.h"
 #include "../h/buf.h"
 #include "../h/conf.h"
 #include "../h/dir.h"
 #include "../h/user.h"
 #include "../h/map.h"
+#include "../h/pte.h"
 #include "../h/mba.h"
 #include "../h/mtpr.h"
 #include "../h/mba.h"
 #include "../h/mtpr.h"
-#include "../h/pte.h"
 #include "../h/uba.h"
 #include "../h/vm.h"
 
 #include "../h/uba.h"
 #include "../h/vm.h"
 
@@ -55,8 +51,8 @@ int   printsw;
  * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive.
  * If DK_NMAX is yet smaller, some drives are not monitored.
  */
  * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive.
  * If DK_NMAX is yet smaller, some drives are not monitored.
  */
-#define        DK_N    1
-#define        DK_NMAX 2
+#define        DK_N    2
+#define        DK_NMAX 3
 
 #define        ushort  unsigned short
 
 
 #define        ushort  unsigned short
 
@@ -84,6 +80,19 @@ struct       device
        ushort  upec2;          /* burst error bit pattern */
 };
 
        ushort  upec2;          /* burst error bit pattern */
 };
 
+/*
+ * Software extension to the upas register, so we can
+ * postpone starting SEARCH commands until the controller
+ * is not transferring.
+ */
+int    upsoftas;
+
+/*
+ * If upseek then we don't issue SEARCH commands but rather just
+ * settle for a SEEK to the correct cylinder.
+ */
+int    upseek;
+
 #define        UPADDR  ((struct device *)(UBA0_DEV + 0176700))
 
 #define        NUP     2               /* Number of drives this installation */
 #define        UPADDR  ((struct device *)(UBA0_DEV + 0176700))
 
 #define        NUP     2               /* Number of drives this installation */
@@ -94,14 +103,18 @@ struct     device
 /*
  * Constants controlling on-cylinder SEARCH usage.
  *
 /*
  * Constants controlling on-cylinder SEARCH usage.
  *
- * We assume that it takes SDIST sectors of time to set up a transfer.
- * If a drive is on-cylinder, and between SDIST and SDIST+RDIST sectors
- * from the first sector to be transferred, then we just perform the
- * transfer.  SDIST represents interrupt latency, RDIST the amount
- * of rotation which is tolerable to avoid another interrupt.
+ *     upSDIST/2 msec          time needed to start transfer
+ *     upRDIST/2 msec          tolerable rotational latency when on-cylinder
+ *
+ * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST
+ * and in the driver then we take it as it is.  Otherwise we do a SEARCH
+ * requesting an interrupt upSDIST sectors in advance.
  */
  */
-#define        SDIST   3               /* 2-3 sectors 1-1.5 msec */
-#define        RDIST   6               /* 5-6 sectors 2.5-3 msec */
+#define        _upSDIST        6               /* 3.0 msec */
+#define        _upRDIST        6               /* 3.0 msec */
+
+int    upSDIST = _upSDIST;
+int    upRDIST = _upRDIST;
 
 /*
  * To fill a 300M drive:
 
 /*
  * To fill a 300M drive:
@@ -119,7 +132,7 @@ struct      size
 } up_sizes[8] = {
        15884,  0,              /* A=cyl 0 thru 26 */
        33440,  27,             /* B=cyl 27 thru 81 */
 } up_sizes[8] = {
        15884,  0,              /* A=cyl 0 thru 26 */
        33440,  27,             /* B=cyl 27 thru 81 */
-       494912, 0,              /* C=cyl 0 thru 814 */
+       495520, 0,              /* C=cyl 0 thru 814 */
        15884,  562,            /* D=cyl 562 thru 588 */
        55936,  589,            /* E=cyl 589 thru 680 */
        81472,  681,            /* F=cyl 681 thru 814 */
        15884,  562,            /* D=cyl 562 thru 588 */
        55936,  589,            /* E=cyl 589 thru 680 */
        81472,  681,            /* F=cyl 681 thru 814 */
@@ -135,7 +148,7 @@ struct      size
  * +/- microinches.  Note that header compare inhibit (HCI) is not
  * tried (this makes sense only during read, in any case.)
  *
  * +/- microinches.  Note that header compare inhibit (HCI) is not
  * tried (this makes sense only during read, in any case.)
  *
- * ARE ALL THESE IMPLEMENTED ON 9300?
+ * NOT ALL OF THESE ARE IMPLEMENTED ON 9300!?!
  */
 #define        P400    020
 #define        M400    0220
  */
 #define        P400    020
 #define        M400    0220
@@ -174,6 +187,7 @@ struct      buf     rupbuf;                 /* Buffer for raw i/o */
 #define        OFFSET  014             /* Offset heads to try to recover error */
 #define        RTC     016             /* Return to center-line after OFFSET */
 #define        SEARCH  030             /* Search for cylinder+sector */
 #define        OFFSET  014             /* Offset heads to try to recover error */
 #define        RTC     016             /* Return to center-line after OFFSET */
 #define        SEARCH  030             /* Search for cylinder+sector */
+#define        SEEK    04              /* Seek to cylinder */
 #define        RECAL   06              /* Recalibrate, needed after seek error */
 #define        DCLR    010             /* Drive clear, after error */
 #define        WCOM    060             /* Write */
 #define        RECAL   06              /* Recalibrate, needed after seek error */
 #define        DCLR    010             /* Drive clear, after error */
 #define        WCOM    060             /* Write */
@@ -182,7 +196,7 @@ struct      buf     rupbuf;                 /* Buffer for raw i/o */
 /* Other bits of upcs1 */
 #define        IE      0100            /* Controller wide interrupt enable */
 #define        TRE     040000          /* Transfer error */
 /* Other bits of upcs1 */
 #define        IE      0100            /* Controller wide interrupt enable */
 #define        TRE     040000          /* Transfer error */
-#define        RDY     020             /* Transfer terminated */
+#define        RDY     0200            /* Transfer terminated */
 
 /* Drive status bits of upds */
 #define        PIP     020000          /* Positioning in progress */
 
 /* Drive status bits of upds */
 #define        PIP     020000          /* Positioning in progress */
@@ -192,6 +206,8 @@ struct      buf     rupbuf;                 /* Buffer for raw i/o */
 #define        MOL     010000          /* Drive is online, heads loaded, etc */
 #define        DRY     0200            /* Drive ready */
 
 #define        MOL     010000          /* Drive is online, heads loaded, etc */
 #define        DRY     0200            /* Drive ready */
 
+/* Bits of upcs2 */
+#define        CLR     040             /* Controller clear */
 /* Bits of uper1 */
 #define        DCK     0100000         /* Ecc error occurred */
 #define        ECH     0100            /* Ecc error was unrecoverable */
 /* Bits of uper1 */
 #define        DCK     0100000         /* Ecc error occurred */
 #define        ECH     0100            /* Ecc error was unrecoverable */
@@ -205,24 +221,27 @@ struct    buf     rupbuf;                 /* Buffer for raw i/o */
 int    up_ubinfo;              /* Information about UBA usage saved here */
 /*
  * The EMULEX controller balks if accessed quickly after
 int    up_ubinfo;              /* Information about UBA usage saved here */
 /*
  * The EMULEX controller balks if accessed quickly after
- * certain operations.  The exact timing has not yet been
- * determined, but delays are known to be needed when changing
- * the selected drive (by writing in upcs2), and thought to be
- * needed after operations like PRESET and DCLR.  The following
- * variables control the delay, DELAY(n) is approximately n usec.
+ * certain operations.  With rev J delays seem to be needed only
+ * when selecting a new unit, and in drive initialization type
+ * like PRESET and DCLR.  The following variables control the delay
+ * DELAY(n) is approximately n usec.
  */
  */
+int    olducode = 1;
 int    idelay = 500;           /* Delay after PRESET or DCLR */
 int    idelay = 500;           /* Delay after PRESET or DCLR */
-int    sdelay = 500;           /* Delay after selecting drive in upcs2 */
-int    iedel1 = 500;
-int    iedel2 = 500;
-int    iedel3 = 0;
-int    iedel4 = 500;
+int    osdelay = 150;          /* Old delay after selecting drive in upcs2 */
+int    ordelay = 100;          /* Old delay after SEARCH */
+int    oasdel = 100;           /* Old delay after clearing bit in upas */
+int    nsdelay = 25;
 
 #define        DELAY(N)                { register int d; d = N; while (--d > 0); }
  
 int    nwaitcs2;               /* How many sdelay loops ? */
 int    neasycs2;               /* How many sdelay loops not needed ? */
 
 
 #define        DELAY(N)                { register int d; d = N; while (--d > 0); }
  
 int    nwaitcs2;               /* How many sdelay loops ? */
 int    neasycs2;               /* How many sdelay loops not needed ? */
 
+int    up_wticks;              /* Ticks waiting for interrupt */
+int    upwstart;               /* Have started guardian */
+int    upwatch();
+
 #ifdef INTRLVE
 daddr_t dkblock();
 #endif
 #ifdef INTRLVE
 daddr_t dkblock();
 #endif
@@ -242,6 +261,10 @@ register struct buf *bp;
        register unit, xunit;
        long sz, bn;
 
        register unit, xunit;
        long sz, bn;
 
+       if (upwstart == 0) {
+               timeout((caddr_t)upwatch, 0, HZ);
+               upwstart++;
+       }
        xunit = minor(bp->b_dev) & 077;
        sz = bp->b_bcount;
        sz = (sz+511) >> 9;             /* transfer size in 512 byte sectors */
        xunit = minor(bp->b_dev) & 077;
        sz = bp->b_bcount;
        sz = (sz+511) >> 9;             /* transfer size in 512 byte sectors */
@@ -258,9 +281,9 @@ register struct buf *bp;
        (void) spl5();
        disksort(dp, bp);
        if (dp->b_active == 0) {
        (void) spl5();
        disksort(dp, bp);
        if (dp->b_active == 0) {
-               upustart(unit);
+               (void) upustart(unit);
                if (uptab.b_actf && uptab.b_active == 0)
                if (uptab.b_actf && uptab.b_active == 0)
-                       upstart();
+                       (void) upstart();
        }
        (void) spl0();
 }
        }
        (void) spl0();
 }
@@ -277,29 +300,55 @@ register unit;
        register struct device *upaddr = UPADDR;
        daddr_t bn;
        int sn, cn, csn;
        register struct device *upaddr = UPADDR;
        daddr_t bn;
        int sn, cn, csn;
+       int didie = 0;
 
 
-       if (printsw&1) printf("upustart\n");
-       if (unit >= NUP)
-               return;
        /*
        /*
-        * Whether or not it was before, this unit is no longer busy.
-        * Check to see if there is (still or now) a request in this
-        * drives queue, and if there is, select this unit.
+        * Other drivers tend to say something like
+        *      upaddr->upcs1 = IE;
+        *      upaddr->upas = 1<<unit;
+        * here, but the SC-11B will cancel a command which
+        * happens to be sitting in the cs1 if you clear the go
+        * bit by storing there (so the first is not safe),
+        * and it also does not like being bothered with operations
+        * such as clearing upas when a transfer is active (as
+        * it may well be.)
+        *
+        * Thus we keep careful track of when we re-enable IE
+        * after an interrupt and do it only if we didn't issue
+        * a command which re-enabled it as a matter of course.
+        * We clear bits in upas in the interrupt routine, when
+        * no transfers are active.
         */
         */
+       if (unit >= NUP)
+               goto out;
        if (unit+DK_N <= DK_NMAX)
                dk_busy &= ~(1<<(unit+DK_N));
        dp = &uputab[unit];
        if ((bp = dp->b_actf) == NULL)
        if (unit+DK_N <= DK_NMAX)
                dk_busy &= ~(1<<(unit+DK_N));
        dp = &uputab[unit];
        if ((bp = dp->b_actf) == NULL)
-               return;
+               goto out;
+       /*
+        * The SC-11B doesn't start SEARCH commands when transfers are
+        * in progress.  In fact, it tends to get confused when given
+        * SEARCH'es during transfers, generating interrupts with neither
+        * RDY nor a bit in the upas register.  Thus we defer
+        * until an interrupt when a transfer is pending.
+        */
+       if (uptab.b_active) {
+               upsoftas |= 1<<unit;
+               return (0);
+       }
+       if (dp->b_active)
+               goto done;
+       dp->b_active = 1;
        if ((upaddr->upcs2 & 07) != unit) {
                upaddr->upcs2 = unit;
        if ((upaddr->upcs2 & 07) != unit) {
                upaddr->upcs2 = unit;
-               DELAY(sdelay);
+               DELAY(olducode ? osdelay : nsdelay);
                nwaitcs2++;
        } else
                neasycs2++;
        /*
         * If we have changed packs or just initialized,
                nwaitcs2++;
        } else
                neasycs2++;
        /*
         * If we have changed packs or just initialized,
-        * the the volume will not be valid; if so, clear
+        * then the volume will not be valid; if so, clear
         * the drive, preset it and put in 16bit/word mode.
         */
        if ((upaddr->upds & VV) == 0) {
         * the drive, preset it and put in 16bit/word mode.
         */
        if ((upaddr->upds & VV) == 0) {
@@ -308,87 +357,68 @@ register unit;
                upaddr->upcs1 = IE|PRESET|GO;
                DELAY(idelay);
                upaddr->upof = FMT22;
                upaddr->upcs1 = IE|PRESET|GO;
                DELAY(idelay);
                upaddr->upof = FMT22;
+               didie = 1;
        }
        }
-       /*
-        * We are called from upstrategy when a new request arrives
-        * if we are not already active (with dp->b_active == 0),
-        * and we then set dp->b_active to 1 if we are to SEARCH
-        * for the desired cylinder, or 2 if we are on-cylinder.
-        * If we SEARCH then we will later be called from upintr()
-        * when the search is complete, and will link this disk onto
-        * the uptab.  We then set dp->b_active to 2 so that upintr()
-        * will not call us again.
-        *
-        * NB: Other drives clear the bit in the attention status
-        * (i.e. upas) register corresponding to the drive when they
-        * place the drive on the ready (i.e. uptab) queue.  This does
-        * not work with the Emulex, as the controller hangs the UBA
-        * of the VAX shortly after the upas register is set, for
-        * reasons unknown.  This only occurs in multi-spindle configurations,
-        * but to avoid the problem we use the fact that dp->b_active is
-        * 2 to replace the clearing of the upas bit.
-        */
-       if (dp->b_active)
-               goto done;
-       dp->b_active = 1;
        if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
        if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
-               goto done;      /* Will redetect error in upstart() soon */
-
+               goto done;
        /*
         * Do enough of the disk address decoding to determine
         * which cylinder and sector the request is on.
        /*
         * Do enough of the disk address decoding to determine
         * which cylinder and sector the request is on.
-        * Then compute the number of the sector SDIST sectors before
-        * the one where the transfer is to start, this being the
-        * point where we wish to attempt to begin the transfer,
-        * allowing approximately SDIST/2 msec for interrupt latency
-        * and preparation of the request.
-        *
         * If we are on the correct cylinder and the desired sector
         * If we are on the correct cylinder and the desired sector
-        * lies between SDIST and SDIST+RDIST sectors ahead of us, then
+        * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then
         * we don't bother to SEARCH but just begin the transfer asap.
         * we don't bother to SEARCH but just begin the transfer asap.
+        * Otherwise ask for a interrupt upSDIST sectors ahead.
         */
        bn = dkblock(bp);
        cn = bp->b_cylin;
        sn = bn%(NSECT*NTRAC);
         */
        bn = dkblock(bp);
        cn = bp->b_cylin;
        sn = bn%(NSECT*NTRAC);
-       sn = (sn+NSECT-SDIST)%NSECT;
+       sn = (sn+NSECT-upSDIST)%NSECT;
 
        if (cn - upaddr->updc)
                goto search;            /* Not on-cylinder */
 
        if (cn - upaddr->updc)
                goto search;            /* Not on-cylinder */
+       else if (upseek)
+               goto done;              /* Ok just to be on-cylinder */
        csn = (upaddr->upla>>6) - sn - 1;
        if (csn < 0)
                csn += NSECT;
        csn = (upaddr->upla>>6) - sn - 1;
        if (csn < 0)
                csn += NSECT;
-       if (csn > NSECT-RDIST)
+       if (csn > NSECT-upRDIST)
                goto done;
 
 search:
        upaddr->updc = cn;
                goto done;
 
 search:
        upaddr->updc = cn;
-       upaddr->upda = sn;
-       if (cs1del&8) DELAY(500);
-       upaddr->upcs1 = IE|SEARCH|GO;
-       if (cs1del&8) DELAY(500);
+       if (upseek)
+               upaddr->upcs1 = IE|SEEK|GO;
+       else {
+               upaddr->upda = sn;
+               upaddr->upcs1 = IE|SEARCH|GO;
+       }
+       didie = 1;
        /*
         * Mark this unit busy.
         */
        unit += DK_N;
        /*
         * Mark this unit busy.
         */
        unit += DK_N;
-       if (unit <= DK_NMAX) {
+       if (unit <= DK_NMAX && DK_N+NUP <= DK_NMAX) {
                dk_busy |= 1<<unit;
                dk_numb[unit]++;
        }
                dk_busy |= 1<<unit;
                dk_numb[unit]++;
        }
-       return;
+       if (olducode)
+               DELAY(ordelay);
+       goto out;
 
 done:
        /*
 
 done:
        /*
-        * This unit is ready to go.  Make active == 2 so
-        * we won't get called again (by upintr() because upas&(1<<unit))
-        * and link us onto the chain of ready disks.
+        * This unit is ready to go so
+        * link it onto the chain of ready disks.
         */
         */
-       dp->b_active = 2;
        dp->b_forw = NULL;
        if (uptab.b_actf == NULL)
                uptab.b_actf = dp;
        else
                uptab.b_actl->b_forw = dp;
        uptab.b_actl = dp;
        dp->b_forw = NULL;
        if (uptab.b_actf == NULL)
                uptab.b_actf = dp;
        else
                uptab.b_actl->b_forw = dp;
        uptab.b_actl = dp;
+
+out:
+       return (didie);
 }
 
 /*
 }
 
 /*
@@ -402,7 +432,6 @@ upstart()
        daddr_t bn;
        int dn, sn, tn, cn, cmd;
 
        daddr_t bn;
        int dn, sn, tn, cn, cmd;
 
-       if (printsw&2) printf("upstart\n");
 loop:
        /*
         * Pick a drive off the queue of ready drives, and
 loop:
        /*
         * Pick a drive off the queue of ready drives, and
@@ -413,7 +442,7 @@ loop:
         * request queue.
         */
        if ((dp = uptab.b_actf) == NULL)
         * request queue.
         */
        if ((dp = uptab.b_actf) == NULL)
-               return;
+               return (0);
        if ((bp = dp->b_actf) == NULL) {
                uptab.b_actf = dp->b_forw;
                goto loop;
        if ((bp = dp->b_actf) == NULL) {
                uptab.b_actf = dp->b_forw;
                goto loop;
@@ -434,11 +463,11 @@ loop:
        upaddr = UPADDR;
        if ((upaddr->upcs2 & 07) != dn) {
                upaddr->upcs2 = dn;
        upaddr = UPADDR;
        if ((upaddr->upcs2 & 07) != dn) {
                upaddr->upcs2 = dn;
-               DELAY(sdelay);
+               /* DELAY(sdelay);               Provided by ubasetup() */
                nwaitcs2++;
        } else
                neasycs2++;
                nwaitcs2++;
        } else
                neasycs2++;
-       up_ubinfo = ubasetup(bp, 1);    /* In a funny place for delay... */
+       up_ubinfo = ubasetup(bp, 1);    /* Providing delay */
        /*
         * If drive is not present and on-line, then
         * get rid of this with an error and loop to get
        /*
         * If drive is not present and on-line, then
         * get rid of this with an error and loop to get
@@ -446,6 +475,7 @@ loop:
         * (Then on to any other ready drives.)
         */
        if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
         * (Then on to any other ready drives.)
         */
        if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
+               printf("!DPR || !MOL, unit %d, ds %o\n", dn, upaddr->upds);
                uptab.b_active = 0;
                uptab.b_errcnt = 0;
                dp->b_actf = bp->av_forw;
                uptab.b_active = 0;
                uptab.b_errcnt = 0;
                dp->b_actf = bp->av_forw;
@@ -474,16 +504,13 @@ loop:
        upaddr->updc = cn;
        upaddr->upda = (tn << 8) + sn;
        upaddr->upba = up_ubinfo;
        upaddr->updc = cn;
        upaddr->upda = (tn << 8) + sn;
        upaddr->upba = up_ubinfo;
-       if (cs1del&1) DELAY(500);
        upaddr->upwc = -bp->b_bcount / sizeof (short);
        cmd = (up_ubinfo >> 8) & 0x300;
        if (bp->b_flags & B_READ)
                cmd |= IE|RCOM|GO;
        else
                cmd |= IE|WCOM|GO;
        upaddr->upwc = -bp->b_bcount / sizeof (short);
        cmd = (up_ubinfo >> 8) & 0x300;
        if (bp->b_flags & B_READ)
                cmd |= IE|RCOM|GO;
        else
                cmd |= IE|WCOM|GO;
-       if (cs1del&1) DELAY(500);
        upaddr->upcs1 = cmd;
        upaddr->upcs1 = cmd;
-       if (cs1del&1) DELAY(500);
        /*
         * This is a controller busy situation.
         * Record in dk slot NUP+DK_N (after last drive)
        /*
         * This is a controller busy situation.
         * Record in dk slot NUP+DK_N (after last drive)
@@ -499,6 +526,7 @@ loop:
                dk_numb[unit]++;
                dk_wds[unit] += bp->b_bcount>>6;
        }
                dk_numb[unit]++;
                dk_wds[unit] += bp->b_bcount>>6;
        }
+       return (1);
 }
 
 /*
 }
 
 /*
@@ -516,8 +544,11 @@ upintr()
        register unit;
        register struct device *upaddr = UPADDR;
        int as = upaddr->upas & 0377;
        register unit;
        register struct device *upaddr = UPADDR;
        int as = upaddr->upas & 0377;
+       int oupsoftas;
+       int needie = 1;
 
 
-       if (printsw&4) printf("upintr as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active);
+       (void) spl6();
+       up_wticks = 0;
        if (uptab.b_active) {
                /*
                 * The drive is transferring, thus the hardware
        if (uptab.b_active) {
                /*
                 * The drive is transferring, thus the hardware
@@ -525,8 +556,10 @@ upintr()
                 * completes; check for it anyways.
                 */
                if ((upaddr->upcs1 & RDY) == 0) {
                 * completes; check for it anyways.
                 */
                if ((upaddr->upcs1 & RDY) == 0) {
-                       printf("upintr b_active && !RDY\n");
-                       goto out;
+                       printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1,
+                           upaddr->upds, upaddr->upwc);
+                       printf("as=%d act %d %d %d\n", as, uptab.b_active,
+                           uputab[0].b_active, uputab[1].b_active);
                }
                /*
                 * Mark controller or drive not busy, and check for an
                }
                /*
                 * Mark controller or drive not busy, and check for an
@@ -539,22 +572,20 @@ upintr()
                        dk_busy &= ~(1<<(DK_N+NUP));
                else if (DK_N+unit <= DK_NMAX)
                        dk_busy &= ~(1<<(DK_N+unit));
                        dk_busy &= ~(1<<(DK_N+NUP));
                else if (DK_N+unit <= DK_NMAX)
                        dk_busy &= ~(1<<(DK_N+unit));
-               if (upaddr->upcs1 & TRE) {
+               if ((upaddr->upcs2 & 07) != unit) {
+                       upaddr->upcs2 = unit;
+                       DELAY(olducode ? osdelay : nsdelay);
+                       nwaitcs2++;
+               } else
+                       neasycs2++;
+               if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) {
                        /*
                         * An error occurred, indeed.  Select this unit
                         * to get at the drive status (a SEARCH may have
                         * intervened to change the selected unit), and
                         * wait for the command which caused the interrupt
                         * to complete (DRY).
                        /*
                         * An error occurred, indeed.  Select this unit
                         * to get at the drive status (a SEARCH may have
                         * intervened to change the selected unit), and
                         * wait for the command which caused the interrupt
                         * to complete (DRY).
-                        *
-                        * WHY IS THE WAIT NECESSARY?
                         */
                         */
-                       if ((upaddr->upcs2 & 07) != unit) {
-                               upaddr->upcs2 = unit;
-                               DELAY(sdelay);
-                               nwaitcs2++;
-                       } else
-                               neasycs2++;
                        while ((upaddr->upds & DRY) == 0)
                                DELAY(25);
                        /*
                        while ((upaddr->upds & DRY) == 0)
                                DELAY(25);
                        /*
@@ -586,6 +617,7 @@ upintr()
                         */
                        upaddr->upcs1 = TRE|IE|DCLR|GO;
                        DELAY(idelay);
                         */
                        upaddr->upcs1 = TRE|IE|DCLR|GO;
                        DELAY(idelay);
+                       needie = 0;
                        if ((uptab.b_errcnt&07) == 4) {
                                upaddr->upcs1 = RECAL|GO|IE;
                                DELAY(idelay);
                        if ((uptab.b_errcnt&07) == 4) {
                                upaddr->upcs1 = RECAL|GO|IE;
                                DELAY(idelay);
@@ -603,17 +635,12 @@ upintr()
                 * on this drive with the upustart routine (if any).
                 */
                if (uptab.b_active) {
                 * on this drive with the upustart routine (if any).
                 */
                if (uptab.b_active) {
-                       if ((upaddr->upcs2 & 07) != unit) {
-                               upaddr->upcs2 = unit;
-                               DELAY(sdelay);
-                               nwaitcs2++;
-                       } else
-                               neasycs2++;
                        if (uptab.b_errcnt >= 16) {
                                upaddr->upcs1 = RTC|GO|IE;
                                DELAY(idelay);
                                while (upaddr->upds & PIP)
                                        DELAY(25);
                        if (uptab.b_errcnt >= 16) {
                                upaddr->upcs1 = RTC|GO|IE;
                                DELAY(idelay);
                                while (upaddr->upds & PIP)
                                        DELAY(25);
+                               needie = 0;
                        }
                        uptab.b_active = 0;
                        uptab.b_errcnt = 0;
                        }
                        uptab.b_active = 0;
                        uptab.b_errcnt = 0;
@@ -622,26 +649,24 @@ upintr()
                        dp->b_errcnt = 0;
                        dp->b_actf = bp->av_forw;
                        bp->b_resid = (-upaddr->upwc * sizeof(short));
                        dp->b_errcnt = 0;
                        dp->b_actf = bp->av_forw;
                        bp->b_resid = (-upaddr->upwc * sizeof(short));
-                       if (cs1del&2) DELAY(500);
-                       upaddr->upcs1 = IE;
-                       if (cs1del&2) DELAY(500);
+                       if (bp->b_resid)
+                               printf("resid %d ds %o er? %o %o %o\n",
+                                   bp->b_resid, upaddr->upds,
+                                   upaddr->uper1, upaddr->uper2, upaddr->uper3);
                        iodone(bp);
                        if(dp->b_actf)
                        iodone(bp);
                        if(dp->b_actf)
-                               upustart(unit);
+                               if (upustart(unit))
+                                       needie = 0;
                }
                as &= ~(1<<unit);
                }
                as &= ~(1<<unit);
+               upsoftas &= ~(1<<unit);
                ubafree(up_ubinfo), up_ubinfo = 0;
                ubafree(up_ubinfo), up_ubinfo = 0;
-       }
-#ifndef notdef
-       else {
-               if (printsw&64) printf("cs1 %o\n", upaddr->upcs1);
+       } else {
                if (upaddr->upcs1 & TRE) {
                        upaddr->upcs1 = TRE;
                        DELAY(idelay);
                if (upaddr->upcs1 & TRE) {
                        upaddr->upcs1 = TRE;
                        DELAY(idelay);
-                       if (printsw&64) printf("after TRE cs1 %o\n", upaddr->upcs1);
                }
        }
                }
        }
-#endif
        /*
         * If we have a unit with an outstanding SEARCH,
         * and the hardware indicates the unit requires attention,
        /*
         * If we have a unit with an outstanding SEARCH,
         * and the hardware indicates the unit requires attention,
@@ -649,22 +674,25 @@ upintr()
         * Finally, if the controller is not transferring
         * start it if any drives are now ready to transfer.
         */
         * Finally, if the controller is not transferring
         * start it if any drives are now ready to transfer.
         */
+       as |= upsoftas;
+       oupsoftas = upsoftas;
+       upsoftas = 0;
        for (unit = 0; unit < NUP; unit++)
        for (unit = 0; unit < NUP; unit++)
-               if (as & (1<<unit))
-                       if (uputab[unit].b_active == 1)
-                               upustart(unit);
-                       else {
+               if ((as|oupsoftas) & (1<<unit)) {
+                       if (as & (1<<unit)) {
                                upaddr->upas = 1<<unit;
                                upaddr->upas = 1<<unit;
-                               DELAY(1000);
+                               if (olducode)
+                                       DELAY(oasdel);
                        }
                        }
+                       if (upustart(unit))
+                               needie = 0;
+               }
        if (uptab.b_actf && uptab.b_active == 0)
        if (uptab.b_actf && uptab.b_active == 0)
-               upstart();
+               if (upstart())
+                       needie = 0;
 out:
 out:
-       if (cs1del&4) DELAY(500);
-       if ((upaddr->upcs1&IE) == 0)
+       if (needie)
                upaddr->upcs1 = IE;
                upaddr->upcs1 = IE;
-       if (cs1del&4) DELAY(500);
-       if (printsw&128) printf("exit cs1 %o\n", upaddr->upcs1);
 }
 
 upread(dev)
 }
 
 upread(dev)
@@ -695,7 +723,6 @@ register struct buf *bp;
        int reg, bit, byte, npf, mask, o, cmd, ubaddr;
        int bn, cn, tn, sn;
 
        int reg, bit, byte, npf, mask, o, cmd, ubaddr;
        int bn, cn, tn, sn;
 
-       if (printsw&8) printf("upecc\n");
        /*
         * Npf is the number of sectors transferred before the sector
         * containing the ECC error, and reg is the UBA register
        /*
         * Npf is the number of sectors transferred before the sector
         * containing the ECC error, and reg is the UBA register
@@ -765,3 +792,58 @@ register struct buf *bp;
        up->upcs1 = cmd;
        return (1);
 }
        up->upcs1 = cmd;
        return (1);
 }
+
+/*
+ * Reset driver after UBA init.
+ * Cancel software state of all pending transfers
+ * and restart all units and the controller.
+ */
+upreset()
+{
+       int unit;
+
+       printf(" up");
+       uptab.b_active = 0;
+       uptab.b_actf = uptab.b_actl = 0;
+       if (DK_N+NUP == DK_NMAX)
+               dk_busy &= ~(1<<(DK_N+NUP));
+       if (up_ubinfo) {
+               printf("<%d>", (up_ubinfo>>28)&0xf);
+               ubafree(up_ubinfo), up_ubinfo = 0;
+       }
+       UPADDR->upcs2 = CLR;            /* clear controller */
+       DELAY(idelay);
+       for (unit = 0; unit < NUP; unit++) {
+               uputab[unit].b_active = 0;
+               (void) upustart(unit);
+       }
+       (void) upstart();
+}
+
+/*
+ * Wake up every second and if an interrupt is pending
+ * but nothing has happened increment a counter.
+ * If nothing happens for 20 seconds, reset the controller
+ * and begin anew.
+ */
+upwatch()
+{
+       int i;
+
+       timeout((caddr_t)upwatch, 0, HZ);
+       if (uptab.b_active == 0) {
+               for (i = 0; i < NUP; i++)
+                       if (uputab[i].b_active)
+                               goto active;
+               up_wticks = 0;          /* idling */
+               return;
+       }
+active:
+       up_wticks++;
+       if (up_wticks >= 20) {
+               up_wticks = 0;
+               printf("LOST INTERRUPT RESET");
+               upreset();
+               printf("\n");
+       }
+}