[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Fujitsu MB87030/MB89352 (SPC) manual
筒井です。
<030525001421.M0129533@mirage.ceres.dti.ne.jp>の記事において
私は書きました。
> 現状としては DMA 転送が終った後の spc_msgin()
> もしくは status phase の spc_datain_pio() で
> 来るべきデータが来ないことがある、みたいな感じです。
ここ一月くらい暇を見てはああでもないこうでもないと
いろいろ試行錯誤してたんですが、
> PIO only では build.sh 完走するくらいには動いてたので、
と書いたものの実際には
- DMA 使っていてもディスクが1台だけならば問題なし
- PIO only でも2台のディスクに同時に激しく(?)アクセスすると現象再現する
という状況でした。
spc_msgin() でも status phase の spc_datain_pio() でも
現状のコードは SCMD_XFER でデータ転送してるわけですが、
これだとなぜか SSTS レジスタの SSTS_DREG_EMPTY がいつまで
待っても立たないという状況が起きるようです。
なかなか思うように再現しないのでどういう状態遷移の時に
こういうことが起こるのか全然追えなかったんですが、
結局上記の message-in phase と status phase の転送を
hp300 の src/arch/hp300/dev/scsi.c:mxfer_in() と同じように
SCMD_SET_ACK, SCMD_RST_ACK を使って手動で行うようにすると
ディスク 3台の ccd で fsck -fp 5時間耐久、とかしても
問題なく動いているようです。(変更前は10分〜1時間くらいで発生)
従来のコードで x68k は複数ディスクで問題なかったのかが
わからないんですが、ひとまず現状の mb89352.c の変更点を
MI に入れてしまっても x68k (luna68k もですけど……)で
問題がないかどなたか試してもらえないでしょうか。
#hp300 で MI の mb89352.c を使うには bus_space(9) を
#直さないといけないのでもう少し時間がかかりそうです……
---
Izumi Tsutsui
tsutsui@ceres.dti.ne.jp
Index: mb89352.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mb89352.c,v
retrieving revision 1.15
diff -u -r1.15 mb89352.c
--- mb89352.c 2003/05/19 14:56:03 1.15
+++ mb89352.c 2003/06/28 13:23:28
@@ -82,14 +82,6 @@
* A few customizable items:
*/
-/* Use doubleword transfers to/from SCSI chip. Note: This requires
- * motherboard support. Basicly, some motherboard chipsets are able to
- * split a 32 bit I/O operation into two 16 bit I/O operations,
- * transparently to the processor. This speeds up some things, notably long
- * data transfers.
- */
-#define SPC_USE_DWORDS 0
-
/* Synchronous data transfers? */
#define SPC_USE_SYNCHRONOUS 0
#define SPC_SYNC_REQ_ACK_OFS 8
@@ -112,16 +104,22 @@
#define SPC_MSGIN_SPIN 1 /* Will spinwait upto ?ms for a new msg byte */
#define SPC_MSGOUT_SPIN 1
-/* Include debug functions? At the end of this file there are a bunch of
+/*
+ * Include debug functions? At the end of this file there are a bunch of
* functions that will print out various information regarding queued SCSI
* commands, driver state and chip contents. You can call them from the
* kernel debugger. If you set SPC_DEBUG to 0 they are not included (the
* kernel uses less memory) but you lose the debugging facilities.
*/
+#if 0
#define SPC_DEBUG 1
+#endif
#define SPC_ABORT_TIMEOUT 2000 /* time to wait for abort */
+/* threshold length for DMA transfer */
+#define SPC_MIN_DMA_LEN 32
+
/* End of customizable parameters */
/*
@@ -151,7 +149,7 @@
#include <dev/ic/mb89352reg.h>
#include <dev/ic/mb89352var.h>
-
+
#ifndef DDB
#define Debugger() panic("should call debugger here (mb89352.c)")
#endif /* ! DDB */
@@ -160,7 +158,6 @@
int spc_debug = 0x00; /* SPC_SHOWSTART|SPC_SHOWMISC|SPC_SHOWTRACE; */
#endif
-void spc_minphys __P((struct buf *));
void spc_done __P((struct spc_softc *, struct spc_acb *));
void spc_dequeue __P((struct spc_softc *, struct spc_acb *));
void spc_scsipi_request __P((struct scsipi_channel *,
@@ -190,7 +187,6 @@
extern struct cfdriver spc_cd;
-
/*
* INITIALIZATION ROUTINES (probe, attach ++)
*/
@@ -229,9 +225,11 @@
/* The following detection is derived from spc.c
* (by Takahide Matsutsuka) in FreeBSD/pccard-test.
*/
- while (bus_space_read_1(iot, ioh, PSNS) && timeout)
+ while (bus_space_read_1(iot, ioh, PSNS) && timeout) {
timeout--;
- if (!timeout) {
+ DELAY(1);
+ }
+ if (timeout == 0) {
printf("spc: find failed\n");
return 0;
}
@@ -273,7 +271,7 @@
sc->sc_adapter.adapt_nchannels = 1;
sc->sc_adapter.adapt_openings = 7;
sc->sc_adapter.adapt_max_periph = 1;
- sc->sc_adapter.adapt_minphys = spc_minphys;
+ sc->sc_adapter.adapt_minphys = minphys;
sc->sc_adapter.adapt_request = spc_scsipi_request;
sc->sc_channel.chan_adapter = &sc->sc_adapter;
@@ -308,6 +306,7 @@
*/
bus_space_write_1(iot, ioh, SCTL, SCTL_DISABLE | SCTL_CTRLRST);
bus_space_write_1(iot, ioh, SCMD, 0);
+ bus_space_write_1(iot, ioh, TMOD, 0);
bus_space_write_1(iot, ioh, PCTL, 0);
bus_space_write_1(iot, ioh, TEMP, 0);
bus_space_write_1(iot, ioh, TCH, 0);
@@ -443,7 +442,7 @@
splx(s);
return acb;
}
-
+
/*
* DRIVER FUNCTIONS CALLABLE FROM HIGHER LEVEL DRIVERS
*/
@@ -476,10 +475,12 @@
{
struct scsipi_xfer *xs;
struct scsipi_periph *periph;
- struct spc_softc *sc = (void *)chan->chan_adapter->adapt_dev;
+ struct spc_softc *sc;
struct spc_acb *acb;
int s, flags;
+ sc = (struct spc_softc *)chan->chan_adapter->adapt_dev;
+
switch (req) {
case ADAPTER_REQ_RUN_XFER:
xs = arg;
@@ -489,11 +490,14 @@
periph->periph_target));
flags = xs->xs_control;
- if ((acb = spc_get_acb(sc)) == NULL) {
- xs->error = XS_DRIVER_STUFFUP;
- scsipi_done(xs);
- return;
+ acb = spc_get_acb(sc);
+#ifdef DIAGNOSTIC
+ if (acb == NULL) {
+ scsipi_printaddr(periph);
+ printf("unable to allocate acb\n");
+ panic("spc_scsipi_request");
}
+#endif
/* Initialize acb */
acb->xs = xs;
@@ -505,9 +509,6 @@
acb->data_length = 0;
} else {
memcpy(&acb->scsipi_cmd, xs->cmd, xs->cmdlen);
-#if 1
- acb->scsipi_cmd.bytes[0] |= periph->periph_lun << 5; /* XXX? */
-#endif
acb->scsipi_cmd_length = xs->cmdlen;
acb->data_addr = xs->data;
acb->data_length = xs->datalen;
@@ -545,24 +546,23 @@
/* XXX Not supported. */
return;
case ADAPTER_REQ_SET_XFER_MODE:
- /* XXX Not supported. */
+ {
+ /*
+ * We don't support Sync, Wide, or Tagged Command Queuing.
+ * Just callback now, to report this.
+ */
+ struct scsipi_xfer_mode *xm = arg;
+
+ xm->xm_mode = 0;
+ xm->xm_period = 0;
+ xm->xm_offset = 0;
+ scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
return;
+ }
}
}
/*
- * Adjust transfer size in buffer structure
- */
-void
-spc_minphys(bp)
- struct buf *bp;
-{
-
- SPC_TRACE(("spc_minphys "));
- minphys(bp);
-}
-
-/*
* Used when interrupt driven I/O isn't allowed, e.g. during boot.
*/
int
@@ -589,7 +589,7 @@
}
return 1;
}
-
+
/*
* LOW LEVEL SCSI UTILITIES
*/
@@ -670,9 +670,10 @@
* X = 2 + 113 / 256
* ==> tch = 2, tcm = 113 (correct?)
*/
+ /* Time to the information transfer phase start. */
+ /* XXX These values should be calculated from sc_freq */
bus_space_write_1(iot, ioh, TCH, 2);
bus_space_write_1(iot, ioh, TCM, 113);
- /* Time to the information transfer phase start. */
bus_space_write_1(iot, ioh, TCL, 3);
bus_space_write_1(iot, ioh, SCMD, SCMD_SELECT);
@@ -753,7 +754,7 @@
spc_sched_msgout(sc, SEND_ABORT);
return (1);
}
-
+
/*
* Schedule a SCSI operation. This has now been pulled out of the interrupt
* handler so that we may call it from spc_scsi_cmd and spc_done. This may
@@ -793,7 +794,7 @@
SPC_MISC(("idle "));
/* Nothing to start; just enable reselections and wait. */
}
-
+
/*
* POST PROCESSING OF SCSI_CMD (usually current)
*/
@@ -873,7 +874,7 @@
else
TAILQ_REMOVE(&sc->ready_list, acb, chain);
}
-
+
/*
* INTERRUPT/PROTOCOL ENGINE
*/
@@ -913,22 +914,6 @@
* itself.
*/
for (;;) {
-#if 0
- for (;;) {
- if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) != 0)
- break;
- /* Wait for REQINIT. XXX Need timeout. */
- }
-#endif
- if (bus_space_read_1(iot, ioh, INTS) != 0) {
- /*
- * Target left MESSAGE IN, probably because it
- * a) noticed our ATN signal, or
- * b) ran out of messages.
- */
- goto out;
- }
-
/* If parity error, just dump everything on the floor. */
if ((bus_space_read_1(iot, ioh, SERR) &
(SERR_SCSI_PAR|SERR_SPC_PAR)) != 0) {
@@ -936,42 +921,32 @@
spc_sched_msgout(sc, SEND_PARITY_ERROR);
}
- /* send TRANSFER command. */
- bus_space_write_1(iot, ioh, TCH, 0);
- bus_space_write_1(iot, ioh, TCM, 0);
- bus_space_write_1(iot, ioh, TCL, 1);
- bus_space_write_1(iot, ioh, PCTL,
- sc->sc_phase | PCTL_BFINT_ENAB);
-#ifdef x68k
- bus_space_write_1(iot, ioh, SCMD,
- SCMD_XFR /* | SCMD_PROG_XFR */);
-#else
- bus_space_write_1(iot, ioh, SCMD,
- SCMD_XFR | SCMD_PROG_XFR); /* XXX */
-#endif
- for (;;) {
-#if 0
- if ((bus_space_read_1(iot, ioh, SSTS) &
- SSTS_BUSY) != 0 &&
- (bus_space_read_1(iot, ioh, SSTS) &
- SSTS_DREG_EMPTY) != 0)
-#endif
- if ((bus_space_read_1(iot, ioh, SSTS) &
- SSTS_DREG_EMPTY) == 0)
- break;
- if (bus_space_read_1(iot, ioh, INTS) != 0)
+ if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_ATN) != 0)
+ bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ATN);
+
+ while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) == 0) {
+ /* XXX needs timeout */
+ if ((bus_space_read_1(iot, ioh, PSNS) & PH_MASK)
+ != PH_MSGIN ||
+ (bus_space_read_1(iot, ioh, SSTS) & SSTS_INITIATOR)
+ == 0)
goto out;
}
+ bus_space_write_1(iot, ioh, PCTL, PH_MSGIN);
+ bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ACK);
+ while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) != 0)
+ continue; /* XXX needs timeout */
+
/* Gather incoming message bytes if needed. */
if ((sc->sc_flags & SPC_DROP_MSGIN) == 0) {
if (n >= SPC_MAX_MSG_LEN) {
- (void) bus_space_read_1(iot, ioh, DREG);
+ (void) bus_space_read_1(iot, ioh, TEMP);
sc->sc_flags |= SPC_DROP_MSGIN;
spc_sched_msgout(sc, SEND_REJECT);
} else {
*sc->sc_imp++ =
- bus_space_read_1(iot, ioh, DREG);
+ bus_space_read_1(iot, ioh, TEMP);
n++;
/*
* This testing is suboptimal, but most
@@ -988,19 +963,16 @@
break;
}
} else
- (void) bus_space_read_1(iot, ioh, DREG);
+ (void) bus_space_read_1(iot, ioh, TEMP);
/*
* If we reach this spot we're either:
* a) in the middle of a multi-byte message, or
* b) dropping bytes.
*/
-#if 0
+
/* Ack the last byte read. */
- /*(void) bus_space_read_1(iot, ioh, DREG);*/
- while ((bus_space_read_1(iot, ioh, PSNS) & ACKI) != 0)
- ;
-#endif
+ bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK);
}
SPC_MISC(("n=%d imess=0x%02x ", n, sc->sc_imess[0]));
@@ -1023,7 +995,7 @@
printf("%s: %d extra bytes from %d:%d\n",
sc->sc_dev.dv_xname, -sc->sc_dleft,
periph->periph_target, periph->periph_lun);
- acb->data_length = 0;
+ sc->sc_dleft = 0;
}
acb->xs->resid = acb->data_length = sc->sc_dleft;
sc->sc_state = SPC_CMDCOMPLETE;
@@ -1172,11 +1144,7 @@
}
/* Ack the last message byte. */
-#if 0 /* XXX? */
- (void) bus_space_read_1(iot, ioh, DREG);
- while ((bus_space_read_1(iot, ioh, PSNS) & ACKI) != 0)
- ;
-#endif
+ bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK);
/* Go get the next message, if any. */
goto nextmsg;
@@ -1184,6 +1152,10 @@
out:
bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK);
SPC_MISC(("n=%d imess=0x%02x ", n, sc->sc_imess[0]));
+
+ while ((bus_space_read_1(iot, ioh, SSTS) & SSTS_ACTIVE)
+ == SSTS_INITIATOR)
+ continue; /* XXX needs timeout */
}
/*
@@ -1322,7 +1294,7 @@
bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* XXX */
#else
bus_space_write_1(iot, ioh, SCMD,
- SCMD_XFR | SCMD_PROG_XFR | SCMD_ICPT_XFR);
+ SCMD_XFR | SCMD_PROG_XFR);
#endif
for (;;) {
if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0)
@@ -1397,7 +1369,7 @@
/* Disable REQ/ACK protocol. */
return;
}
-
+
/*
* spc_dataout_pio: perform a data transfer using the FIFO datapath in the spc
* Precondition: The SCSI bus should be in the DOUT phase, with REQ asserted
@@ -1428,7 +1400,7 @@
bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* XXX */
#else
bus_space_write_1(iot, ioh, SCMD,
- SCMD_XFR | SCMD_PROG_XFR | SCMD_ICPT_XFR); /* XXX */
+ SCMD_XFR | SCMD_PROG_XFR); /* XXX */
#endif
for (;;) {
if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0)
@@ -1463,9 +1435,8 @@
n -= xfer;
out += xfer;
- while (xfer-- > 0) {
- bus_space_write_1(iot, ioh, DREG, *p++);
- }
+ bus_space_write_multi_1(iot, ioh, DREG, p, xfer);
+ p += xfer;
}
if (out == 0) {
@@ -1508,7 +1479,7 @@
return out;
}
-
+
/*
* spc_datain_pio: perform data transfers using the FIFO datapath in the spc
* Precondition: The SCSI bus should be in the DIN phase, with REQ asserted
@@ -1526,8 +1497,8 @@
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
- u_short intstat;
int in = 0;
+ u_int8_t intstat, sstat;
#define DINAMOUNT 8 /* Full FIFO */
SPC_TRACE(("spc_datain_pio "));
@@ -1557,41 +1528,33 @@
while (n > 0) {
int xfer;
-#define INTSMASK 0xff
/* Wait for fifo half full or phase mismatch */
for (;;) {
- intstat = (bus_space_read_1(iot, ioh, SSTS) << 8) |
- bus_space_read_1(iot, ioh, INTS);
- if ((intstat & (INTSMASK | (SSTS_DREG_FULL << 8))) !=
- 0)
- break;
- if ((intstat & (SSTS_DREG_EMPTY << 8)) == 0)
+ /* XXX needs timeout */
+ intstat = bus_space_read_1(iot, ioh, INTS);
+ sstat = bus_space_read_1(iot, ioh, SSTS);
+ if (intstat != 0 ||
+ (sstat & SSTS_DREG_EMPTY) == 0)
break;
}
-
-#if 1
- if ((intstat & INTSMASK) != 0)
- goto phasechange;
-#else
- if ((intstat & INTSMASK) != 0 &&
- (intstat & (SSTS_DREG_EMPTY << 8)))
- goto phasechange;
-#endif
- if ((intstat & (SSTS_DREG_FULL << 8)) != 0)
- xfer = min(DINAMOUNT, n);
- else
- xfer = min(1, n);
-
- SPC_MISC((">%d ", xfer));
- n -= xfer;
- in += xfer;
-
- while (xfer-- > 0) {
+ if (sstat & SSTS_DREG_FULL) {
+ xfer = DINAMOUNT;
+ n -= xfer;
+ in += xfer;
+ bus_space_read_multi_1(iot, ioh, DREG, p, xfer);
+ p += xfer;
+ }
+ while ((bus_space_read_1(iot, ioh, SSTS) &
+ SSTS_DREG_EMPTY) == 0) {
+ if (n == 0)
+ break;
+ n--;
+ in++;
*p++ = bus_space_read_1(iot, ioh, DREG);
}
- if ((intstat & INTSMASK) != 0)
+ if (intstat != 0)
goto phasechange;
}
@@ -1604,6 +1567,7 @@
*/
if (in == 0) {
for (;;) {
+ /* XXX needs timeout */
if (bus_space_read_1(iot, ioh, INTS) != 0)
break;
}
@@ -1617,7 +1581,7 @@
return in;
}
-
+
/*
* Catch an interrupt from the adaptor
*/
@@ -1647,6 +1611,17 @@
SPC_TRACE(("spcintr "));
+ ints = bus_space_read_1(iot, ioh, INTS);
+ if (ints == 0)
+ goto out;
+
+ if (sc->sc_dma_done != NULL &&
+ sc->sc_state == SPC_CONNECTED &&
+ (sc->sc_flags & SPC_DOINGDMA) != 0 &&
+ (sc->sc_phase == PH_DATAOUT || sc->sc_phase == PH_DATAIN)) {
+ (*sc->sc_dma_done)(sc);
+ }
+
loop:
/*
* Loop until transfer completion.
@@ -1953,6 +1928,12 @@
if (sc->sc_state != SPC_CONNECTED)
break;
SPC_MISC(("dataout dleft=%d ", sc->sc_dleft));
+ if (sc->sc_dma_start != NULL &&
+ sc->sc_dleft > SPC_MIN_DMA_LEN) {
+ (*sc->sc_dma_start)(sc, sc->sc_dp, sc->sc_dleft, 0);
+ sc->sc_prevphase = PH_DATAOUT;
+ goto out;
+ }
n = spc_dataout_pio(sc, sc->sc_dp, sc->sc_dleft);
sc->sc_dp += n;
sc->sc_dleft -= n;
@@ -1963,6 +1944,12 @@
if (sc->sc_state != SPC_CONNECTED)
break;
SPC_MISC(("datain "));
+ if (sc->sc_dma_start != NULL &&
+ sc->sc_dleft > SPC_MIN_DMA_LEN) {
+ (*sc->sc_dma_start)(sc, sc->sc_dp, sc->sc_dleft, 1);
+ sc->sc_prevphase = PH_DATAIN;
+ goto out;
+ }
n = spc_datain_pio(sc, sc->sc_dp, sc->sc_dleft);
sc->sc_dp += n;
sc->sc_dleft -= n;
@@ -1974,10 +1961,18 @@
break;
SPC_ASSERT(sc->sc_nexus != NULL);
acb = sc->sc_nexus;
-#if 0
- acb->target_stat = bus_space_read_1(iot, ioh, DREG);
-#endif
- spc_datain_pio(sc, &acb->target_stat, 1);
+
+ if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_ATN) != 0)
+ bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ATN);
+ while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) == 0)
+ continue; /* XXX needs timeout */
+ bus_space_write_1(iot, ioh, PCTL, PH_STAT);
+ bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ACK);
+ while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) != 0)
+ continue; /* XXX needs timeout */
+ acb->target_stat = bus_space_read_1(iot, ioh, TEMP);
+ bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK);
+
SPC_MISC(("target_stat=0x%02x ", acb->target_stat));
sc->sc_prevphase = PH_STAT;
goto loop;
@@ -2063,7 +2058,7 @@
splx(s);
}
-
+
#ifdef SPC_DEBUG
/*
* The following functions are mostly used for debugging purposes, either
Index: mb89352var.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mb89352var.h,v
retrieving revision 1.3
diff -u -r1.3 mb89352var.h
--- mb89352var.h 2001/04/25 17:53:33 1.3
+++ mb89352var.h 2003/06/28 13:23:28
@@ -144,7 +144,7 @@
u_char sc_flags;
#define SPC_DROP_MSGIN 0x01 /* Discard all msgs (parity err detected) */
#define SPC_ABORTING 0x02 /* Bailing out */
-#define SPC_DOINGDMA 0x04 /* The FIFO data path is active! */
+#define SPC_DOINGDMA 0x04 /* doing DMA */
#define SPC_INACTIVE 0x80 /* The FIFO data path is active! */
u_char sc_selid; /* Reselection ID */
@@ -172,6 +172,10 @@
int sc_freq; /* Clock frequency in MHz */
int sc_minsync; /* Minimum sync period / 4 */
int sc_maxsync; /* Maximum sync period / 4 */
+
+ /* DMA function set from MD code */
+ void (*sc_dma_start)(struct spc_softc *, void *, size_t, int);
+ void (*sc_dma_done)(struct spc_softc *);
};
#if SPC_DEBUG