dont servo-offset on write retries
[unix-history] / usr / src / sys / vax / uba / up.c
index 53bf735..06325df 100644 (file)
@@ -1,16 +1,18 @@
-/*     %H%     3.14    %G%     */
+/*     up.c    3.23    %G%     */
 
 
-#define        spl5    spl6            /* block clock, for delay loop's sake */
 /*
 /*
- * 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)
  * 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.
- *
- * NB: OUR SYSTEM CURRENTLY GETS UBA ERRORS WHEN RUNNING THIS DRIVER
- *     AND THE BUS OCCASIONALLY HANGS, NECESSITATING THE DEVIE RESET
- *     CODE WHICH RE-INITS THE UNIBUS.  YECHHH.
  */
 
 #include "../h/param.h"
  */
 
 #include "../h/param.h"
@@ -42,9 +36,9 @@
 #include "../h/dir.h"
 #include "../h/user.h"
 #include "../h/map.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"
 
@@ -91,7 +85,7 @@ struct        device
  * postpone starting SEARCH commands until the controller
  * is not transferring.
  */
  * postpone starting SEARCH commands until the controller
  * is not transferring.
  */
-int    softas;
+int    upsoftas;
 
 /*
  * If upseek then we don't issue SEARCH commands but rather just
 
 /*
  * If upseek then we don't issue SEARCH commands but rather just
@@ -138,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 */
@@ -154,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
@@ -202,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 */
@@ -212,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 */
@@ -225,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 = 150;           /* Delay after selecting drive in upcs2 */
-int    rdelay = 100;           /* Delay after SEARCH */
-int    asdel = 100;            /* Delay after clearing bit in upas */
-
-int    csdel2 = 0;             /* ??? Delay in upstart ??? */
+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
@@ -262,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 */
@@ -331,7 +334,7 @@ register unit;
         * until an interrupt when a transfer is pending.
         */
        if (uptab.b_active) {
         * until an interrupt when a transfer is pending.
         */
        if (uptab.b_active) {
-               softas |= 1<<unit;
+               upsoftas |= 1<<unit;
                return (0);
        }
        if (dp->b_active)
                return (0);
        }
        if (dp->b_active)
@@ -339,7 +342,7 @@ register unit;
        dp->b_active = 1;
        if ((upaddr->upcs2 & 07) != unit) {
                upaddr->upcs2 = unit;
        dp->b_active = 1;
        if ((upaddr->upcs2 & 07) != unit) {
                upaddr->upcs2 = unit;
-               DELAY(sdelay);
+               DELAY(olducode ? osdelay : nsdelay);
                nwaitcs2++;
        } else
                neasycs2++;
                nwaitcs2++;
        } else
                neasycs2++;
@@ -354,7 +357,6 @@ register unit;
                upaddr->upcs1 = IE|PRESET|GO;
                DELAY(idelay);
                upaddr->upof = FMT22;
                upaddr->upcs1 = IE|PRESET|GO;
                DELAY(idelay);
                upaddr->upof = FMT22;
-               printf("VV done ds %o, er? %o %o %o\n", upaddr->upds, upaddr->uper1, upaddr->uper2, upaddr->uper3);
                didie = 1;
        }
        if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
                didie = 1;
        }
        if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
@@ -395,11 +397,12 @@ search:
         * 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]++;
        }
-       DELAY(rdelay);
+       if (olducode)
+               DELAY(ordelay);
        goto out;
 
 done:
        goto out;
 
 done:
@@ -430,7 +433,6 @@ upstart()
        int dn, sn, tn, cn, cmd;
 
 loop:
        int dn, sn, tn, cn, cmd;
 
 loop:
-       if (csdel2) DELAY(csdel2);
        /*
         * Pick a drive off the queue of ready drives, and
         * perform the first transfer on its queue.
        /*
         * Pick a drive off the queue of ready drives, and
         * perform the first transfer on its queue.
@@ -473,21 +475,26 @@ 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;
-               dp->b_active = 0;
-               bp->b_flags |= B_ERROR;
-               iodone(bp);
-               ubafree(up_ubinfo), up_ubinfo = 0;      /* A funny place ... */
-               goto loop;
+               printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds);
+               if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
+                       printf("-- hard\n");
+                       uptab.b_active = 0;
+                       uptab.b_errcnt = 0;
+                       dp->b_actf = bp->av_forw;
+                       dp->b_active = 0;
+                       bp->b_flags |= B_ERROR;
+                       iodone(bp);
+                       /* A funny place to do this ... */
+                       ubafree(up_ubinfo), up_ubinfo = 0;
+                       goto loop;
+               }
+               printf("-- came back\n");
        }
        /*
         * If this is a retry, then with the 16'th retry we
         * begin to try offsetting the heads to recover the data.
         */
        }
        /*
         * If this is a retry, then with the 16'th retry we
         * begin to try offsetting the heads to recover the data.
         */
