Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/mca edc_cmd_wait(): it is absolutely necessary to wa...



details:   https://anonhg.NetBSD.org/src/rev/3103fd183eac
branches:  trunk
changeset: 524823:3103fd183eac
user:      jdolecek <jdolecek%NetBSD.org@localhost>
date:      Sun Mar 31 10:01:26 2002 +0000

description:
edc_cmd_wait(): it is absolutely necessary to wait for the BSR_CMD_INPROGRESS
  flag to clear, even when the COMMAND COMPLETE interrupt already did happen,
  otherwise we get ATTENTION ERROR for next command if it happens soon
  enough; this fixes the reliability problems introduced by previous change

some other cleanup & simlify of edc_cmd_wait()/edc_run_cmd(), the 'secs'
  is just a hint used in !poll case only
add some comments
move status_block[] back to edc_mca_softc, to save stack memory
make #ifdef DEBUG #ifdef EDC_DEBUG and g/c some obsolete debug stuff
make some EAGAINs EIOs
edc_intr(): wakeup the waiter for any command, not just READ/WRITE DATA

diffstat:

 sys/dev/mca/edc_mca.c |  202 +++++++++++++++++++++----------------------------
 1 files changed, 88 insertions(+), 114 deletions(-)

diffs (truncated from 401 to 300 lines):

diff -r af64926972e3 -r 3103fd183eac sys/dev/mca/edc_mca.c
--- a/sys/dev/mca/edc_mca.c     Sun Mar 31 08:02:08 2002 +0000
+++ b/sys/dev/mca/edc_mca.c     Sun Mar 31 10:01:26 2002 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: edc_mca.c,v 1.15 2001/12/31 22:09:27 thorpej Exp $     */
+/*     $NetBSD: edc_mca.c,v 1.16 2002/03/31 10:01:26 jdolecek Exp $    */
 
 /*
  * Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -50,7 +50,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: edc_mca.c,v 1.15 2001/12/31 22:09:27 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: edc_mca.c,v 1.16 2002/03/31 10:01:26 jdolecek Exp $");
 
 #include "rnd.h"
 
@@ -106,8 +106,14 @@
                                         * controller */
 
        /* I/O results variables */
-       volatile int sc_error;
+       volatile int sc_stat;
+#define        STAT_START      0
+#define        STAT_ERROR      1
+#define        STAT_DONE       2
        volatile int sc_resblk;         /* residual block count */
+
+       /* CMD status block - only set & used in edc_intr() */
+       u_int16_t status_block[EDC_MAX_CMD_RES_LEN];
 };
 
 int    edc_mca_probe   __P((struct device *, struct cfdata *, void *));
@@ -121,7 +127,7 @@
 static void    edc_dump_status_block __P((struct edc_mca_softc *,
                    u_int16_t *, int));
 static int     edc_do_attn __P((struct edc_mca_softc *, int, int, int));
-static int     edc_cmd_wait __P((struct edc_mca_softc *, int, int));
+static void    edc_cmd_wait __P((struct edc_mca_softc *, int, int));
 static void    edcworker __P((void *));
 static void    edc_spawn_worker __P((void *));
 
@@ -283,7 +289,7 @@
        }
                
        /*
-        * Since interrupts are disabled ATM, it's necessary
+        * Since interrupts are disabled, it's necessary
         * to detect the interrupt request and call edc_intr()
         * explicitly. See also edc_run_cmd().
         */
@@ -350,8 +356,7 @@
        struct edc_mca_softc *sc = arg;
        u_int8_t isr, intr_id;
        u_int16_t sifr;
-       int cmd=-1, devno, error=0;
-       u_int16_t status_block[EDC_MAX_CMD_RES_LEN]; /* CMD status block */
+       int cmd=-1, devno;
 
        /*
         * Check if the interrupt was for us.
@@ -367,7 +372,7 @@
        isr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ISR);
        intr_id = isr & ISR_INTR_ID_MASK;
 
-#ifdef DEBUG
+#ifdef EDC_DEBUG
        if (intr_id == 0 || intr_id == 2 || intr_id == 4) {
                printf("%s: bogus interrupt id %d\n", sc->sc_dev.dv_xname,
                        (int) intr_id);
@@ -401,16 +406,20 @@
                cmd = sifr & SIFR_CMD_MASK;
 
                /* Read whole status block */
