diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/NOTES | 9 | ||||
-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 | ||||
-rw-r--r-- | sys/pci/ide_pci.c | 632 |
6 files changed, 474 insertions, 296 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 1761da2..4ffd358 100644 --- a/sys/conf/NOTES +++ b/sys/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/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; diff --git a/sys/pci/ide_pci.c b/sys/pci/ide_pci.c index 21de978..9f16080 100644 --- a/sys/pci/ide_pci.c +++ b/sys/pci/ide_pci.c @@ -26,12 +26,13 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * From: wd82371.c,v 1.5.2.1 1996/11/16 21:19:51 phk Exp $ - * $Id: ide_pci.c,v 1.1 1997/07/29 12:57:09 sos Exp $ + * From: wd82371.c,v 1.5.2.1 1996/11/16 21:19:51 phk Exp $ + * $Id: ide_pci.c,v 1.2 1997/08/02 14:33:09 bde Exp $ */ #include "pci.h" #if NPCI > 0 +#include "opt_wd.h" #include <sys/param.h> #include <sys/systm.h> @@ -47,6 +48,10 @@ #include <pci/pcireg.h> #include <pci/ide_pcireg.h> +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + struct ide_pci_cookie; /* structs vendor_fns, ide_pci_cookie are recursive */ struct vendor_fns { @@ -57,11 +62,7 @@ struct vendor_fns { void *); void (*vendor_status) /* prints off DMA timing info */ - (int iobase_wd, - int unit, - int iobase_bm, - pcici_t tag, - pcidi_t type); + (struct ide_pci_cookie *cookie); }; /* @@ -76,6 +77,7 @@ struct vendor_fns { struct ide_pci_cookie { LIST_ENTRY(ide_pci_cookie) le; int iobase_wd; + int ctlr; /* pri/sec controller on this PCI IDE interface */ int unit; int iobase_bm; /* SFF-8038 control registers */ pcici_t tag; @@ -94,32 +96,31 @@ generic_dmainit(struct ide_pci_cookie *cookie, int(*wdcmd)(int, void *), void *wdinfo); static void -generic_status(int iobase_wd, - int unit, - int iobase_bm, - pcici_t tag, - pcidi_t type); +generic_status(struct ide_pci_cookie *cookie); + static void -via_571_status(int iobase_wd, - int unit, - int iobase_bm, - pcici_t tag, - pcidi_t type); +via_571_status(struct ide_pci_cookie *cookie); + static void intel_piix_dump_drive(char *ctlr, int sitre, + int is_piix4, int word40, int word44, + int word48, + int word4a, int drive); static void -intel_piix_status(int iobase_wd, - int unit, - int iobase_bm, - pcici_t tag, - pcidi_t type); +intel_piix_status(struct ide_pci_cookie *cookie); +static int +intel_piix_dmainit(struct ide_pci_cookie *cookie, + struct wdparams *wp, + int(*wdcmd)(int, void *), + void *wdinfo); static struct ide_pci_cookie * mkcookie(int iobase_wd, + int ctlr, int unit, int iobase_bm, pcici_t tag, @@ -173,16 +174,13 @@ generic_dmainit(struct ide_pci_cookie *cookie, int(*wdcmd)(int, void *), void *wdinfo) { - int mode, r; /* - * XXX punt on the whole timing issue by looking for either a + * punt on the whole timing issue by looking for either a * drive programmed for both PIO4 and mDMA2 (which use similar * timing) or a drive in an UltraDMA mode (hopefully all * controllers have separate timing for UDMA). one hopes that if * the drive's DMA mode has been configured by the BIOS, the - * controller's has also. this code may eventually be replaced - * by gunk in the hw-specific code to deal with specific - * controllers. + * controller's has also. */ /* XXX way too sick and twisted conditional */ if (!((((wp->wdp_atavalid & 2) == 2) && @@ -192,35 +190,11 @@ generic_dmainit(struct ide_pci_cookie *cookie, (wp->wdp_udmamode == 4)))) return 0; -#if 0 - /* - * XXX flesh this out into real code that actually - * does something-- this was just testing gunk. - */ - if (((wp->wdp_atavalid & 0x4) == 0x4) && - (wp->wdp_udmamode == 4)) { - printf("UDMA mode\n"); - mode = 0x42; /* XXX where's the #defines... */ - } - else { - printf("MDMA mode\n"); - mode = 0x24; - } - - r = wdcmd(mode, wdinfo); - printf("dmainit out like we expect\n"); - if (!r) - return 0; -#endif return 1; } static void -generic_status(int iobase_wd, - int unit, - int iobase_bm, - pcici_t tag, - pcidi_t type) +generic_status(struct ide_pci_cookie *cookie) { printf("generic_status: no PCI IDE timing info available\n"); } @@ -234,15 +208,22 @@ static struct vendor_fns vs_generic = /* VIA Technologies "82C571" PCI-IDE controller core */ static void -via_571_status(int iobase_wd, - int unit, - int iobase_bm, - pcici_t tag, - pcidi_t type) +via_571_status(struct ide_pci_cookie *cookie) { + int iobase_wd; + int unit; + int iobase_bm; + pcici_t tag; + pcidi_t type; unsigned int word40[5]; int i; + iobase_wd = cookie->iobase_wd; + unit = cookie->unit; + iobase_bm = cookie->iobase_bm; + tag = cookie->tag; + type = cookie->type; + /* XXX how to handle four calls for one controller? */ if (iobase_wd != 0x1f0 || unit != 0) return; @@ -272,7 +253,6 @@ via_571_status(int iobase_wd, ((word40[2] >> (((3 - i) * 8) + 4)) & 0x0f) + 1, ((word40[2] >> ((3 - i) * 8)) & 0x0f) + 1); - /* XXX could go on and do UDMA status for '586B */ } @@ -287,8 +267,11 @@ static struct vendor_fns vs_via_571 = static void intel_piix_dump_drive(char *ctlr, int sitre, + int is_piix4, int word40, int word44, + int word48, + int word4a, int drive) { char *ms; @@ -325,43 +308,203 @@ intel_piix_status: fast PIO %s%s\n", (word40 & 1) ? "enabled" : "disabled", ((word40 & 9) == 9) ? " (overridden by fastDMAonly)" : "" ); - /* XXX extend to dump 82371AB's UltraDMA modes */ + if (is_piix4) + printf("intel_piix_status: UltraDMA %s, CT/RP = %d/%d\n", + word48 ? "enabled": "disabled", + 4 - (word4a & 3), + 6 - (word4a & 3)); + } static void -intel_piix_status(int iobase_wd, - int unit, - int iobase_bm, - pcici_t tag, - pcidi_t type) +intel_piix_status(struct ide_pci_cookie *cookie) { - unsigned int word40, word44; - int sitre; + int iobase_wd; + int unit; + int iobase_bm; + pcici_t tag; + pcidi_t type; + int ctlr; + unsigned int word40, word44, word48; + int sitre, is_piix4; - /* XXX how to handle four calls for one controller? */ - if (iobase_wd != 0x1f0 || unit != 0) - return; + iobase_wd = cookie->iobase_wd; + unit = cookie->unit; + iobase_bm = cookie->iobase_bm; + tag = cookie->tag; + type = cookie->type; + ctlr = cookie->ctlr; word40 = pci_conf_read(tag, 0x40); word44 = pci_conf_read(tag, 0x44); + word48 = pci_conf_read(tag, 0x48); + + /* + * XXX will not be right for the *next* generation of upward-compatible + * intel IDE controllers... + */ + is_piix4 = pci_conf_read(tag, PCI_CLASS_REG) == 0x71118086; sitre = word40 & 0x4000; - intel_piix_dump_drive("primary", sitre, word40 & 0xffff, word44 & 0x0f, 0); - intel_piix_dump_drive("primary", sitre, word40 & 0xffff, word44 & 0x0f, 1); - intel_piix_dump_drive("secondary", - sitre, - (word40 >> 16) & 0xffff, - (word44 >> 4) & 0x0f,0); - intel_piix_dump_drive("secondary", - sitre, - (word40 >> 16) & 0xffff, - (word44 >> 4) & 0x0f,1); + switch (ctlr * 2 + unit) { + case 0: + intel_piix_dump_drive("primary", + sitre, + is_piix4, + word40 & 0xffff, + word44 & 0x0f, + word48, + word48 >> 16, + 0); + case 1: + intel_piix_dump_drive("primary", + sitre, + is_piix4, + word40 & 0xffff, + word44 & 0x0f, + word48 >> 1, + word48 >> 20, + 1); + case 2: + intel_piix_dump_drive("secondary", + sitre, + is_piix4, + (word40 >> 16) & 0xffff, + (word44 >> 4) & 0x0f, + word48 >> 2, + word48 >> 24, + 0); + case 3: + intel_piix_dump_drive("secondary", + sitre, + is_piix4, + (word40 >> 16) & 0xffff, + (word44 >> 4) & 0x0f, + word48 >> 3, + word48 >> 28, + 1); + } +} + +static int +intel_piix_dmainit(struct ide_pci_cookie *cookie, + struct wdparams *wp, + int(*wdcmd)(int, void *), + void *wdinfo) +{ + int r, pci_id; + + pci_id = pci_conf_read(cookie->tag, PCI_CLASS_REG); + + /* + * If the drive is already in mDMA2 or UDMA2, we leave it and the + * controller alone, on the theory that the BIOS already DTRT. + * + * XXX this does not handle the conceivable case where the drive + * has been left in a the right mode from a previous boot, the + * BIOS has not reset it, and the BIOS has also not set the modes + * on the controller. The one case seen so far where the BIOS + * doesn't dink the drives (Tyan S1563D with Award v4.01) it + * *does* seem to correctly program the controller. + * + * Aren't PC's fun? + */ + + /* XXX way too sick and twisted conditional */ + if ((((wp->wdp_atavalid & 2) == 2) && + ((wp->wdp_dmamword & 0x404) == 0x404) && + ((wp->wdp_eidepiomodes & 2) == 2)) || + (((wp->wdp_atavalid & 4) == 4) && + (wp->wdp_udmamode & 0x404 == 0x404))) + return 1; + + /* If it's a UDMA drive and a PIIX4, set it up */ + if (((wp->wdp_atavalid & 4) == 4) && + ((wp->wdp_udmamode & 4) == 4) && + pci_id == 0x71118086) { + /* Set UDMA mode 2 on controller */ + int unitno, mask, new; + + if (bootverbose) + printf("intel_piix_dmainit: setting ultra DMA mode 2\n"); + r = wdcmd(WDDMA_UDMA2, wdinfo); + if (!r) { + printf("intel_piix_dmainit: setting DMA mode failed\n"); + return 0; + } + + unitno = cookie->ctlr * 2 + cookie->unit; + + mask = 1 << unitno + 3 << (16 + unitno * 4); + new = 1 << unitno + 2 << (16 + unitno * 4); + + pci_conf_write(cookie->tag, + 0x48, + (pci_conf_read(cookie->tag, 0x48) & ~mask) | new); + } + /* + * if the SITRE bit is not set, indicating independent programming + * of drive timing, we punt; we're not gonna fuss with trying to + * coordinate timing modes between drives. if this is you, get a + * BIOS that does this for us, or get a new motherboard if it's an + * 82371FB (Triton FX). Or contribute patches :) + */ + else if ((pci_conf_read(cookie->tag, 0x40) >> (16 * cookie->ctlr)) + & 0x4000 == 0) + return 0; + /* otherwise, program it for MW DMA mode 2 */ + else if (((wp->wdp_atavalid & 2) == 2) && + ((wp->wdp_dmamword & 4) == 4)) { + /* Set multiword DMA mode 2 on controller */ + unsigned int mask40, mask44, new40, new44; + + if (bootverbose) + printf("intel_piix_dmainit: setting multiword DMA mode 2\n"); + r = wdcmd(WDDMA_MDMA2, wdinfo); + if (!r) { + printf("intel_piix_dmainit: setting DMA mode failed\n"); + return 0; + } + + /* + * backward compatible hardware leaves us with such twisted masses + * of software (aka twiddle the extremely weird register layout on + * a PIIX3) + */ + if (cookie->unit == 0) { + mask40 = 0x330f; + new40 = 0x2307; + mask44 = 0; + new44 = 0; + } + else { + mask40 = 0x00f0; + new40 = 0x0070; + mask44 = 0x000f; + new44 = 0x000b; + } + if (cookie->ctlr) { + mask40 <<= 16; + new40 <<= 16; + mask44 <<= 4; + new44 <<= 4; + } + pci_conf_write(cookie->tag, + 0x40, + (pci_conf_read(cookie->tag, 0x40) & ~mask40) | new40); + pci_conf_write(cookie->tag, + 0x44, + (pci_conf_read(cookie->tag, 0x44) & ~mask44) | new44); + } + if (bootverbose) + intel_piix_status(cookie); + return 1; } static struct vendor_fns vs_intel_piix = { - generic_dmainit, + intel_piix_dmainit, intel_piix_status }; @@ -374,6 +517,7 @@ static struct vendor_fns vs_intel_piix = static struct ide_pci_cookie * mkcookie(int iobase_wd, + int ctlr, int unit, int iobase_bm, pcici_t tag, @@ -386,6 +530,7 @@ mkcookie(int iobase_wd, if (!cp) return cp; cp->iobase_wd = iobase_wd; + cp->ctlr = ctlr; cp->unit = unit; cp->tag = tag; cp->type = type; @@ -400,7 +545,7 @@ mkcookie(int iobase_wd, } if (((int)prdbuf >> PAGE_SHIFT) ^ (((int)prdbuf + PRD_ALLOC_SIZE - 1) >> PAGE_SHIFT)) { - printf("ide_pci: prdbuf straddles page boundary, no DMA"); + printf("ide_pci: prdbuf straddles page boundary, no DMA\n"); FREE(cp, M_DEVBUF); FREE(prdbuf, M_DEVBUF); return 0; @@ -411,12 +556,17 @@ mkcookie(int iobase_wd, cp->prd = prdbuf_next; (char *)prdbuf_next += PRD_BUF_SIZE; - if ((char *)prdbuf_next > ((char *)prdbuf + PRD_ALLOC_SIZE)) - panic("ide_pci: too many prdbufs allocated"); + if ((char *)prdbuf_next > ((char *)prdbuf + PRD_ALLOC_SIZE)) { + printf("ide_pci: mkcookie %04x:%d: no more space for PRDs, no DMA\n", + iobase_wd, unit); + return 0; + } +#if 0 if (bootverbose) printf("ide_pci: mkcookie %04x:%d: PRD vstart = %08x vend = %08x\n", iobase_wd, unit, (int)cp->prd, ((int)cp->prd)+PRD_BUF_SIZE); +#endif LIST_INSERT_HEAD(&softc.cookies, cp, le); return cp; } @@ -426,29 +576,26 @@ ide_pci_probe(pcici_t tag, pcidi_t type) { int data = pci_conf_read(tag, PCI_CLASS_REG); - switch (data & PCI_CLASS_MASK) { - - case PCI_CLASS_MASS_STORAGE: - if ((data & PCI_SUBCLASS_MASK) == 0x00010000) { - if (type == 0x71118086) - return ("Intel PIIX4 Bus-master IDE controller"); - if (type == 0x70108086) - return ("Intel PIIX3 Bus-master IDE controller"); - if (type == 0x12308086) - return ("Intel PIIX Bus-master IDE controller"); - if (type == 0x05711106) - return ("VIA 82C586x (Apollo) Bus-master IDE controller"); - if (data & 0x8000) - return ("PCI IDE controller (busmaster capable)"); -/* - * XXX leave this out for now, to allow CMD640B hack to work. said - * hack should be better integrated, or something. - */ -#if 0 - else - return ("PCI IDE controller (not busmaster capable)"); + if ((data & PCI_CLASS_MASK) == PCI_CLASS_MASS_STORAGE && + (data & PCI_SUBCLASS_MASK) == 0x00010000) { + if (type == 0x71118086) + return ("Intel PIIX4 Bus-master IDE controller"); + if (type == 0x70108086) + return ("Intel PIIX3 Bus-master IDE controller"); + if (type == 0x12308086) + return ("Intel PIIX Bus-master IDE controller"); + if (type == 0x05711106) + return ("VIA 82C586x (Apollo) Bus-master IDE controller"); + if (data & 0x8000) + return ("PCI IDE controller (busmaster capable)"); +#ifndef CMD640 + /* + * XXX the CMD640B hack should be better integrated, or + * something. + */ + else + return ("PCI IDE controller (not busmaster capable)"); #endif - } }; return ((char*)0); } @@ -458,11 +605,12 @@ ide_pci_attach(pcici_t tag, int unit) { u_long idetm; int class; - int bmista; - int iobase_wd, iobase_bm; + int bmista_1, bmista_2; + int iobase_wd_1, iobase_wd_2, iobase_bm_1, iobase_bm_2; int cmd; struct vendor_fns *vp; pcidi_t type; + struct ide_pci_cookie *cookie; if (unit) return; @@ -496,70 +644,77 @@ ide_pci_attach(pcici_t tag, int unit) break; } - iobase_wd = (class & 0x100) ? - (pci_conf_read(tag, 0x10) & 0xfffc) : - 0x1f0; - iobase_bm = pci_conf_read(tag, 0x20) & 0xfffc; + iobase_wd_1 = (class & 0x100) ? + (pci_conf_read(tag, 0x10) & 0xfffc) : + 0x1f0; + iobase_bm_1 = pci_conf_read(tag, 0x20) & 0xfffc; + + iobase_wd_2 = (class & 0x400) ? + (pci_conf_read(tag, 0x10) & 0xfffc) : + 0x170; + iobase_bm_2 = iobase_bm_1 + SFF8038_CTLR_1; + + if (iobase_bm_1 == 0) { + printf("ide_pci: BIOS has not configured busmaster I/O address,\n" + "ide_pci: giving up\n"); + return; + } + + wddma.wdd_candma = ide_pci_candma; + wddma.wdd_dmainit = ide_pci_dmainit; + wddma.wdd_dmaverify = ide_pci_dmaverify; + wddma.wdd_dmaprep = ide_pci_dmasetup; + wddma.wdd_dmastart = ide_pci_dmastart; + wddma.wdd_dmadone = ide_pci_dmadone; + wddma.wdd_dmastatus = ide_pci_status; + bmista_1 = inb(iobase_bm_1 + BMISTA_PORT); + bmista_2 = inb(iobase_bm_2 + BMISTA_PORT); + if (!ide_pci_softc_cookies_initted) { LIST_INIT(&softc.cookies); ide_pci_softc_cookies_initted = 1; } - bmista = inb(iobase_bm + BMISTA_PORT); + if (iobase_wd_1 != 0) { + cookie = mkcookie(iobase_wd_1, 0, 0, iobase_bm_1, tag, type, vp); + if (bootverbose) + vp->vendor_status(cookie); + cookie = mkcookie(iobase_wd_1, 0, 1, iobase_bm_1, tag, type, vp); + if (bootverbose) { + vp->vendor_status(cookie); - if (bootverbose) printf("ide_pci: busmaster 0 status: %02x from port: %08x\n", - bmista, iobase_bm+BMISTA_PORT); - - if (!(bmista & BMISTA_DMA0CAP)) - printf("ide_pci: warning, ide0:0 not configured for DMA?\n"); - mkcookie(iobase_wd, 0, iobase_bm, tag, type, vp); - if (bootverbose) - vp->vendor_status(iobase_wd, 0, iobase_bm, tag, type); + bmista_1, iobase_bm_1+BMISTA_PORT); - if (!(bmista & BMISTA_DMA1CAP)) - printf("ide_pci: warning, ide0:1 not configured for DMA?\n"); - mkcookie(iobase_wd, 1, iobase_bm, tag, type, vp); - if (bootverbose) - vp->vendor_status(iobase_wd, 1, iobase_bm, tag, type); - - if (bmista & BMISTA_SIMPLEX) { - printf("ide_pci: primary is simplex-only, no DMA on secondary\n"); - } else { - iobase_wd = (class & 0x400) ? - (pci_conf_read(tag, 0x10) & 0xfffc) : - 0x170; - iobase_bm += SFF8038_CTLR_1; - bmista = inb(iobase_bm + BMISTA_PORT); - - if (bootverbose) - printf("ide_pci: busmaster 1 status: %02x from port: %08x\n", - bmista, iobase_bm+BMISTA_PORT); + if (bmista_1 & BMISTA_DMA0CAP) + printf("ide_pci: ide0:0 has been configured for DMA by BIOS\n"); + if (bmista_1 & BMISTA_DMA1CAP) + printf("ide_pci: ide0:1 has been configured for DMA by BIOS\n"); + } + } - if (bmista & BMISTA_SIMPLEX) { - printf("ide_pci: secondary is simplex-only, no DMA on secondary\n"); - } else { - if (!(bmista & BMISTA_DMA0CAP)) - printf("ide_pci: warning, ide1:0 not configured for DMA?\n"); - mkcookie(iobase_wd, 0, iobase_bm, tag, type, vp); - if (bootverbose) - vp->vendor_status(iobase_wd, 0, iobase_bm, tag, type); - if (!(bmista & BMISTA_DMA1CAP)) - printf("ide_pci: warning, ide1:1 not configured for DMA?\n"); - mkcookie(iobase_wd, 1, iobase_bm, tag, type, vp); - if (bootverbose) - vp->vendor_status(iobase_wd, 1, iobase_bm, tag, type); - } + if (bmista_1 & BMISTA_SIMPLEX || bmista_2 & BMISTA_SIMPLEX) { + printf("ide_pci: controller is simplex, no DMA on secondary channel\n"); + } + else if (iobase_wd_2 != 0) { + cookie = mkcookie(iobase_wd_2, 1, 0, iobase_bm_2, tag, type, vp); + if (bootverbose) + vp->vendor_status(cookie); + cookie = mkcookie(iobase_wd_2, 1, 1, iobase_bm_2, tag, type, vp); + if (bootverbose) { + vp->vendor_status(cookie); + + printf("ide_pci: busmaster 1 status: %02x from port: %08x\n", + bmista_2, iobase_bm_2+BMISTA_PORT); + + if (bmista_2 & BMISTA_DMA0CAP) + printf("ide_pci: ide1:0 has been configured for DMA by BIOS\n"); + if (bmista_2 & BMISTA_DMA1CAP) + printf("ide_pci: ide1:1 has been configured for DMA by BIOS\n"); + } } - wddma.wdd_candma = ide_pci_candma; - wddma.wdd_dmainit = ide_pci_dmainit; - wddma.wdd_dmaverify = ide_pci_dmaverify; - wddma.wdd_dmaprep = ide_pci_dmasetup; - wddma.wdd_dmastart = ide_pci_dmastart; - wddma.wdd_dmadone = ide_pci_dmadone; - wddma.wdd_dmastatus = ide_pci_status; } static u_long ide_pci_count; @@ -605,6 +760,15 @@ ide_pci_dmainit(void *cookie, { struct ide_pci_cookie *cp = cookie; + /* + * If the controller status indicates that DMA is configured already, + * we flounce happily away + */ + if (inb(cp->iobase_bm + BMISTA_PORT) & + ((cp->unit == 0) ? BMISTA_DMA0CAP : BMISTA_DMA1CAP)) + return 1; + + /* We take a stab at it with device-dependent code */ return(cp->vs.vendor_dmainit(cp, wp, wdcmd, wdinfo)); } /* @@ -620,16 +784,12 @@ ide_pci_dmaverify(void *xcp, char *vaddr, u_long count, int dir) * check for nonaligned or odd-length Stuff */ badfu = ((unsigned int)vaddr & 1) || (count & 1); -#if 1 +#ifdef DIAGNOSTIC if (badfu) { printf("ide_pci: dmaverify odd vaddr or length, "); printf("vaddr = %08x length = %08x\n", (int)vaddr, count); } #endif - /* - * XXX should perhaps be checking that length of generated table - * does not exceed space available, but that Would Be Hairy - */ return (!badfu); } @@ -639,118 +799,107 @@ ide_pci_dmaverify(void *xcp, char *vaddr, u_long count, int dir) * is called. */ static int -ide_pci_dmasetup(void *xcp, char *vaddr, u_long count, int dir) +ide_pci_dmasetup(void *xcp, char *vaddr, u_long vcount, int dir) { struct ide_pci_cookie *cp = xcp; struct ide_pci_prd *prd; int i; - u_long pgresid; + u_long firstpage; + u_long prd_base, prd_count; + u_long nbase, ncount, nend; int iobase_bm; - static int trashmore; + static int trashmore = 0xdeadbeef; static int *trashmore_p = 0; - + u_long count, checkcount; prd = cp->prd; + + count = vcount; + i = 0; iobase_bm = cp->iobase_bm; - /* - * ensure that 0-length transfers get a PRD that won't smash much - */ - if (!trashmore_p) - trashmore_p = (void *)vtophys(&trashmore); - - prd[0].prd_base = (unsigned int)trashmore_p; - prd[0].prd_count = 0x80000002; if (count == 0) { printf("ide_pci: dmasetup 0-length transfer, "); printf("vaddr = %08x length = %08x\n", (int)vaddr, count); + return 1; } - /* - * XXX the PRD generation code is somewhat ugly and will not - * port easily to big endian systems. - * - * but it works. - */ + /* Generate first PRD entry, which may be non-aligned. */ - /* - * Deal with transfers that don't start on a page - * boundary. - */ - pgresid = (u_long)vaddr % PAGE_SIZE; - if (pgresid) { - prd[i].prd_base = vtophys(vaddr); - if (count >= (PAGE_SIZE - pgresid)) - prd[i].prd_count = PAGE_SIZE - pgresid; - else - prd[i].prd_count = count; - vaddr += prd[i].prd_count; - count -= prd[i].prd_count; - i++; - } + firstpage = PAGE_SIZE - ((u_long)vaddr & PAGE_MASK); - /* - * We have now ensured that vaddr is page-aligned, so just - * step through the pages adding each one onto the list. - */ - while(count) { - u_long phys, n; + prd_base = vtophys(vaddr); + prd_count = MIN(count, firstpage); - phys = vtophys(vaddr); - n = ((count > PAGE_SIZE) ? PAGE_SIZE : count); - /* - * If the current page is physically contiguous with - * whatever we have in the previous PRD, just tack it - * onto the end. - * CAVEAT: due to a hardware deficiency, PRDs - * cannot cross a 64K boundary. - * XXX should we bother with this collapsing? scattered - * pages appear to be the common case anyway. - */ - if (i > 0 - && (phys == prd[i - 1].prd_base + prd[i - 1].prd_count) - && ((prd[i - 1].prd_base & 0xffff) - + prd[i - 1].prd_count + n) <= 65535) { + vaddr += prd_count; + count -= prd_count; + + /* Step through virtual pages, coalescing as needed. */ + while (count) { + + nbase = vtophys(vaddr); + ncount = MIN(count, PAGE_SIZE); + nend = nbase + ncount; - prd[i - 1].prd_count += n; + /* Coalesce if physically contiguous and not crossing 64k boundary. */ + if ((prd_base + prd_count == nbase) && + ((((nend - 1) ^ prd_base) & ~0xffff) == 0)) { + prd_count += ncount; } else { - prd[i].prd_base = phys; - prd[i].prd_count = n; + prd[i].prd_base = prd_base; + prd[i].prd_count = (prd_count & 0xffff); i++; - if (i >= PRD_MAX_SEGS) - panic("wd82371: too many segments\n"); + if (i >= PRD_MAX_SEGS) { + printf("wd82371: too many segments in PRD table\n"); + return 1; + } + prd_base = nbase; + prd_count = ncount; } - count -= n; - vaddr += n; + vaddr += ncount; + count -= ncount; } - /* put a sign at the edge of the cliff... */ - prd[(i>0) ? (i-1) : 0].prd_count |= PRD_EOT_BIT; + /* Write last PRD entry. */ + prd[i].prd_base = prd_base; + prd[i].prd_count = (prd_count & 0xffff) | PRD_EOT_BIT; + +#ifdef DIAGNOSTIC + /* sanity check the transfer for length and page-alignment, at least */ + checkcount = 0; + for (i = 0;; i++) { + unsigned int modcount; + + modcount = prd[i].prd_count & 0xffffe; + if (modcount == 0) modcount = 0x10000; + checkcount += modcount; + if (i != 0 && ((prd[i].prd_base & PAGE_MASK) != 0)) { + printf("ide_pci: dmasetup() diagnostic fails-- unaligned page\n"); + return 1; + } + if (prd[i].prd_count & PRD_EOT_BIT) + break; + } - if (i == 0) - printf("ide_pci: dmasetup 0-length PRD???\n"); + if (checkcount != vcount) { + printf("ide_pci: dmasetup() diagnostic fails-- bad length\n"); + return 1; + } +#endif /* Set up PRD base register */ outl(iobase_bm + BMIDTP_PORT, vtophys(prd)); /* Set direction of transfer */ - if (dir == B_READ) { - outb(iobase_bm + BMICOM_PORT, BMICOM_READ_WRITE); - } else { - outb(iobase_bm + BMICOM_PORT, 0); - } + outb(iobase_bm + BMICOM_PORT, (dir == B_READ) ? BMICOM_READ_WRITE : 0); /* Clear interrupt and error bits */ outb(iobase_bm + BMISTA_PORT, (inb(iobase_bm + BMISTA_PORT) | (BMISTA_INTERRUPT | BMISTA_DMA_ERROR))); - /* printf("dma enable: iobase_bm = %08x command/status = %08x pointer = %08x\n", iobase_bm, inl(iobase_bm + BMICOM_PORT), inl(iobase_bm + BMIDTP_PORT)); */ - - /* printf("P"); */ - return 0; } @@ -765,7 +914,6 @@ ide_pci_dmastart(void *xcp) outb(iobase_bm + BMICOM_PORT, inb(iobase_bm + BMICOM_PORT) | BMICOM_STOP_START); - /* printf("["); */ } static int @@ -780,8 +928,6 @@ ide_pci_dmadone(void *xcp) outb(iobase_bm + BMICOM_PORT, inb(iobase_bm + BMICOM_PORT) & ~BMICOM_STOP_START); - /* printf("]"); */ - return status; } @@ -796,8 +942,6 @@ ide_pci_status(void *xcp) bmista = inb(iobase_bm + BMISTA_PORT); - /* printf("dmastatus: iobase_bm = %08x status = %02x command/status = %08x pointer = %08x\n", iobase_bm, bmista, inl(iobase_bm + BMICOM_PORT), inl(iobase_bm + BMIDTP_PORT)); */ - if (bmista & BMISTA_INTERRUPT) status |= WDDS_INTERRUPT; if (bmista & BMISTA_DMA_ERROR) @@ -805,8 +949,6 @@ ide_pci_status(void *xcp) if (bmista & BMISTA_DMA_ACTIVE) status |= WDDS_ACTIVE; - /* printf( (bmista == BMISTA_INTERRUPT)? "?":"!"); */ - return status; } |