-       if (uptab.b_errcnt >= 16) {
+       if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE)) {
                upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
                upaddr->upcs1 = IE|OFFSET|GO;
                DELAY(idelay);
                upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
                upaddr->upcs1 = IE|OFFSET|GO;
                DELAY(idelay);
@@ -542,10 +549,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 osoftas;
+       int oupsoftas;
        int needie = 1;
 
        (void) spl6();
        int needie = 1;
 
        (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
@@ -555,7 +563,8 @@ upintr()
                if ((upaddr->upcs1 & RDY) == 0) {
                        printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1,
                            upaddr->upds, upaddr->upwc);
                if ((upaddr->upcs1 & RDY) == 0) {
                        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);
+                       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
@@ -570,11 +579,11 @@ printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1]
                        dk_busy &= ~(1<<(DK_N+unit));
                if ((upaddr->upcs2 & 07) != unit) {
                        upaddr->upcs2 = unit;
                        dk_busy &= ~(1<<(DK_N+unit));
                if ((upaddr->upcs2 & 07) != unit) {
                        upaddr->upcs2 = unit;
-                       DELAY(sdelay);
+                       DELAY(olducode ? osdelay : nsdelay);
                        nwaitcs2++;
                } else
                        neasycs2++;
                        nwaitcs2++;
                } else
                        neasycs2++;
-               if (upaddr->upds & ERR) {
+               if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) {
                        /*
                         * An error occurred, indeed.  Select this unit
                         * to get at the drive status (a SEARCH may have
                        /*
                         * An error occurred, indeed.  Select this unit
                         * to get at the drive status (a SEARCH may have
@@ -646,7 +655,8 @@ printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1]
                        dp->b_actf = bp->av_forw;
                        bp->b_resid = (-upaddr->upwc * sizeof(short));
                        if (bp->b_resid)
                        dp->b_actf = bp->av_forw;
                        bp->b_resid = (-upaddr->upwc * sizeof(short));
                        if (bp->b_resid)
-                               printf("resid %d ds %o er? %o %o %o\n", bp->b_resid, upaddr->upds,
+                               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)
                                    upaddr->uper1, upaddr->uper2, upaddr->uper3);
                        iodone(bp);
                        if(dp->b_actf)
@@ -654,7 +664,7 @@ printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1]
                                        needie = 0;
                }
                as &= ~(1<<unit);
                                        needie = 0;
                }
                as &= ~(1<<unit);
-               softas &= ~(1<<unit);
+               upsoftas &= ~(1<<unit);
                ubafree(up_ubinfo), up_ubinfo = 0;
        } else {
                if (upaddr->upcs1 & TRE) {
                ubafree(up_ubinfo), up_ubinfo = 0;
        } else {
                if (upaddr->upcs1 & TRE) {
@@ -669,14 +679,15 @@ printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1]
         * 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 |= softas;
-       osoftas = softas;
-       softas = 0;
+       as |= upsoftas;
+       oupsoftas = upsoftas;
+       upsoftas = 0;
        for (unit = 0; unit < NUP; unit++)
        for (unit = 0; unit < NUP; unit++)
-               if ((as|osoftas) & (1<<unit)) {
+               if ((as|oupsoftas) & (1<<unit)) {
                        if (as & (1<<unit)) {
                                upaddr->upas = 1<<unit;
                        if (as & (1<<unit)) {
                                upaddr->upas = 1<<unit;
-                               if (asdel) DELAY(asdel);
+                               if (olducode)
+                                       DELAY(oasdel);
                        }
                        if (upustart(unit))
                                needie = 0;
                        }
                        if (upustart(unit))
                                needie = 0;
@@ -805,9 +816,39 @@ upreset()
                printf("<%d>", (up_ubinfo>>28)&0xf);
                ubafree(up_ubinfo), up_ubinfo = 0;
        }
                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();
 }
        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");
+       }
+}