]> err.no Git - linux-2.6/commitdiff
libata: improve SATA PHY speed down logic
authorTejun Heo <htejun@gmail.com>
Mon, 16 Jul 2007 05:29:40 +0000 (14:29 +0900)
committerJeff Garzik <jeff@garzik.org>
Fri, 20 Jul 2007 12:19:05 +0000 (08:19 -0400)
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>
drivers/ata/libata-core.c
drivers/ata/libata-eh.c
include/linux/libata.h

index 5718c247e23ad2ad33d4d925b406efda7345f94b..c325b7a4246a1e49929aac0cc439179827112a8e 100644 (file)
@@ -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
index 19f9947bd96b448fa85e315bdb9852b77dda265b..183eaf466d4fb222a168ea5b99ecce8f81c79f9b 100644 (file)
@@ -1799,12 +1799,18 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
        }
 
        if (rc == 0) {
+               u32 sstatus;
+
                /* After the reset, the device state is PIO 0 and the
                 * controller state is undefined.  Record the mode.
                 */
                for (i = 0; i < ATA_MAX_DEVICES; i++)
                        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)
                        postreset(ap, classes);
 
index c732b3e78e28f98fa0c54f79dfa9963f246e3fc8..16ebdf152c75c19e651b2f31e5398a02e3976dee 100644 (file)
@@ -531,6 +531,7 @@ struct ata_port {
        unsigned int            cbl;    /* cable type; ATA_CBL_xxx */
        unsigned int            hw_sata_spd_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 */
        struct ata_eh_info      eh_info;