-               memset(status_block, 0, sizeof(status_block)); /* zero first */
-               status_block[0] = sifr;
+               sc->status_block[0] = sifr;
                for(i=1; i < len; i++) {
                        while((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR)
                                & BSR_SIFR_FULL) == 0)
-                               delay(1);
+                               ;
 
-                       status_block[i] = le16toh(
+                       sc->status_block[i] = le16toh(
                                bus_space_read_2(sc->sc_iot, sc->sc_ioh, SIFR));
                }
+               /* zero out rest */
+               if (i < EDC_MAX_CMD_RES_LEN) {
+                       memset(&sc->status_block[i], 0,
+                               (EDC_MAX_CMD_RES_LEN-i)*sizeof(u_int16_t));
+               }
        }
 
        switch (intr_id) {
@@ -422,31 +431,45 @@
                bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR,
                        BCR_INT_ENABLE|BCR_DMA_ENABLE);
                break;
+
        case ISR_COMPLETED:
        case ISR_COMPLETED_WITH_ECC:
        case ISR_COMPLETED_RETRIES:
        case ISR_COMPLETED_WARNING:
-               error = 0;
-
                /*
                 * Copy device config data if appropriate. sc->sc_ed[]
                 * entry might be NULL during probe.
                 */
                if (cmd == CMD_GET_DEV_CONF && sc->sc_ed[devno]) {
-                       memcpy(sc->sc_ed[devno]->sense_data, status_block,
+                       memcpy(sc->sc_ed[devno]->sense_data, sc->status_block,
                                sizeof(sc->sc_ed[devno]->sense_data));
                }
 
+               sc->sc_stat = STAT_DONE;
                break;
+
        case ISR_RESET_COMPLETED:
        case ISR_ABORT_COMPLETED:
                /* nothing to do */
                break;
+
+       case ISR_ATTN_ERROR:
+               /*
+                * Basically, this means driver bug or something seriously
+                * hosed. panic rather than extending the lossage. 
+                * No status block available, so no further info.
+                */
+               panic("%s: dev %d: attention error",
+                       sc->sc_dev.dv_xname,
+                       devno);
+               /* NOTREACHED */
+               break;
+
        default:
                if ((sc->sc_flags & DASD_QUIET) == 0)
-                       edc_dump_status_block(sc, status_block, intr_id);
+                       edc_dump_status_block(sc, sc->status_block, intr_id);
 
-               error = EIO;
+               sc->sc_stat = STAT_ERROR;
                break;
        }
                        
@@ -460,10 +483,9 @@
                edc_do_attn(sc, ATN_END_INT, devno, intr_id);
 
        /* If Read or Write Data, wakeup worker thread to finish it */
-       if (intr_id != ISR_DATA_TRANSFER_RDY
-           && (cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA)) {
-               if ((sc->sc_error = error) == 0)
-                       sc->sc_resblk = status_block[SB_RESBLKCNT_IDX];
+       if (intr_id != ISR_DATA_TRANSFER_RDY) {
+               if (cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA)
+                       sc->sc_resblk = sc->status_block[SB_RESBLKCNT_IDX];
                wakeup_one(sc);
        }
 
@@ -489,30 +511,30 @@
         *    a RESET COMPLETED interrupt.
         */
        if (intr_id != ISR_RESET_COMPLETED) {
+#ifdef EDC_DEBUG
+               if (attn_type == ATN_CMD_REQ
+                   && (bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR)
+                           & BSR_INT_PENDING))
+                       panic("%s: edc int pending", sc->sc_dev.dv_xname);
+#endif
+
                for(tries=1; tries < EDC_ATTN_MAXTRIES; tries++) {
                        if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR)
-                            & BSR_BUSY) == 0) {
-#ifdef DEBUG
-                               if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh,
-                                       BSR) & BSR_INT_PENDING) && intr_id)
-                                       panic("foobar");
-#endif
+                            & BSR_BUSY) == 0)
                                break;
