libata-pmp-prep: implement ATA_LFLAG_DISABLED
[pandora-kernel.git] / drivers / ata / libata-eh.c
index 2ddc2ed..fbbf791 100644 (file)
@@ -74,7 +74,6 @@ static const unsigned long ata_eh_reset_timeouts[] = {
 };
 
 static void __ata_port_freeze(struct ata_port *ap);
-static void ata_eh_finish(struct ata_port *ap);
 #ifdef CONFIG_PM
 static void ata_eh_handle_port_suspend(struct ata_port *ap);
 static void ata_eh_handle_port_resume(struct ata_port *ap);
@@ -151,6 +150,73 @@ void ata_ehi_clear_desc(struct ata_eh_info *ehi)
        ehi->desc_len = 0;
 }
 
+/**
+ *     ata_port_desc - append port description
+ *     @ap: target ATA port
+ *     @fmt: printf format string
+ *
+ *     Format string according to @fmt and append it to port
+ *     description.  If port description is not empty, " " is added
+ *     in-between.  This function is to be used while initializing
+ *     ata_host.  The description is printed on host registration.
+ *
+ *     LOCKING:
+ *     None.
+ */
+void ata_port_desc(struct ata_port *ap, const char *fmt, ...)
+{
+       va_list args;
+
+       WARN_ON(!(ap->pflags & ATA_PFLAG_INITIALIZING));
+
+       if (ap->link.eh_info.desc_len)
+               __ata_ehi_push_desc(&ap->link.eh_info, " ");
+
+       va_start(args, fmt);
+       __ata_ehi_pushv_desc(&ap->link.eh_info, fmt, args);
+       va_end(args);
+}
+
+#ifdef CONFIG_PCI
+
+/**
+ *     ata_port_pbar_desc - append PCI BAR description
+ *     @ap: target ATA port
+ *     @bar: target PCI BAR
+ *     @offset: offset into PCI BAR
+ *     @name: name of the area
+ *
+ *     If @offset is negative, this function formats a string which
+ *     contains the name, address, size and type of the BAR and
+ *     appends it to the port description.  If @offset is zero or
+ *     positive, only name and offsetted address is appended.
+ *
+ *     LOCKING:
+ *     None.
+ */
+void ata_port_pbar_desc(struct ata_port *ap, int bar, ssize_t offset,
+                       const char *name)
+{
+       struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+       char *type = "";
+       unsigned long long start, len;
+
+       if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM)
+               type = "m";
+       else if (pci_resource_flags(pdev, bar) & IORESOURCE_IO)
+               type = "i";
+
+       start = (unsigned long long)pci_resource_start(pdev, bar);
+       len = (unsigned long long)pci_resource_len(pdev, bar);
+
+       if (offset < 0)
+               ata_port_desc(ap, "%s %s%llu@0x%llx", name, type, len, start);
+       else
+               ata_port_desc(ap, "%s 0x%llx", name, start + offset);
+}
+
+#endif /* CONFIG_PCI */
+
 static void ata_ering_record(struct ata_ering *ering, int is_io,
                             unsigned int err_mask)
 {
@@ -291,7 +357,7 @@ enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd)
 void ata_scsi_error(struct Scsi_Host *host)
 {
        struct ata_port *ap = ata_shost_to_port(host);
-       int i, repeat_cnt = ATA_EH_MAX_REPEAT;
+       int i;
        unsigned long flags;
 
        DPRINTK("ENTER\n");
@@ -357,6 +423,9 @@ void ata_scsi_error(struct Scsi_Host *host)
                        __ata_port_freeze(ap);
 
                spin_unlock_irqrestore(ap->lock, flags);
+
+               /* initialize eh_tries */
+               ap->eh_tries = ATA_EH_MAX_TRIES;
        } else
                spin_unlock_wait(ap->lock);
 
@@ -382,6 +451,7 @@ void ata_scsi_error(struct Scsi_Host *host)
 
                ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
                ap->pflags &= ~ATA_PFLAG_EH_PENDING;
+               ap->excl_link = NULL;   /* don't maintain exclusion over EH */
 
                spin_unlock_irqrestore(ap->lock, flags);
 
