patch-2.3.28 linux/drivers/block/ide.c

Next file: linux/drivers/block/pdc202xx.c
Previous file: linux/drivers/block/ide-tape.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.27/linux/drivers/block/ide.c linux/drivers/block/ide.c
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c		Version 6.20	July 10, 1999
+ *  linux/drivers/block/ide.c		Version 6.21	November 9, 1999
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -106,15 +106,18 @@
  *			  ATA-66 compliant, but have yet to determine a method
  *			  of verification of the 80c cable presence.
  *			  Specifically Promise's PDC20262 chipset.
+ * Version 6.21		Fixing/Fixed SMP spinlock issue with insight from an old
+ *			  hat that clarified original low level driver design.
  *
  *  Some additional driver compile-time options are in ./include/linux/ide.h
  *
  *  To do, in likely order of completion:
  *	- modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
-*/
+ *
+ */
 
-#define	REVISION	"Revision: 6.20"
-#define	VERSION		"Id: ide.c 6.20 1999/07/10"
+#define	REVISION	"Revision: 6.21"
+#define	VERSION		"Id: ide.c 6.21 1999/11/10"
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
@@ -186,7 +189,7 @@
  * For really screwy hardware (hey, at least it *can* be used with Linux)
  * we can enforce a minimum delay time between successive operations.
  */
-static unsigned long read_timer(void)
+static unsigned long read_timer (void)
 {
 	unsigned long t, flags;
 	int i;
@@ -472,7 +475,7 @@
 #if 0
 	udelay(1);	/* need to guarantee 400ns since last command was issued */
 #endif
-	if (GET_STAT() & BUSY_STAT)
+	if (GET_STAT() & BUSY_STAT)	/* Note: this may clear a pending IRQ!! */
 		return 0;	/* drive busy:  definitely not interrupting */
 	return 1;		/* drive ready: *might* be interrupting */
 }
@@ -480,7 +483,7 @@
 /*
  * This is our end_request replacement function.
  */
-void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup)
+void ide_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
 {
 	struct request *rq;
 	unsigned long flags;
@@ -511,18 +514,16 @@
 	unsigned long flags;
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
-#ifdef DEBUG
+	spin_lock_irqsave(&io_request_lock, flags);
 	if (hwgroup->handler != NULL) {
 		printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
 			drive->name, hwgroup->handler, handler);
 	}
-#endif
 	hwgroup->handler	= handler;
 	hwgroup->expiry		= expiry;
 	hwgroup->timer.expires	= jiffies + timeout;
-	add_timer(&(hwgroup->timer));
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+	add_timer(&hwgroup->timer);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 /*
@@ -557,7 +558,7 @@
 	}
 }
 
-static void do_reset1 (ide_drive_t *, int);		/* needed below */
+static ide_startstop_t do_reset1 (ide_drive_t *, int);		/* needed below */
 
 /*
  * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
@@ -565,7 +566,7 @@
  * and we have not yet hit our maximum waiting time, then the timer is restarted
  * for another 50ms.
  */
-static void atapi_reset_pollfunc (ide_drive_t *drive)
+static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive)
 {
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	byte stat;
@@ -578,14 +579,14 @@
 	} else {
 		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
 			ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL);
-			return;	/* continue polling */
+			return ide_started;	/* continue polling */
 		}
 		hwgroup->poll_timeout = 0;	/* end of polling */
 		printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
-		do_reset1 (drive, 1);	/* do it the old fashioned way */
-		return;
+		return do_reset1 (drive, 1);	/* do it the old fashioned way */
 	}
 	hwgroup->poll_timeout = 0;	/* done polling */
+	return ide_stopped;
 }
 
 /*
@@ -594,7 +595,7 @@
  * and we have not yet hit our maximum waiting time, then the timer is restarted
  * for another 50ms.
  */
-static void reset_pollfunc (ide_drive_t *drive)
+static ide_startstop_t reset_pollfunc (ide_drive_t *drive)
 {
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	ide_hwif_t *hwif = HWIF(drive);
@@ -603,7 +604,7 @@
 	if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
 		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
 			ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL);
-			return;	/* continue polling */
+			return ide_started;	/* continue polling */
 		}
 		printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
 	} else  {
@@ -635,6 +636,7 @@
 		}
 	}
 	hwgroup->poll_timeout = 0;	/* done polling */
+	return ide_stopped;
 }
 
 static void pre_reset (ide_drive_t *drive)
@@ -664,7 +666,7 @@
  * (up to 30 seconds worstcase).  So, instead of busy-waiting here for it,
  * we set a timer to poll at 50ms intervals.
  */
-static void do_reset1 (ide_drive_t *drive, int  do_not_try_atapi)
+static ide_startstop_t do_reset1 (ide_drive_t *drive, int  do_not_try_atapi)
 {
 	unsigned int unit;
 	unsigned long flags;
@@ -683,7 +685,7 @@
 		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
 		ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL);
 		__restore_flags (flags);	/* local CPU only */
-		return;
+		return ide_started;
 	}
 
 	/*
@@ -696,7 +698,7 @@
 #if OK_TO_RESET_CONTROLLER
 	if (!IDE_CONTROL_REG) {
 		__restore_flags(flags);
-		return;
+		return ide_stopped;
 	}
 	/*
 	 * Note that we also set nIEN while resetting the device,
@@ -724,14 +726,15 @@
 #endif	/* OK_TO_RESET_CONTROLLER */
 
 	__restore_flags (flags);	/* local CPU only */
+	return ide_started;
 }
 
 /*
  * ide_do_reset() is the entry point to the drive/interface reset code.
  */
-void ide_do_reset (ide_drive_t *drive)
+ide_startstop_t ide_do_reset (ide_drive_t *drive)
 {
-	do_reset1 (drive, 0);
+	return do_reset1 (drive, 0);
 }
 
 /*
@@ -757,11 +760,8 @@
 	HWGROUP(drive)->rq = NULL;
 	rq->rq_status = RQ_INACTIVE;
 	spin_unlock_irqrestore(&io_request_lock, flags);
-	save_flags(flags);	/* all CPUs; overkill? */
-	cli();			/* all CPUs; overkill? */
 	if (rq->sem != NULL)
 		up(rq->sem);	/* inform originator that rq has been serviced */
