summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkato <kato@FreeBSD.org>1997-09-05 10:16:02 +0000
committerkato <kato@FreeBSD.org>1997-09-05 10:16:02 +0000
commit9c70a71cfd12c8a4e484172b9749aca2306c641f (patch)
treed427834e17cc9cd029a498b41fe7cf658544be07
parent94107c4ca8d66686bce2035d953f0131182c2891 (diff)
downloadFreeBSD-src-9c70a71cfd12c8a4e484172b9749aca2306c641f.zip
FreeBSD-src-9c70a71cfd12c8a4e484172b9749aca2306c641f.tar.gz
Synchronize with sys/i386/isa/wd.c revision 1.136.
-rw-r--r--sys/pc98/pc98/wd.c158
1 files changed, 103 insertions, 55 deletions
diff --git a/sys/pc98/pc98/wd.c b/sys/pc98/pc98/wd.c
index 5cb1633..e3da9df 100644
--- a/sys/pc98/pc98/wd.c
+++ b/sys/pc98/pc98/wd.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)wd.c 7.2 (Berkeley) 5/9/91
- * $Id: wd.c,v 1.28 1997/08/06 09:41:59 kato Exp $
+ * $Id: wd.c,v 1.29 1997/08/09 06:41:36 kato Exp $
*/
/* TODO:
@@ -215,7 +215,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];
/*
@@ -919,8 +918,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;
@@ -1018,6 +1022,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.
@@ -1027,7 +1034,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) {
@@ -1097,11 +1107,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;
@@ -1145,23 +1157,22 @@ wdintr(int unit)
#endif
bp = wdtab[unit].controller_queue.tqh_first;
du = wddrives[dkunit(bp->b_dev)];
- du->dk_timeout = 0;
#ifdef PC98
outb(0x432,(du->dk_unit)%2);
#endif
- /* 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 */
@@ -1182,27 +1193,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 (old_epson_note) {
- if( (du->dk_status & DKFL_MULTI)
- && (epson_inb(du->dk_port) & WDERR_ABORT)) {
- wderror(bp, du,
- "reverting to non-multi sector mode");
- du->dk_multi = 1;
- }
- }
- else {
- if( (du->dk_flags & DKFL_MULTI) && (inb(du->dk_port) & WDERR_ABORT)) {
- wderror(bp, du,
- "reverting to non-multi sector mode");
- du->dk_multi = 1;
- }
+ 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;
+ } else if((du->dk_flags & DKFL_MULTI) &&
+ (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
@@ -1229,7 +1245,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");
}
@@ -1237,7 +1253,7 @@ oops:
* If this was a successful read operation, fetch the data.
*/
if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ)
- && wdtab[unit].b_active) {
+ && !((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA)
int chk, dummy, multisize;
multisize = chk = du->dk_currentiosize * DEV_BSIZE;
if( du->dk_bc < chk) {
@@ -1258,31 +1274,19 @@ oops:
}
/* suck in data */
- if (!old_epson_note) {
- if( du->dk_flags & DKFL_32BIT)
- insl(du->dk_port + wd_data,
- (void *)((int)bp->b_un.b_addr
- + du->dk_skip * DEV_BSIZE),
+ if( du->dk_flags & DKFL_32BIT)
+ insl(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
chk / sizeof(long));
- else
- insw(du->dk_port + wd_data,
- (void *)((int)bp->b_un.b_addr
- + du->dk_skip * DEV_BSIZE),
- chk / sizeof(short));
- }
else
- epson_insw(du->dk_port + wd_data,
- (void *)((int)bp->b_un.b_addr
- + du->dk_skip * DEV_BSIZE),
+ insw(du->dk_port + wd_data,
+ (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
chk / sizeof(short));
du->dk_bc -= chk;
/* XXX for obsolete fractional sector reads. */
while (chk < multisize) {
- if (!old_epson_note)
- insw(du->dk_port + wd_data, &dummy, 1);
- else
- epson_insw(du->dk_port + wd_data, &dummy, 1);
+ insw(du->dk_port + wd_data, &dummy, 1);
chk += sizeof(short);
}
@@ -1290,6 +1294,20 @@ oops:
dk_wds[du->dk_dkunit] += chk >> 6;
}
+ /* final cleanup on DMA */
+ if (((bp->b_flags & B_ERROR) == 0)
+ && ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA)
+ && wdtab[unit].b_active) {
+ int iosize;
+
+ iosize = du->dk_currentiosize * DEV_BSIZE;
+
+ du->dk_bc -= iosize;
+
+ if (du->dk_dkunit >= 0)
+ dk_wds[du->dk_dkunit] += iosize >> 6;
+ }
+
outt:
if (wdtab[unit].b_active) {
if ((bp->b_flags & B_ERROR) == 0) {
@@ -1321,7 +1339,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);
}
@@ -1375,6 +1392,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
@@ -1660,6 +1678,8 @@ wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
epson_outb(wdc + wd_features, count);
else
outb(wdc + wd_features, count);
+ if ( count == WDFEA_SETXFER )
+ outb(wdc + wd_seccnt, sector);
} else {
if (old_epson_note) {
epson_outb(wdc + wd_precomp, du->dk_dd.d_precompcyl/4);
@@ -1804,6 +1824,26 @@ wdwsetctlr(struct disk *du)
#endif
/*
+ * gross little callback function for wdddma interface. returns 1 for
+ * success, 0 for failure.
+ */
+static int
+wdsetmode(int mode, void *wdinfo)
+{
+ int i;
+ struct disk *du;
+
+ du = wdinfo;
+ if (bootverbose)
+ 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;
+ return i;
+}
+
+/*
* issue READP to drive to ask it what it is.
*/
static int
@@ -2464,11 +2504,19 @@ wdtimeout(void *cdu)
outb(0x432,(du->dk_unit)%2);
#endif
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;
OpenPOWER on IntegriCloud