diff options
Diffstat (limited to 'sys/i386')
-rw-r--r-- | sys/i386/conf/files.i386 | 4 | ||||
-rw-r--r-- | sys/i386/isa/wd.c | 125 | ||||
-rw-r--r-- | sys/i386/isa/wdreg.h | 104 |
3 files changed, 219 insertions, 14 deletions
diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386 index fe46c28..8a79192 100644 --- a/sys/i386/conf/files.i386 +++ b/sys/i386/conf/files.i386 @@ -1,7 +1,7 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # -# $Id: files.i386,v 1.167 1997/07/24 23:45:17 fsmp Exp $ +# $Id: files.i386,v 1.168 1997/07/25 11:53:20 phk Exp $ # aic7xxx_asm optional ahc device-driver \ dependency "$S/dev/aic7xxx/*.[chyl]" \ @@ -293,4 +293,4 @@ gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate gnu/i386/isa/dgb.c optional dgb device-driver -pci/wd82371.c optional wd device-driver +pci/ide_pci.c optional wd device-driver diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c index 09beae0..09231ad 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.131 1997/07/01 00:22:45 bde Exp $ + * $Id: wd.c,v 1.132 1997/07/20 14:10:17 bde Exp $ */ /* TODO: @@ -110,6 +110,7 @@ extern void wdstart(int ctrlr); /* can't handle that in all cases */ #define WDOPT_32BIT 0x8000 #define WDOPT_SLEEPHACK 0x4000 +#define WDOPT_DMA 0x2000 #define WDOPT_FORCEHD(x) (((x)&0x0f00)>>8) #define WDOPT_MULTIMASK 0x00ff @@ -173,12 +174,17 @@ struct disk { #define DKFL_32BIT 0x00100 /* use 32-bit i/o mode */ #define DKFL_MULTI 0x00200 /* use multi-i/o mode */ #define DKFL_BADSCAN 0x00400 /* report all errors */ +#define DKFL_USEDMA 0x00800 /* use DMA for data transfers */ +#define DKFL_DMA 0x01000 /* using DMA on this transfer-- DKFL_SINGLE + * overrides this + */ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */ int dk_dkunit; /* disk stats unit number */ int dk_multi; /* multi transfers */ int dk_currentiosize; /* current io size */ struct diskgeom dk_dd; /* device configuration data */ struct diskslices *dk_slices; /* virtual drives */ + void *dk_dmacookie; /* handle for DMA services */ }; #define WD_COUNT_RETRIES @@ -215,6 +221,7 @@ static int wdsetctlr(struct disk *du); #if 0 static int wdwsetctlr(struct disk *du); #endif +static int wdsetmode(int mode, void *wdinfo); static int wdgetctlr(struct disk *du); static void wderror(struct buf *bp, struct disk *du, char *mesg); static void wdflushirq(struct disk *du, int old_ipl); @@ -381,6 +388,7 @@ wdattach(struct isa_device *dvp) int unit, lunit; struct isa_device *wdup; struct disk *du; + struct wdparams *wp; if (dvp->id_unit >= NWDC) return (0); @@ -443,6 +451,8 @@ wdattach(struct isa_device *dvp) dvp->id_unit, unit, lunit, sizeof du->dk_params.wdp_model, du->dk_params.wdp_model); + if (du->dk_flags & DKFL_USEDMA) + printf(", DMA"); if (du->dk_flags & DKFL_32BIT) printf(", 32-bit"); if (du->dk_multi > 1) @@ -465,6 +475,17 @@ wdattach(struct isa_device *dvp) du->dk_dd.d_nsectors, du->dk_dd.d_secsize); + if (bootverbose) { + wp = &du->dk_params; + printf( +"wd%d: ATA INQUIRE valid = %04x, dmamword = %04x, apio = %04x, udma = %04x\n", + du->dk_lunit, + wp->wdp_atavalid, + wp->wdp_dmamword, + wp->wdp_eidepiomodes, + wp->wdp_udmamode); + } + /* * Start timeout routine for this drive. * XXX timeout should be per controller. @@ -818,7 +839,19 @@ wdstart(int ctrlr) count = 1; du->dk_currentiosize = 1; } else { - if( (count > 1) && (du->dk_multi > 1)) { + if((du->dk_flags & DKFL_USEDMA) && + wddma.wdd_dmaverify(du->dk_dmacookie, + (void *)((int)bp->b_un.b_addr + + du->dk_skip * DEV_BSIZE), + du->dk_bc, + bp->b_flags & B_READ)) { + du->dk_flags |= DKFL_DMA; + if( bp->b_flags & B_READ) + command = WDCC_READ_DMA; + else + command = WDCC_WRITE_DMA; + du->dk_currentiosize = count; + } else if( (count > 1) && (du->dk_multi > 1)) { du->dk_flags |= DKFL_MULTI; if( bp->b_flags & B_READ) { command = WDCC_READ_MULTI; @@ -854,6 +887,14 @@ wdstart(int ctrlr) if(du->dk_dkunit >= 0) { dk_busy |= 1 << du->dk_dkunit; } + + if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { + wddma.wdd_dmaprep(du->dk_dmacookie, + (void *)((int)bp->b_un.b_addr + + du->dk_skip * DEV_BSIZE), + du->dk_bc, + bp->b_flags & B_READ); + } while (wdcommand(du, cylin, head, sector, count, command) != 0) { wderror(bp, du, @@ -884,6 +925,12 @@ wdstart(int ctrlr) */ 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) { + wddma.wdd_dmastart(du->dk_dmacookie); + return; + } + /* If this is a read operation, just go away until it's done. */ if (bp->b_flags & B_READ) return; @@ -988,6 +1035,15 @@ wdintr(int unit) du = wddrives[dkunit(bp->b_dev)]; du->dk_timeout = 0; + /* finish off DMA. ignore errors if we're not using it. */ + if (du->dk_flags & (DKFL_DMA|DKFL_USEDMA)) { + 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 */ + } + } + if (wdwait(du, 0, TIMEOUT) < 0) { wderror(bp, du, "wdintr: timeout waiting for status"); du->dk_status |= WDCS_ERR; /* XXX */ @@ -1014,7 +1070,12 @@ oops: * XXX bogus inb() here, register 0 is assumed and intr status * is reset. */ - if( (du->dk_flags & DKFL_MULTI) && (inb(du->dk_port) & WDERR_ABORT)) { + if((du->dk_flags & DKFL_DMA ) && + (inb(du->dk_port) & WDERR_ABORT)) { + wderror(bp, du, "reverting to PIO mode"); + du->dk_flags |= ~DKFL_USEDMA; + } 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; } @@ -1052,6 +1113,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) && wdtab[unit].b_active) { int chk, dummy, multisize; multisize = chk = du->dk_currentiosize * DEV_BSIZE; @@ -1093,6 +1155,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) + && 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) { @@ -1124,7 +1200,7 @@ outt: done: ; /* done with this transfer, with or without error */ - du->dk_flags &= ~DKFL_SINGLE; + du->dk_flags &= ~(DKFL_SINGLE|DKFL_DMA); TAILQ_REMOVE(&wdtab[unit].controller_queue, bp, b_act); wdtab[unit].b_errcnt = 0; bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE; @@ -1441,7 +1517,7 @@ wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector, outb(wdc + wd_sector, sector + 1); outb(wdc + wd_seccnt, count); } - if (wdwait(du, command == WDCC_DIAGNOSE || command == WDCC_IDC + if (wdwait(du, (command == WDCC_DIAGNOSE || command == WDCC_IDC) ? 0 : WDCS_READY, TIMEOUT) < 0) return (1); outb(wdc + wd_command, command); @@ -1557,6 +1633,25 @@ 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("wdsetmode() setting transfer mode to %02x\n", 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 @@ -1711,6 +1806,21 @@ failed: du->dk_multi = wp->wdp_nsecperint & 0xff; wdsetmulti(du); + du->dk_dmacookie = NULL; + /* + * check drive's DMA capability + */ + /* does user want this? */ + if ((du->cfg_flags & WDOPT_DMA) && + /* have we got a DMA controller? */ + (wddma.wdd_candma && + (du->dk_dmacookie = wddma.wdd_candma(du->dk_port, + du->dk_unit))) && + /* can said drive do DMA? */ + (wddma.wdd_dmainit(du->dk_dmacookie, wp, wdsetmode, du))) + + du->dk_flags |= DKFL_USEDMA; + #ifdef WDDEBUG printf( "\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", @@ -2061,6 +2171,9 @@ wdreset(struct disk *du) { int wdc, err = 0; + if ((du->dk_flags & (DKFL_DMA|DKFL_USEDMA)) && du->dk_dmacookie) + wddma.wdd_dmadone(du->dk_dmacookie); + wdc = du->dk_port; (void)wdwait(du, 0, TIMEOUT); outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST); @@ -2112,7 +2225,7 @@ wdtimeout(void *cdu) if(timeouts++ == 5) wderror((struct buf *)NULL, du, "Last time I say: interrupt timeout. Probably a portable PC."); - else if(timeouts++ < 5) + else if(timeouts < 5) wderror((struct buf *)NULL, du, "interrupt timeout"); wdunwedge(du); wdflushirq(du, x); diff --git a/sys/i386/isa/wdreg.h b/sys/i386/isa/wdreg.h index 3ad2403..f7dfa488 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$ + * $Id: wdreg.h,v 1.17 1997/02/22 09:37:27 peter Exp $ */ /* @@ -122,6 +122,8 @@ #define WDCC_READ_MULTI 0xC4 /* read multiple */ #define WDCC_WRITE_MULTI 0xC5 /* write multiple */ #define WDCC_SET_MULTI 0xC6 /* set multiple count */ +#define WDCC_READ_DMA 0xC8 /* read using DMA */ +#define WDCC_WRITE_DMA 0xCA /* write using DMA */ #define WDCC_EXTDCMD 0xE0 /* send extended command */ @@ -130,6 +132,7 @@ #define WDFEA_RCACHE 0xAA /* read cache enable */ #define WDFEA_WCACHE 0x02 /* write cache enable */ +#define WDFEA_SETXFER 0x03 /* set transfer mode */ #define WD_STEP 0 /* winchester- default 35us step */ @@ -140,10 +143,14 @@ * read parameters command returns this: */ struct wdparams { + /* + * XXX partly based on DRAFT X3T13/1153D rev 14. + * by the time you read this it will have changed. + */ /* drive info */ short wdp_config; /* general configuration bits */ u_short wdp_cylinders; /* number of cylinders */ - short wdp_reserved; + short wdp_reserved2; u_short wdp_heads; /* number of heads */ short wdp_unfbytespertrk; /* number of unformatted bytes/track */ short wdp_unfbytes; /* number of unformatted bytes/sector */ @@ -159,8 +166,58 @@ struct wdparams { short wdp_necc; /* ecc bytes appended */ char wdp_rev[8]; /* firmware revision */ char wdp_model[40]; /* model name */ - short wdp_nsecperint; /* sectors per interrupt */ + char wdp_nsecperint; /* sectors per interrupt */ + char wdp_vendorunique1; short wdp_usedmovsd; /* can use double word read/write? */ + char wdp_vendorunique2; + char wdp_capability; /* various capability bits */ + short wdp_cap_validate; /* validation for above */ + char wdp_vendorunique3; + char wdp_opiomode; /* PIO modes 0-2 */ + char wdp_vendorunique4; + char wdp_odmamode; /* old DMA modes, not in ATA-3 */ + short wdp_atavalid; /* validation for newer fields */ + short wdp_currcyls; + short wdp_currheads; + short wdp_currsectors; + short wdp_currsize0; + short wdp_currsize1; + char wdp_currmultsect; + char wdp_multsectvalid; + int wdp_lbasize; + short wdp_dmasword; /* obsolete in ATA-3 */ + short wdp_dmamword; /* multiword DMA modes */ + short wdp_eidepiomodes; /* advanced PIO modes */ + short wdp_eidedmamin; /* fastest possible DMA timing */ + short wdp_eidedmanorm; /* recommended DMA timing */ + short wdp_eidepioblind; /* fastest possible blind PIO */ + short wdp_eidepioacked; /* fastest possible IORDY PIO */ + short wdp_reserved69; + short wdp_reserved70; + short wdp_reserved71; + short wdp_reserved72; + short wdp_reserved73; + short wdp_reserved74; + short wdp_queuelen; + short wdp_reserved76; + short wdp_reserved77; + short wdp_reserved78; + short wdp_reserved79; + short wdp_versmaj; + short wdp_versmin; + short wdp_featsupp1; + short wdp_featsupp2; + short wdp_featsupp3; + short wdp_featenab1; + short wdp_featenab2; + short wdp_featenab3; + short wdp_udmamode; /* UltraDMA modes */ + short wdp_erasetime; + short wdp_enherasetime; + short wdp_apmlevel; + short wdp_reserved92[34]; + short wdp_rmvcap; + short wdp_securelevel; }; /* @@ -179,12 +236,18 @@ int wdformat(struct buf *bp); * To use this: * For each drive which you might want to do DMA on, call wdd_candma() * to get a cookie. If it returns a null pointer, then the drive - * can't do DMA. + * can't do DMA. Then call wdd_dmainit() to initialize the controller + * and drive. wdd_dmainit should leave PIO modes operational, though + * perhaps with suboptimal performance. * - * Set up the transfer be calling wdd_dmaprep(). The cookie is what + * Check the transfer by calling wdd_dmaverify(). The cookie is what * you got before; vaddr is the virtual address of the buffer to be * written; len is the length of the buffer; and direction is either - * B_READ or B_WRITE. + * B_READ or B_WRITE. This function verifies that the DMA hardware is + * capable of handling the request you've made. + * + * Setup the transfer by calling wdd_dmaprep(). This takes the same + * paramaters as wdd_dmaverify(). * * Send a read/write DMA command to the drive. * @@ -199,6 +262,8 @@ int wdformat(struct buf *bp); struct wddma { void *(*wdd_candma) /* returns a cookie if can do DMA */ __P((int ctlr, int drive)); + int (*wdd_dmaverify) /* verify that request is DMA-able */ + __P((void *cookie, char *vaddr, u_long len, int direction)); int (*wdd_dmaprep) /* prepare DMA hardware */ __P((void *cookie, char *vaddr, u_long len, int direction)); void (*wdd_dmastart) /* begin DMA transfer */ @@ -207,12 +272,39 @@ struct wddma { __P((void *cookie)); int (*wdd_dmastatus) /* return status of DMA */ __P((void *cookie)); + int (*wdd_dmainit) /* initialize controller and drive */ + __P((void *cookie, + struct wdparams *wp, + int(wdcmd)__P((int mode, void *wdinfo)), + void *wdinfo)); }; +/* logical status bits returned by wdd_dmastatus */ #define WDDS_ACTIVE 0x0001 #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 +/* flow-controlled PIO modes */ +#define WDDMA_PIO 0x10 +#define WDDMA_PIO3 0x10 +#define WDDMA_PIO4 0x11 +/* multi-word DMA timing modes */ +#define WDDMA_MDMA 0x20 +#define WDDMA_MDMA0 0x20 +#define WDDMA_MDMA1 0x21 +#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 + extern struct wddma wddma; #endif /* KERNEL */ |