Subject: Re: sync negotiation with drives
To: <>
From: Michael <macallan18@earthlink.net>
List: port-macppc
Date: 10/29/2004 22:32:15
Hello,

> So I had a look at the Linux driver which apparently works in synchronous mode, this comment looks interesting:
Yeah, but it wasn't the cause. This however was:
/*
  * We can get a phase mismatch here if the target
  * changes to the status phase, even though we have
  * had a command complete interrupt.  Then, if we
  * issue the SEQ_STATUS command, we'll get a sequence
  * error interrupt.  Which isn't so bad except that
  * occasionally the mesh actually executes the
  * SEQ_STATUS *as well as* giving us the sequence
  * error and phase mismatch exception.
*/
We get these interrupts after nearly every transfer, and after a while the whole thing locks up. So we write a 0 into the command register whenever a command completes and clear all interrupts again, so we still get more interrupts than necessary but the controller won't lock up - my Barracuda works now in synchronous mode, performance according to bonnie++ increased from ~6MB/s to ~8MB/s - both, reading and writing. Still a far cry from the maximum ( it's certainly not the drives fault - it does >15MB/s ) but at least something. 
It works well with the disk and my CDROM ( a Pioneer 24x, at 10MB/s ) but not the ZIP - it hangs when trying to negotiate sync, so we should probably leave sync disabled by default, or at least disable it for target 5 and 6 where ZIPs are likely to be found - most Macs don't have room for more than 5 internal SCSI devices anyway. In async mode it sort of works - reading is ok, writing is flaky, but that's exactly what it did before.
Another way would be to add a quirk table to mesh and never try to negotiate sync with a ZIP drive. On the other hand this table would better be global, I doubt that the ZIP has only problems with mesh.

Here's the patch:

diff -u -b -B -r1.19 mesh.c
--- mesh.c      15 Jul 2003 02:43:29 -0000      1.19
+++ mesh.c      30 Oct 2004 02:23:48 -0000
@@ -68,6 +68,7 @@

 #define T_SYNCMODE 0x01                /* target uses sync mode */
 #define T_SYNCNEGO 0x02                /* sync negotiation done */
+#define T_SYNCRJCT 0x04                /* reject sync negotiation */

 struct mesh_tinfo {
        int flags;
@@ -357,7 +358,8 @@
                DPRINTF("%s: NULL nexus\n", sc->sc_dev.dv_xname);
                return 1;
        }
-
+       if(intr & MESH_INTR_CMDDONE)
+       {
        if (sc->sc_flags & MESH_DMA_ACTIVE) {
                dbdma_stop(sc->sc_dmareg);

@@ -365,7 +367,9 @@
                scb->resid = MESH_GET_XFER(sc);

                fifocnt = mesh_read_reg(sc, MESH_FIFO_COUNT);
-               if (fifocnt != 0 && (scb->flags & MESH_READ)) {
+                       if (fifocnt != 0)
+                       {
+                               if (scb->flags & MESH_READ) {
                        char *cp = (char *)scb->daddr + scb->dlen - fifocnt;

                        DPRINTF("fifocnt = %d, resid = %d\n", fifocnt,
@@ -376,9 +380,16 @@
                        }
                } else
                        mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO);
+                       } else
+                       {
+                               mesh_set_reg(sc, MESH_SEQUENCE, 0);
+                               mesh_set_reg(sc, MESH_INTERRUPT, 7);
+                       }
+               }
        }

        if (intr & MESH_INTR_ERROR) {
+               printf("error %02x %02x\n",error,exception);
                mesh_error(sc, scb, error, 0);
                return 1;
        }