-                       }
                }
 
                if (tries == EDC_ATTN_MAXTRIES) {
                        printf("%s: edc_do_attn: timeout waiting for attachment to become available\n",
                                        sc->sc_ed[devno]->sc_dev.dv_xname);
-                       return (EAGAIN);
+                       return (EIO);
                }
        }
 
        /*
         * 3. Write proper DEVICE NUMBER and Attention number to ATN.
         */ 
-       bus_space_write_1(sc->sc_iot, sc->sc_ioh, ATN,
-               attn_type | (devno << 5));
+       bus_space_write_1(sc->sc_iot, sc->sc_ioh, ATN, attn_type | (devno<<5));
 
        /*
         * 4. Enable interrupts via BCR.
@@ -527,59 +549,36 @@
  * We use mono_time, since we don't need actual RTC, just time
  * interval.
  */
-static int
+static void
 edc_cmd_wait(sc, secs, poll)
        struct edc_mca_softc *sc;
        int secs, poll;
 {
-       int val, delayed;
+       int val;
 
        if (!poll) {
-               int error;
+               int s;
 
                /* Not polling, can sleep. Sleep until we are awakened,
                 * but maximum secs seconds.
                 */
-               error = tsleep(sc, PRIBIO, "edcwcmd", secs * hz);
-               if (error)
-                       goto err;
-               return (0);
+               s = splbio();
+               if (sc->sc_stat != STAT_DONE)
+                       (void) tsleep(sc, PRIBIO, "edcwcmd", secs * hz);
+               splx(s);
        }
 
-       /* Poll the controller until command finishes */
-       delayed = 0;
-       do {
-               val = bus_space_read_1(sc->sc_iot,sc->sc_ioh, BSR);
-               if ((val & BSR_CMD_INPROGRESS) == 0)
-                       break;
-
-               if (val & BSR_INTR)
-                       break;
-
-               delay(1);
-
-               /*
-                * This is not as accurate as checking mono_time, but
-                * it works with hardclock interrupts disabled too.
-                */
-               delayed++;
-               if (delayed == 1000000) {
-                       delayed = 0;
-                       secs--;
-               }
-       } while(secs > 0);
-
-       if (secs == 0 &&
-           bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_CMD_INPROGRESS){
-    err:
-               printf("%s: timed out waiting for cmd to finish\n",
-                       sc->sc_dev.dv_xname);
-               return (EAGAIN);
+       /* Wait until the command is completely finished */
+       while((val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR))
+           & BSR_CMD_INPROGRESS) {
+               if (poll && (val & BSR_INTR))
+                       edc_intr(sc);
        }
-
-       return (0);
 }
-         
+ 
+/*
+ * Command controller to execute specified command on a device.
+ */
 int
 edc_run_cmd(sc, cmd, devno, cmd_args, cmd_len, poll)
        struct edc_mca_softc *sc;
@@ -591,10 +590,7 @@
        int i, error, tries;
        u_int16_t cmd0;
 
-       if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_BUSY) {
-               printf("%s: device busy?\n", sc->sc_dev.dv_xname);
-               return (EAGAIN);
-       }
+       sc->sc_stat = STAT_START;
 
        /* Do Attention Request for Command Request. */
        if ((error = edc_do_attn(sc, ATN_CMD_REQ, devno, 0)))
@@ -622,53 +618,32 @@
         * Write word of CMD to the CIFR. This sets "Command
         * Interface Register Full (CMD IN)" in BSR. Once the attachment
         * detects it, it reads the word and clears CMD IN. This all should
-        * be quite fast, so don't bother with sleeps for !poll case.
+        * be quite fast, so don't sleep in !poll case neither.
         */
        for(i=0; i < cmd_len; i++) {
                bus_space_write_2(sc->sc_iot, sc->sc_ioh, CIFR,
                        htole16(cmd_args[i]));



Home | Main Index | Thread Index | Old Index