Fixes and improvements from John Dyson:

Fixed the I/O statistics
Allow WD1007 type controllers to work
Support MULTI-BLOCK I/O
Correct delay to use port 0x84, reading the status register
	might not be a long enough delay.
Changed probe message to match SCSI type devices.
This commit is contained in:
David Greenman 1995-03-22 05:23:01 +00:00
parent c419d77e29
commit efea4e5256
2 changed files with 149 additions and 46 deletions

View File

@ -1,6 +1,3 @@
#define WD_COUNT_RETRIES
static int wdtest = 0;
/*-
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
@ -37,7 +34,7 @@ static int wdtest = 0;
* SUCH DAMAGE.
*
* from: @(#)wd.c 7.2 (Berkeley) 5/9/91
* $Id: wd.c,v 1.68 1995/03/06 05:40:44 davidg Exp $
* $Id: wd.c,v 1.69 1995/03/16 18:12:08 bde Exp $
*/
/* TODO:
@ -45,7 +42,6 @@ static int wdtest = 0;
* o Satisfy ATA timing in all cases.
* o Finish merging berry/sos timeout code (bump error count...).
* o Merge/fix TIH/NetBSD bad144 code.
* o Merge/fix Dyson/NetBSD clustering code.
* o Don't use polling except for initialization. Need to
* reorganize the state machine. Then "extra" interrupts
* shouldn't happen (except maybe one for initialization).
@ -53,8 +49,6 @@ static int wdtest = 0;
* bad144 in standard versions.
* o Support extended DOS partitions.
* o Support swapping to DOS partitions.
* o Look at latest linux clustering methods. Our disksort()
* gets in the way of clustering.
* o Handle bad sectors, clustering, disklabelling, DOS
* partitions and swapping driver-independently. Use
* i386/dkbad.c for bad sectors. Swapping will need new
@ -97,6 +91,9 @@ static int wdtest = 0;
/* correct max is 256 but some controllers */
/* can't handle that in all cases */
#define BAD144_NO_CYL 0xffff /* XXX should be in dkbad.h; bad144.c uses -1 */
#ifndef NSECS_MULTI
#define NSECS_MULTI 16
#endif
#ifdef notyet
#define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
@ -223,6 +220,7 @@ struct disk {
#define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
#define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */
#define DKFL_32BIT 0x00100 /* use 32-bit i/o mode */
#define DKFL_MULTI 0x00200 /* use multi-i/o mode */
struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
int dk_dkunit; /* number of statistics purposes */
struct disklabel dk_dd; /* device configuration data */
@ -230,9 +228,14 @@ struct disk {
struct dos_partition
dk_dospartitions[NDOSPART]; /* DOS view of disk */
struct dkbad dk_bad; /* bad sector table */
int dk_multi; /* multi transfers */
int dk_currentiosize; /* current io size */
long dk_badsect[127]; /* 126 plus trailing -1 marker */
};
#define WD_COUNT_RETRIES
static int wdtest = 0;
static struct disk *wddrives[NWD]; /* table of units */
static struct buf wdtab[NWDC];
static struct buf wdutab[NWD]; /* head of queue per drive */
@ -405,7 +408,9 @@ wdattach(struct isa_device *dvp)
printf("wdc%d: unit %d (wd%d): <%s>",
dvp->id_unit, unit, lunit, buf);
if (du->dk_flags & DKFL_32BIT)
printf(", 32-bit data path");
printf(", 32-bit");
if (du->dk_multi > 1)
printf(", multi-block-%d", du->dk_multi);
printf("\n");
if (du->dk_params.wdp_heads == 0 &&
du->dk_dd.d_secperunit > 100)
@ -414,12 +419,12 @@ wdattach(struct isa_device *dvp)
else if (du->dk_params.wdp_heads == 0)
printf("wd%d: size unknown: ", lunit);
else
printf("wd%d: %luMB (%lu total sec), ",
printf("wd%d: %luMB (%lu sectors), ",
lunit,
du->dk_dd.d_secperunit
* du->dk_dd.d_secsize / (1024 * 1024),
du->dk_dd.d_secperunit);
printf("%lu cyl, %lu head, %lu sec, bytes/sec %lu\n",
printf("%lu C %lu H %lu S/T %lu B/S\n",
du->dk_dd.d_ncylinders,
du->dk_dd.d_ntracks,
du->dk_dd.d_nsectors,
@ -590,6 +595,7 @@ wdstart(int ctrlr)
long blknum, cylin, head, sector;
long secpertrk, secpercyl;
int lunit;
int count;
loop:
/* is there a drive for the controller to do a transfer with? */
@ -627,11 +633,13 @@ wdstart(int ctrlr)
secpertrk = lp->d_nsectors;
secpercyl = lp->d_secpercyl;
if(du->dk_dkunit >= 0) {
dk_wds[du->dk_dkunit] += bp->b_bcount >> 6;
}
if (du->dk_skip == 0) {
if(du->dk_dkunit >= 0) {
dk_wds[du->dk_dkunit] += bp->b_bcount >> 6;
}
du->dk_bc = bp->b_bcount;
if (bp->b_flags & B_BAD
@ -671,10 +679,6 @@ wdstart(int ctrlr)
}
cylin = blknum / secpercyl;
head = (blknum % secpercyl) / secpertrk;
sector = blknum % secpertrk;
wdtab[ctrlr].b_active = 1; /* mark controller active */
/* if starting a multisector transfer, or doing single transfers */
@ -682,8 +686,15 @@ wdstart(int ctrlr)
u_int command;
u_int count;
cylin = blknum / secpercyl;
head = (blknum % secpercyl) / secpertrk;
sector = blknum % secpertrk;
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;
#ifdef B_FORMAT
if (bp->b_flags & B_FORMAT) {
@ -692,13 +703,33 @@ wdstart(int ctrlr)
sector = lp->d_gap3 - 1; /* + 1 later */
} else
#endif
{
if (du->dk_flags & DKFL_SINGLE)
if (du->dk_flags & DKFL_SINGLE) {
command = (bp->b_flags & B_READ)
? WDCC_READ : WDCC_WRITE;
count = 1;
else
count = howmany(du->dk_bc, DEV_BSIZE);
command = (bp->b_flags & B_READ)
? WDCC_READ : WDCC_WRITE;
du->dk_currentiosize = 1;
} else {
if( (count > 1) && (du->dk_multi > 1)) {
du->dk_flags |= DKFL_MULTI;
if( bp->b_flags & B_READ) {
command = WDCC_READ_MULTI;
} else {
command = WDCC_WRITE_MULTI;
}
du->dk_currentiosize = du->dk_multi;
if( du->dk_currentiosize > count)
du->dk_currentiosize = count;
} else {
if( bp->b_flags & B_READ) {
command = WDCC_READ;
} else {
command = WDCC_WRITE;
}
du->dk_currentiosize = 1;
}
}
}
/*
@ -765,16 +796,25 @@ wdstart(int ctrlr)
*/
}
/* then send it! */
count = 1;
if( du->dk_flags & DKFL_MULTI) {
count = howmany(du->dk_bc, DEV_BSIZE);
if( count > du->dk_multi)
count = du->dk_multi;
if( du->dk_currentiosize > count)
du->dk_currentiosize = count;
}
if (du->dk_flags & DKFL_32BIT)
outsl(du->dk_port + wd_data,
(void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
DEV_BSIZE / sizeof(long));
(count * DEV_BSIZE) / sizeof(long));
else
outsw(du->dk_port + wd_data,
(void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
DEV_BSIZE / sizeof(short));
du->dk_bc -= DEV_BSIZE;
(count * DEV_BSIZE) / sizeof(short));
du->dk_bc -= DEV_BSIZE * count;
}
/* Interrupt routine for the controller. Acknowledge the interrupt, check for
@ -798,7 +838,6 @@ wdintr(int unit)
bp = wdtab[unit].b_actf;
du = wddrives[wdunit(bp->b_dev)];
dp = &wdutab[du->dk_lunit];
du->dk_timeout = 0;
if (wdwait(du, 0, TIMEOUT) < 0) {
@ -823,6 +862,10 @@ wdintr(int unit)
/* have we an error? */
if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) {
oops:
if( (du->dk_status & DKFL_MULTI) && (inb(du->dk_port) & WDERR_ABORT)) {
wderror(bp, du, "reverting to non-multi sector mode");
du->dk_multi = 1;
}
#ifdef WDDEBUG
wderror(bp, du, "wdintr");
#endif
@ -856,6 +899,16 @@ wdintr(int unit)
*/
if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ)
&& wdtab[unit].b_active) {
int chk, dummy, multisize;
multisize = chk = du->dk_currentiosize * DEV_BSIZE;
if( du->dk_bc < chk) {
chk = du->dk_bc;
if( ((chk + DEV_BSIZE - 1) / DEV_BSIZE) < du->dk_currentiosize) {
du->dk_currentiosize = (chk + DEV_BSIZE - 1) / DEV_BSIZE;
multisize = du->dk_currentiosize * DEV_BSIZE;
}
}
/* ready to receive data? */
if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
!= (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
@ -866,15 +919,21 @@ wdintr(int unit)
}
/* suck in data */
if (du->dk_flags & DKFL_32BIT)
if( du->dk_flags & DKFL_32BIT)
insl(du->dk_port + wd_data,
bp->b_un.b_addr + du->dk_skip * DEV_BSIZE,
DEV_BSIZE / sizeof(long));
(void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
chk / sizeof(long));
else
insw(du->dk_port + wd_data,
bp->b_un.b_addr + du->dk_skip * DEV_BSIZE,
DEV_BSIZE / sizeof(short));
du->dk_bc -= DEV_BSIZE;
(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) {
insw(du->dk_port + wd_data, &dummy, 1);
chk += sizeof(short);
}
}
wdxfer[du->dk_lunit]++;
@ -887,15 +946,20 @@ wdintr(int unit)
outt:
if (wdtab[unit].b_active) {
if ((bp->b_flags & B_ERROR) == 0) {
du->dk_skip++; /* add to successful sectors */
du->dk_skip += du->dk_currentiosize;/* add to successful sectors */
if (wdtab[unit].b_errcnt)
wderror(bp, du, "soft error");
wdtab[unit].b_errcnt = 0;
/* see if more to transfer */
if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
wdtab[unit].b_active = 0;
wdstart(unit);
if( (du->dk_flags & DKFL_SINGLE) ||
((bp->b_flags & B_READ) == 0)) {
wdtab[unit].b_active = 0;
wdstart(unit);
} else {
du->dk_timeout = 1 + 3;
}
return; /* next chunk is started */
} else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR))
== DKFL_ERROR) {
@ -1170,12 +1234,16 @@ wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
if (wdwait(du, 0, TIMEOUT) < 0)
return (1);
wdc = du->dk_port;
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);
outb(wdc + wd_seccnt, count);
if( command == WDCC_FEATURES) {
outb(wdc + wd_features, count);
} 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);
outb(wdc + wd_seccnt, count);
}
if (wdwait(du, command == WDCC_DIAGNOSE || command == WDCC_IDC
? 0 : WDCS_READY, TIMEOUT) < 0)
return (1);
@ -1257,10 +1325,17 @@ wdgetctlr(struct disk *du)
{
int i;
char tb[DEV_BSIZE], tb2[DEV_BSIZE];
struct wdparams *wp;
struct wdparams *wp = NULL;
again:
if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0
|| wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) {
/*
* if we failed on the second try, assume non-32bit
*/
if( du->dk_flags & DKFL_32BIT)
goto failed;
/* XXX need to check error status after final transfer. */
/*
* Old drives don't support WDCC_READP. Try a seek to 0.
@ -1352,6 +1427,7 @@ wdgetctlr(struct disk *du)
/* check that we really have 32-bit controller */
if (bcmp (tb, tb2, sizeof(struct wdparams)) != 0) {
failed:
/* test failed, use 16-bit i/o mode */
bcopy(tb2, tb, sizeof(struct wdparams));
du->dk_flags &= ~DKFL_32BIT;
@ -1403,6 +1479,19 @@ wdgetctlr(struct disk *du)
du->dk_dd.d_type = DTYPE_ESDI;
du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
du->dk_multi = 1;
if( (NSECS_MULTI != 1) && ((wp->wdp_nsecperint & 0xff) >= NSECS_MULTI)){
if( !wdcommand(du, 0, 0, 0, NSECS_MULTI, WDCC_SET_MULTI)) {
du->dk_multi = NSECS_MULTI;
}
}
#ifdef NOTYET
/* set read caching and write caching */
wdcommand(du, 0, 0, 0, WDFEA_RCACHE, WDCC_FEATURES);
wdcommand(du, 0, 0, 0, WDFEA_WCACHE, WDCC_FEATURES);
#endif
return (0);
}
@ -1953,7 +2042,12 @@ wdwait(struct disk *du, u_char bits_wanted, int timeout)
timeout += POLLING;
/* dummy read for delay */
(void) inb(wdc + wd_status);
/*
* the reason that we are reading from an *unused* port,
* is that it might be *really* fast to read from the
* wd port.
*/
(void) inb(0x84);
do {
#ifdef WD_COUNT_RETRIES

View File

@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)wdreg.h 7.1 (Berkeley) 5/9/91
* $Id: wdreg.h,v 1.6 1994/01/04 20:05:26 nate Exp $
* $Id: wdreg.h,v 1.7 1995/02/26 01:15:29 bde Exp $
*/
/*
@ -43,6 +43,7 @@
#define wd_data 0x0 /* data register (R/W - 16 bits) */
#define wd_error 0x1 /* error register (R) */
#define wd_precomp wd_error /* write precompensation (W) */
#define wd_features wd_error /* features register (W) */
#define wd_seccnt 0x2 /* sector count (R/W) */
#define wd_sector 0x3 /* first sector number (R/W) */
#define wd_cyl_lo 0x4 /* cylinder address, low byte (R/W) */
@ -71,6 +72,7 @@
#define WDCS_ERR 0x01 /* Error detect bit. */
#define WDCS_BITS "\020\010busy\006rdy\006wrtflt\005seekdone\004drq\003ecc_cor\002index\001err"
#define WDERR_ABORT 0x04
#define WDERR_BITS "\020\010badblk\007uncorr\006id_crc\005no_id\003abort\002tr000\001no_dam"
@ -87,10 +89,17 @@
#define WDCC_FORMAT 0x50 /* disk format code */
#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */
#define WDCC_IDC 0x91 /* initialize drive command */
#define WDCC_READ_MULTI 0xC4 /* read multiple */
#define WDCC_WRITE_MULTI 0xC5 /* write multiple */
#define WDCC_SET_MULTI 0xC6 /* set multiple count */
#define WDCC_EXTDCMD 0xE0 /* send extended command */
#define WDCC_READP 0xEC /* read parameters from controller */
#define WDCC_CACHEC 0xEF /* cache control */
#define WDCC_FEATURES 0xEF /* features control */
#define WDFEA_RCACHE 0xAA /* read cache enable */
#define WDFEA_WCACHE 0x02 /* write cache enable */
#define WD_STEP 0 /* winchester- default 35us step */