Commit 6c8ea89c authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik

libata: implement LPM support for port multipliers

Port multipliers can do DIPM on fan-out links fine.  Implement support
for it.  Tested w/ SIMG 57xx and marvell PMPs.  Both the host and
fan-out links enter power save modes nicely.

SIMG 37xx and 47xx report link offline on SStatus causing EH to detach
the devices.  Blacklisted.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 6b7ae954
......@@ -3232,7 +3232,7 @@ static int ata_eh_maybe_retry_flush(struct ata_device *dev)
static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
struct ata_device **r_failed_dev)
{
struct ata_port *ap = link->ap;
struct ata_port *ap = ata_is_host_link(link) ? link->ap : NULL;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL;
unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM;
......@@ -3278,9 +3278,12 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
}
}
if (ap) {
rc = ap->ops->set_lpm(link, policy, hints);
if (!rc && ap->slave_link)
rc = ap->ops->set_lpm(ap->slave_link, policy, hints);
} else
rc = sata_pmp_set_lpm(link, policy, hints);
/*
* Attribute link config failure to the first (LPM) enabled
......@@ -3412,8 +3415,14 @@ static int ata_eh_schedule_probe(struct ata_device *dev)
ehc->saved_ncq_enabled &= ~(1 << dev->devno);
/* the link maybe in a deep sleep, wake it up */
if (link->lpm_policy > ATA_LPM_MAX_POWER)
link->ap->ops->set_lpm(link, ATA_LPM_MAX_POWER, ATA_LPM_EMPTY);
if (link->lpm_policy > ATA_LPM_MAX_POWER) {
if (ata_is_host_link(link))
link->ap->ops->set_lpm(link, ATA_LPM_MAX_POWER,
ATA_LPM_EMPTY);
else
sata_pmp_set_lpm(link, ATA_LPM_MAX_POWER,
ATA_LPM_EMPTY);
}
/* Record and count probe trials on the ering. The specific
* error mask used is irrelevant. Because a successful device
......
......@@ -185,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
......@@ -365,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.
*/
......@@ -380,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;
......@@ -392,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
......@@ -952,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;
......
......@@ -176,6 +176,8 @@ extern int ata_ering_map(struct ata_ering *ering,
#ifdef CONFIG_SATA_PMP
extern int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val);
extern int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val);
extern int sata_pmp_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
unsigned hints);
extern int sata_pmp_attach(struct ata_device *dev);
#else /* CONFIG_SATA_PMP */
static inline int sata_pmp_scr_read(struct ata_link *link, int reg, u32 *val)
......@@ -188,6 +190,12 @@ static inline int sata_pmp_scr_write(struct ata_link *link, int reg, u32 val)
return -EINVAL;
}
static inline int sata_pmp_set_lpm(struct ata_link *link,
enum ata_lpm_policy policy, unsigned hints)
{
return -EINVAL;
}
static inline int sata_pmp_attach(struct ata_device *dev)
{
return -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment