]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/ata/libata-pmp.c
Merge tag 'v2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / ata / libata-pmp.c
index 224faabd7b7efd1adf769ded4f0d4989604a012c..3120596d4afc9aaba293f100f7a075ace6438f9e 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/libata.h>
 #include <linux/slab.h>
 #include "libata.h"
+#include "libata-transport.h"
 
 const struct ata_port_operations sata_pmp_port_ops = {
        .inherits               = &sata_port_ops,
@@ -184,6 +185,27 @@ int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val)
        return 0;
 }
 
+/**
+ *     sata_pmp_set_lpm - configure LPM for a PMP link
+ *     @link: PMP link to configure LPM for
+ *     @policy: target LPM policy
+ *     @hints: LPM hints
+ *
+ *     Configure LPM for @link.  This function will contain any PMP
+ *     specific workarounds if necessary.
+ *
+ *     LOCKING:
+ *     EH context.
+ *
+ *     RETURNS:
+ *     0 on success, -errno on failure.
+ */
+int sata_pmp_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
+                    unsigned hints)
+{
+       return sata_link_scr_lpm(link, policy, true);
+}
+
 /**
  *     sata_pmp_read_gscr - read GSCR block of SATA PMP
  *     @dev: PMP device
@@ -312,10 +334,10 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info)
        return rc;
 }
 
-static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
+static int sata_pmp_init_links (struct ata_port *ap, int nr_ports)
 {
        struct ata_link *pmp_link = ap->pmp_link;
-       int i;
+       int i, err;
 
        if (!pmp_link) {
                pmp_link = kzalloc(sizeof(pmp_link[0]) * SATA_PMP_MAX_PORTS,
@@ -327,6 +349,13 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
                        ata_link_init(ap, &pmp_link[i], i);
 
                ap->pmp_link = pmp_link;
+
+               for (i = 0; i < SATA_PMP_MAX_PORTS; i++) {
+                       err = ata_tlink_add(&pmp_link[i]);
+                       if (err) {
+                               goto err_tlink;
+                       }
+               }
        }
 
        for (i = 0; i < nr_ports; i++) {
@@ -339,6 +368,12 @@ static int sata_pmp_init_links(struct ata_port *ap, int nr_ports)
        }
 
        return 0;
+  err_tlink:
+       while (--i >= 0)
+               ata_tlink_delete(&pmp_link[i]);
+       kfree(pmp_link);
+       ap->pmp_link = NULL;
+       return err;
 }
 
 static void sata_pmp_quirks(struct ata_port *ap)
@@ -351,6 +386,9 @@ static void sata_pmp_quirks(struct ata_port *ap)
        if (vendor == 0x1095 && devid == 0x3726) {
                /* sil3726 quirks */
                ata_for_each_link(link, ap, EDGE) {
+                       /* link reports offline after LPM */
+                       link->flags |= ATA_LFLAG_NO_LPM;
+
                        /* Class code report is unreliable and SRST
                         * times out under certain configurations.
                         */
@@ -366,6 +404,9 @@ static void sata_pmp_quirks(struct ata_port *ap)
        } else if (vendor == 0x1095 && devid == 0x4723) {
                /* sil4723 quirks */
                ata_for_each_link(link, ap, EDGE) {
+                       /* link reports offline after LPM */
+                       link->flags |= ATA_LFLAG_NO_LPM;
+
                        /* class code report is unreliable */
                        if (link->pmp < 2)
                                link->flags |= ATA_LFLAG_ASSUME_ATA;
@@ -378,6 +419,9 @@ static void sata_pmp_quirks(struct ata_port *ap)
        } else if (vendor == 0x1095 && devid == 0x4726) {
                /* sil4726 quirks */
                ata_for_each_link(link, ap, EDGE) {
+                       /* link reports offline after LPM */
+                       link->flags |= ATA_LFLAG_NO_LPM;
+
                        /* Class code report is unreliable and SRST
                         * times out under certain configurations.
                         * Config device can be at port 0 or 5 and
@@ -938,15 +982,25 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
        if (rc)
                goto link_fail;
 
-       /* Connection status might have changed while resetting other
-        * links, check SATA_PMP_GSCR_ERROR before returning.
-        */
-
        /* clear SNotification */
        rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);
        if (rc == 0)
                sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);
 
+       /*
+        * If LPM is active on any fan-out port, hotplug wouldn't
+        * work.  Return w/ PHY event notification disabled.
+        */
+       ata_for_each_link(link, ap, EDGE)
+               if (link->lpm_policy > ATA_LPM_MAX_POWER)
+                       return 0;
+
+       /*
+        * Connection status might have changed while resetting other
+        * links, enable notification and check SATA_PMP_GSCR_ERROR
+        * before returning.
+        */
+
        /* enable notification */
        if (pmp_dev->flags & ATA_DFLAG_AN) {
                gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY;