[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