-	restore_flags(flags);	/* all CPUs; overkill? */
 }
 
 /*
@@ -854,19 +854,19 @@
 /*
  * ide_error() takes action based on the error returned by the drive.
  */
-void ide_error (ide_drive_t *drive, const char *msg, byte stat)
+ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat)
 {
 	struct request *rq;
 	byte err;
 
 	err = ide_dump_status(drive, msg, stat);
 	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
-		return;
+		return ide_stopped;
 	/* retry only "normal" I/O: */
 	if (rq->cmd == IDE_DRIVE_CMD) {
 		rq->errors = 1;
 		ide_end_drive_cmd(drive, stat, err);
-		return;
+		return ide_stopped;
 	}
 	if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */
 		rq->errors |= ERROR_RESET;
@@ -875,7 +875,7 @@
 			/* err has different meaning on cdrom and tape */
 			if (err == ABRT_ERR) {
 				if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY)
-					return;	/* some newer drives don't support WIN_SPECIFY */
+					return ide_stopped; /* some newer drives don't support WIN_SPECIFY */
 			} else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR))
 				; /* UDMA crc error -- just retry the operation */
 			else if (err & (BBD_ERR | ECC_ERR))	/* retries won't help these */
@@ -897,19 +897,20 @@
 	} else {
 		if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
 			++rq->errors;
-			ide_do_reset(drive);
-			return;
-		} else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+			return ide_do_reset(drive);
+		}
+		if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
 			drive->special.b.recalibrate = 1;
 		++rq->errors;
 	}
+	return ide_stopped;
 }
 
 /*
  * Issue a simple drive command
  * The drive must be selected beforehand.
  */
-void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
+void ide_cmd (ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
 {
 	ide_set_handler (drive, handler, WAIT_CMD, NULL);
 	if (IDE_CONTROL_REG)
@@ -921,7 +922,7 @@
 /*
  * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
  */
-static void drive_cmd_intr (ide_drive_t *drive)
+static ide_startstop_t drive_cmd_intr (ide_drive_t *drive)
 {
 	struct request *rq = HWGROUP(drive)->rq;
 	byte *args = (byte *) rq->buffer;
@@ -938,17 +939,17 @@
 			udelay(100);
 	}
 
-	if (OK_STAT(stat, READY_STAT, BAD_STAT))
-		ide_end_drive_cmd (drive, stat, GET_ERR());
-	else
-		ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
+	if (!OK_STAT(stat, READY_STAT, BAD_STAT))
+		return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
+	ide_end_drive_cmd (drive, stat, GET_ERR());
+	return ide_stopped;
 }
 
 /*
  * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
  * commands to a drive.  It used to do much more, but has been scaled back.
  */
-static inline void do_special (ide_drive_t *drive)
+static ide_startstop_t do_special (ide_drive_t *drive)
 {
 	special_t *s = &drive->special;
 
@@ -961,11 +962,12 @@
 		if (tuneproc != NULL)
 			tuneproc(drive, drive->tune_req);
 	} else if (drive->driver != NULL) {
-		DRIVER(drive)->special(drive);
+		return DRIVER(drive)->special(drive);
 	} else if (s->all) {
 		printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
 		s->all = 0;
 	}
+	return ide_stopped;
 }
 
 /*
@@ -979,12 +981,11 @@
  * setting a timer to wake up at half second intervals thereafter,
  * until timeout is achieved, before timing out.
  */
-int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
-{
+int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) {
 	byte stat;
 	int i;
 	unsigned long flags;
-
+ 
 	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
 	if ((stat = GET_STAT()) & BUSY_STAT) {
 		__save_flags(flags);	/* local CPU only */
@@ -993,7 +994,7 @@
 		while ((stat = GET_STAT()) & BUSY_STAT) {
 			if (0 < (signed long)(jiffies - timeout)) {
 				__restore_flags(flags);	/* local CPU only */
-				ide_error(drive, "status timeout", stat);
+				*startstop = ide_error(drive, "status timeout", stat);
 				return 1;
 			}
 		}
@@ -1011,7 +1012,7 @@
 		if (OK_STAT((stat = GET_STAT()), good, bad))
 			return 0;
 	}
-	ide_error(drive, "status error", stat);
+	*startstop = ide_error(drive, "status error", stat);
 	return 1;
 }
 
@@ -1019,7 +1020,7 @@
  * execute_drive_cmd() issues a special drive command,
  * usually initiated by ioctl() from the external hdparm program.
  */