@@ -401,15 +471,12 @@ void ata_scsi_error(struct Scsi_Host *host)
                spin_lock_irqsave(ap->lock, flags);
 
                if (ap->pflags & ATA_PFLAG_EH_PENDING) {
-                       if (--repeat_cnt) {
-                               ata_port_printk(ap, KERN_INFO,
-                                       "EH pending after completion, "
-                                       "repeating EH (cnt=%d)\n", repeat_cnt);
+                       if (--ap->eh_tries) {
                                spin_unlock_irqrestore(ap->lock, flags);
                                goto repeat;
                        }
                        ata_port_printk(ap, KERN_ERR, "EH pending after %d "
-                                       "tries, giving up\n", ATA_EH_MAX_REPEAT);
+                                       "tries, giving up\n", ATA_EH_MAX_TRIES);
                        ap->pflags &= ~ATA_PFLAG_EH_PENDING;
                }
 
@@ -948,7 +1015,7 @@ void ata_eh_qc_retry(struct ata_queued_cmd *qc)
  *     LOCKING:
  *     None.
  */
-static void ata_eh_detach_dev(struct ata_device *dev)
+void ata_eh_detach_dev(struct ata_device *dev)
 {
        struct ata_link *link = dev->link;
        struct ata_port *ap = link->ap;
@@ -985,8 +1052,8 @@ static void ata_eh_detach_dev(struct ata_device *dev)
  *     LOCKING:
  *     None.
  */
-static void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
-                              unsigned int action)
+void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
+                       unsigned int action)
 {
        struct ata_port *ap = link->ap;
        struct ata_eh_info *ehi = &link->eh_info;
@@ -1028,8 +1095,8 @@ static void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
  *     LOCKING:
  *     None.
  */
-static void ata_eh_done(struct ata_link *link, struct ata_device *dev,
-                       unsigned int action)
+void ata_eh_done(struct ata_link *link, struct ata_device *dev,
+                unsigned int action)
 {
        struct ata_eh_context *ehc = &link->eh_context;
 
@@ -1241,6 +1308,7 @@ static void ata_eh_analyze_serror(struct ata_link *link)
        struct ata_eh_context *ehc = &link->eh_context;
        u32 serror = ehc->i.serror;
        unsigned int err_mask = 0, action = 0;
+       u32 hotplug_mask;
 
        if (serror & SERR_PERSISTENT) {
                err_mask |= AC_ERR_ATA_BUS;
@@ -1259,7 +1327,20 @@ static void ata_eh_analyze_serror(struct ata_link *link)
                err_mask |= AC_ERR_SYSTEM;
                action |= ATA_EH_HARDRESET;
        }
-       if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
+
+       /* Determine whether a hotplug event has occurred.  Both
+        * SError.N/X are considered hotplug events for enabled or
+        * host links.  For disabled PMP links, only N bit is
+        * considered as X bit is left at 1 for link plugging.
+        */
+       hotplug_mask = 0;
+
+       if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link))
+               hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+       else
+               hotplug_mask = SERR_PHYRDY_CHG;
+
+       if (serror & hotplug_mask)
                ata_ehi_hotplugged(&ehc->i);
 
        ehc->i.err_mask |= err_mask;
@@ -1689,7 +1770,7 @@ static void ata_eh_link_autopsy(struct ata_link *link)
  *     LOCKING:
  *     Kernel thread context (may sleep).
  */
