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

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)