-static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
+static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq)
 {
 	byte *args = rq->buffer;
 	if (args) {
@@ -1033,11 +1034,11 @@
 			OUT_BYTE(args[2],IDE_FEATURE_REG);
 			OUT_BYTE(args[1],IDE_SECTOR_REG);
 			ide_cmd(drive, args[0], args[3], &drive_cmd_intr);
-			return;
+			return ide_started;
 		}
 		OUT_BYTE(args[2],IDE_FEATURE_REG);
 		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
-		return;
+		return ide_started;
 	} else {
 		/*
 		 * NULL is actually a valid way of waiting for
@@ -1047,21 +1048,21 @@
 		printk("%s: DRIVE_CMD (null)\n", drive->name);
 #endif
 		ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
-		return;
+		return ide_stopped;
 	}
 }
 
 /*
  * start_request() initiates handling of a new I/O request
  */
-static inline void start_request (ide_drive_t *drive)
+static ide_startstop_t start_request (ide_drive_t *drive)
 {
+	ide_startstop_t startstop;
 	unsigned long block, blockend;
 	struct request *rq = drive->queue;
 	unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
 	ide_hwif_t *hwif = HWIF(drive);
 
-	ide__sti();	/* local CPU only */
 #ifdef DEBUG
 	printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
 #endif
@@ -1094,29 +1095,27 @@
 #endif
 
 	SELECT_DRIVE(hwif, drive);
-	if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+	if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
 		printk("%s: drive not ready for command\n", drive->name);
-		return;
+		return startstop;
 	}
 	if (!drive->special.all) {
 		if (rq->cmd == IDE_DRIVE_CMD) {
-			execute_drive_cmd(drive, rq);
-			return;
+			return execute_drive_cmd(drive, rq);
 		}
 		if (drive->driver != NULL) {
-			DRIVER(drive)->do_request(drive, rq, block);
-			return;
+			return (DRIVER(drive)->do_request(drive, rq, block));
 		}
 		printk("%s: media type %d not supported\n", drive->name, drive->media);
 		goto kill_rq;
 	}
-	do_special(drive);
-	return;
+	return do_special(drive);
 kill_rq:
 	if (drive->driver != NULL)
 		DRIVER(drive)->end_request(0, HWGROUP(drive));
 	else
 		ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
 }
 
 /*
@@ -1177,22 +1176,54 @@
 }
 
 /*
- * Caller must have already acquired spinlock using *spinflags 
+ * Issue a new request to a drive from hwgroup
+ * Caller must have already done spin_lock_irqsave(&io_request_lock, ..);
+ *
+ * A hwgroup is a serialized group of IDE interfaces.  Usually there is
+ * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640)
+ * may have both interfaces in a single hwgroup to "serialize" access.
+ * Or possibly multiple ISA interfaces can share a common IRQ by being grouped
+ * together into one hwgroup for serialized access.
+ *
+ * Note also that several hwgroups can end up sharing a single IRQ,
+ * possibly along with many other devices.  This is especially common in
+ * PCI-based systems with off-board IDE controller cards.
+ *
+ * The IDE driver uses the single global io_request_lock spinlock to protect
+ * access to the request queues, and to protect the hwgroup->busy flag.
+ *
+ * The first thread into the driver for a particular hwgroup sets the
+ * hwgroup->busy flag to indicate that this hwgroup is now active,
+ * and then initiates processing of the top request from the request queue.
+ *
+ * Other threads attempting entry notice the busy setting, and will simply
+ * queue their new requests and exit immediately.  Note that hwgroup->busy
+ * remains set even when the driver is merely awaiting the next interrupt.
+ * Thus, the meaning is "this hwgroup is busy processing a request".
+ *
+ * When processing of a request completes, the completing thread or IRQ-handler
+ * will start the next request from the queue.  If no more work remains,
+ * the driver will clear the hwgroup->busy flag and exit.
+ *
+ * The io_request_lock (spinlock) is used to protect all access to the
+ * hwgroup->busy flag, but is otherwise not needed for most processing in
+ * the driver.  This makes the driver much more friendlier to shared IRQs
+ * than previous designs, while remaining 100% (?) SMP safe and capable.
  */
-static void ide_do_request (ide_hwgroup_t *hwgroup, unsigned long *hwgroup_flags, int masked_irq)
+static void ide_do_request (ide_hwgroup_t *hwgroup)
 {
 	struct blk_dev_struct *bdev;
 	ide_drive_t	*drive;
 	ide_hwif_t	*hwif;
-	unsigned long	io_flags;
+	ide_startstop_t	startstop;
 
-	hwgroup->busy = 1;
-	while (hwgroup->handler == NULL) {
-		spin_lock_irqsave(&io_request_lock, io_flags);
+	ide_get_lock(&ide_lock, ide_intr, hwgroup);	/* for atari only: POSSIBLY BROKEN HERE(?) */
+
+	while (!hwgroup->busy) {
+		hwgroup->busy = 1;
 		drive = choose_drive(hwgroup);
 		if (drive == NULL) {
 			unsigned long sleep = 0;
-
 			hwgroup->rq = NULL;
 			drive = hwgroup->drive;
 			do {
@@ -1202,27 +1233,34 @@
 				if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep)))
 					sleep = drive->sleep;
 			} while ((drive = drive->next) != hwgroup->drive);
-			spin_unlock_irqrestore(&io_request_lock, io_flags);
 			if (sleep) {
+				/*
+				 * Take a short snooze, and then wake up this hwgroup again.
+				 * This gives other hwgroups on the same a chance to
+				 * play fairly with us, just in case there are big differences
+				 * in relative throughputs.. don't want to hog the cpu too much.
+				 */
 				if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) 
 					sleep = jiffies + WAIT_MIN_SLEEP;
 #if 1
 				if (hwgroup->timer.next || hwgroup->timer.prev)
 					printk("ide_set_handler: timer already active\n");
 #endif
+				hwgroup->sleeping = 1;	/* so that ide_timer_expiry knows what to do */
 				mod_timer(&hwgroup->timer, sleep);
+				/* we purposely leave hwgroup->busy==1 while sleeping */
 			} else {
 				/* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
 				ide_release_lock(&ide_lock);	/* for atari only */
+				hwgroup->busy = 0;
 			}
-			hwgroup->busy = 0;
-			return;
+			return;		/* no more work for this hwgroup (for now) */
 		}
 		hwif = HWIF(drive);
-		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif &&
-		    hwif->io_ports[IDE_CONTROL_OFFSET])
+		if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && hwif->io_ports[IDE_CONTROL_OFFSET]) {
 			/* set nIEN for previous hwif */
 			OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
+		}
 		hwgroup->hwif = hwif;
 		hwgroup->drive = drive;
 		drive->sleep = 0;
@@ -1232,39 +1270,13 @@
 		if (bdev->current_request == &bdev->plug)	/* FIXME: paranoia */
 			printk("%s: Huh? nuking plugged queue\n", drive->name);
 		bdev->current_request = hwgroup->rq = drive->queue;