-static void ata_eh_autopsy(struct ata_port *ap)
+void ata_eh_autopsy(struct ata_port *ap)
 {
        struct ata_link *link;
 
@@ -1711,6 +1792,7 @@ static void ata_eh_link_report(struct ata_link *link)
        struct ata_port *ap = link->ap;
        struct ata_eh_context *ehc = &link->eh_context;
        const char *frozen, *desc;
+       char tries_buf[6];
        int tag, nr_failed = 0;
 
        desc = NULL;
@@ -1735,18 +1817,23 @@ static void ata_eh_link_report(struct ata_link *link)
        if (ap->pflags & ATA_PFLAG_FROZEN)
                frozen = " frozen";
 
+       memset(tries_buf, 0, sizeof(tries_buf));
+       if (ap->eh_tries < ATA_EH_MAX_TRIES)
+               snprintf(tries_buf, sizeof(tries_buf) - 1, " t%d",
+                        ap->eh_tries);
+
        if (ehc->i.dev) {
                ata_dev_printk(ehc->i.dev, KERN_ERR, "exception Emask 0x%x "
-                              "SAct 0x%x SErr 0x%x action 0x%x%s\n",
-                              ehc->i.err_mask, link->sactive,
-                              ehc->i.serror, ehc->i.action, frozen);
+                              "SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
+                              ehc->i.err_mask, link->sactive, ehc->i.serror,
+                              ehc->i.action, frozen, tries_buf);
                if (desc)
                        ata_dev_printk(ehc->i.dev, KERN_ERR, "%s\n", desc);
        } else {
                ata_link_printk(link, KERN_ERR, "exception Emask 0x%x "
-                               "SAct 0x%x SErr 0x%x action 0x%x%s\n",
-                               ehc->i.err_mask, link->sactive,
-                               ehc->i.serror, ehc->i.action, frozen);
+                               "SAct 0x%x SErr 0x%x action 0x%x%s%s\n",
+                               ehc->i.err_mask, link->sactive, ehc->i.serror,
+                               ehc->i.action, frozen, tries_buf);
                if (desc)
                        ata_link_printk(link, KERN_ERR, "%s\n", desc);
        }
@@ -1794,7 +1881,7 @@ static void ata_eh_link_report(struct ata_link *link)
  *     LOCKING:
  *     None.
  */
-static void ata_eh_report(struct ata_port *ap)
+void ata_eh_report(struct ata_port *ap)
 {
        struct ata_link *link;
 
@@ -1833,21 +1920,25 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
        return 0;
 }
 