@@ -392,6 +403,7 @@

                /* phase mismatch */
                if (exception & MESH_EXC_PHASEMM) {
+                       //printf("!");
                        DPRINTF("%s: PHASE MISMATCH; nextstate = %d -> ",
                                sc->sc_dev.dv_xname, sc->sc_nextstate);
                        sc->sc_nextstate = status0 & MESH_PHASE_MASK;
@@ -477,7 +488,7 @@
 {
        struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target];
        int timeout;
-
+       int cnt;
        DPRINTF("mesh_select\n");

        mesh_setsync(sc, ti);
@@ -491,10 +502,18 @@
         * initiator ID to DestID register temporarily.
         */
        mesh_set_reg(sc, MESH_DEST_ID, sc->sc_id);
+
        mesh_set_reg(sc, MESH_INTR_MASK, 0);    /* disable intr. */
+
        mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_ARBITRATE);

-       while (mesh_read_reg(sc, MESH_INTERRUPT) == 0);
+       cnt=0;
+       while ((mesh_read_reg(sc, MESH_INTERRUPT) == 0)&&(cnt<250))
+       {
+               cnt++;
+               delay(1);
+       }
+
        mesh_set_reg(sc, MESH_INTERRUPT, 1);
        mesh_set_reg(sc, MESH_INTR_MASK, 7);

@@ -521,8 +539,8 @@

        DPRINTF("mesh_identify\n");
        mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO);
-
-       if ((ti->flags & T_SYNCNEGO) == 0) {
+       if ((ti->flags & T_SYNCNEGO) == 0)
+       {
                ti->period = sc->sc_minsync;
                ti->offset = 15;
                mesh_msgout(sc, SEND_IDENTIFY | SEND_SDTR);
@@ -672,6 +688,9 @@
        struct mesh_softc *sc;
        struct mesh_scb *scb;
 {
+#ifdef MESH_DEBUG
+       int i;
+#endif
        DPRINTF("mesh_msgin\n");

        if (mesh_read_reg(sc, MESH_FIFO_COUNT) == 0) {  /* XXX cheat */
@@ -760,10 +779,20 @@
                scsipi_printaddr(scb->xs->xs_periph);
                /* XXX if (offset != 0) ... */
                printf("max sync rate %d.%02dMb/s\n", r, s);
+               if(ti->flags & T_SYNCRJCT)
+               {
+                       printf("sync requested - I'll ignore it and use asynchronous transfers.\n");
+                       ti->period = 0;
+                       ti->offset = 0;
+                       ti->flags |= T_SYNCNEGO;
+                       /*ti->flags |= T_SYNCMODE;*/
+               } else
+               {
                ti->period = period;
                ti->offset = offset;
                ti->flags |= T_SYNCNEGO;
                ti->flags |= T_SYNCMODE;
+               }
                mesh_setsync(sc, ti);
                goto done;
          }
@@ -894,7 +923,9 @@
                ti->flags = 0;
                ti->period = ti->offset = 0;
                if (sc->sc_cfflags & (0x100 << i))
-                       ti->flags |= T_SYNCNEGO;
+               {
+                       ti->flags |= (T_SYNCNEGO|T_SYNCRJCT);
+               }
        }
        sc->sc_nexus = NULL;
 }
@@ -1141,17 +1172,18 @@
        struct mesh_softc *sc =
            (void *)scb->xs->xs_periph->periph_channel->chan_adapter->adapt_dev;        int s;
-       int status0, status1;
+       int status0, status1, imsk;
        int intr, error, exception;

        printf("%s: timeout state %d\n", sc->sc_dev.dv_xname, sc->sc_nextstate);
        intr = mesh_read_reg(sc, MESH_INTERRUPT);
+       imsk = mesh_read_reg(sc, MESH_INTR_MASK);
        exception = mesh_read_reg(sc, MESH_EXCEPTION);
        error = mesh_read_reg(sc, MESH_ERROR);
        status0 = mesh_read_reg(sc, MESH_BUS_STATUS0);
        status1 = mesh_read_reg(sc, MESH_BUS_STATUS1);
-
+       printf("%02x %02x %02x %02x %02x %02x\n",intr, imsk, exception,error,status0,status1);
        s = splbio();
        if (sc->sc_flags & MESH_DMA_ACTIVE) {
                printf("mesh: resetting DMA\n");

not really cleaned up yet and I have no idea what it might break - for me it seems to work.

have fun
Michael