]> err.no Git - linux-2.6/blobdiff - drivers/ata/libata-eh.c
libata: add deadline support to prereset and reset methods
[linux-2.6] / drivers / ata / libata-eh.c
index 7349c3dbf774e672bfbcc8ce1ab5e8d6f2ac5ce0..b3f7d3c8ae60c83aca1e564feb1ce5ceb92b0af7 100644 (file)
@@ -982,26 +982,27 @@ static int ata_eh_read_log_10h(struct ata_device *dev,
  *     RETURNS:
  *     0 on success, AC_ERR_* mask on failure
  */
-static unsigned int atapi_eh_request_sense(struct ata_device *dev,
-                                          unsigned char *sense_buf)
+static unsigned int atapi_eh_request_sense(struct ata_queued_cmd *qc)
 {
+       struct ata_device *dev = qc->dev;
+       unsigned char *sense_buf = qc->scsicmd->sense_buffer;
        struct ata_port *ap = dev->ap;
        struct ata_taskfile tf;
        u8 cdb[ATAPI_CDB_LEN];
 
        DPRINTK("ATAPI request sense\n");
 
-       ata_tf_init(dev, &tf);
-
        /* FIXME: is this needed? */
        memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE);
 
-       /* XXX: why tf_read here? */
-       ap->ops->tf_read(ap, &tf);
-
-       /* fill these in, for the case where they are -not- overwritten */
+       /* initialize sense_buf with the error register,
+        * for the case where they are -not- overwritten
+        */
        sense_buf[0] = 0x70;
-       sense_buf[2] = tf.feature >> 4;
+       sense_buf[2] = qc->result_tf.feature >> 4;
+
+       /* some devices time out if garbage left in tf */ 
+       ata_tf_init(dev, &tf);
 
        memset(cdb, 0, ATAPI_CDB_LEN);
        cdb[0] = REQUEST_SENSE;
@@ -1055,7 +1056,7 @@ static void ata_eh_analyze_serror(struct ata_port *ap)
        }
        if (serror & SERR_INTERNAL) {
                err_mask |= AC_ERR_SYSTEM;
-               action |= ATA_EH_SOFTRESET;
+               action |= ATA_EH_HARDRESET;
        }
        if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
                ata_ehi_hotplugged(&ehc->i);
@@ -1150,7 +1151,9 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
                return ATA_EH_SOFTRESET;
        }
 