-static int ata_eh_followup_srst_needed(int rc, int classify,
+static int ata_eh_followup_srst_needed(struct ata_link *link,
+                                      int rc, int classify,
                                       const unsigned int *classes)
 {
+       if (link->flags & ATA_LFLAG_NO_SRST)
+               return 0;
        if (rc == -EAGAIN)
                return 1;
        if (rc != 0)
                return 0;
-       if (classify && classes[0] == ATA_DEV_UNKNOWN)
+       if (classify && !(link->flags & ATA_LFLAG_ASSUME_CLASS) &&
+           classes[0] == ATA_DEV_UNKNOWN)
                return 1;
        return 0;
 }
 
-static int ata_eh_reset(struct ata_link *link, int classify,
-                       ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
-                       ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
+int ata_eh_reset(struct ata_link *link, 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 = &link->eh_context;
        unsigned int *classes = ehc->classes;
@@ -1867,7 +1958,8 @@ static int ata_eh_reset(struct ata_link *link, int classify,
         */
        action = ehc->i.action;
        ehc->i.action &= ~ATA_EH_RESET_MASK;
-       if (softreset && (!hardreset || (!sata_set_spd_needed(link) &&
+       if (softreset && (!hardreset || (!(link->flags & ATA_LFLAG_NO_SRST) &&
+                                        !sata_set_spd_needed(link) &&
                                         !(action & ATA_EH_HARDRESET))))
                ehc->i.action |= ATA_EH_SOFTRESET;
        else
@@ -1930,7 +2022,7 @@ static int ata_eh_reset(struct ata_link *link, int classify,
        rc = ata_do_reset(link, reset, classes, deadline);
 
        if (reset == hardreset &&
-           ata_eh_followup_srst_needed(rc, classify, classes)) {
+           ata_eh_followup_srst_needed(link, rc, classify, classes)) {
                /* okay, let's do follow-up softreset */
                reset = softreset;
 
@@ -1945,8 +2037,8 @@ static int ata_eh_reset(struct ata_link *link, int classify,
                ata_eh_about_to_do(link, NULL, ATA_EH_RESET_MASK);
                rc = ata_do_reset(link, reset, classes, deadline);
 
-               if (rc == 0 && classify &&
-                   classes[0] == ATA_DEV_UNKNOWN) {
+               if (rc == 0 && classify && classes[0] == ATA_DEV_UNKNOWN &&
+                   !(link->flags & ATA_LFLAG_ASSUME_CLASS)) {
                        ata_link_printk(link, KERN_ERR,
                                        "classification failed\n");
                        rc = -EINVAL;
@@ -1954,6 +2046,10 @@ static int ata_eh_reset(struct ata_link *link, int classify,
                }
        }
 
+       /* if we skipped follow-up srst, clear rc */
+       if (rc == -EAGAIN)
+               rc = 0;
+
        if (rc && try < ARRAY_SIZE(ata_eh_reset_timeouts)) {
                unsigned long now = jiffies;
 
@@ -1978,12 +2074,25 @@ static int ata_eh_reset(struct ata_link *link, int classify,
        if (rc == 0) {
                u32 sstatus;
 
-               /* After the reset, the device state is PIO 0 and the
-                * controller state is undefined.  Record the mode.
-                */
-               ata_link_for_each_dev(dev, link)
+               ata_link_for_each_dev(dev, link) {
+                       /* After the reset, the device state is PIO 0
+                        * and the controller state is undefined.
+                        * Record the mode.
+                        */
                        dev->pio_mode = XFER_PIO_0;
 
+                       if (ata_link_offline(link))
+                               continue;
+
+                       /* apply class override and convert UNKNOWN to NONE */
+                       if (link->flags & ATA_LFLAG_ASSUME_ATA)
+                               classes[dev->devno] = ATA_DEV_ATA;
+                       else if (link->flags & ATA_LFLAG_ASSUME_SEMB)
+                               classes[dev->devno] = ATA_DEV_SEMB_UNSUP; /* not yet */
+                       else if (classes[dev->devno] == ATA_DEV_UNKNOWN)
+                               classes[dev->devno] = ATA_DEV_NONE;
+               }
+
                /* record current link speed */
                if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0)
                        link->sata_spd = (sstatus >> 4) & 0xf;
@@ -2031,7 +2140,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
                        }
 
                        ata_eh_about_to_do(link, dev, ATA_EH_REVALIDATE);
-                       rc = ata_dev_revalidate(dev, readid_flags);
+                       rc = ata_dev_revalidate(dev, ehc->classes[dev->devno],
+                                               readid_flags);
                        if (rc)
                                goto err;
 
@@ -2131,6 +2241,10 @@ static int ata_eh_skip_recovery(struct ata_link *link)
        struct ata_eh_context *ehc = &link->eh_context;
        struct ata_device *dev;
 
+       /* skip disabled links */
+       if (link->flags & ATA_LFLAG_DISABLED)
+               return 1;
+
        /* thaw frozen port, resume link and recover failed devices */
        if ((link->ap->pflags & ATA_PFLAG_FROZEN) ||
            (ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_link_nr_enabled(link))
@@ -2222,15 +2336,16 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
  *     RETURNS:
  *     0 on success, -errno on failure.
  */
-static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
-                         ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
-                         ata_postreset_fn_t postreset,
-                         struct ata_link **r_failed_link)
+int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
+                  ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
+                  ata_postreset_fn_t postreset,
+                  struct ata_link **r_failed_link)
 {
        struct ata_link *link;
        struct ata_device *dev;
        int nr_failed_devs, nr_disabled_devs;
        int reset, rc;
+       unsigned long flags;
 
        DPRINTK("ENTER\n");
 
@@ -2238,8 +2353,20 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
        ata_port_for_each_link(link, ap) {
                struct ata_eh_context *ehc = &link->eh_context;
 
+               /* re-enable link? */
+               if (ehc->i.action & ATA_EH_ENABLE_LINK) {
+                       ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK);
+                       spin_lock_irqsave(ap->lock, flags);
+                       link->flags &= ~ATA_LFLAG_DISABLED;
+                       spin_unlock_irqrestore(ap->lock, flags);
+                       ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
+               }
+
                ata_link_for_each_dev(dev, link) {
-                       ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
+                       if (link->flags & ATA_LFLAG_NO_RETRY)
+                               ehc->tries[dev->devno] = 1;
+                       else
+                               ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
 
                        /* collect port action mask recorded in dev actions */
                        ehc->i.action |= ehc->i.dev_action[dev->devno] &
@@ -2371,7 +2498,7 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
  *     LOCKING:
  *     None.
  */
-static void ata_eh_finish(struct ata_port *ap)
+void ata_eh_finish(struct ata_port *ap)
 {
        int tag;
 
@@ -2401,6 +2528,10 @@ static void ata_eh_finish(struct ata_port *ap)
                        }
                }
        }
+
+       /* make sure nr_active_links is zero after EH */
+       WARN_ON(ap->nr_active_links);
+       ap->nr_active_links = 0;
 }
 
 /**