summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>1999-07-20 22:43:53 +0000
committerjulian <julian@FreeBSD.org>1999-07-20 22:43:53 +0000
commit7c71676effa00a366c7a8176221f108e37d74043 (patch)
treed3ebb9a080cbba7782f6ef9ad4e9be0d8b09ec3e /sys
parentf74540c3fbb35fbab137a9eb81ca3d0d0722abb2 (diff)
downloadFreeBSD-src-7c71676effa00a366c7a8176221f108e37d74043.zip
FreeBSD-src-7c71676effa00a366c7a8176221f108e37d74043.tar.gz
Slight cleanups of the Cyrix 5530 UDMA code.
Also includes a workaround fro an apparent chip bug where UDMA mode 2 can overpower the UDMA engine enough that it will hog the PCI bus to the exclusion of the processor.
Diffstat (limited to 'sys')
-rw-r--r--sys/pci/ide_pci.c225
1 files changed, 125 insertions, 100 deletions
diff --git a/sys/pci/ide_pci.c b/sys/pci/ide_pci.c
index 033f470..88432dd 100644
--- a/sys/pci/ide_pci.c
+++ b/sys/pci/ide_pci.c
@@ -26,7 +26,7 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: ide_pci.c,v 1.34 1999/05/09 17:06:47 peter Exp $
+ * $Id: ide_pci.c,v 1.35 1999/07/03 18:34:04 peter Exp $
*/
#include "wd.h"
@@ -189,12 +189,16 @@ extern struct isa_driver wdcdriver;
/*
* PRD_ALLOC_SIZE should be something that will not be allocated across a 64k
* boundary.
+ * DMA_PG_SZ is the size of the chunks that each DMA scatter gather element
+ * represents. In some systems there can be reasons to make this smaller than
+ * a pagesize (usually due to broken hardware).
* PRD_MAX_SEGS is defined to be the maximum number of segments required for
* a transfer on an IDE drive, for an xfer that is linear in virtual memory.
* PRD_BUF_SIZE is the size of the buffer needed for a PRD table.
*/
#define PRD_ALLOC_SIZE PAGE_SIZE
-#define PRD_MAX_SEGS ((256 * 512 / PAGE_SIZE) + 1)
+#define DMA_PG_SZ PAGE_SIZE
+#define PRD_MAX_SEGS ((256 * 512 / DMA_PG_SZ) + 1)
#define PRD_BUF_SIZE PRD_MAX_SEGS * 8
static void *prdbuf = 0;
@@ -391,7 +395,6 @@ via_571_dmainit(struct ide_pci_cookie *cookie,
int(*wdcmd)(int, void *),
void *wdinfo)
{
- int r;
u_long pci_revision;
int unitno;
@@ -426,8 +429,7 @@ via_571_dmainit(struct ide_pci_cookie *cookie,
/* Set UDMA mode 2 on drive */
if (bootverbose)
printf("via_571_dmainit: setting ultra DMA mode 2\n");
- r = wdcmd(WDDMA_UDMA2, wdinfo);
- if (!r) {
+ if (!wdcmd(WDDMA_UDMA2, wdinfo)) {
printf("via_571_dmainit: setting DMA mode failed\n");
return 0;
}
@@ -445,8 +447,7 @@ via_571_dmainit(struct ide_pci_cookie *cookie,
/* Set multiword DMA mode 2 on drive */
if (bootverbose)
printf("via_571_dmainit: setting multiword DMA mode 2\n");
- r = wdcmd(WDDMA_MDMA2, wdinfo);
- if (!r) {
+ if (!wdcmd(WDDMA_MDMA2, wdinfo)) {
printf("via_571_dmainit: setting DMA mode failed\n");
return 0;
}
@@ -558,13 +559,6 @@ cyrix_5530_status(struct ide_pci_cookie *cookie)
unitno = ctlr * 2 + unit;
- /* set some values the BIOS should have set */
- printf("Using 0x%x\n", cookie->iobase_bm);
- outl(iobase_bm + (unit * 0x10) + 0x20, 0x00040010);
- outl(iobase_bm + (unit * 0x10) + 0x24, 0x00911030);
- /* if ((ctlr == 0) && (unit == 0)) */ /* XXX */
- /* outb(iobase_bm + (unit * 0x10) + BMISTA_PORT, 0xe6);*/
-
PIO_config = inl(iobase_bm + (unit * 0x10) + 0x20);
DMA_config = inl(iobase_bm + (unit * 0x10) + 0x24);
@@ -586,11 +580,13 @@ cyrix_5530_dmainit(struct ide_pci_cookie *cookie,
int(*wdcmd)(int, void *),
void *wdinfo)
{
- int r;
u_long pci_revision;
int unitno;
int iobase_bm;
int unit;
+ int drivemode;
+ int mode;
+ int regval;
/*cookie->unit = 0; */ /* XXX */
unit = cookie->unit;
@@ -603,8 +599,6 @@ cyrix_5530_dmainit(struct ide_pci_cookie *cookie,
printf("Setting using 0x%x\n", iobase_bm);
if ((cookie->ctlr == 0) && (unit == 0)) /* XXX */
outb(iobase_bm + (unit * 0x10) + BMISTA_PORT, 0xe6);
- outl(iobase_bm + (unit * 0x10) + 0x20, 0x00040010);
- outl(iobase_bm + (unit * 0x10) + 0x24, 0x00911030);
/* If it's a UDMA drive on a '5530, set it up */
/*
* depending on what the drive can do,
@@ -614,20 +608,47 @@ cyrix_5530_dmainit(struct ide_pci_cookie *cookie,
unitno,
mwdma_mode(wp), pio_mode(wp),
pci_revision, udma_mode(wp));
- if (/* pci_revision >= 1 && */ udma_mode(wp) >= 2) {
- /*outl(iobase_bm + 0x20 + (cookie->unit * 16), 0x00100010);*/
- outl(iobase_bm + 0x24 + (cookie->unit * 16), 0x00911030);
+ if (/* pci_revision >= 1 && */ udma_mode(wp) >= 0) {
+ switch(udma_mode(wp)) {
+ case 0:
+ mode = 0;
+ drivemode = WDDMA_UDMA0;
+ regval = 0x00921250;
+ break;
+ default: /* newer modes not supported */
+ case 2:
+#if 0
+/*
+ * XXX The 5530 can do mode 2 but if you do use it, it will block all
+ * access to the PCI bus (and thus the ISA bus, PICs, PIT, etc. etc.) until the
+ * transfer is complete. Mode 2 swamps the 5530 so much it can't really cope
+ * with any other operations. Therefore, use mode 1 for drives that can
+ * do mode 2 (or more). (FALL THROUGH)
+ */
+
+ mode = 2;
+ drivemode = WDDMA_UDMA2;
+ regval = 0x00911030;
+ break;
+#endif
+ case 1:
+ mode = 1;
+ drivemode = WDDMA_UDMA1;
+ regval = 0x00911140;
+ break;
+ }
/*
- * With the Cx5530, drive configuration should come *after* the
- * controller configuration, to make sure the controller sees
+ * With the Cx5530, drive configuration
+ * should come *after* the controller configuration,
+ * to make sure the controller sees
* the command and does the right thing.
*/
- /* Set UDMA mode 2 on drive */
+ /* Set UDMA mode on drive */
if (bootverbose)
- printf("cyrix_5530_dmainit: setting ultra DMA mode 2\n");
- r = wdcmd(WDDMA_UDMA2, wdinfo);
- if (!r) {
+ printf("cyrix_5530_dmainit: set UDMA mode %d\n", mode);
+ outl(iobase_bm+0x24 + (unit * 16), regval);
+ if (!wdcmd(drivemode, wdinfo)) {
printf("cyrix_5530_dmainit: setting DMA mode failed\n");
return 0;
}
@@ -639,21 +660,39 @@ cyrix_5530_dmainit(struct ide_pci_cookie *cookie,
}
/* otherwise, try and program it for MW DMA mode 2 */
- else if (mwdma_mode(wp) >= 2 && pio_mode(wp) >= 4) {
+ else if (mwdma_mode(wp) >= 0 && pio_mode(wp) >= 4) {
+
+ switch(mwdma_mode(wp)) {
+ case 0:
+ mode = 0;
+ drivemode = WDDMA_MDMA0;
+ regval = 0x00017771;
+ break;
+ case 1:
+ mode = 1;
+ drivemode = WDDMA_MDMA1;
+ regval = 0x00012121;
+ break;
+ default: /* newer modes not supported */
+ case 2:
+ mode = 2;
+ drivemode = WDDMA_MDMA2;
+ regval = 0x00002020;
+ break;
+ }
/* Set multiword DMA mode 2 on drive */
if (bootverbose)
- printf("cyrix_5530_dmainit: setting multiword DMA mode 2\n");
- r = wdcmd(WDDMA_MDMA2, wdinfo);
- if (!r) {
+ printf("cyrix_5530_dmainit: multiword DMA mode %d\n",
+ mode);
+ if (!wdcmd(drivemode, wdinfo)) {
printf("cyrix_5530_dmainit: setting DMA mode failed\n");
return 0;
}
- /* Configure the controller appropriately for MWDMA mode 2 */
+ /* Configure the controller appropriately for MWDMA mode */
- /*outl(iobase_bm + 0x20 + (cookie->unit * 16), 0x00100010);*/
- outl(iobase_bm + 0x24 + (cookie->unit * 16), 0x00002020);
+ outl(iobase_bm + 0x24 + (unit * 16), regval);
if (bootverbose)
cyrix_5530_status(cookie);
@@ -661,6 +700,40 @@ cyrix_5530_dmainit(struct ide_pci_cookie *cookie,
return 1;
}
+ /*
+ * Always set the PIO mode values.
+ */
+ switch(pio_mode(wp)) {
+ case 0:
+ mode = 0;
+ drivemode = WDDMA_MDMA0;
+ regval = 0x0000E132;
+ break;
+ case 1:
+ mode = 1;
+ drivemode = WDDMA_MDMA1;
+ regval = 0x00001812;
+ break;
+ case 2:
+ mode = 2;
+ drivemode = WDDMA_MDMA1;
+ regval = 0x00024020;
+ break;
+ case 3:
+ mode = 3;
+ drivemode = WDDMA_MDMA1;
+ regval = 0x00032010;
+ break;
+ default: /* newer modes not supported */
+ case 4:
+ mode = 4;
+ drivemode = WDDMA_MDMA2;
+ regval = 0x00040010;
+
+ break;
+ }
+ outl(iobase_bm + 0x20 + (unit * 16), regval);
+ printf("cyrix_5530_dmainit: setting PIO mode %d\n", mode);
return 0;
}
@@ -867,7 +940,6 @@ intel_piix_dmainit(struct ide_pci_cookie *cookie,
int(*wdcmd)(int, void *),
void *wdinfo)
{
- int r;
/* If it's a UDMA drive and a PIIX4, set it up */
if (cookie->type == 0x71118086 && udma_mode(wp) >= 2) {
@@ -877,9 +949,7 @@ intel_piix_dmainit(struct ide_pci_cookie *cookie,
if (bootverbose)
printf("intel_piix_dmainit: setting ultra DMA mode 2\n");
- r = wdcmd(WDDMA_UDMA2, wdinfo);
-
- if (!r) {
+ if (!wdcmd(WDDMA_UDMA2, wdinfo)) {
printf("intel_piix_dmainit: setting DMA mode failed\n");
return 0;
}
@@ -924,8 +994,7 @@ intel_piix_dmainit(struct ide_pci_cookie *cookie,
/* Set multiword DMA mode 2 on drive */
if (bootverbose)
printf("intel_piix_dmainit: setting multiword DMA mode 2\n");
- r = wdcmd(WDDMA_MDMA2, wdinfo);
- if (!r) {
+ if (!wdcmd(WDDMA_MDMA2, wdinfo)) {
printf("intel_piix_dmainit: setting DMA mode failed\n");
return 0;
}
@@ -976,9 +1045,7 @@ intel_piix_dmainit(struct ide_pci_cookie *cookie,
if (bootverbose)
printf("intel_piix_dmainit: setting multiword DMA mode 2\n");
- r = wdcmd(WDDMA_MDMA2, wdinfo);
-
- if (!r) {
+ if (!wdcmd(WDDMA_MDMA2, wdinfo)) {
printf("intel_piix_dmainit: setting DMA mode failed\n");
return 0;
}
@@ -1754,9 +1821,6 @@ ide_pci_dmasetup(void *xcp, char *vaddr, u_long vcount, int dir)
u_long nbase, ncount, nend;
int iobase_bm;
u_long count;
-#ifdef DIAGNOSTIC
- u_long checkcount;
-#endif
prd = cp->prd;
@@ -1774,7 +1838,7 @@ ide_pci_dmasetup(void *xcp, char *vaddr, u_long vcount, int dir)
/* Generate first PRD entry, which may be non-aligned. */
- firstpage = PAGE_SIZE - ((uintptr_t)vaddr & PAGE_MASK);
+ firstpage = DMA_PG_SZ - ((uintptr_t)vaddr & DMA_PG_SZ);
prd_base = vtophys(vaddr);
prd_count = MIN(count, firstpage);
@@ -1782,42 +1846,26 @@ ide_pci_dmasetup(void *xcp, char *vaddr, u_long vcount, int dir)
vaddr += prd_count;
count -= prd_count;
- /* Step through virtual pages, coalescing as needed. */
+ /*
+ * Step through virtual pages.
+ * Note that it is not worth trying to coalesce pages that are
+ * next to each other physically, and some DMA engines (e.g.
+ * Cyrix Cx5530) actually blow up if you do.
+ */
while (count) {
nbase = vtophys(vaddr);
- ncount = MIN(count, PAGE_SIZE);
+ ncount = MIN(count, DMA_PG_SZ);
nend = nbase + ncount;
- /*
- * Coalesce if physically contiguous and not crossing
- * 64k boundary.
- */
-#if 0
- /*
- * Aggregation is NOT an optimisation worth doing,
- * and the Cyrix UDMA controller screws itself
- * in some aggregated situations.
- * We might as well just assign each 4K page a DMA entry
- * as this doesn't really gain us anything to aggregate them.
- * This was basically copied from my agregation code in the aha
- * driver, but I doubt it helped much there either. [JRE]
- */
- if ((prd_base + prd_count == nbase) &&
- ((((nend - 1) ^ prd_base) & ~0xffff) == 0)) {
- prd_count += ncount;
- } else
-#endif
- {
- prd[i].prd_base = prd_base;
- prd[i].prd_count = (prd_count & 0xffff);
- i++;
- if (i >= PRD_MAX_SEGS) {
- printf("wd82371: too many segments in PRD table\n");
- return 1;
- }
- prd_base = nbase;
- prd_count = ncount;
+ prd[i].prd_base = prd_base;
+ prd[i].prd_count = (prd_count & 0xffff);
+ i++;
+ if (i >= PRD_MAX_SEGS) {
+ printf("wd82371: too many segments in PRD table\n");
+ return 1;
}
+ prd_base = nbase;
+ prd_count = ncount;
vaddr += ncount;
count -= ncount;
}
@@ -1826,29 +1874,6 @@ ide_pci_dmasetup(void *xcp, char *vaddr, u_long vcount, int dir)
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 (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));
OpenPOWER on IntegriCloud