ahci: add "em_buffer" attribute for AHCI hosts
[safe/jmp/linux-2.6] / drivers / ata / libata-pmp.c
index 04a486a..224faab 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/kernel.h>
 #include <linux/libata.h>
+#include <linux/slab.h>
 #include "libata.h"
 
 const struct ata_port_operations sata_pmp_port_ops = {
@@ -221,6 +222,8 @@ static const char *sata_pmp_spec_rev_str(const u32 *gscr)
 {
        u32 rev = gscr[SATA_PMP_GSCR_REV];
 
+       if (rev & (1 << 3))
+               return "1.2";
        if (rev & (1 << 2))
                return "1.1";
        if (rev & (1 << 1))
@@ -228,10 +231,14 @@ static const char *sata_pmp_spec_rev_str(const u32 *gscr)
        return "<unknown>";
 }
 
+#define PMP_GSCR_SII_POL 129
+
 static int sata_pmp_configure(struct ata_device *dev, int print_info)
 {
        struct ata_port *ap = dev->link->ap;
        u32 *gscr = dev->gscr;
+       u16 vendor = sata_pmp_gscr_vendor(gscr);
+       u16 devid = sata_pmp_gscr_devid(gscr);
        unsigned int err_mask = 0;
        const char *reason;
        int nr_ports, rc;
@@ -257,15 +264,26 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info)
                goto fail;
        }
 
-       /* turn off notification till fan-out ports are reset and configured */
-       if (gscr[SATA_PMP_GSCR_FEAT_EN] & SATA_PMP_FEAT_NOTIFY) {
-               gscr[SATA_PMP_GSCR_FEAT_EN] &= ~SATA_PMP_FEAT_NOTIFY;
+       /* Disable sending Early R_OK.
+        * With "cached read" HDD testing and multiple ports busy on a SATA
+        * host controller, 3726 PMP will very rarely drop a deferred
+        * R_OK that was intended for the host. Symptom will be all
+        * 5 drives under test will timeout, get reset, and recover.
+        */
+       if (vendor == 0x1095 && devid == 0x3726) {
+               u32 reg;
 
-               err_mask = sata_pmp_write(dev->link, SATA_PMP_GSCR_FEAT_EN,
-                                         gscr[SATA_PMP_GSCR_FEAT_EN]);
+               err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, &reg);
                if (err_mask) {
                        rc = -EIO;
-                       reason = "failed to write GSCR_FEAT_EN";
+                       reason = "failed to read Sil3726 Private Register";
+                       goto fail;
+               }
+               reg &= ~0x1;
+               err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg);
+               if (err_mask) {
+                       rc = -EIO;
+                       reason = "failed to write Sil3726 Private Register";
                        goto fail;
                }
        }
