Enable Fast SCSI transfers for WD33C93B parts with appropriate input

clocks.

My Seagate ST32430N now sees about 50% greater throughput (about 97% as
fast as IRIX can push it). However, there appear to be other performance
issues with the driver as other disks perform significantly worse than they
should, with or without this change.

Reviewed by bjh21. Tested on my Indy and Challenge S.
This commit is contained in:
rumble 2007-05-08 00:20:15 +00:00
parent 10c3d35ca0
commit 11dc14c9fe
3 changed files with 109 additions and 92 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: wd33c93.c,v 1.11 2007/03/12 18:18:30 ad Exp $ */
/* $NetBSD: wd33c93.c,v 1.12 2007/05/08 00:20:15 rumble Exp $ */
/*
* Copyright (c) 1990 The Regents of the University of California.
@ -79,7 +79,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: wd33c93.c,v 1.11 2007/03/12 18:18:30 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: wd33c93.c,v 1.12 2007/05/08 00:20:15 rumble Exp $");
#include "opt_ddb.h"
@ -144,8 +144,7 @@ void wd33c93_sched_msgout (struct wd33c93_softc *, u_short);
void wd33c93_msgout (struct wd33c93_softc *);
void wd33c93_timeout (void *arg);
void wd33c93_watchdog (void *arg);
int wd33c93_div2stp (struct wd33c93_softc *, int);
int wd33c93_stp2div (struct wd33c93_softc *, int);
u_char wd33c93_stp2syn (struct wd33c93_softc *, struct wd33c93_tinfo *);
void wd33c93_setsync (struct wd33c93_softc *, struct wd33c93_tinfo *);
void wd33c93_update_xfer_mode (struct wd33c93_softc *, int);
@ -222,11 +221,18 @@ wd33c93_attach(struct wd33c93_softc *dev)
dev->sc_cfflags = device_cfdata(&dev->sc_dev)->cf_flags;
wd33c93_init(dev);
printf(": %s revision %d, %d.%d MHz, SCSI ID %d\n",
wd33c93_chip_names[dev->sc_chip], dev->sc_rev,
printf(": %s (%d.%d MHz clock, SCSI ID %d)\n",
wd33c93_chip_names[dev->sc_chip],
dev->sc_clkfreq / 10, dev->sc_clkfreq % 10,
dev->sc_channel.chan_id);
if (dev->sc_chip == SBIC_CHIP_WD33C93B) {
printf("%s: microcode revision 0x%02x",
dev->sc_dev.dv_xname, dev->sc_rev);
if (dev->sc_minsyncperiod < 50)
printf(", Fast SCSI");
printf("\n");
}
dev->sc_child = config_found(&dev->sc_dev, &dev->sc_channel,
scsiprint);
@ -277,7 +283,7 @@ wd33c93_init(struct wd33c93_softc *dev)
ti->flags |= T_NOSYNC;
if (dev->sc_cfflags & (1<<i) || wd33c93_nodisc)
ti->flags |= T_NODISC;
ti->period = dev->sc_syncperiods[0];
ti->period = dev->sc_minsyncperiod;
ti->offset = 0;
}
}
@ -297,6 +303,40 @@ wd33c93_reset(struct wd33c93_softc *dev)
(*dev->sc_reset)(dev);
my_id = dev->sc_channel.chan_id & SBIC_ID_MASK;
/* Enable advanced features and really(!) advanced features */
#if 1
my_id |= (SBIC_ID_EAF | SBIC_ID_RAF); /* XXX - MD Layer */
#endif
SET_SBIC_myid(dev, my_id);
/* Reset the chip */
SET_SBIC_cmd(dev, SBIC_CMD_RESET);
DELAY(25);
SBIC_WAIT(dev, SBIC_ASR_INT, 0);
/* Set up various chip parameters */
SET_SBIC_control(dev, SBIC_CTL_EDI | SBIC_CTL_IDI);
GET_SBIC_csr(dev, csr); /* clears interrupt also */
GET_SBIC_cdb1(dev, dev->sc_rev); /* valid with RAF on wd33c93b */
switch (csr) {
case SBIC_CSR_RESET:
dev->sc_chip = SBIC_CHIP_WD33C93;
break;
case SBIC_CSR_RESET_AM:
SET_SBIC_queue_tag(dev, 0x55);
GET_SBIC_queue_tag(dev, reg);
dev->sc_chip = (reg == 0x55) ?
SBIC_CHIP_WD33C93B : SBIC_CHIP_WD33C93A;
SET_SBIC_queue_tag(dev, 0x0);
break;
default:
dev->sc_chip = SBIC_CHIP_UNKNOWN;
}
/*
* Choose a suitable clock divisor and work out the resulting
* sync transfer periods in 4ns units.
@ -312,46 +352,25 @@ wd33c93_reset(struct wd33c93_softc *dev)
div = 4;
} else
panic("wd33c93: invalid clock speed %d", dev->sc_clkfreq);
for (i = 0; i < 7; i++)
dev->sc_syncperiods[i] =
(i + 2) * div * 1250 / dev->sc_clkfreq;
dev->sc_minsyncperiod = dev->sc_syncperiods[0];
SBIC_DEBUG(SYNC, ("available sync periods: %d %d %d %d %d %d %d\n",
dev->sc_syncperiods[0], dev->sc_syncperiods[1],
dev->sc_syncperiods[2], dev->sc_syncperiods[3],
dev->sc_syncperiods[4], dev->sc_syncperiods[5],
dev->sc_syncperiods[6]));
/* Enable advanced features */
#if 1
my_id |= SBIC_ID_EAF; /* XXX - MD Layer */
#endif
SET_SBIC_myid(dev, my_id);
/* Reset the chip */
SET_SBIC_cmd(dev, SBIC_CMD_RESET);
DELAY(25);
SBIC_WAIT(dev, SBIC_ASR_INT, 0);
/* Set up various chip parameters */
SET_SBIC_control(dev, SBIC_CTL_EDI | SBIC_CTL_IDI);
GET_SBIC_csr(dev, csr); /* clears interrupt also */
GET_SBIC_cdb1(dev, dev->sc_rev);
switch (csr) {
case SBIC_CSR_RESET:
dev->sc_chip = SBIC_CHIP_WD33C93;
break;
case SBIC_CSR_RESET_AM:
SET_SBIC_queue_tag(dev, 0x55);
GET_SBIC_queue_tag(dev, reg);
dev->sc_chip = (reg == 0x55) ?
SBIC_CHIP_WD33C93B : SBIC_CHIP_WD33C93A;
SET_SBIC_queue_tag(dev, 0x0);
break;
default:
dev->sc_chip = SBIC_CHIP_UNKNOWN;
if (dev->sc_clkfreq >= 160 && dev->sc_chip == SBIC_CHIP_WD33C93B) {
for (i = 0; i < 3; i++)
dev->sc_fsyncperiods[i] =
(i + 2) * 2 * 1250 / dev->sc_clkfreq;
SBIC_DEBUG(SYNC, ("available fast sync periods: %d %d %d\n",
dev->sc_fsyncperiods[0], dev->sc_fsyncperiods[1],
dev->sc_fsyncperiods[2]));
dev->sc_minsyncperiod = dev->sc_fsyncperiods[0];
}
/*
@ -383,25 +402,50 @@ wd33c93_error(struct wd33c93_softc *dev, struct wd33c93_acb *acb)
printf("SCSI Error\n");
}
/*
* Determine an appropriate value for the synchronous transfer register
* given the period and offset values in *ti.
*/
u_char
wd33c93_stp2syn(struct wd33c93_softc *dev, struct wd33c93_tinfo *ti)
{
unsigned i;
/* see if we can handle fast scsi (100-200ns) first */
if (ti->period < 50 && dev->sc_minsyncperiod < 50) {
for (i = 0; i < 3; i++)
if (dev->sc_fsyncperiods[i] >= ti->period)
return (SBIC_SYN(ti->offset, i + 2, 1));
}
for (i = 0; i < 7; i++) {
if (dev->sc_syncperiods[i] >= ti->period) {
if (i == 6)
return (SBIC_SYN(0, 0, 0));
else
return (SBIC_SYN(ti->offset, i + 2, 0));
}
}
/* XXX - can't handle it; do async */
return (SBIC_SYN(0, 0, 0));
}
/*
* Setup sync mode for given target
*/
void
wd33c93_setsync(struct wd33c93_softc *dev, struct wd33c93_tinfo *ti)
{
u_char offset, period;
u_char syncreg;
if (ti->flags & T_SYNCMODE) {
offset = ti->offset;
period = wd33c93_stp2div(dev, ti->period);
} else {
offset = 0;
period = 0;
}
if (ti->flags & T_SYNCMODE)
syncreg = wd33c93_stp2syn(dev, ti);
else
syncreg = SBIC_SYN(0, 0, 0);
SBIC_DEBUG(SYNC, ("wd33c93_setsync: sync reg = 0x%02x\n",
SBIC_SYN(offset, period)));
SET_SBIC_syn(dev, SBIC_SYN(offset, period));
SBIC_DEBUG(SYNC, ("wd33c93_setsync: sync reg = 0x%02x\n", syncreg));
SET_SBIC_syn(dev, syncreg);
}
/*
@ -1039,7 +1083,7 @@ wd33c93_selectbus(struct wd33c93_softc *dev, struct wd33c93_acb *acb)
/* Inititae a SDTR message */
SBIC_DEBUG(SYNC, ("Sending SDTR to target %d\n", id));
if (ti->flags & T_WANTSYNC) {
ti->period = dev->sc_syncperiods[0];
ti->period = dev->sc_minsyncperiod;
ti->offset = dev->sc_maxoffset;
} else {
ti->period = 0;
@ -1051,7 +1095,7 @@ wd33c93_selectbus(struct wd33c93_softc *dev, struct wd33c93_acb *acb)
dev->sc_omsg[2] = MSG_EXT_SDTR_LEN;
dev->sc_omsg[3] = MSG_EXT_SDTR;
if (ti->flags & T_WANTSYNC) {
dev->sc_omsg[4] = dev->sc_syncperiods[0];
dev->sc_omsg[4] = dev->sc_minsyncperiod;
dev->sc_omsg[5] = dev->sc_maxoffset;
} else {
dev->sc_omsg[4] = 0;
@ -1611,7 +1655,7 @@ void wd33c93_msgin(struct wd33c93_softc *dev, u_char *msgaddr, int msglen)
goto reject;
ti->period =
MAX(msgaddr[3], dev->sc_syncperiods[0]);
MAX(msgaddr[3], dev->sc_minsyncperiod);
ti->offset = MIN(msgaddr[4], dev->sc_maxoffset);
if (!(ti->flags & T_WANTSYNC))
ti->period = ti->offset = 0;
@ -2208,39 +2252,6 @@ wd33c93_update_xfer_mode(struct wd33c93_softc *sc, int target)
scsipi_async_event(&sc->sc_channel, ASYNC_EVENT_XFER_MODE, &xm);
}
/*
* Calculate SCSI Tranfser Period Factor (4ns units each) from the
* WD33c93 divisor value
*
* cycle = DIV / (2 * CLK)
* DIV = FS + 2
* best we can do is 200ns at 20 MHz, 2 cycles
*/
int
wd33c93_div2stp(struct wd33c93_softc *dev, int div)
{
if (div < 2)
div = 8; /* map to Cycles */
return dev->sc_syncperiods[div - 2];
}
/*
* Convert SCSI Transfer Period Factor (in 4ns units) to the divisor
* value used by the WD33c93 controller.
*/
int
wd33c93_stp2div(struct wd33c93_softc *dev, int stp)
{
unsigned i;
for (i = 0; i < 7; i++)
if (dev->sc_syncperiods[i] >= stp)
return (i == 6 ? 0 : i + 2);
return 0; /* XXX we can't slow down far enough */
}
void
wd33c93_timeout(void *arg)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: wd33c93reg.h,v 1.1 2006/08/26 22:06:37 bjh21 Exp $ */
/* $NetBSD: wd33c93reg.h,v 1.2 2007/05/08 00:20:15 rumble Exp $ */
/*
* Copyright (c) 1990 The Regents of the University of California.
@ -145,7 +145,7 @@
/* 11 MHz is invalid */
#define SBIC_ID_FS_12_15 0x40 /* Input clock is 12-15 MHz */
#define SBIC_ID_FS_16_20 0x80 /* Input clock is 16-20 MHz */
#define SBIC_ID_RAF 0x20 /* */
#define SBIC_ID_RAF 0x20 /* Enable Really Advanced Features */
#define SBIC_ID_EHP 0x10 /* Enable host parity */
#define SBIC_ID_EAF 0x08 /* Enable Advanced Features */
#define SBIC_ID_MASK 0x07
@ -208,15 +208,19 @@
/*
* Synchronous xfer register
*
* NB: SBIC_SYN_FSS only valid on WD33C93B with 16-20MHz clock.
*/
#define SBIC_SYN_OFF_MASK 0x0f
#define SBIC_SYN_MAX_OFFSET SBIC_FIFO_DEEP
#define SBIC_SYN_PER_MASK 0x70
#define SBIC_SYN_MIN_PERIOD 2 /* upto 8, encoded as 0 */
#define SBIC_SYN_FSS 0x80 /* Enable Fast SCSI Transfers (10MB/s)*/
#define SBIC_SYN(o,p) \
(((o) & SBIC_SYN_OFF_MASK) | (((p) << 4) & SBIC_SYN_PER_MASK))
#define SBIC_SYN(o,p,f) \
(((o) & SBIC_SYN_OFF_MASK) | (((p) << 4) & SBIC_SYN_PER_MASK) | \
((f) ? SBIC_SYN_FSS : 0))
/*
* Transfer count register

View File

@ -1,4 +1,4 @@
/* $NetBSD: wd33c93var.h,v 1.5 2007/03/04 06:02:03 christos Exp $ */
/* $NetBSD: wd33c93var.h,v 1.6 2007/05/08 00:20:15 rumble Exp $ */
/*
* Copyright (c) 1990 The Regents of the University of California.
@ -171,8 +171,10 @@ struct wd33c93_softc {
int sc_rev; /* Chip revision */
int sc_cfflags; /* Copy of config flags */
int sc_maxxfer; /* Maximum transfer size */
uint8_t sc_maxoffset; /* Maximum sync ofset (bytes) */
uint8_t sc_maxoffset; /* Maximum sync offset (bytes) */
uint8_t sc_minsyncperiod; /* Minimum supported sync xfer period */
uint8_t sc_syncperiods[7]; /* Sync transfer periods (4ns units) */
uint8_t sc_fsyncperiods[3]; /* Sync transfer periods for Fast SCSI*/
int (*sc_dmasetup) (struct wd33c93_softc *, void **,
size_t *, int, size_t *);