summaryrefslogtreecommitdiffstats
path: root/sys/pci/ide_pci.c
diff options
context:
space:
mode:
authorsos <sos@FreeBSD.org>1997-09-04 18:49:53 +0000
committersos <sos@FreeBSD.org>1997-09-04 18:49:53 +0000
commit4d6dd4fc2d4b7a4112c261c7c34037dbe9de3e56 (patch)
treed9d1bc4feef0cf975d010ef759400307cbbd7302 /sys/pci/ide_pci.c
parentfb633824a1760e5b18d4ec464cabc4e5e9c28cbf (diff)
downloadFreeBSD-src-4d6dd4fc2d4b7a4112c261c7c34037dbe9de3e56.zip
FreeBSD-src-4d6dd4fc2d4b7a4112c261c7c34037dbe9de3e56.tar.gz
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>
Diffstat (limited to 'sys/pci/ide_pci.c')
-rw-r--r--sys/pci/ide_pci.c632
1 files changed, 387 insertions, 245 deletions
diff --git a/sys/pci/ide_pci.c b/sys/pci/ide_pci.c
index 21de978..9f16080 100644
--- a/sys/pci/ide_pci.c
+++ b/sys/pci/ide_pci.c
@@ -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,70 +644,77 @@ 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;
+
+ iobase_wd_2 = (class & 0x400) ?
+ (pci_conf_read(tag, 0x10) & 0xfffc) :
+ 0x170;
+ iobase_bm_2 = iobase_bm_1 + SFF8038_CTLR_1;
+
+ 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;
+ wddma.wdd_dmainit = ide_pci_dmainit;
+ wddma.wdd_dmaverify = ide_pci_dmaverify;
+ wddma.wdd_dmaprep = ide_pci_dmasetup;
+ 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;
}
- bmista = inb(iobase_bm + BMISTA_PORT);
+ 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);
- 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);
+ bmista_1, iobase_bm_1+BMISTA_PORT);
- 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_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 & 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 (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");
+ }
}
- wddma.wdd_candma = ide_pci_candma;
- wddma.wdd_dmainit = ide_pci_dmainit;
- wddma.wdd_dmaverify = ide_pci_dmaverify;
- wddma.wdd_dmaprep = ide_pci_dmasetup;
- wddma.wdd_dmastart = ide_pci_dmastart;
- wddma.wdd_dmadone = ide_pci_dmadone;
- wddma.wdd_dmastatus = ide_pci_status;
}
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;
+
+ /* Step through virtual pages, coalescing as needed. */
+ while (count) {
+
+ nbase = vtophys(vaddr);
+ ncount = MIN(count, PAGE_SIZE);
+ nend = nbase + ncount;
- prd[i - 1].prd_count += n;
+ /* 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;
+
+#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 (i == 0)
- printf("ide_pci: dmasetup 0-length PRD???\n");
+ 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;
}
OpenPOWER on IntegriCloud