diff options
author | sos <sos@FreeBSD.org> | 1997-09-04 18:49:53 +0000 |
---|---|---|
committer | sos <sos@FreeBSD.org> | 1997-09-04 18:49:53 +0000 |
commit | 4d6dd4fc2d4b7a4112c261c7c34037dbe9de3e56 (patch) | |
tree | d9d1bc4feef0cf975d010ef759400307cbbd7302 /sys/i386 | |
parent | fb633824a1760e5b18d4ec464cabc4e5e9c28cbf (diff) | |
download | FreeBSD-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/LINT | 9 | ||||
-rw-r--r-- | sys/i386/conf/NOTES | 9 | ||||
-rw-r--r-- | sys/i386/isa/wd.c | 91 | ||||
-rw-r--r-- | sys/i386/isa/wdreg.h | 20 |
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; |