]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ide: use correct IDE error recovery
authorSuleiman Souhlal <suleiman@google.com>
Wed, 4 Apr 2007 19:28:52 +0000 (15:28 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 13 Apr 2007 20:47:05 +0000 (13:47 -0700)
ide: use correct IDE error recovery

IDE error recovery is using IDLE IMMEDIATE if the drive is busy or has DRQ set.
This violates the ATA spec (can only send IDLEÃ\82 IMMEDIATE when drive is not
busy) and really hoses up some drives (modern drives will not be able to
recover using this error handling).  The correct thing to do is issue a SRST
followed by a SET FEATURES command.  This is what Western Digital recommends
for error recovery and what Western Digital says Windows does. Ã\82 ItÃ\82 also does
not violate the ATA spec as far as I can tell.

Bart:
* port the patch over the current tree
* undo the recalibration code removal
* send SET FEATURES command after checking for good drive status
* don't check whether the current request is of REQ_TYPE_ATA_{CMD,TASK}
  type because we need to send SET FEATURES before handling any requests
* some pre-ATA4 drives require INITIALIZE DEVICE PARAMETERS command before
  other commands (except IDENTIFY) so send SET FEATURES only if there are
  no pending drive->special requests
* update comments and patch description
* any bugs introduced by this patch are mine and not Suleiman's :-)

Signed-off-by: Suleiman Souhlal <suleiman@google.com>
Acked-by: Alan Cox <alan@redhat.com>
Cc: Chuck Ebbert <cebbert@redhat.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
drivers/ide/ide-io.c
drivers/ide/ide-iops.c
include/linux/ide.h

index 99d1c43f433eed5e1ae9aadd765e27aff57136ea..d6ff4f2292cb48a7a0b5ec6e4cdd342ac7152195 100644 (file)
@@ -519,21 +519,24 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8
        if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ && hwif->err_stops_fifo == 0)
                try_to_flush_leftover_data(drive);
 
+       if (rq->errors >= ERROR_MAX || blk_noretry_request(rq)) {
+               ide_kill_rq(drive, rq);
+               return ide_stopped;
+       }
+
        if (hwif->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT))
-               /* force an abort */
-               hwif->OUTB(WIN_IDLEIMMEDIATE, IDE_COMMAND_REG);
+               rq->errors |= ERROR_RESET;
 
-       if (rq->errors >= ERROR_MAX || blk_noretry_request(rq))
-               ide_kill_rq(drive, rq);
-       else {
-               if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
-                       ++rq->errors;
-                       return ide_do_reset(drive);
-               }
-               if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
-                       drive->special.b.recalibrate = 1;
+       if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
                ++rq->errors;
+               return ide_do_reset(drive);
        }
+
+       if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+               drive->special.b.recalibrate = 1;
+
+       ++rq->errors;
+
        return ide_stopped;
 }
 
@@ -1025,6 +1028,13 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
        if (!drive->special.all) {
                ide_driver_t *drv;
 
+               /*
+                * We reset the drive so we need to issue a SETFEATURES.
+                * Do it _after_ do_special() restored device parameters.
+                */
+               if (drive->current_speed == 0xff)
+                       ide_config_drive_speed(drive, drive->desired_speed);
+
                if (rq->cmd_type == REQ_TYPE_ATA_CMD ||
                    rq->cmd_type == REQ_TYPE_ATA_TASK ||
                    rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
index 59b10a0f0d73c2645caae31bcc293ecf07e6832d..133d9cd7be50e245bedbb1cd328fc6c16dddbe63 100644 (file)
@@ -1123,6 +1123,9 @@ static void pre_reset(ide_drive_t *drive)
        if (HWIF(drive)->pre_reset != NULL)
                HWIF(drive)->pre_reset(drive);
 
+       if (drive->current_speed != 0xff)
+               drive->desired_speed = drive->current_speed;
+       drive->current_speed = 0xff;
 }
 
 /*
index 3808698867ec0fe40ccec2fffde6b5edc919d792..63e111e2c0194ac66c770a80d143cd7518abb773 100644 (file)
@@ -607,6 +607,7 @@ typedef struct ide_drive_s {
         u8     init_speed;     /* transfer rate set at boot */
         u8     pio_speed;      /* unused by core, used by some drivers for fallback from DMA */
         u8     current_speed;  /* current transfer rate set */
+       u8      desired_speed;  /* desired transfer rate set */
         u8     dn;             /* now wide spread use */
         u8     wcache;         /* status of write cache */
        u8      acoustic;       /* acoustic management */