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: Tejun Heo <tj@kernel.org> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
This commit is contained in:
parent
6b7ae9545a
commit
6c8ea89cec
3 changed files with 67 additions and 10 deletions
|
@ -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,
|
|||
}
|
||||
}
|
||||
|
||||
rc = ap->ops->set_lpm(link, policy, hints);
|
||||
if (!rc && ap->slave_link)
|
||||
rc = ap->ops->set_lpm(ap->slave_link, policy, hints);
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue