Upgrade of EIDE DMA support, Johns comments:

* lots of fixes to error handling-- mostly works now
* improve DMA timing config for Triton chipsets-- PIIX4 and UDMA drive
  still untested
* generally improve DMA config in many ways-- mostly cleanup
* clean up boot-time messages
* rewrite PRD generation algorithm
* first wd timeout is now longer, to handle drive spinup

Submitted by: John Hood <cgull@smoke.marlboro.vt.us>
This commit is contained in:
sos 1997-09-04 18:49:53 +00:00
parent fb633824a1
commit 4d6dd4fc2d
7 changed files with 479 additions and 298 deletions

View File

@ -24,7 +24,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: wd.4,v 1.5 1997/03/07 02:50:00 jmg Exp $
.\" $Id: wd.4,v 1.6 1997/07/15 09:44:05 charnier Exp $
.\"
.Dd August 31, 1994
.Dt WD 4 i386
@ -61,6 +61,9 @@ Test and use the 32bit transfer capability of the drive.
.It 0x4000
If drive looks like it's comming out of sleep mode, assume it's
confused and reinitialize it.
.It 0x2000
Probe for and use the bus-mastering DMA capabilities of modern
PCI chipsets.
.It 0x0f00
Force number of heads to ((flags & 0xf00)>>8), recalculate number
of cylinders to match.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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,61 +644,20 @@ 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;
if (!ide_pci_softc_cookies_initted) {
LIST_INIT(&softc.cookies);
ide_pci_softc_cookies_initted = 1;
}
iobase_wd_2 = (class & 0x400) ?
(pci_conf_read(tag, 0x10) & 0xfffc) :
0x170;
iobase_bm_2 = iobase_bm_1 + SFF8038_CTLR_1;
bmista = inb(iobase_bm + BMISTA_PORT);
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);
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 & 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 (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;
@ -560,6 +667,54 @@ ide_pci_attach(pcici_t tag, int unit)
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;
}
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);
printf("ide_pci: busmaster 0 status: %02x from port: %08x\n",
bmista_1, iobase_bm_1+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_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");
}
}
}
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;
prd[i - 1].prd_count += n;
/* Step through virtual pages, coalescing as needed. */
while (count) {
nbase = vtophys(vaddr);
ncount = MIN(count, PAGE_SIZE);
nend = nbase + ncount;
/* 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;
if (i == 0)
printf("ide_pci: dmasetup 0-length PRD???\n");
#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 (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;
}