[PATCH] libata-hp-prep: implement followup softreset handling
authorTejun Heo <htejun@gmail.com>
Wed, 31 May 2006 09:27:50 +0000 (18:27 +0900)
committerTejun Heo <htejun@gmail.com>
Wed, 31 May 2006 09:27:50 +0000 (18:27 +0900)
In some cases, hardreset must be followed by SRST.

* some controllers can't classify with hardreset
* some controllers can't wait for !BSY after hardreset (LLDD should
  explicitly request followup softreset by returning -EAGAIN)
* (later) PM needs SRST w/ PMP==15 to operate after hardreset

To handle above cases, this patch implements follow-up softreset.
After a hardreset, ata_eh_reset() checks whether any of above
conditions are met and do a follow-up softreset if necessary.

Signed-off-by: Tejun Heo <htejun@gmail.com>
drivers/scsi/libata-eh.c

index 0e66f14..8ecb842 100644 (file)
@@ -1318,16 +1318,28 @@ static void ata_eh_report(struct ata_port *ap)
        }
 }
 
-static int ata_eh_reset(struct ata_port *ap,
+static int ata_eh_followup_srst_needed(int rc, int classify,
+                                      const unsigned int *classes)
+{
+       if (rc == -EAGAIN)
+               return 1;
+       if (rc != 0)
+               return 0;
+       if (classify && classes[0] == ATA_DEV_UNKNOWN)
+               return 1;
+       return 0;
+}
+
+static int ata_eh_reset(struct ata_port *ap, int classify,
                        ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
                        ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
 {
        struct ata_eh_context *ehc = &ap->eh_context;
-       unsigned int classes[ATA_MAX_DEVICES];
+       unsigned int *classes = ehc->classes;
        int tries = ATA_EH_RESET_TRIES;
        unsigned int action;
        ata_reset_fn_t reset;
-       int i, rc;
+       int i, did_followup_srst, rc;
 
        /* Determine which reset to use and record in ehc->i.action.
         * prereset() may examine and modify it.
@@ -1381,10 +1393,44 @@ static int ata_eh_reset(struct ata_port *ap,
 
        rc = ata_do_reset(ap, reset, classes);
 
+       did_followup_srst = 0;
+       if (reset == hardreset &&
+           ata_eh_followup_srst_needed(rc, classify, classes)) {
+               /* okay, let's do follow-up softreset */
+               did_followup_srst = 1;
+               reset = softreset;
+
+               if (!reset) {
+                       ata_port_printk(ap, KERN_ERR,
+                                       "follow-up softreset required "
+                                       "but no softreset avaliable\n");
+                       return -EINVAL;
+               }
+
+               ata_eh_about_to_do(ap, ATA_EH_RESET_MASK);
+               rc = ata_do_reset(ap, reset, classes);
+
+               if (rc == 0 && classify &&
+                   classes[0] == ATA_DEV_UNKNOWN) {
+                       ata_port_printk(ap, KERN_ERR,
+                                       "classification failed\n");
+                       return -EINVAL;
+               }
+       }
+
        if (rc && --tries) {
+               const char *type;
+
+               if (reset == softreset) {
+                       if (did_followup_srst)
+                               type = "follow-up soft";
+                       else
+                               type = "soft";
+               } else
+                       type = "hard";
+
                ata_port_printk(ap, KERN_WARNING,
-                               "%sreset failed, retrying in 5 secs\n",
-                               reset == softreset ? "soft" : "hard");
+                               "%sreset failed, retrying in 5 secs\n", type);
                ssleep(5);
 
                if (reset == hardreset)
@@ -1508,7 +1554,7 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
        if (ehc->i.action & ATA_EH_RESET_MASK) {
                ata_eh_freeze_port(ap);
 
-               rc = ata_eh_reset(ap, prereset, softreset, hardreset,
+               rc = ata_eh_reset(ap, 0, prereset, softreset, hardreset,
                                  postreset);
                if (rc) {
                        ata_port_printk(ap, KERN_ERR,