Commit 008a7896 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik

libata: improve SATA PHY speed down logic

sata_down_spd_limit() first reads the current SPD from SStatus and
limit the speed to the lower one of one below the current limit or one
below the current SPD in SStatus.  SPD may not be accessible or valid
when SPD down is requested making sata_down_spd_limit() fail when it's
most needed.

This patch makes the current SPD cached after each successful reset
and forces GEN I speed (1.5Gbps) if neither of SStatus or the cached
value is valid, so sata_down_spd_limit() is now guaranteed to lower
the speed limit if lower speed is available.
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent badc2341
...@@ -2389,21 +2389,35 @@ int sata_down_spd_limit(struct ata_port *ap) ...@@ -2389,21 +2389,35 @@ int sata_down_spd_limit(struct ata_port *ap)
u32 sstatus, spd, mask; u32 sstatus, spd, mask;
int rc, highbit; int rc, highbit;
if (!sata_scr_valid(ap))
return -EOPNOTSUPP;
/* If SCR can be read, use it to determine the current SPD.
* If not, use cached value in ap->sata_spd.
*/
rc = sata_scr_read(ap, SCR_STATUS, &sstatus); rc = sata_scr_read(ap, SCR_STATUS, &sstatus);
if (rc) if (rc == 0)
return rc; spd = (sstatus >> 4) & 0xf;
else
spd = ap->sata_spd;
mask = ap->sata_spd_limit; mask = ap->sata_spd_limit;
if (mask <= 1) if (mask <= 1)
return -EINVAL; return -EINVAL;
/* unconditionally mask off the highest bit */
highbit = fls(mask) - 1; highbit = fls(mask) - 1;
mask &= ~(1 << highbit); mask &= ~(1 << highbit);
spd = (sstatus >> 4) & 0xf; /* Mask off all speeds higher than or equal to the current
if (spd <= 1) * one. Force 1.5Gbps if current SPD is not available.
return -EINVAL; */
spd--; if (spd > 1)
mask &= (1 << spd) - 1; mask &= (1 << (spd - 1)) - 1;
else
mask &= 1;
/* were we already at the bottom? */
if (!mask) if (!mask)
return -EINVAL; return -EINVAL;
...@@ -5995,6 +6009,7 @@ void ata_dev_init(struct ata_device *dev) ...@@ -5995,6 +6009,7 @@ void ata_dev_init(struct ata_device *dev)
/* SATA spd limit is bound to the first device */ /* SATA spd limit is bound to the first device */
ap->sata_spd_limit = ap->hw_sata_spd_limit; ap->sata_spd_limit = ap->hw_sata_spd_limit;
ap->sata_spd = 0;
/* High bits of dev->flags are used to record warm plug /* High bits of dev->flags are used to record warm plug
* requests which occur asynchronously. Synchronize using * requests which occur asynchronously. Synchronize using
......
...@@ -1799,12 +1799,18 @@ static int ata_eh_reset(struct ata_port *ap, int classify, ...@@ -1799,12 +1799,18 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
} }
if (rc == 0) { if (rc == 0) {
u32 sstatus;
/* After the reset, the device state is PIO 0 and the /* After the reset, the device state is PIO 0 and the
* controller state is undefined. Record the mode. * controller state is undefined. Record the mode.
*/ */
for (i = 0; i < ATA_MAX_DEVICES; i++) for (i = 0; i < ATA_MAX_DEVICES; i++)
ap->device[i].pio_mode = XFER_PIO_0; ap->device[i].pio_mode = XFER_PIO_0;
/* record current link speed */
if (sata_scr_read(ap, SCR_STATUS, &sstatus) == 0)
ap->sata_spd = (sstatus >> 4) & 0xf;
if (postreset) if (postreset)
postreset(ap, classes); postreset(ap, classes);
......
...@@ -531,6 +531,7 @@ struct ata_port { ...@@ -531,6 +531,7 @@ struct ata_port {
unsigned int cbl; /* cable type; ATA_CBL_xxx */ unsigned int cbl; /* cable type; ATA_CBL_xxx */
unsigned int hw_sata_spd_limit; unsigned int hw_sata_spd_limit;
unsigned int sata_spd_limit; /* SATA PHY speed limit */ unsigned int sata_spd_limit; /* SATA PHY speed limit */
unsigned int sata_spd; /* current SATA PHY speed */
/* record runtime error info, protected by host lock */ /* record runtime error info, protected by host lock */
struct ata_eh_info eh_info; struct ata_eh_info eh_info;
......
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