@@ -273,9 +291,7 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info)
        if (print_info) {
                ata_dev_printk(dev, KERN_INFO, "Port Multiplier %s, "
                               "0x%04x:0x%04x r%d, %d ports, feat 0x%x/0x%x\n",
-                              sata_pmp_spec_rev_str(gscr),
-                              sata_pmp_gscr_vendor(gscr),
-                              sata_pmp_gscr_devid(gscr),
+                              sata_pmp_spec_rev_str(gscr), vendor, devid,
                               sata_pmp_gscr_rev(gscr),
                               nr_ports, gscr[SATA_PMP_GSCR_FEAT_EN],
                               gscr[SATA_PMP_GSCR_FEAT]);
@@ -334,10 +350,13 @@ static void sata_pmp_quirks(struct ata_port *ap)
 
        if (vendor == 0x1095 && devid == 0x3726) {
                /* sil3726 quirks */
-               ata_port_for_each_link(link, ap) {
-                       /* class code report is unreliable */
+               ata_for_each_link(link, ap, EDGE) {
+                       /* Class code report is unreliable and SRST
+                        * times out under certain configurations.
+                        */
                        if (link->pmp < 5)
-                               link->flags |= ATA_LFLAG_ASSUME_ATA;
+                               link->flags |= ATA_LFLAG_NO_SRST |
+                                              ATA_LFLAG_ASSUME_ATA;
 
                        /* port 5 is for SEMB device and it doesn't like SRST */
                        if (link->pmp == 5)
@@ -346,7 +365,7 @@ static void sata_pmp_quirks(struct ata_port *ap)
                }
        } else if (vendor == 0x1095 && devid == 0x4723) {
                /* sil4723 quirks */
-               ata_port_for_each_link(link, ap) {
+               ata_for_each_link(link, ap, EDGE) {
                        /* class code report is unreliable */
                        if (link->pmp < 2)
                                link->flags |= ATA_LFLAG_ASSUME_ATA;
@@ -358,7 +377,7 @@ static void sata_pmp_quirks(struct ata_port *ap)
                }
        } else if (vendor == 0x1095 && devid == 0x4726) {
                /* sil4726 quirks */
-               ata_port_for_each_link(link, ap) {
+               ata_for_each_link(link, ap, EDGE) {
                        /* Class code report is unreliable and SRST
                         * times out under certain configurations.
                         * Config device can be at port 0 or 5 and
@@ -460,7 +479,7 @@ int sata_pmp_attach(struct ata_device *dev)
        if (ap->ops->pmp_attach)
                ap->ops->pmp_attach(ap);
 
-       ata_port_for_each_link(tlink, ap)
+       ata_for_each_link(tlink, ap, EDGE)
                sata_link_init_spd(tlink);
 
        ata_acpi_associate_sata_port(ap);
@@ -497,7 +516,7 @@ static void sata_pmp_detach(struct ata_device *dev)
        if (ap->ops->pmp_detach)
                ap->ops->pmp_detach(ap);
 
-       ata_port_for_each_link(tlink, ap)
+       ata_for_each_link(tlink, ap, EDGE)
                ata_eh_detach_dev(tlink->device);
 
        spin_lock_irqsave(ap->lock, flags);
@@ -710,7 +729,7 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
                }
 
                /* PMP is reset, SErrors cannot be trusted, scan all */
-               ata_port_for_each_link(tlink, ap) {
+               ata_for_each_link(tlink, ap, EDGE) {
                        struct ata_eh_context *ehc = &tlink->eh_context;
 
                        ehc->i.probe_mask |= ATA_ALL_DEVICES;
@@ -737,19 +756,12 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
                }
 
                if (tries) {
-                       int sleep = ehc->i.flags & ATA_EHI_DID_RESET;
-
                        /* consecutive revalidation failures? speed down */
                        if (reval_failed)
-                               sata_down_spd_limit(link);
+                               sata_down_spd_limit(link, 0);
                        else
                                reval_failed = 1;
 
-                       ata_dev_printk(dev, KERN_WARNING,
-                                      "retrying reset%s\n",
-                                      sleep ? " in 5 secs" : "");
-                       if (sleep)
-                               ssleep(5);
                        ehc->i.action |= ATA_EH_RESET;
                        goto retry;
                } else {
@@ -785,7 +797,7 @@ static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap)
 
        spin_lock_irqsave(ap->lock, flags);
 
-       ata_port_for_each_link(link, ap) {
+       ata_for_each_link(link, ap, EDGE) {
                if (!(link->flags & ATA_LFLAG_DISABLED))
                        continue;
 
@@ -795,7 +807,8 @@ static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap)
                 * SError.N working.
                 */
                sata_link_hardreset(link, sata_deb_timing_normal,
-                               jiffies + ATA_TMOUT_INTERNAL_QUICK, NULL, NULL);
+                               ata_deadline(jiffies, ATA_TMOUT_INTERNAL_QUICK),
+                               NULL, NULL);
 
                /* unconditionally clear SError.N */
                rc = sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG);
@@ -860,6 +873,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
        struct ata_link *pmp_link = &ap->link;
        struct ata_device *pmp_dev = pmp_link->device;
        struct ata_eh_context *pmp_ehc = &pmp_link->eh_context;
+       u32 *gscr = pmp_dev->gscr;
        struct ata_link *link;
        struct ata_device *dev;
        unsigned int err_mask;
@@ -867,7 +881,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
        int cnt, rc;
 
        pmp_tries = ATA_EH_PMP_TRIES;
-       ata_port_for_each_link(link, ap)
+       ata_for_each_link(link, ap, EDGE)
                link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;
 
  retry:
@@ -876,7 +890,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
                rc = ata_eh_recover(ap, ops->prereset, ops->softreset,
                                    ops->hardreset, ops->postreset, NULL);
                if (rc) {
-                       ata_link_for_each_dev(dev, &ap->link)
+                       ata_for_each_dev(dev, &ap->link, ALL)
                                ata_dev_disable(dev);
                        return rc;
                }
@@ -885,7 +899,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
                        return 0;
 
                /* new PMP online */
-               ata_port_for_each_link(link, ap)
+               ata_for_each_link(link, ap, EDGE)
                        link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;
 
                /* fall through */
@@ -897,6 +911,22 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
        if (rc)
                goto pmp_fail;
 
+       /* PHY event notification can disturb reset and other recovery
+        * operations.  Turn it off.
+        */
+       if (gscr[SATA_PMP_GSCR_FEAT_EN] & SATA_PMP_FEAT_NOTIFY) {
+               gscr[SATA_PMP_GSCR_FEAT_EN] &= ~SATA_PMP_FEAT_NOTIFY;
+
+               err_mask = sata_pmp_write(pmp_link, SATA_PMP_GSCR_FEAT_EN,
+                                         gscr[SATA_PMP_GSCR_FEAT_EN]);
+               if (err_mask) {
+                       ata_link_printk(pmp_link, KERN_WARNING,
+                               "failed to disable NOTIFY (err_mask=0x%x)\n",
+                               err_mask);
+                       goto pmp_fail;
+               }
+       }
+
        /* handle disabled links */
        rc = sata_pmp_eh_handle_disabled_links(ap);
        if (rc)
@@ -919,10 +949,10 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
 
        /* enable notification */
        if (pmp_dev->flags & ATA_DFLAG_AN) {
-               pmp_dev->gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY;
+               gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY;
 
-               err_mask = sata_pmp_write(pmp_dev->link, SATA_PMP_GSCR_FEAT_EN,
-                                         pmp_dev->gscr[SATA_PMP_GSCR_FEAT_EN]);
+               err_mask = sata_pmp_write(pmp_link, SATA_PMP_GSCR_FEAT_EN,
+                                         gscr[SATA_PMP_GSCR_FEAT_EN]);
                if (err_mask) {
                        ata_dev_printk(pmp_dev, KERN_ERR, "failed to write "
                                       "PMP_FEAT_EN (Emask=0x%x)\n", err_mask);
@@ -941,7 +971,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
        }
 
        cnt = 0;
-       ata_port_for_each_link(link, ap) {
+       ata_for_each_link(link, ap, EDGE) {
                if (!(gscr_error & (1 << link->pmp)))
                        continue;
 
@@ -983,10 +1013,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
                goto retry;
 
        if (--pmp_tries) {
-               ata_port_printk(ap, KERN_WARNING,
-                               "failed to recover PMP, retrying in 5 secs\n");
                pmp_ehc->i.action |= ATA_EH_RESET;
-               ssleep(5);
                goto retry;
        }