-		spin_unlock_irqrestore(&io_request_lock, io_flags);
-
-#if 0
-		if (hwif->irq != masked_irq)
-			disable_irq_nosync(hwif->irq);
-		spin_unlock_irqrestore(&hwgroup->spinlock, *hwgroup_flags);
-		start_request(drive);
-		spin_lock_irqsave(&hwgroup->spinlock, *hwgroup_flags);
-		if (hwif->irq != masked_irq)
-			enable_irq(hwif->irq);
-#else
-
-		if (masked_irq && hwif->irq != masked_irq) {
-			printk("%s: (disable_irq) %smasked_irq %d\n",
-				drive->name,
-				masked_irq  ? "" : "un_", hwif->irq);
-
-#if 0
-			disable_irq(hwif->irq);
-#else
-			disable_irq_nosync(hwif->irq);
-#endif
-		}
-		spin_unlock_irqrestore(&hwgroup->spinlock, *hwgroup_flags);
-		start_request(drive);
-		spin_lock_irqsave(&hwgroup->spinlock, *hwgroup_flags);
-		if (masked_irq && hwif->irq != masked_irq) {
-			printk("%s: (enable_irq) %smasked_irq %d\n",
-				drive->name,
-				masked_irq ? "" : "un_", hwif->irq);
-			enable_irq(hwif->irq);
-		}
-#endif
+		spin_unlock(&io_request_lock);
+		if (!hwif->serialized)	/* play it safe with buggy hardware */
+			ide__sti();
+		startstop = start_request(drive);
+		spin_lock_irq(&io_request_lock);
+		if (startstop == ide_stopped)
+			hwgroup->busy = 0;
 	}
 }
 
@@ -1278,179 +1290,158 @@
 	return &hwif->drives[DEVICE_NR(dev) & 1].queue;
 }
 
-/*
- * do_hwgroup_request() invokes ide_do_request() after claiming hwgroup->busy.
- */
-static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
-	if (hwgroup->busy) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		return;
-	}
-	del_timer(&hwgroup->timer);
-	ide_get_lock(&ide_lock, ide_intr, hwgroup);	/* for atari only */
-	ide_do_request(hwgroup, &flags, 0);
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-}
-
-/*
- * ll_rw_blk.c invokes our do_idex_request() function
- * with the io_request_spinlock already grabbed.
- * Since we need to do our own spinlock's internally,
- * on paths that don't necessarily originate through the
- * do_idex_request() path, we have to undo the spinlock on entry,
- * and restore it again on exit.
- * Fortunately, this is mostly a nop for non-SMP kernels.
- */
-static inline void unlock_do_hwgroup_request (ide_hwgroup_t *hwgroup)
-{
-	spin_unlock(&io_request_lock);
-	do_hwgroup_request (hwgroup);
-	spin_lock_irq(&io_request_lock);
-}
-
 void do_ide0_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[0].hwgroup);
+	ide_do_request (ide_hwifs[0].hwgroup);
 }
 
 #if MAX_HWIFS > 1
 void do_ide1_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[1].hwgroup);
+	ide_do_request (ide_hwifs[1].hwgroup);
 }
 #endif /* MAX_HWIFS > 1 */
 
 #if MAX_HWIFS > 2
 void do_ide2_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[2].hwgroup);
+	ide_do_request (ide_hwifs[2].hwgroup);
 }
 #endif /* MAX_HWIFS > 2 */
 
 #if MAX_HWIFS > 3
 void do_ide3_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[3].hwgroup);
+	ide_do_request (ide_hwifs[3].hwgroup);
 }
 #endif /* MAX_HWIFS > 3 */
 
 #if MAX_HWIFS > 4
 void do_ide4_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[4].hwgroup);
+	ide_do_request (ide_hwifs[4].hwgroup);
 }
 #endif /* MAX_HWIFS > 4 */
 
 #if MAX_HWIFS > 5
 void do_ide5_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[5].hwgroup);
+	ide_do_request (ide_hwifs[5].hwgroup);
 }
 #endif /* MAX_HWIFS > 5 */
 
 #if MAX_HWIFS > 6
 void do_ide6_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[6].hwgroup);
+	ide_do_request (ide_hwifs[6].hwgroup);
 }
 #endif /* MAX_HWIFS > 6 */
 
 #if MAX_HWIFS > 7
 void do_ide7_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[7].hwgroup);
+	ide_do_request (ide_hwifs[7].hwgroup);
 }
 #endif /* MAX_HWIFS > 7 */
 
 #if MAX_HWIFS > 8
 void do_ide8_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[8].hwgroup);
+	ide_do_request (ide_hwifs[8].hwgroup);
 }
 #endif /* MAX_HWIFS > 8 */
 
 #if MAX_HWIFS > 9
 void do_ide9_request (void)
 {
-	unlock_do_hwgroup_request (ide_hwifs[9].hwgroup);
+	ide_do_request (ide_hwifs[9].hwgroup);
 }
 #endif /* MAX_HWIFS > 9 */
 
-static void start_next_request (ide_hwgroup_t *hwgroup, int masked_irq)
-{
-	unsigned long	flags;
-	ide_drive_t	*drive;
-
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
-	if (hwgroup->handler != NULL) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		return;
-	}
-	drive = hwgroup->drive;
-	set_recovery_timer(HWIF(drive));
-	drive->service_time = jiffies - drive->service_start;
-	ide_do_request(hwgroup, &flags, masked_irq);
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-}
-
+/*
+ * ide_timer_expiry() is our timeout function for all drive operations.
+ * But note that it can also be invoked as a result of a "sleep" operation
+ * triggered by the mod_timer() call in ide_do_request.
+ */
 void ide_timer_expiry (unsigned long data)
 {
-	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
-	ide_drive_t   *drive;
-	ide_handler_t *handler;
-	ide_expiry_t  *expiry;
-	unsigned long flags;
-	unsigned long wait;
+	ide_hwgroup_t	*hwgroup = (ide_hwgroup_t *) data;
+	ide_handler_t	*handler;
+	ide_expiry_t	*expiry;
+ 	unsigned long	flags;
+	unsigned long	wait;
 
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
-	drive = hwgroup->drive;
-	if ((handler = hwgroup->handler) == NULL) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		do_hwgroup_request(hwgroup);
-		return;
-	}
-
-	hwgroup->busy = 1;	/* should already be "1" */
+	spin_lock_irqsave(&io_request_lock, flags);
+	del_timer(&hwgroup->timer);
 