-       if (!(qc->err_mask & AC_ERR_DEV))
+       if (stat & (ATA_ERR | ATA_DF))
+               qc->err_mask |= AC_ERR_DEV;
+       else
                return 0;
 
        switch (qc->dev->class) {
@@ -1165,8 +1168,7 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc,
 
        case ATA_DEV_ATAPI:
                if (!(qc->ap->pflags & ATA_PFLAG_FROZEN)) {
-                       tmp = atapi_eh_request_sense(qc->dev,
-                                                    qc->scsicmd->sense_buffer);
+                       tmp = atapi_eh_request_sense(qc);
                        if (!tmp) {
                                /* ATA_QCFLAG_SENSE_VALID is used to
                                 * tell atapi_qc_complete() that sense
@@ -1556,14 +1558,14 @@ static void ata_eh_report(struct ata_port *ap)
 }
 
 static int ata_do_reset(struct ata_port *ap, ata_reset_fn_t reset,
-                       unsigned int *classes)
+                       unsigned int *classes, unsigned long deadline)
 {
        int i, rc;
 
        for (i = 0; i < ATA_MAX_DEVICES; i++)
                classes[i] = ATA_DEV_UNKNOWN;
 
-       rc = reset(ap, classes);
+       rc = reset(ap, classes, deadline);
        if (rc)
                return rc;
 
@@ -1622,11 +1624,17 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
                ehc->i.action |= ATA_EH_HARDRESET;
 
        if (prereset) {
-               rc = prereset(ap);
+               rc = prereset(ap, jiffies + 40 * HZ);
                if (rc) {
                        if (rc == -ENOENT) {
-                               ata_port_printk(ap, KERN_DEBUG, "port disabled. ignoring.\n");
+                               ata_port_printk(ap, KERN_DEBUG,
+                                               "port disabled. ignoring.\n");
                                ap->eh_context.i.action &= ~ATA_EH_RESET_MASK;
+
+                               for (i = 0; i < ATA_MAX_DEVICES; i++)
+                                       classes[i] = ATA_DEV_NONE;
+
+                               rc = 0;
                        } else
                                ata_port_printk(ap, KERN_ERR,
                                        "prereset failed (errno=%d)\n", rc);
@@ -1663,9 +1671,12 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
                                reset == softreset ? "soft" : "hard");
 
        /* mark that this EH session started with reset */
-       ehc->i.flags |= ATA_EHI_DID_RESET;
+       if (reset == hardreset)
+               ehc->i.flags |= ATA_EHI_DID_HARDRESET;
+       else
+               ehc->i.flags |= ATA_EHI_DID_SOFTRESET;
 
-       rc = ata_do_reset(ap, reset, classes);
+       rc = ata_do_reset(ap, reset, classes, jiffies + 40 * HZ);
 
        did_followup_srst = 0;
        if (reset == hardreset &&
@@ -1682,7 +1693,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
                }
 
                ata_eh_about_to_do(ap, NULL, ATA_EH_RESET_MASK);
-               rc = ata_do_reset(ap, reset, classes);
+               rc = ata_do_reset(ap, reset, classes, jiffies + 40 * HZ);
 
                if (rc == 0 && classify &&
                    classes[0] == ATA_DEV_UNKNOWN) {
@@ -1737,12 +1748,17 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
 {
        struct ata_eh_context *ehc = &ap->eh_context;
        struct ata_device *dev;
+       unsigned int new_mask = 0;
        unsigned long flags;
        int i, rc = 0;
 
        DPRINTK("ENTER\n");
 
-       for (i = 0; i < ATA_MAX_DEVICES; i++) {
+       /* For PATA drive side cable detection to work, IDENTIFY must
+        * be done backwards such that PDIAG- is released by the slave
+        * device before the master device is identified.
+        */
+       for (i = ATA_MAX_DEVICES - 1; i >= 0; i--) {
                unsigned int action, readid_flags = 0;
 
                dev = &ap->device[i];
@@ -1754,13 +1770,13 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
                if (action & ATA_EH_REVALIDATE && ata_dev_ready(dev)) {
                        if (ata_port_offline(ap)) {
                                rc = -EIO;
-                               break;
+                               goto err;
                        }
 
                        ata_eh_about_to_do(ap, dev, ATA_EH_REVALIDATE);
                        rc = ata_dev_revalidate(dev, readid_flags);
                        if (rc)
-                               break;
+                               goto err;
 
                        ata_eh_done(ap, dev, ATA_EH_REVALIDATE);
 
@@ -1778,40 +1794,57 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
 
                        rc = ata_dev_read_id(dev, &dev->class, readid_flags,
                                             dev->id);
-                       if (rc == 0) {
-                               ehc->i.flags |= ATA_EHI_PRINTINFO;
-                               rc = ata_dev_configure(dev);
-                               ehc->i.flags &= ~ATA_EHI_PRINTINFO;
-                       } else if (rc == -ENOENT) {
+                       switch (rc) {
+                       case 0:
+                               new_mask |= 1 << i;
+                               break;
+                       case -ENOENT:
                                /* IDENTIFY was issued to non-existent
                                 * device.  No need to reset.  Just
                                 * thaw and kill the device.
                                 */
                                ata_eh_thaw_port(ap);
-                               dev->class = ATA_DEV_UNKNOWN;
-                               rc = 0;
-                       }
-
-                       if (rc) {
                                dev->class = ATA_DEV_UNKNOWN;
                                break;
+                       default:
+                               dev->class = ATA_DEV_UNKNOWN;
+                               goto err;
                        }
+               }
+       }
 
-                       if (ata_dev_enabled(dev)) {
-                               spin_lock_irqsave(ap->lock, flags);
-                               ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;
-                               spin_unlock_irqrestore(ap->lock, flags);
+       /* PDIAG- should have been released, ask cable type if post-reset */
+       if ((ehc->i.flags & ATA_EHI_DID_RESET) && ap->ops->cable_detect)
+               ap->cbl = ap->ops->cable_detect(ap);
 
-                               /* new device discovered, configure xfermode */
-                               ehc->i.flags |= ATA_EHI_SETMODE;
-                       }
-               }
+       /* Configure new devices forward such that user doesn't see
+        * device detection messages backwards.
+        */
+       for (i = 0; i < ATA_MAX_DEVICES; i++) {
+               dev = &ap->device[i];
+
+               if (!(new_mask & (1 << i)))
+                       continue;
+
+               ehc->i.flags |= ATA_EHI_PRINTINFO;
+               rc = ata_dev_configure(dev);
+               ehc->i.flags &= ~ATA_EHI_PRINTINFO;
+               if (rc)
+                       goto err;
+
+               spin_lock_irqsave(ap->lock, flags);
+               ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;
+               spin_unlock_irqrestore(ap->lock, flags);
+
+               /* new device discovered, configure xfermode */
+               ehc->i.flags |= ATA_EHI_SETMODE;
        }
 
-       if (rc)
-               *r_failed_dev = dev;
+       return 0;
 
-       DPRINTK("EXIT\n");
+ err:
+       *r_failed_dev = dev;
+       DPRINTK("EXIT rc=%d\n", rc);
        return rc;
 }