patch-2.3.1 linux/drivers/block/ide-dma.c
Next file: linux/drivers/block/ide-floppy.c
Previous file: linux/drivers/block/ide-disk.c
Back to the patch index
Back to the overall index
- Lines: 284
- Date:
Thu May 13 11:04:54 1999
- Orig file:
v2.3.0/linux/drivers/block/ide-dma.c
- Orig date:
Sun Jan 17 18:23:01 1999
diff -u --recursive --new-file v2.3.0/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c
@@ -1,5 +1,12 @@
/*
- * linux/drivers/block/ide-dma.c Version 4.08 December 31, 1997
+ * linux/drivers/block/ide-dma.c Version 4.09 April 23, 1999
+ *
+ * Copyright (c) 1999 Andre Hedrick
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * Special Thanks to Mark for his Six years of work.
*
* Copyright (c) 1995-1998 Mark Lord
* May be copied or modified under the terms of the GNU General Public License
@@ -24,7 +31,7 @@
*
* Use "hdparm -i" to view modes supported by a given drive.
*
- * The hdparm-2.4 (or later) utility can be used for manually enabling/disabling
+ * The hdparm-3.5 (or later) utility can be used for manually enabling/disabling
* DMA support, but must be (re-)compiled against this kernel version or later.
*
* To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
@@ -79,11 +86,51 @@
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/init.h>
+#include <linux/ide.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include "ide.h"
+#define IDE_DMA_NEW_LISTINGS 0
+
+#if IDE_DMA_NEW_LISTINGS
+struct drive_list_entry {
+ char * id_model;
+ char * id_firmware;
+};
+
+struct drive_list_entry drive_whitelist [] = {
+
+ { "Micropolis 2112A" , "ALL" },
+ { "CONNER CTMA 4000" , "ALL" },
+ { "CONNER CTT8000-A" , "ALL" },
+ { "ST34342A" , "ALL" },
+ { 0 , 0 }
+};
+
+struct drive_list_entry drive_blacklist [] = {
+
+ { "WDC AC11000H" , "ALL" },
+ { "WDC AC22100H" , "ALL" },
+ { "WDC AC32500H" , "ALL" },
+ { "WDC AC33100H" , "ALL" },
+ { "WDC AC31600H" , "ALL" },
+ { "WDC AC32100H" , "24.09P07" },
+ { "WDC AC23200L" , "21.10N21" },
+ { 0 , 0 }
+
+};
+
+int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table)
+{
+ for ( ; drive_table->id_model ; drive_table++)
+ if ((!strcmp(drive_table->id_model, id->model)) &&
+ ((!strstr(drive_table->id_firmware, id->fw_rev)) ||
+ (!strcmp(drive_table->id_firmware, "ALL"))))
+ return 1;
+ return 0;
+}
+#else /* !IDE_DMA_NEW_LISTINGS */
/*
* good_dma_drives() lists the model names (from "hdparm -i")
@@ -109,11 +156,14 @@
*/
const char *bad_dma_drives[] = {"WDC AC11000H",
"WDC AC22100H",
+ "WDC AC32100H",
"WDC AC32500H",
"WDC AC33100H",
"WDC AC31600H",
NULL};
+#endif /* IDE_DMA_NEW_LISTINGS */
+
/*
* Our Physical Region Descriptor (PRD) table should be large enough
* to handle the biggest I/O request we are likely to see. Since requests
@@ -164,11 +214,12 @@
* Returns 0 if all went okay, returns 1 otherwise.
* May also be invoked from trm290.c
*/
-int ide_build_dmatable (ide_drive_t *drive)
+int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func)
{
struct request *rq = HWGROUP(drive)->rq;
struct buffer_head *bh = rq->bh;
unsigned int size, addr, *table = (unsigned int *)HWIF(drive)->dmatable;
+ unsigned char *virt_addr;
#ifdef CONFIG_BLK_DEV_TRM290
unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290);
#else
@@ -185,11 +236,13 @@
* than two possibly non-adjacent physical 4kB pages.
*/
if (bh == NULL) { /* paging requests have (rq->bh == NULL) */
- addr = virt_to_bus (rq->buffer);
+ virt_addr = rq->buffer;
+ addr = virt_to_bus (virt_addr);
size = rq->nr_sectors << 9;
} else {
/* group sequential buffers into one large buffer */
- addr = virt_to_bus (bh->b_data);
+ virt_addr = bh->b_data;
+ addr = virt_to_bus (virt_addr);
size = bh->b_size;
while ((bh = bh->b_reqnext) != NULL) {
if ((addr + size) != virt_to_bus (bh->b_data))
@@ -206,6 +259,20 @@
printk("%s: misaligned DMA buffer\n", drive->name);
return 0;
}
+
+ /*
+ * Some CPUs without cache snooping need to invalidate/write
+ * back their caches before DMA transfers to guarantee correct
+ * data. -- rmk
+ */
+ if (size) {
+ if (func == ide_dma_read) {
+ dma_cache_inv((unsigned int)virt_addr, size);
+ } else {
+ dma_cache_wback((unsigned int)virt_addr, size);
+ }
+ }
+
while (size) {
if (++count >= PRD_ENTRIES) {
printk("%s: DMA table too small\n", drive->name);
@@ -224,10 +291,17 @@
}
}
} while (bh != NULL);
- if (!count)
+ if (!count) {
printk("%s: empty DMA table?\n", drive->name);
- else if (!is_trm290_chipset)
- *--table |= cpu_to_le32(0x80000000); /* set End-Of-Table (EOT) bit */
+ } else {
+ if (!is_trm290_chipset)
+ *--table |= cpu_to_le32(0x80000000); /* set End-Of-Table (EOT) bit */
+ /*
+ * Some CPUs need to flush the DMA table to physical RAM
+ * before DMA can start. -- rmk
+ */
+ dma_cache_wback((unsigned long)HWIF(drive)->dmatable, count * sizeof(unsigned int) * 2);
+ }
return count;
}
@@ -238,9 +312,22 @@
*/
int check_drive_lists (ide_drive_t *drive, int good_bad)
{
- const char **list;
struct hd_driveid *id = drive->id;
+#if IDE_DMA_NEW_LISTINGS
+
+ if (good_bad) {
+ return in_drive_list(id, drive_whitelist);
+ } else {
+ int blacklist = in_drive_list(id, drive_blacklist);
+ if (blacklist)
+ printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
+ return(blacklist);
+ }
+#else /* !IDE_DMA_NEW_LISTINGS */
+
+ const char **list;
+
if (good_bad) {
/* Consult the list of known "good" drives */
list = good_dma_drives;
@@ -259,6 +346,7 @@
}
}
}
+#endif /* IDE_DMA_NEW_LISTINGS */
return 0;
}
@@ -269,18 +357,24 @@
if (id && (id->capability & 1) && hwif->autodma) {
/* Consult the list of known "bad" drives */
- if (check_drive_lists(drive, BAD_DMA_DRIVE))
+ if (ide_dmaproc(ide_dma_bad_drive, drive))
return hwif->dmaproc(ide_dma_off, drive);
+#if 0
+ /* Enable DMA on any drive that has UltraDMA (mode 3/4) enabled */
+ if ((id->field_valid & 4) && (id->word93 & 0x2000))
+ if ((id->dma_ultra & (id->dma_ultra >> 11) & 3))
+ return hwif->dmaproc(ide_dma_on, drive);
+#endif
/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
if (id->field_valid & 4) /* UltraDMA */
- if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
+ if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
return hwif->dmaproc(ide_dma_on, drive);
/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
if (id->field_valid & 2) /* regular DMA */
if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
return hwif->dmaproc(ide_dma_on, drive);
/* Consult the list of known "good" drives */
- if (check_drive_lists(drive, GOOD_DMA_DRIVE))
+ if (ide_dmaproc(ide_dma_good_drive, drive))
return hwif->dmaproc(ide_dma_on, drive);
}
return hwif->dmaproc(ide_dma_off_quietly, drive);
@@ -321,7 +415,7 @@
case ide_dma_read:
reading = 1 << 3;
case ide_dma_write:
- if (!(count = ide_build_dmatable(drive)))
+ if (!(count = ide_build_dmatable(drive, func)))
return 1; /* try PIO instead of DMA */
outl(virt_to_bus(hwif->dmatable), dma_base + 4); /* PRD table */
outb(reading, dma_base); /* specify r/w */
@@ -348,6 +442,9 @@
case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
dma_stat = inb(dma_base+2);
return (dma_stat & 4) == 4; /* return 1 if INTR asserted */
+ case ide_dma_bad_drive:
+ case ide_dma_good_drive:
+ return check_drive_lists(drive, (func == ide_dma_good_drive));
default:
printk("ide_dmaproc: unsupported func: %d\n", func);
return 1;
@@ -425,7 +522,7 @@
} else {
dma_base = dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
if (!dma_base || dma_base == PCI_BASE_ADDRESS_IO_MASK) {
- printk("%s: dma_base is invalid (0x%04lx, BIOS problem), please report to <mj@ucw.cz>\n", name, dma_base);
+ printk("%s: dma_base is invalid (0x%04lx)\n", name, dma_base);
dma_base = 0;
}
}
@@ -434,9 +531,29 @@
request_region(dma_base+16, extra, name);
dma_base += hwif->channel ? 8 : 0;
hwif->dma_extra = extra;
- if (inb(dma_base+2) & 0x80) {
- printk("%s: simplex device: DMA disabled\n", name);
- dma_base = 0;
+
+ switch(dev->device) {
+ case PCI_DEVICE_ID_CMD_643:
+ /*
+ * Lets attempt to use the same Ali tricks
+ * to fix CMD643.....
+ */
+ case PCI_DEVICE_ID_AL_M5219:
+ case PCI_DEVICE_ID_AL_M5229:
+ outb(inb(dma_base+2) & 0x60, dma_base+2);
+ /*
+ * Ali 15x3 chipsets know as ALI IV and V report
+ * this as simplex, skip this test for them.
+ */
+ if (inb(dma_base+2) & 0x80) {
+ printk("%s: simplex device: DMA forced\n", name);
+ }
+ break;
+ default:
+ if (inb(dma_base+2) & 0x80) {
+ printk("%s: simplex device: DMA disabled\n", name);
+ dma_base = 0;
+ }
}
}
return dma_base;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)