-	if ((expiry = hwgroup->expiry) != NULL) {
-		/* continue */
-		if ((wait = expiry(drive)) != 0) {
-			/* reset timer */
-			hwgroup->timer.expires	= jiffies + wait;
-			add_timer(&(hwgroup->timer));
-			spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-			return;
+	if ((handler = hwgroup->handler) == NULL) {
+		/*
+		 * Either a marginal timeout occured
+		 * (got the interrupt just as timer expired),
+		 * or we were "sleeping" to give other devices a chance.
+		 * Either way, we don't really want to complain about anything.
+		 */
+		if (hwgroup->sleeping) {
+			hwgroup->sleeping = 0;
+			hwgroup->busy = 0;
 		}
-	}
-
-	hwgroup->handler = NULL;
-
-	if (hwgroup->poll_timeout != 0) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		handler(drive);
-	} else if (drive_is_ready(drive)) {
-		printk("%s: lost interrupt\n", drive->name);
-		(void) hwgroup->hwif->dmaproc(ide_dma_lostirq, drive);
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		handler(drive);
 	} else {
-		if (drive->waiting_for_dma) {
-			(void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
-			printk("%s: timeout waiting for DMA\n", drive->name);
-	/*
-	 *  need something here for HPT34X.......AMH
-	 *  irq timeout: status=0x58 { DriveReady SeekComplete DataRequest }
-	 */
-			(void) hwgroup->hwif->dmaproc(ide_dma_timeout, drive);
+		ide_drive_t *drive = hwgroup->drive;
+		hwgroup->handler = NULL;
+		if (!drive) {
+			printk("ide_timer_expiry: hwgroup->drive was NULL\n");
+		} else {
+			ide_hwif_t *hwif;
+			ide_startstop_t startstop;
+			if (!hwgroup->busy) {
+				hwgroup->busy = 1;	/* paranoia */
+				printk("%s: ide_timer_expiry: hwgroup->busy was 0 ??\n", drive->name);
+			}
+			if ((expiry = hwgroup->expiry) != NULL) {
+				/* continue */
+				if ((wait = expiry(drive)) != 0) {
+					/* reset timer */
+					hwgroup->timer.expires  = jiffies + wait;
+					add_timer(&hwgroup->timer);
+					spin_unlock_irqrestore(&io_request_lock, flags);
+					return;
+				}
+			}
+			/*
+			 * We need to simulate a real interrupt when invoking
+			 * the handler() function, which means we need to globally
+			 * mask the specific IRQ:
+			 */
+			spin_unlock(&io_request_lock);
+			hwif  = HWIF(drive);
+			disable_irq(hwif->irq);
+			__cli();	/* local CPU only, as if we were handling an interrupt */
+			if (hwgroup->poll_timeout != 0) {
+				startstop = handler(drive);
+			} else if (drive_is_ready(drive)) {
+				if (drive->waiting_for_dma)
+					(void) hwgroup->hwif->dmaproc(ide_dma_lostirq, drive);
+				(void)ide_ack_intr(hwif);
+				printk("%s: lost interrupt\n", drive->name);
+				startstop = handler(drive);
+			} else {
+				if (drive->waiting_for_dma) {
+					(void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
+					printk("%s: timeout waiting for DMA\n", drive->name);
+					(void) hwgroup->hwif->dmaproc(ide_dma_timeout, drive);
+				}
+				startstop = ide_error(drive, "irq timeout", GET_STAT());
+			}
+			set_recovery_timer(hwif);
+			drive->service_time = jiffies - drive->service_start;
+			enable_irq(hwif->irq);
+			spin_lock_irq(&io_request_lock);
+			if (startstop == ide_stopped)
+				hwgroup->busy = 0;
 		}
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
-		ide_error(drive, "irq timeout", GET_STAT());
 	}
-	start_next_request(hwgroup, 0);
+	ide_do_request(hwgroup);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 /*
@@ -1500,6 +1491,7 @@
 		}
 	} while ((hwif = hwif->next) != hwgroup->hwif);
 }
+
 /*
  * entry point for all interrupts, caller does __cli() for us
  */
@@ -1510,13 +1502,13 @@
 	ide_hwif_t *hwif;
 	ide_drive_t *drive;
 	ide_handler_t *handler;
+	ide_startstop_t startstop;
 
-	__cli();	/* local CPU only */
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
+	spin_lock_irqsave(&io_request_lock, flags);
 	hwif = hwgroup->hwif;
 
 	if (!ide_ack_intr(hwif)) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		return;
 	}
 
@@ -1542,29 +1534,70 @@
 			 * so we can safely try to do something about it:
 			 */
 			unexpected_intr(irq, hwgroup);
+#ifdef CONFIG_BLK_DEV_IDEPCI
+		} else {
+			/*
+			 * Whack the status register, just in case we have a leftover pending IRQ.
+			 *
+			 * Unless we are some version of a Promise Ultra66 :: PDC20262.
+			 * We will hang like a rock....
+			 */
+			byte skip_status = ((hwif->pci_dev->device == PCI_DEVICE_ID_PROMISE_20262) ||
+					    (hwif->pci_dev->device == PCI_DEVICE_ID_TTI_HPT366)) ? 1 : 0;
+			if (!skip_status)
+				(void) IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+#endif /* CONFIG_BLK_DEV_IDEPCI */
 		}
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		return;
 	}
 	drive = hwgroup->drive;
-	if (!drive || !drive_is_ready(drive)) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+	if (!drive) {
+		/*
+		 * This should NEVER happen, and there isn't much we could do about it here.
+		 */
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		return;
 	}
+	if (!drive_is_ready(drive)) {
+		/*
+		 * This happens regularly when we share a PCI IRQ with another device.
+		 * Unfortunately, it can also happen with some buggy drives that trigger
+		 * the IRQ before their status register is up to date.  Hopefully we have
+		 * enough advance overhead that the latter isn't a problem.
+		 */
+		spin_unlock_irqrestore(&io_request_lock, flags);
+		return;
+	}
+	if (!hwgroup->busy) {
+		hwgroup->busy = 1;	/* paranoia */
+		printk("%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
+	}
 	hwgroup->handler = NULL;
-	del_timer(&(hwgroup->timer));
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+	del_timer(&hwgroup->timer);
+	spin_unlock(&io_request_lock);
 	if (drive->unmask)
 		ide__sti();	/* local CPU only */
-	handler(drive);		/* service this interrupt, may set handler for next interrupt */
+	startstop = handler(drive);		/* service this interrupt, may set handler for next interrupt */
+	spin_lock_irq(&io_request_lock);
 	/*
 	 * Note that handler() may have set things up for another
 	 * interrupt to occur soon, but it cannot happen until
 	 * we exit from this routine, because it will be the
-	 * same irq as is currently being serviced here,
-	 * and Linux won't allow another (on any CPU) until we return.
+	 * same irq as is currently being serviced here, and Linux
+	 * won't allow another of the same (on any CPU) until we return.
 	 */
-	start_next_request(hwgroup, hwif->irq);
+	set_recovery_timer(HWIF(drive));
+	drive->service_time = jiffies - drive->service_start;
+	if (startstop == ide_stopped) {
+		if (hwgroup->handler == NULL) {	/* paranoia */
+			hwgroup->busy = 0;
+			ide_do_request(hwgroup);
+		} else {
+			printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
+		}
+	}
+	spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 /*
@@ -1649,7 +1682,6 @@
 	rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
 	if (action == ide_wait)
 		rq->sem = &sem;
-
 	spin_lock_irqsave(&io_request_lock, flags);
 	cur_rq = drive->queue;
 	if (cur_rq == NULL || action == ide_preempt) {
@@ -1665,13 +1697,14 @@
 		rq->next = cur_rq->next;
 		cur_rq->next = rq;
 	}
+	ide_do_request(hwgroup);
 	spin_unlock_irqrestore(&io_request_lock, flags);
-	do_hwgroup_request(hwgroup);
 	if (action == ide_wait) {
-		down(&sem);	/* wait for it to be serviced */
-		rq->sem = NULL;
+		down(&sem);			/* wait for it to be serviced */
+		return rq->errors ? -EIO : 0;	/* return -EIO if errors */
 	}
-	return rq->errors ? -EIO : 0;	/* return -EIO if errors */
+	return 0;
+
 }
 
 /*
@@ -1682,7 +1715,7 @@
  * usage == 1 (we need an open channel to use an ioctl :-), so this
  * is our limit.
  */
