summaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-pmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/libata-pmp.c')
-rw-r--r--drivers/ata/libata-pmp.c66
1 files changed, 60 insertions, 6 deletions
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
index 224faabd7b7e..3120596d4afc 100644
--- a/drivers/ata/libata-pmp.c
+++ b/drivers/ata/libata-pmp.c
@@ -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,
@@ -185,6 +186,27 @@ int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val)
}
/**
+ * 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
* @gscr: buffer to read GSCR block into
@@ -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;