1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-30 12:04:07 +00:00

Synchronize with sys/i386/isa/wd.c revision 1.136.

This commit is contained in:
KATO Takenori 1997-09-05 10:16:02 +00:00
parent 8ccbc9119c
commit 02dddc814c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=29133

View File

@ -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 @@ wdintr(int unit)
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 @@ wdintr(int unit)
* 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 @@ wdintr(int unit)
}
/* 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 @@ wdintr(int unit)
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);
@ -1803,6 +1823,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.
*/
@ -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;