-int ide_revalidate_disk(kdev_t i_rdev)
+int ide_revalidate_disk (kdev_t i_rdev)
 {
 	ide_drive_t *drive;
 	ide_hwgroup_t *hwgroup;
@@ -1694,14 +1727,14 @@
 	major = MAJOR(i_rdev);
 	minor = drive->select.b.unit << PARTN_BITS;
 	hwgroup = HWGROUP(drive);
-	spin_lock_irqsave(&hwgroup->spinlock, flags);
+	spin_lock_irqsave(&io_request_lock, flags);
 	if (drive->busy || (drive->usage > 1)) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		return -EBUSY;
 	};
 	drive->busy = 1;
 	MOD_INC_USE_COUNT;
-	spin_unlock_irqrestore(&hwgroup->spinlock, flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 
 	for (p = 0; p < (1<<PARTN_BITS); ++p) {
 		if (drive->part[p].nr_sects > 0) {
@@ -1767,7 +1800,7 @@
 #endif /* CONFIG_KMOD */
 }
 
-static int ide_open(struct inode * inode, struct file * filp)
+static int ide_open (struct inode * inode, struct file * filp)
 {
 	ide_drive_t *drive;
 	int rc;
@@ -1807,7 +1840,7 @@
  * Releasing a block device means we sync() it, so that it can safely
  * be forgotten about...
  */
-static int ide_release(struct inode * inode, struct file * file)
+static int ide_release (struct inode * inode, struct file * file)
 {
 	ide_drive_t *drive;
 
@@ -1821,7 +1854,7 @@
 	return 0;
 }
 
-int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
+int ide_replace_subdriver (ide_drive_t *drive, const char *driver)
 {
 	if (!drive->present || drive->busy || drive->usage)
 		goto abort;
@@ -1979,6 +2012,7 @@
 	init_hwif_data (index);	/* restore hwif data to pristine status */
 	hwif->hwgroup = old_hwif.hwgroup;
 	hwif->tuneproc = old_hwif.tuneproc;
+	hwif->resetproc = old_hwif.resetproc;
 	hwif->dmaproc = old_hwif.dmaproc;
 	hwif->dma_base = old_hwif.dma_base;
 	hwif->dma_extra = old_hwif.dma_extra;
@@ -2089,7 +2123,7 @@
 	return ide_register_hw(&hw, NULL);
 }
 
-void ide_add_setting(ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
+void ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
 {
 	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
 
@@ -2115,7 +2149,7 @@
 		kfree(setting);
 }
 
-void ide_remove_setting(ide_drive_t *drive, char *name)
+void ide_remove_setting (ide_drive_t *drive, char *name)
 {
 	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting;
 
@@ -2128,7 +2162,7 @@
 	kfree(setting);
 }
 
-static ide_settings_t *ide_find_setting_by_ioctl(ide_drive_t *drive, int cmd)
+static ide_settings_t *ide_find_setting_by_ioctl (ide_drive_t *drive, int cmd)
 {
 	ide_settings_t *setting = drive->settings;
 
@@ -2140,7 +2174,7 @@
 	return setting;
 }
 
-ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name)
+ide_settings_t *ide_find_setting_by_name (ide_drive_t *drive, char *name)
 {
 	ide_settings_t *setting = drive->settings;
 
@@ -2152,7 +2186,7 @@
 	return setting;
 }
 
-static void auto_remove_settings(ide_drive_t *drive)
+static void auto_remove_settings (ide_drive_t *drive)
 {
 	ide_settings_t *setting;
 repeat:
@@ -2166,13 +2200,13 @@
 	}
 }
 
-int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
+int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting)
 {
 	int		val = -EINVAL;
 	unsigned long	flags;
 
 	if ((setting->rw & SETTING_READ)) {
-		spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags);
+		spin_lock_irqsave(&io_request_lock, flags);
 		switch(setting->data_type) {
 			case TYPE_BYTE:
 				val = *((u8 *) setting->data);
@@ -2185,7 +2219,7 @@
 				val = *((u32 *) setting->data);
 				break;
 		}
-		spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 	}
 	return val;
 }
@@ -2195,20 +2229,29 @@
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	unsigned long timeout = jiffies + (3 * HZ);
 
