summaryrefslogtreecommitdiffstats
path: root/sys/i386
diff options
context:
space:
mode:
authorsos <sos@FreeBSD.org>1997-09-04 18:49:53 +0000
committersos <sos@FreeBSD.org>1997-09-04 18:49:53 +0000
commit4d6dd4fc2d4b7a4112c261c7c34037dbe9de3e56 (patch)
treed9d1bc4feef0cf975d010ef759400307cbbd7302 /sys/i386
parentfb633824a1760e5b18d4ec464cabc4e5e9c28cbf (diff)
downloadFreeBSD-src-4d6dd4fc2d4b7a4112c261c7c34037dbe9de3e56.zip
FreeBSD-src-4d6dd4fc2d4b7a4112c261c7c34037dbe9de3e56.tar.gz
Upgrade of EIDE DMA support, Johns comments:
* lots of fixes to error handling-- mostly works now * improve DMA timing config for Triton chipsets-- PIIX4 and UDMA drive still untested * generally improve DMA config in many ways-- mostly cleanup * clean up boot-time messages * rewrite PRD generation algorithm * first wd timeout is now longer, to handle drive spinup Submitted by: John Hood <cgull@smoke.marlboro.vt.us>
Diffstat (limited to 'sys/i386')
-rw-r--r--sys/i386/conf/LINT9
-rw-r--r--sys/i386/conf/NOTES9
-rw-r--r--sys/i386/isa/wd.c91
-rw-r--r--sys/i386/isa/wdreg.h20
4 files changed, 82 insertions, 47 deletions
diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT
index 1761da2..4ffd358 100644
--- a/sys/i386/conf/LINT
+++ b/sys/i386/conf/LINT
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.359 1997/08/28 12:18:07 jkh Exp $
+# $Id: LINT,v 1.360 1997/08/28 15:00:05 jlemon Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -724,8 +724,6 @@ controller wds0 at isa? port 0x350 bio irq 15 drq 6 vector wdsintr
#
# ST-506, ESDI, and IDE hard disks: `wdc' and `wd'
#
-# NB: ``Enhanced IDE'' is NOT supported at this time.
-#
# The flags fields are used to enable the multi-sector I/O and
# the 32BIT I/O modes. The flags may be used in either the controller
# definition or in the individual disk definitions. The controller
@@ -735,7 +733,10 @@ controller wds0 at isa? port 0x350 bio irq 15 drq 6 vector wdsintr
# The low 8 bits are the maximum value for the multi-sector I/O,
# where 0xff defaults to the maximum that the drive can handle.
# The high bit of the 16 bit flags (0x8000) allows probing for
-# 32 bit transfers.
+# 32 bit transfers. Bit 14 (0x4000) enables a hack to wake
+# up powered-down laptop drives. Bit 13 (0x2000) allows
+# probing for PCI IDE DMA controllers, such as Intel's PIIX
+# south bridges. See the wd.4 man page.
#
# The flags field for the drives can be specified in the controller
# specification with the low 16 bits for drive 0, and the high 16 bits
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index 1761da2..4ffd358 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.359 1997/08/28 12:18:07 jkh Exp $
+# $Id: LINT,v 1.360 1997/08/28 15:00:05 jlemon Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -724,8 +724,6 @@ controller wds0 at isa? port 0x350 bio irq 15 drq 6 vector wdsintr
#
# ST-506, ESDI, and IDE hard disks: `wdc' and `wd'
#
-# NB: ``Enhanced IDE'' is NOT supported at this time.
-#
# The flags fields are used to enable the multi-sector I/O and
# the 32BIT I/O modes. The flags may be used in either the controller
# definition or in the individual disk definitions. The controller
@@ -735,7 +733,10 @@ controller wds0 at isa? port 0x350 bio irq 15 drq 6 vector wdsintr
# The low 8 bits are the maximum value for the multi-sector I/O,
# where 0xff defaults to the maximum that the drive can handle.
# The high bit of the 16 bit flags (0x8000) allows probing for
-# 32 bit transfers.
+# 32 bit transfers. Bit 14 (0x4000) enables a hack to wake
+# up powered-down laptop drives. Bit 13 (0x2000) allows
+# probing for PCI IDE DMA controllers, such as Intel's PIIX
+# south bridges. See the wd.4 man page.
#
# The flags field for the drives can be specified in the controller
# specification with the low 16 bits for drive 0, and the high 16 bits
diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c
index c5e2e52..5537a4a 100644
--- a/sys/i386/isa/wd.c
+++ b/sys/i386/isa/wd.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)wd.c 7.2 (Berkeley) 5/9/91
- * $Id: wd.c,v 1.134 1997/08/04 05:26:49 dyson Exp $
+ * $Id: wd.c,v 1.135 1997/08/09 01:44:25 julian Exp $
*/
/* TODO:
@@ -193,7 +193,6 @@ static int wdtest = 0;
static struct disk *wddrives[NWD]; /* table of units */
static struct buf_queue_head drive_queue[NWD]; /* head of queue per drive */
static struct {
- int b_errcnt;
int b_active;
} wdutab[NWD];
/*
@@ -818,8 +817,13 @@ wdstart(int ctrlr)
head = (blknum % secpercyl) / secpertrk;
sector = blknum % secpertrk;
+ /*
+ * XXX this looks like an attempt to skip bad sectors
+ * on write.
+ */
if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0)
du->dk_bc += DEV_BSIZE;
+
count = howmany( du->dk_bc, DEV_BSIZE);
du->dk_flags &= ~DKFL_MULTI;
@@ -914,6 +918,9 @@ wdstart(int ctrlr)
* unmarked bad blocks can take 3 seconds! Then it is not good that
* we retry 5 times.
*
+ * On the first try, we give it 10 seconds, for drives that may need
+ * to spin up.
+ *
* XXX wdtimeout() doesn't increment the error count so we may loop
* forever. More seriously, the loop isn't forever but causes a
* crash.
@@ -923,7 +930,10 @@ wdstart(int ctrlr)
* think). Discarding them would be OK if the (special) file offset
* was not advanced.
*/
- du->dk_timeout = 1 + 3;
+ if (wdtab[ctrlr].b_errcnt == 0)
+ du->dk_timeout = 1 + 10;
+ else
+ du->dk_timeout = 1 + 3;
/* if this is a DMA op, start DMA and go away until it's done. */
if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) {
@@ -985,11 +995,13 @@ wdstart(int ctrlr)
* the next request. Also check for a partially done transfer, and
* continue with the next chunk if so.
*/
+
void
wdintr(int unit)
{
register struct disk *du;
register struct buf *bp;
+ int dmastat;
#ifdef CMD640
int ctrlr_atapi;
@@ -1033,20 +1045,19 @@ wdintr(int unit)
#endif
bp = wdtab[unit].controller_queue.tqh_first;
du = wddrives[dkunit(bp->b_dev)];
- du->dk_timeout = 0;
- /* finish off DMA. ignore errors if we're not using it. */
+ /* finish off DMA */
if (du->dk_flags & (DKFL_DMA|DKFL_USEDMA)) {
- if ((wddma.wdd_dmastatus(du->dk_dmacookie) & WDDS_INTERRUPT) == 0)
+ /* XXX SMP boxes sometimes generate an early intr. Why? */
+ if ((wddma.wdd_dmastatus(du->dk_dmacookie) & WDDS_INTERRUPT)
+ == 0)
return;
-
- if ((wddma.wdd_dmadone(du->dk_dmacookie) != WDDS_INTERRUPT) &&
- !(du->dk_flags & DKFL_USEDMA)) {
- wderror(bp, du, "wdintr: DMA failure");
- du->dk_status |= WDCS_ERR; /* XXX totally bogus err */
- }
+ dmastat = wddma.wdd_dmadone(du->dk_dmacookie);
}
+ du->dk_timeout = 0;
+
+ /* check drive status/failure */
if (wdwait(du, 0, TIMEOUT) < 0) {
wderror(bp, du, "wdintr: timeout waiting for status");
du->dk_status |= WDCS_ERR; /* XXX */
@@ -1067,21 +1078,32 @@ wdintr(int unit)
}
/* have we an error? */
- if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) {
+ if ((du->dk_status & (WDCS_ERR | WDCS_ECCCOR))
+ || (((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA)
+ && dmastat != WDDS_INTERRUPT)) {
+
+ unsigned int errstat;
oops:
/*
- * XXX bogus inb() here, register 0 is assumed and intr status
- * is reset.
+ * XXX bogus inb() here
*/
- if((du->dk_flags & DKFL_DMA ) &&
- (inb(du->dk_port) & WDERR_ABORT)) {
+ errstat = inb(du->dk_port + wd_error);
+
+ if(((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) &&
+ (errstat & WDERR_ABORT)) {
wderror(bp, du, "reverting to PIO mode");
- du->dk_flags |= ~DKFL_USEDMA;
+ du->dk_flags &= ~DKFL_USEDMA;
} else if((du->dk_flags & DKFL_MULTI) &&
- (inb(du->dk_port) & WDERR_ABORT)) {
+ (errstat & WDERR_ABORT)) {
wderror(bp, du, "reverting to non-multi sector mode");
du->dk_multi = 1;
}
+
+ if (!(du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) &&
+ (((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) &&
+ (dmastat != WDDS_INTERRUPT)))
+ printf("wd%d: DMA failure, DMA status %b\n",
+ du->dk_lunit, dmastat, WDDS_BITS);
#ifdef WDDEBUG
wderror(bp, du, "wdintr");
#endif
@@ -1108,7 +1130,7 @@ oops:
bp->b_error = EIO;
bp->b_flags |= B_ERROR; /* flag the error */
}
- } else
+ } else if (du->dk_status & WDCS_ECCCOR)
wderror(bp, du, "soft ecc");
}
@@ -1116,7 +1138,7 @@ oops:
* If this was a successful read operation, fetch the data.
*/
if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ)
- && !(du->dk_flags & DKFL_DMA)
+ && !((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA)
&& wdtab[unit].b_active) {
int chk, dummy, multisize;
multisize = chk = du->dk_currentiosize * DEV_BSIZE;
@@ -1160,7 +1182,7 @@ oops:
/* final cleanup on DMA */
if (((bp->b_flags & B_ERROR) == 0)
- && (du->dk_flags & DKFL_DMA)
+ && ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA)
&& wdtab[unit].b_active) {
int iosize;
@@ -1208,7 +1230,6 @@ done: ;
wdtab[unit].b_errcnt = 0;
bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE;
wdutab[du->dk_lunit].b_active = 0;
- wdutab[du->dk_lunit].b_errcnt = 0;
du->dk_skip = 0;
biodone(bp);
}
@@ -1258,6 +1279,7 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
du->dk_flags &= ~DKFL_BADSCAN;
+ /* spin waiting for anybody else reading the disk label */
while (du->dk_flags & DKFL_LABELLING)
tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1);
#if 1
@@ -1512,6 +1534,8 @@ wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
return (1);
if( command == WDCC_FEATURES) {
outb(wdc + wd_features, count);
+ if ( count == WDFEA_SETXFER )
+ outb(wdc + wd_seccnt, sector);
} else {
outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4);
outb(wdc + wd_cyl_lo, cylinder);
@@ -1647,7 +1671,8 @@ wdsetmode(int mode, void *wdinfo)
du = wdinfo;
if (bootverbose)
- printf("wdsetmode() setting transfer mode to %02x\n", mode);
+ printf("wd%d: wdsetmode() setting transfer mode to %02x\n",
+ du->dk_lunit, mode);
i = wdcommand(du, 0, 0, mode, WDFEA_SETXFER,
WDCC_FEATURES) == 0 &&
wdwait(du, WDCS_READY, TIMEOUT) == 0;
@@ -2234,11 +2259,19 @@ wdtimeout(void *cdu)
du = (struct disk *)cdu;
x = splbio();
if (du->dk_timeout != 0 && --du->dk_timeout == 0) {
- if(timeouts++ == 5)
- wderror((struct buf *)NULL, du,
- "Last time I say: interrupt timeout. Probably a portable PC.");
- else if(timeouts < 5)
- wderror((struct buf *)NULL, du, "interrupt timeout");
+ if(timeouts++ <= 5) {
+ char *msg;
+
+ msg = (timeouts > 5) ?
+"Last time I say: interrupt timeout. Probably a portable PC." :
+"interrupt timeout";
+ wderror((struct buf *)NULL, du, msg);
+ if (du->dk_dmacookie)
+ printf("wd%d: wdtimeout() DMA status %b\n",
+ du->dk_lunit,
+ wddma.wdd_dmastatus(du->dk_dmacookie),
+ WDDS_BITS);
+ }
wdunwedge(du);
wdflushirq(du, x);
du->dk_skip = 0;
diff --git a/sys/i386/isa/wdreg.h b/sys/i386/isa/wdreg.h
index f7dfa488..9bb2f9b 100644
--- a/sys/i386/isa/wdreg.h
+++ b/sys/i386/isa/wdreg.h
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)wdreg.h 7.1 (Berkeley) 5/9/91
- * $Id: wdreg.h,v 1.17 1997/02/22 09:37:27 peter Exp $
+ * $Id: wdreg.h,v 1.12.2.3 1997/01/14 17:32:07 bde Exp $
*/
/*
@@ -284,10 +284,11 @@ struct wddma {
#define WDDS_ERROR 0x0002
#define WDDS_INTERRUPT 0x0004
-#if 0
-/* XXX are these now useless? */
-/* local defines for ATA timing modes */
-#define WDDMA_GRPMASK 0xf0
+#define WDDS_BITS "\20\4interrupt\2error\1active"
+
+/* defines for ATA timing modes */
+#define WDDMA_GRPMASK 0xf8
+#define WDDMA_MODEMASK 0x07
/* flow-controlled PIO modes */
#define WDDMA_PIO 0x10
#define WDDMA_PIO3 0x10
@@ -299,11 +300,10 @@ struct wddma {
#define WDDMA_MDMA2 0x22
/* Ultra DMA timing modes */
-#define WDDMA_UDMA 0x30
-#define WDDMA_UDMA0 0x30
-#define WDDMA_UDMA1 0x31
-#define WDDMA_UDMA2 0x32
-#endif
+#define WDDMA_UDMA 0x40
+#define WDDMA_UDMA0 0x40
+#define WDDMA_UDMA1 0x41
+#define WDDMA_UDMA2 0x42
extern struct wddma wddma;
OpenPOWER on IntegriCloud