aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-core.c
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2007-07-16 14:29:40 +0900
committerJeff Garzik <jeff@garzik.org>2007-07-20 08:19:05 -0400
commit008a78961ec72990d09d7625ef9499d7317d040d (patch)
treeaa356bbd961b7425017a3037fec509ffb761d332 /drivers/ata/libata-core.c
parentbadc2341579511a247f5993865aa68379e283c5c (diff)
downloadkernel_samsung_tuna-008a78961ec72990d09d7625ef9499d7317d040d.tar.gz
kernel_samsung_tuna-008a78961ec72990d09d7625ef9499d7317d040d.tar.bz2
kernel_samsung_tuna-008a78961ec72990d09d7625ef9499d7317d040d.zip
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: Tejun Heo <htejun@gmail.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r--drivers/ata/libata-core.c29
1 files changed, 22 insertions, 7 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 5718c247e23..c325b7a4246 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2389,21 +2389,35 @@ int sata_down_spd_limit(struct ata_port *ap)
u32 sstatus, spd, mask;
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);
- if (rc)
- return rc;
+ if (rc == 0)
+ spd = (sstatus >> 4) & 0xf;
+ else
+ spd = ap->sata_spd;
mask = ap->sata_spd_limit;
if (mask <= 1)
return -EINVAL;
+
+ /* unconditionally mask off the highest bit */
highbit = fls(mask) - 1;
mask &= ~(1 << highbit);
- spd = (sstatus >> 4) & 0xf;
- if (spd <= 1)
- return -EINVAL;
- spd--;
- mask &= (1 << spd) - 1;
+ /* Mask off all speeds higher than or equal to the current
+ * one. Force 1.5Gbps if current SPD is not available.
+ */
+ if (spd > 1)
+ mask &= (1 << (spd - 1)) - 1;
+ else
+ mask &= 1;
+
+ /* were we already at the bottom? */
if (!mask)
return -EINVAL;
@@ -5995,6 +6009,7 @@ void ata_dev_init(struct ata_device *dev)
/* SATA spd limit is bound to the first device */
ap->sata_spd_limit = ap->hw_sata_spd_limit;
+ ap->sata_spd = 0;
/* High bits of dev->flags are used to record warm plug
* requests which occur asynchronously. Synchronize using