From 2a0c61d401732a2c21b0dc7fe6d1aeec79da8c05 Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Mon, 29 Oct 2012 05:24:01 +0000 Subject: [PATCH] ahci: Support spin-up and link-up separately Add HDD handling to the SSD-only AHCI driver, by separately dealing with spin-up and link-up. Signed-off-by: Marc Jones Signed-off-by: Simon Glass --- drivers/block/ahci.c | 45 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index 719574f220..19c5f137f8 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -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"); -- 2.39.5