-	spin_lock_irqsave(&hwgroup->spinlock, *flags);
+	spin_lock_irqsave(&io_request_lock, *flags);
 	while (hwgroup->busy) {
-		spin_unlock_irqrestore(&hwgroup->spinlock, *flags);
-		__sti();	/* local CPU only; needed for jiffies */
+		unsigned long lflags;
+		spin_unlock_irqrestore(&io_request_lock, *flags);
+		__save_flags(lflags);	/* local CPU only */
+		__sti();		/* local CPU only; needed for jiffies */
 		if (0 < (signed long)(jiffies - timeout)) {
+			__restore_flags(lflags);	/* local CPU only */
 			printk("%s: channel busy\n", drive->name);
 			return -EBUSY;
 		}
-		spin_lock_irqsave(&hwgroup->spinlock, *flags);
+		__restore_flags(lflags);	/* local CPU only */
+		spin_lock_irqsave(&io_request_lock, *flags);
 	}
 	return 0;
 }
 
-int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
+/*
+ * FIXME:  This should be changed to enqueue a special request
+ * to the driver to change settings, and then wait on a sema for completion.
+ * The current scheme of polling is kludgey, though safe enough.
+ */
+int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val)
 {
 	unsigned long flags;
 	int i;
@@ -2240,7 +2283,7 @@
 				*p = val;
 			break;
 	}
-	spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 	return 0;
 }
 
@@ -2254,7 +2297,7 @@
 	return 0;
 }
 
-static int set_using_dma(ide_drive_t *drive, int arg)
+static int set_using_dma (ide_drive_t *drive, int arg)
 {
 	if (!drive->driver || !DRIVER(drive)->supports_dma)
 		return -EPERM;
@@ -2265,7 +2308,7 @@
 	return 0;
 }
 
-static int set_pio_mode(ide_drive_t *drive, int arg)
+static int set_pio_mode (ide_drive_t *drive, int arg)
 {
 	struct request rq;
 
@@ -2280,7 +2323,7 @@
 	return 0;
 }
 
