ahci: Support spin-up and link-up separately
authorMarc Jones <marc.jones@chromium.org>
Mon, 29 Oct 2012 05:24:01 +0000 (05:24 +0000)
committerTom Rini <trini@ti.com>
Fri, 2 Nov 2012 22:20:43 +0000 (15:20 -0700)
Add HDD handling to the SSD-only AHCI driver, by separately dealing with
spin-up and link-up.

Signed-off-by: Marc Jones <marc.jones@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
drivers/block/ahci.c

index 719574f220b93067184741a5bf1a130591af27f7..19c5f137f84bafbfd54feda470f7cfa8535c0e0a 100644 (file)
@@ -53,6 +53,7 @@ hd_driveid_t *ataid[AHCI_MAX_PORTS];
 #endif
 
 /* Maximum timeouts for each event */
+#define WAIT_MS_SPINUP 10000
 #define WAIT_MS_DATAIO 5000
 #define WAIT_MS_LINKUP 4
 
@@ -129,7 +130,7 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
        unsigned short vendor;
 #endif
        volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base;
-       u32 tmp, cap_save;
+       u32 tmp, cap_save, cmd;
        int i, j;
        volatile u8 *port_mmio;
 
@@ -137,7 +138,7 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
 
        cap_save = readl(mmio + HOST_CAP);
        cap_save &= ((1 << 28) | (1 << 17));
-       cap_save |= (1 << 27);
+       cap_save |= (1 << 27);  /* Staggered Spin-up. Not needed. */
 
        /* global controller reset */
        tmp = readl(mmio + HOST_CTL);
@@ -201,9 +202,18 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
                        msleep(500);
                }
 
-               debug("Spinning up port %d... ", i);
-               writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
-
+               /* Add the spinup command to whatever mode bits may
+                * already be on in the command register.
+                */
+               cmd = readl(port_mmio + PORT_CMD);
+               cmd |= PORT_CMD_FIS_RX;
+               cmd |= PORT_CMD_SPIN_UP;
+               writel_with_flush(cmd, port_mmio + PORT_CMD);
+
+               /* Bring up SATA link.
+                * SATA link bringup time is usually less than 1 ms; only very
+                * rarely has it taken between 1-2 ms. Never seen it above 2 ms.
+                */
                j = 0;
                while (j < WAIT_MS_LINKUP) {
                        tmp = readl(port_mmio + PORT_SCR_STAT);
@@ -212,7 +222,30 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
                        udelay(1000);
                        j++;
                }
-               if (j == WAIT_MS_LINKUP)
+               if (j == WAIT_MS_LINKUP) {
+                       printf("SATA link %d timeout.\n", i);
+                       continue;
+               } else {
+                       debug("SATA link ok.\n");
+               }
+
+               /* Clear error status */
+               tmp = readl(port_mmio + PORT_SCR_ERR);
+               if (tmp)
+                       writel(tmp, port_mmio + PORT_SCR_ERR);
+
+               debug("Spinning up device on SATA port %d... ", i);
+
+               j = 0;
+               while (j < WAIT_MS_SPINUP) {
+                       tmp = readl(port_mmio + PORT_TFDATA);
+                       if (!(tmp & (ATA_STAT_BUSY | ATA_STAT_DRQ)))
+                               break;
+                       udelay(1000);
+                       j++;
+               }
+               printf("Target spinup took %d ms.\n", j);
+               if (j == WAIT_MS_SPINUP)
                        debug("timeout.\n");
                else
                        debug("ok.\n");