diff options
author | peter <peter@FreeBSD.org> | 1998-04-19 03:26:05 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1998-04-19 03:26:05 +0000 |
commit | 538a55c2428a63d5e8d41bde9560d3afb3009433 (patch) | |
tree | e9bd99bd9a964eaaa56db6bfac01d73dac62cd54 /sys | |
parent | 959a007c3a7c2927e121d62d1ea30160c4b452b9 (diff) | |
download | FreeBSD-src-538a55c2428a63d5e8d41bde9560d3afb3009433.zip FreeBSD-src-538a55c2428a63d5e8d41bde9560d3afb3009433.tar.gz |
Back out previous commit, obrien doesn't seem to be watching. The problem
is that the previous commit spammed a hacked 2.2-stable onto -current,
deleting the DMA support etc. (I guess that's one way of minimizing diffs
between -current and -stable.. :-] )
Diffstat (limited to 'sys')
-rw-r--r-- | sys/i386/isa/wd.c | 416 |
1 files changed, 318 insertions, 98 deletions
diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c index 674211d..26b3832 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.119.2.10 1998/01/16 22:28:44 pst Exp $ + * $Id: wd.c,v 1.157 1998/04/18 13:25:49 obrien Exp $ */ /* TODO: @@ -61,11 +61,15 @@ #endif #include "wdc.h" -#include "opt_wd.h" -#include "opt_atapi.h" #if NWDC > 0 +#include "opt_atapi.h" +#include "opt_devfs.h" +#include "opt_hw_wdog.h" +#include "opt_wd.h" +#include "pci.h" + #include <sys/param.h> #include <sys/dkbad.h> #include <sys/systm.h> @@ -110,6 +114,8 @@ 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_LBA 0x1000 #define WDOPT_FORCEHD(x) (((x)&0x0f00)>>8) #define WDOPT_MULTIMASK 0x00ff @@ -156,11 +162,13 @@ struct disk { #endif int dk_unit; /* physical unit number */ int dk_lunit; /* logical unit number */ + int dk_interface; /* interface (two ctrlrs per interface) */ char dk_state; /* control state */ u_char dk_status; /* copy of status reg. */ u_char dk_error; /* copy of error reg. */ u_char dk_timeout; /* countdown to next timeout */ int dk_port; /* i/o port base */ + int dk_altport; /* altstatus port base */ #ifdef DEVFS void *dk_bdev; /* devfs token for whole disk */ void *dk_cdev; /* devfs token for raw whole disk */ @@ -173,12 +181,18 @@ 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 + */ +#define DKFL_LBA 0x02000 /* use LBA for data transfers */ 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 @@ -187,7 +201,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]; /* @@ -199,7 +212,7 @@ static struct { int b_active; } wdtab[NWDC]; -struct wddma wddma; +struct wddma wddma[NWDC]; #ifdef notyet static struct buf rwdbuf[NWD]; /* buffers for raw IO */ @@ -215,6 +228,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); @@ -241,7 +255,7 @@ static d_psize_t wdsize; static struct cdevsw wd_cdevsw; static struct bdevsw wd_bdevsw = { wdopen, wdclose, wdstrategy, wdioctl, /*0*/ - wddump, wdsize, 0, "wd", &wd_cdevsw, -1 }; + wddump, wdsize, D_DISK, "wd", &wd_cdevsw, -1 }; #ifdef CMD640 static int atapictrlr; @@ -270,6 +284,7 @@ static int wdprobe(struct isa_device *dvp) { int unit = dvp->id_unit; + int interface; struct disk *du; if (unit >= NWDC) @@ -280,8 +295,21 @@ wdprobe(struct isa_device *dvp) return (0); bzero(du, sizeof *du); du->dk_ctrlr = dvp->id_unit; + interface = du->dk_ctrlr / 2; + du->dk_interface = interface; +#if !defined(DISABLE_PCI_IDE) && (NPCI > 0) + if (wddma[interface].wdd_candma) { + du->dk_dmacookie = wddma[interface].wdd_candma(dvp->id_iobase, du->dk_ctrlr); + du->dk_port = dvp->id_iobase; + du->dk_altport = wddma[interface].wdd_altiobase(du->dk_dmacookie); + } else { + du->dk_port = dvp->id_iobase; + du->dk_altport = du->dk_port + wd_ctlr; + } +#else du->dk_port = dvp->id_iobase; - + du->dk_altport = du->dk_port + wd_ctlr; +#endif /* check if we have registers that work */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* set unit 0 */ @@ -311,14 +339,16 @@ wdprobe(struct isa_device *dvp) goto reset_ok; #endif DELAY(RECOVERYTIME); - if (wdreset(du) != 0) + if (wdreset(du) != 0) { goto nodevice; + } reset_ok: /* execute a controller only command */ if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0 - || wdwait(du, 0, TIMEOUT) < 0) + || wdwait(du, 0, TIMEOUT) < 0) { goto nodevice; + } /* * drive(s) did not time out during diagnostic : @@ -332,7 +362,6 @@ reset_ok: * drive 2. (This seems to contradict the ATA spec.) */ du->dk_error = inb(du->dk_port + wd_error); - /* printf("Error : %x\n", du->dk_error); */ if(du->dk_error != 0x01 && du->dk_error != 0) { if(du->dk_error & 0x80) { /* drive 1 failure */ @@ -381,6 +410,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); @@ -389,13 +419,13 @@ wdattach(struct isa_device *dvp) if (eide_quirks & Q_CMD640B) { if (dvp->id_unit == PRIMARY) { printf("wdc0: CMD640B workaround enabled\n"); - TAILQ_INIT( &wdtab[PRIMARY].controller_queue); + bufq_init(&wdtab[PRIMARY].controller_queue); } } else - TAILQ_INIT( &wdtab[dvp->id_unit].controller_queue); + bufq_init(&wdtab[dvp->id_unit].controller_queue); #else - TAILQ_INIT( &wdtab[dvp->id_unit].controller_queue); + bufq_init(&wdtab[dvp->id_unit].controller_queue); #endif for (wdup = isa_biotab_wdc; wdup->id_driver != 0; wdup++) { @@ -413,7 +443,7 @@ wdattach(struct isa_device *dvp) if (wddrives[lunit] != NULL) panic("drive attached twice"); wddrives[lunit] = du; - TAILQ_INIT( &drive_queue[lunit]); + bufq_init(&drive_queue[lunit]); bzero(du, sizeof *du); du->dk_ctrlr = dvp->id_unit; #ifdef CMD640 @@ -427,6 +457,7 @@ wdattach(struct isa_device *dvp) du->dk_lunit = lunit; du->dk_port = dvp->id_iobase; + du->dk_altport = du->dk_port + wd_ctlr; /* * Use the individual device flags or the controller * flags. @@ -443,6 +474,10 @@ 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_LBA) + printf(", LBA"); + if (du->dk_flags & DKFL_USEDMA) + printf(", DMA"); if (du->dk_flags & DKFL_32BIT) printf(", 32-bit"); if (du->dk_multi > 1) @@ -465,6 +500,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. @@ -472,15 +518,15 @@ wdattach(struct isa_device *dvp) wdtimeout(du); #ifdef DEVFS - mynor = dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART); + mynor = dkmakeminor(lunit, WHOLE_DISK_SLICE, RAW_PART); du->dk_bdev = devfs_add_devswf(&wd_bdevsw, mynor, DV_BLK, UID_ROOT, GID_OPERATOR, 0640, - "wd%d", unit); + "wd%d", lunit); du->dk_cdev = devfs_add_devswf(&wd_cdevsw, mynor, DV_CHR, UID_ROOT, GID_OPERATOR, 0640, - "rwd%d", unit); + "rwd%d", lunit); #endif if (dk_ndrive < DK_NDRIVE) { @@ -596,7 +642,7 @@ wdstrategy(register struct buf *bp) /* queue transfer on drive, activate drive and controller if idle */ s = splbio(); - tqdisksort(&drive_queue[lunit], bp); + bufqdisksort(&drive_queue[lunit], bp); if (wdutab[lunit].b_active == 0) wdustart(du); /* start drive */ @@ -670,14 +716,14 @@ wdustart(register struct disk *du) return; - bp = drive_queue[du->dk_lunit].tqh_first; + bp = bufq_first(&drive_queue[du->dk_lunit]); if (bp == NULL) { /* yes, an assign */ return; } - TAILQ_REMOVE( &drive_queue[du->dk_lunit], bp, b_act); + bufq_remove(&drive_queue[du->dk_lunit], bp); /* link onto controller queue */ - TAILQ_INSERT_TAIL( &wdtab[ctrlr].controller_queue, bp, b_act); + bufq_insert_tail(&wdtab[ctrlr].controller_queue, bp); /* mark the drive unit as busy */ wdutab[du->dk_lunit].b_active = 1; @@ -719,7 +765,7 @@ wdstart(int ctrlr) return; #endif /* is there a drive for the controller to do a transfer with? */ - bp = wdtab[ctrlr].controller_queue.tqh_first; + bp = bufq_first(&wdtab[ctrlr].controller_queue); if (bp == NULL) { #ifdef ATAPI #ifdef CMD640 @@ -757,7 +803,7 @@ wdstart(int ctrlr) (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, blknum); else - printf(" %d)%x", du->dk_skip, inb(du->dk_port + wd_altsts)); + printf(" %d)%x", du->dk_skip, inb(du->dk_altport)); #endif lp = &du->dk_dd; @@ -794,12 +840,23 @@ wdstart(int ctrlr) u_int count1; long cylin, head, sector; - cylin = blknum / secpercyl; - head = (blknum % secpercyl) / secpertrk; - sector = blknum % secpertrk; - + if (du->dk_flags & DKFL_LBA) { + sector = (blknum >> 0) & 0xff; + cylin = (blknum >> 8) & 0xffff; + head = ((blknum >> 24) & 0xf) | WDSD_LBA; + } + else { + cylin = blknum / secpercyl; + 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; + count1 = howmany( du->dk_bc, DEV_BSIZE); du->dk_flags &= ~DKFL_MULTI; @@ -819,7 +876,19 @@ wdstart(int ctrlr) count1 = 1; du->dk_currentiosize = 1; } else { - if( (count1 > 1) && (du->dk_multi > 1)) { + if((du->dk_flags & DKFL_USEDMA) && + wddma[du->dk_interface].wdd_dmaverify(du->dk_dmacookie, + (void *)((int)bp->b_data + + 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 = count1; + } else if( (count1 > 1) && (du->dk_multi > 1)) { du->dk_flags |= DKFL_MULTI; if( bp->b_flags & B_READ) { command = WDCC_READ_MULTI; @@ -855,6 +924,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[du->dk_interface].wdd_dmaprep(du->dk_dmacookie, + (void *)((int)bp->b_data + + du->dk_skip * DEV_BSIZE), + du->dk_bc, + bp->b_flags & B_READ); + } while (wdcommand(du, cylin, head, sector, count1, command) != 0) { wderror(bp, du, @@ -864,8 +941,8 @@ wdstart(int ctrlr) #ifdef WDDEBUG printf("cylin %ld head %ld sector %ld addr %x sts %x\n", cylin, head, sector, - (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, - inb(du->dk_port + wd_altsts)); + (int)bp->b_data + du->dk_skip * DEV_BSIZE, + inb(du->dk_altport)); #endif } @@ -874,6 +951,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. @@ -883,7 +963,16 @@ 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) { + wddma[du->dk_interface].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) @@ -915,11 +1004,11 @@ wdstart(int ctrlr) if (du->dk_flags & DKFL_32BIT) outsl(du->dk_port + wd_data, - (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), + (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(long)); else outsw(du->dk_port + wd_data, - (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), + (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(short)); du->dk_bc -= DEV_BSIZE * count; if (du->dk_dkunit >= 0) { @@ -939,11 +1028,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 = 0; /* Shut up GCC */ #ifdef CMD640 int ctrlr_atapi; @@ -985,10 +1076,21 @@ wdintr(int unit) return; } #endif - bp = wdtab[unit].controller_queue.tqh_first; + bp = bufq_first(&wdtab[unit].controller_queue); du = wddrives[dkunit(bp->b_dev)]; + + /* finish off DMA */ + if (du->dk_flags & (DKFL_DMA|DKFL_USEDMA)) { + /* XXX SMP boxes sometimes generate an early intr. Why? */ + if ((wddma[du->dk_interface].wdd_dmastatus(du->dk_dmacookie) & WDDS_INTERRUPT) + == 0) + return; + dmastat = wddma[du->dk_interface].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 */ @@ -1009,16 +1111,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_MULTI) && (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; + } 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 @@ -1045,7 +1163,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"); } @@ -1053,6 +1171,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|DKFL_SINGLE)) == DKFL_DMA) && wdtab[unit].b_active) { int chk, dummy, multisize; multisize = chk = du->dk_currentiosize * DEV_BSIZE; @@ -1076,11 +1195,11 @@ oops: /* suck in data */ if( du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, - (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE), + (void *)((int)bp->b_data + 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), + (void *)((int)bp->b_data + du->dk_skip * DEV_BSIZE), chk / sizeof(short)); du->dk_bc -= chk; @@ -1094,6 +1213,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) { @@ -1125,12 +1258,11 @@ outt: done: ; /* done with this transfer, with or without error */ - du->dk_flags &= ~DKFL_SINGLE; - TAILQ_REMOVE(&wdtab[unit].controller_queue, bp, b_act); + du->dk_flags &= ~(DKFL_SINGLE|DKFL_DMA); + bufq_remove( &wdtab[unit].controller_queue, bp); 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); } @@ -1147,7 +1279,7 @@ done: ; /* anything more for controller to do? */ #ifndef ATAPI /* This is not valid in ATAPI mode. */ - if (wdtab[unit].controller_queue.tqh_first) + if (bufq_first(&wdtab[unit].controller_queue) != NULL) #endif wdstart(unit); } @@ -1180,6 +1312,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 @@ -1434,15 +1567,20 @@ 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); outb(wdc + wd_cyl_hi, cylinder >> 8); outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head); - outb(wdc + wd_sector, sector + 1); + if (head & WDSD_LBA) + outb(wdc + wd_sector, sector); + else + 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); @@ -1486,42 +1624,44 @@ wdsetctlr(struct disk *du) du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors); #endif - if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) { - struct wdparams *wp; - - printf("wd%d: can't handle %lu heads from partition table ", - du->dk_lunit, du->dk_dd.d_ntracks); - /* obtain parameters */ - wp = &du->dk_params; - if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) { - printf("(controller value %u restored)\n", - wp->wdp_heads); - du->dk_dd.d_ntracks = wp->wdp_heads; + if (!(du->dk_flags & DKFL_LBA)) { + if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) { + struct wdparams *wp; + + printf("wd%d: can't handle %lu heads from partition table ", + du->dk_lunit, du->dk_dd.d_ntracks); + /* obtain parameters */ + wp = &du->dk_params; + if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) { + printf("(controller value %u restored)\n", + wp->wdp_heads); + du->dk_dd.d_ntracks = wp->wdp_heads; + } + else { + printf("(truncating to 16)\n"); + du->dk_dd.d_ntracks = 16; + } } - else { - printf("(truncating to 16)\n"); - du->dk_dd.d_ntracks = 16; + + if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) { + printf("wd%d: cannot handle %lu sectors (max 255)\n", + du->dk_lunit, du->dk_dd.d_nsectors); + error = 1; } - } - - if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) { - printf("wd%d: cannot handle %lu sectors (max 255)\n", - du->dk_lunit, du->dk_dd.d_nsectors); - error = 1; - } - if (error) { + if (error) { #ifdef CMD640 - wdtab[du->dk_ctrlr_cmd640].b_errcnt += RETRIES; + wdtab[du->dk_ctrlr_cmd640].b_errcnt += RETRIES; #else - wdtab[du->dk_ctrlr].b_errcnt += RETRIES; + wdtab[du->dk_ctrlr].b_errcnt += RETRIES; #endif - return (1); - } - if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, - du->dk_dd.d_nsectors, WDCC_IDC) != 0 - || wdwait(du, WDCS_READY, TIMEOUT) < 0) { - wderror((struct buf *)NULL, du, "wdsetctlr failed"); - return (1); + return (1); + } + if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, + du->dk_dd.d_nsectors, WDCC_IDC) != 0 + || wdwait(du, WDCS_READY, TIMEOUT) < 0) { + wderror((struct buf *)NULL, du, "wdsetctlr failed"); + return (1); + } } wdsetmulti(du); @@ -1558,6 +1698,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 @@ -1712,6 +1872,23 @@ failed: du->dk_multi = wp->wdp_nsecperint & 0xff; wdsetmulti(du); + /* + * check drive's DMA capability + */ + if (wddma[du->dk_interface].wdd_candma) { + du->dk_dmacookie = wddma[du->dk_interface].wdd_candma(du->dk_port, du->dk_ctrlr); + /* does user want this? */ + if ((du->cfg_flags & WDOPT_DMA) && + /* have we got a DMA controller? */ + du->dk_dmacookie && + /* can said drive do DMA? */ + wddma[du->dk_interface].wdd_dmainit(du->dk_dmacookie, wp, wdsetmode, du)) { + du->dk_flags |= DKFL_USEDMA; + } + } else { + du->dk_dmacookie = NULL; + } + #ifdef WDDEBUG printf( "\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", @@ -1722,11 +1899,40 @@ failed: /* update disklabel given drive information */ du->dk_dd.d_secsize = DEV_BSIZE; - du->dk_dd.d_ncylinders = wp->wdp_cylinders; /* +- 1 */ - du->dk_dd.d_ntracks = wp->wdp_heads; - du->dk_dd.d_nsectors = wp->wdp_sectors; - du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; - du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; + if ((du->cfg_flags & WDOPT_LBA) && wp->wdp_lbasize) { + du->dk_dd.d_nsectors = 63; + if (wp->wdp_lbasize < 16*63*1024) { /* <=528.4 MB */ + du->dk_dd.d_ntracks = 16; + } + else if (wp->wdp_lbasize < 32*63*1024) { /* <=1.057 GB */ + du->dk_dd.d_ntracks = 32; + } + else if (wp->wdp_lbasize < 64*63*1024) { /* <=2.114 GB */ + du->dk_dd.d_ntracks = 64; + } + else if (wp->wdp_lbasize < 128*63*1024) { /* <=4.228 GB */ + du->dk_dd.d_ntracks = 128; + } + else if (wp->wdp_lbasize < 128*63*1024) { /* <=8.422 GB */ + du->dk_dd.d_ntracks = 255; + } + else { /* >8.422 GB */ + du->dk_dd.d_ntracks = 255; /* XXX */ + } + du->dk_dd.d_secpercyl= du->dk_dd.d_ntracks*du->dk_dd.d_nsectors; + du->dk_dd.d_ncylinders = wp->wdp_lbasize/du->dk_dd.d_secpercyl; + du->dk_dd.d_secperunit = wp->wdp_lbasize; + du->dk_flags |= DKFL_LBA; + } + else { + du->dk_dd.d_ncylinders = wp->wdp_cylinders; /* +- 1 */ + du->dk_dd.d_ntracks = wp->wdp_heads; + du->dk_dd.d_nsectors = wp->wdp_sectors; + du->dk_dd.d_secpercyl = + du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; + du->dk_dd.d_secperunit = + du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; + } if (WDOPT_FORCEHD(du->cfg_flags)) { du->dk_dd.d_ntracks = WDOPT_FORCEHD(du->cfg_flags); du->dk_dd.d_secpercyl = @@ -1770,7 +1976,7 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p) wdsleep(du->dk_ctrlr, "wdioct"); error = dsioctl("wd", dev, cmd, addr, flags, &du->dk_slices, wdstrategy1, (ds_setgeom_t *)NULL); - if (error != -1) + if (error != ENOIOCTL) return (error); switch (cmd) { @@ -2073,25 +2279,28 @@ wdflushirq(struct disk *du, int old_ipl) static int wdreset(struct disk *du) { - int wdc, err = 0; + int err = 0; + + if ((du->dk_flags & (DKFL_DMA|DKFL_USEDMA)) && du->dk_dmacookie) + wddma[du->dk_interface].wdd_dmadone(du->dk_dmacookie); - wdc = du->dk_port; (void)wdwait(du, 0, TIMEOUT); - outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST); + outb(du->dk_altport, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); - outb(wdc + wd_ctlr, WDCTL_IDS); + outb(du->dk_altport, WDCTL_IDS); #ifdef ATAPI if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) err = 1; /* no IDE drive found */ - du->dk_error = inb(wdc + wd_error); + du->dk_error = inb(du->dk_port + wd_error); if (du->dk_error != 0x01) err = 1; /* the drive is incompatible */ #else - if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0 - || (du->dk_error = inb(wdc + wd_error)) != 0x01) + if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) { + printf("wdreset: error1: 0x%x\n", du->dk_error); return (1); + } #endif - outb(wdc + wd_ctlr, WDCTL_4BIT); + outb(du->dk_altport, WDCTL_4BIT); return (err); } @@ -2123,11 +2332,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[du->dk_interface].wdd_dmastatus(du->dk_dmacookie), + WDDS_BITS); + } wdunwedge(du); wdflushirq(du, x); du->dk_skip = 0; @@ -2236,8 +2453,9 @@ wdwait(struct disk *du, u_char bits_wanted, int timeout) * command completion. */ } - if ((status & bits_wanted) == bits_wanted) + if ((status & bits_wanted) == bits_wanted) { return (status & WDCS_ERR); + } } if (timeout < TIMEOUT) /* @@ -2259,6 +2477,8 @@ static void wd_drvinit(void *unused) { if( ! wd_devsw_installed ) { + if (wd_bdevsw.d_maxio == 0) + wd_bdevsw.d_maxio = 248 * 512; bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &wd_bdevsw); wd_devsw_installed = 1; } |