-void ide_add_generic_settings(ide_drive_t *drive)
+void ide_add_generic_settings (ide_drive_t *drive)
 {
 /*
  *			drive	setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function
@@ -2325,136 +2368,6 @@
 	while (0 < (signed long)(timeout - jiffies));
 }
 
-int ide_config_drive_speed (ide_drive_t *drive, byte speed)
-{
-	struct hd_driveid *id = drive->id;
-	unsigned long flags;
-	int err;
-
-	__save_flags(flags);	/* local CPU only */
-	__cli();		/* local CPU only */
-
-	/*
-	 * Don't use ide_wait_cmd here - it will
-	 * attempt to set_geometry and recalibrate,
-	 * but for some reason these don't work at
-	 * this point (lost interrupt).
-	 */
-	SELECT_DRIVE(HWIF(drive), drive);
-	if (IDE_CONTROL_REG)
-	    OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
-	OUT_BYTE(speed, IDE_NSECTOR_REG);
-	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
-	OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
-
-	err = ide_wait_stat(drive, DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT, WAIT_CMD);
-
-#if 0
-	if (IDE_CONTROL_REG)
-		OUT_BYTE(drive->ctl, IDE_CONTROL_REG);
-#endif
-
-	__restore_flags(flags);	/* local CPU only */
-
-        switch(speed) {
-		case XFER_UDMA_4:
-			if (!((id->dma_ultra >> 8) & 16)) {
-				drive->id->dma_ultra &= ~0xFF00;
-				drive->id->dma_ultra |= 0x1010;
-			}
-			drive->id->dma_mword &= ~0x0F00;
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-		case XFER_UDMA_3:
-			if (!((id->dma_ultra >> 8) & 8)) {
-				drive->id->dma_ultra &= ~0xFF00;
-				drive->id->dma_ultra |= 0x0808;
-			}
-			drive->id->dma_mword &= ~0x0F00;
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-		case XFER_UDMA_2:
-			if (!((id->dma_ultra >> 8) & 4)) {
-				drive->id->dma_ultra &= ~0xFF00;
-				drive->id->dma_ultra |= 0x0404;
-			}
-			drive->id->dma_mword &= ~0x0F00;
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-		case XFER_UDMA_1:
-			if (!((id->dma_ultra >> 8) & 2)) {
-				drive->id->dma_ultra &= ~0xFF00;
-				drive->id->dma_ultra |= 0x0202;
-			}
-			drive->id->dma_mword &= ~0x0F00;
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-		case XFER_UDMA_0:
-			if (!((id->dma_ultra >> 8) & 1)) {
-				drive->id->dma_ultra &= ~0xFF00;
-				drive->id->dma_ultra |= 0x0101;
-			}
-			drive->id->dma_mword &= ~0x0F00;
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-		case XFER_MW_DMA_2:
-			drive->id->dma_ultra &= ~0xFF00;
-			if (!((id->dma_mword >> 8) & 4)) {
-				drive->id->dma_mword &= ~0x0F00;
-				drive->id->dma_mword |= 0x0404;
-			}
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-		case XFER_MW_DMA_1:
-			drive->id->dma_ultra &= ~0xFF00;
-			if (!((id->dma_mword >> 8) & 2)) {
-				drive->id->dma_mword &= ~0x0F00;
-				drive->id->dma_mword |= 0x0202;
-			}
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-		case XFER_MW_DMA_0:
-			drive->id->dma_ultra &= ~0xFF00;
-			if (!((id->dma_mword >> 8) & 1)) {
-				drive->id->dma_mword &= ~0x0F00;
-				drive->id->dma_mword |= 0x0101;
-			}
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-		case XFER_SW_DMA_2:
-			drive->id->dma_ultra &= ~0xFF00;
-			drive->id->dma_mword &= ~0x0F00;
-			if (!((id->dma_1word >> 8) & 4)) {
-				drive->id->dma_1word &= ~0x0F00;
-				drive->id->dma_1word |= 0x0404;
-			}
-			break;
-		case XFER_SW_DMA_1:
-			drive->id->dma_ultra &= ~0xFF00;
-			drive->id->dma_mword &= ~0x0F00;
-			if (!((id->dma_1word >> 8) & 2)) {
-				drive->id->dma_1word &= ~0x0F00;
-				drive->id->dma_1word |= 0x0202;
-			}
-			break;
-		case XFER_SW_DMA_0:
-			drive->id->dma_ultra &= ~0xFF00;
-			drive->id->dma_mword &= ~0x0F00;
-			if (!((id->dma_1word >> 8) & 1)) {
-				drive->id->dma_1word &= ~0x0F00;
-				drive->id->dma_1word |= 0x0101;
-			}
-			break;
-		default:
-			drive->id->dma_ultra &= ~0xFF00;
-			drive->id->dma_mword &= ~0x0F00;
-			drive->id->dma_1word &= ~0x0F00;
-			break;
-	}
-
-	return(err);
-}
-
 static int ide_ioctl (struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
@@ -2509,8 +2422,7 @@
 				return -EINVAL;
 			if (drive->id == NULL)
 				return -ENOMSG;
-			if (copy_to_user((char *)arg, (char *)drive->id,
-					 (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
+			if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
 				return -EFAULT;
 			return 0;
 
@@ -2537,28 +2449,16 @@
 					return -ENOMEM;
 				memcpy(argbuf, args, 4);
 			}
-			if ((((byte *)arg)[0] == WIN_SETFEATURES) &&
-			    (((byte *)arg)[1] > 66) &&
-			    (((byte *)arg)[2] == 3)) {
-				if (!HWIF(drive)->udma_four) {
-					printk("%s: Speed warnings UDMA 3/4 is not functional.\n", HWIF(drive)->name);
-					goto abort;
-				}
-				if ((drive->id->word93 & 0x2000) == 0) {
-					printk("%s: Speed warnings UDMA 3/4 is not functional.\n", drive->name);
-					goto abort;
-				}
-			}
+			if (ide_ata66_check(drive, args[0], args[1], args[2]))
+				goto abort;
+
 			err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
-			if (!err &&
-			    (((byte *)arg)[0] == WIN_SETFEATURES) &&
-			    (((byte *)arg)[1] >= 16) &&
-			    (((byte *)arg)[2] == 3) &&
-			    (drive->id->dma_ultra ||
-			     drive->id->dma_mword ||
-			     drive->id->dma_1word)) {
 
-				/* 
+			if (!err && set_transfer(drive, args[0], args[1], args[2])) {
+
+#if 1
+				ide_driveid_update(drive);
+#else				/* 
 				 * Re-read drive->id for possible DMA mode 
 				 * change (copied from ide-probe.c)
 				 */
@@ -2602,6 +2502,7 @@
 #endif
 				kfree(id);
 				}
+#endif
 			}
 		abort:
 			if (copy_to_user((void *)arg, argbuf, argsize))
@@ -2694,32 +2595,6 @@
 }
 
 /*
- *
- */
-char *ide_xfer_verbose (byte xfer_rate) {
-	switch(xfer_rate) {
-		case XFER_UDMA_4:	return("UDMA 4");
-		case XFER_UDMA_3:	return("UDMA 3");
-		case XFER_UDMA_2:	return("UDMA 2");
-		case XFER_UDMA_1:	return("UDMA 1");
-		case XFER_UDMA_0:	return("UDMA 0");
-		case XFER_MW_DMA_2:	return("MW DMA 2");
-		case XFER_MW_DMA_1:	return("MW DMA 1");
-		case XFER_MW_DMA_0:	return("MW DMA 0");
-		case XFER_SW_DMA_2:	return("SW DMA 2");
-		case XFER_SW_DMA_1:	return("SW DMA 1");
-		case XFER_SW_DMA_0:	return("SW DMA 0");
-		case XFER_PIO_4:	return("PIO 4");
-		case XFER_PIO_3:	return("PIO 3");
-		case XFER_PIO_2:	return("PIO 2");
-		case XFER_PIO_1:	return("PIO 1");
-		case XFER_PIO_0:	return("PIO 0");
-		case XFER_PIO_SLOW:	return("PIO SLOW");
-		default:		return("XFER ERROR");
-	}
-}
-
-/*
  * stridx() returns the offset of c within s,
  * or -1 if c is '\0' or not found within s.
  */
@@ -2796,7 +2671,7 @@
  * "hdx=nowerr"		: ignore the WRERR_STAT bit on this drive
  * "hdx=cdrom"		: drive is present, and is a cdrom drive
  * "hdx=cyl,head,sect"	: disk drive is present, with specified geometry
- * "hdx=noremap"        : do not remap 0->1 even though EZD was detected
+ * "hdx=noremap"	: do not remap 0->1 even though EZD was detected
  * "hdx=autotune"	: driver will attempt to tune interface speed
  *				to the fastest PIO mode supported,
  *				if possible for this drive only.
@@ -3198,6 +3073,14 @@
 }
 
 /*
+ * __setup("ide", ide_setup);
+ * #ifdef CONFIG_BLK_DEV_VIA82CXXX
+ * __setup("splitfifo", ide_setup);
+ * #endif
+ * __setup("hd", ide_setup);
+ */
+
+/*
  * probe_for_hwifs() finds/initializes "known" IDE interfaces
  */
 static void __init probe_for_hwifs (void)
@@ -3332,9 +3215,10 @@
 	return ide_unregister_subdriver(drive);
 }
 
-static void default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
+static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
 {
 	ide_end_request(0, HWGROUP(drive));
+	return ide_stopped;
 }
  
 static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
@@ -3372,12 +3256,13 @@
 	return 0x7fffffff;	/* cdrom or tape */
 }
 
-static void default_special (ide_drive_t *drive)
+static ide_startstop_t default_special (ide_drive_t *drive)
 {
 	special_t *s = &drive->special;
 
 	s->all = 0;
 	drive->mult_req = 0;
+	return ide_stopped;
 }
 
 static void setup_driver_defaults (ide_drive_t *drive)
@@ -3577,7 +3462,6 @@
 EXPORT_SYMBOL(ide_cmd);
 EXPORT_SYMBOL(ide_wait_cmd);
 EXPORT_SYMBOL(ide_delay_50ms);
-EXPORT_SYMBOL(ide_config_drive_speed);
 EXPORT_SYMBOL(ide_stall_queue);
 #ifdef CONFIG_PROC_FS
 EXPORT_SYMBOL(ide_add_proc_entries);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)