libata: move ata_dev_disable() to libata-eh.c
[safe/jmp/linux-2.6] / drivers / ata / libata-eh.c
index d673f37..aafe82b 100644 (file)
@@ -491,6 +491,31 @@ enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
        return ret;
 }
 
+static void ata_eh_unload(struct ata_port *ap)
+{
+       struct ata_link *link;
+       struct ata_device *dev;
+       unsigned long flags;
+
+       /* Restore SControl IPM and SPD for the next driver and
+        * disable attached devices.
+        */
+       ata_for_each_link(link, ap, PMP_FIRST) {
+               sata_scr_write(link, SCR_CONTROL, link->saved_scontrol & 0xff0);
+               ata_for_each_dev(dev, link, ALL)
+                       ata_dev_disable(dev);
+       }
+
+       /* freeze and set UNLOADED */
+       spin_lock_irqsave(ap->lock, flags);
+
+       ata_port_freeze(ap);                    /* won't be thawed */
+       ap->pflags &= ~ATA_PFLAG_EH_PENDING;    /* clear pending from freeze */
+       ap->pflags |= ATA_PFLAG_UNLOADED;
+
+       spin_unlock_irqrestore(ap->lock, flags);
+}
+
 /**
  *     ata_scsi_error - SCSI layer error handler callback
  *     @host: SCSI host on which error occurred
@@ -618,8 +643,13 @@ void ata_scsi_error(struct Scsi_Host *host)
                /* invoke EH, skip if unloading or suspended */
                if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED)))
                        ap->ops->error_handler(ap);
-               else
+               else {
+                       /* if unloading, commence suicide */
+                       if ((ap->pflags & ATA_PFLAG_UNLOADING) &&
+                           !(ap->pflags & ATA_PFLAG_UNLOADED))
+                               ata_eh_unload(ap);
                        ata_eh_finish(ap);
+               }
 
                /* process port suspend request */
                ata_eh_handle_port_suspend(ap);
@@ -1146,6 +1176,27 @@ void ata_eh_qc_retry(struct ata_queued_cmd *qc)
 }
 
 /**
+ *     ata_dev_disable - disable ATA device
+ *     @dev: ATA device to disable
+ *
+ *     Disable @dev.
+ *
+ *     Locking:
+ *     EH context.
+ */
+void ata_dev_disable(struct ata_device *dev)
+{
+       if (!ata_dev_enabled(dev))
+               return;
+
+       if (ata_msg_drv(dev->link->ap))
+               ata_dev_printk(dev, KERN_WARNING, "disabled\n");
+       ata_acpi_on_disable(dev);
+       ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | ATA_DNXFER_QUIET);
+       dev->class++;
+}
+
+/**
  *     ata_eh_detach_dev - detach ATA device
  *     @dev: ATA device to detach
  *
@@ -2949,12 +3000,13 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
                /* give it just one more chance */
                ehc->tries[dev->devno] = min(ehc->tries[dev->devno], 1);
        case -EIO:
-               if (ehc->tries[dev->devno] == 1 && dev->pio_mode > XFER_PIO_0) {
+               if (ehc->tries[dev->devno] == 1) {
                        /* This is the last chance, better to slow
                         * down than lose it.
                         */
                        sata_down_spd_limit(ata_dev_phys_link(dev));
-                       ata_down_xfermask_limit(dev, ATA_DNXFER_PIO);
+                       if (dev->pio_mode > XFER_PIO_0)
+                               ata_down_xfermask_limit(dev, ATA_DNXFER_PIO);
                }
        }