summaryrefslogtreecommitdiffstats
path: root/drivers/ata/libata-sff.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata/libata-sff.c')
-rw-r--r--drivers/ata/libata-sff.c161
1 files changed, 67 insertions, 94 deletions
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index e530baccc9c..6e8de3c1595 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -310,7 +310,7 @@ int ata_sff_busy_sleep(struct ata_port *ap,
/**
* ata_sff_wait_ready - sleep until BSY clears, or timeout
- * @ap: port containing status register to be polled
+ * @link: SFF link to wait ready status for
* @deadline: deadline jiffies for the operation
*
* Sleep until ATA Status register bit BSY clears, or timeout
@@ -322,26 +322,52 @@ int ata_sff_busy_sleep(struct ata_port *ap,
* RETURNS:
* 0 on success, -errno otherwise.
*/
-int ata_sff_wait_ready(struct ata_port *ap, unsigned long deadline)
+int ata_sff_wait_ready(struct ata_link *link, unsigned long deadline)
{
+ struct ata_port *ap = link->ap;
unsigned long start = jiffies;
+ unsigned long nodev_deadline = start + ATA_TMOUT_FF_WAIT;
int warned = 0;
+ if (time_after(nodev_deadline, deadline))
+ nodev_deadline = deadline;
+
while (1) {
u8 status = ap->ops->sff_check_status(ap);
unsigned long now = jiffies;
if (!(status & ATA_BUSY))
return 0;
- if (!ata_link_online(&ap->link) && status == 0xff)
- return -ENODEV;
+
+ /* No device status could be transient. Ignore it if
+ * link is online. Also, some SATA devices take a
+ * long time to clear 0xff after reset. For example,
+ * HHD424020F7SV00 iVDR needs >= 800ms while Quantum
+ * GoVault needs even more than that. Wait for
+ * ATA_TMOUT_FF_WAIT on -ENODEV if link isn't offline.
+ *
+ * Note that some PATA controllers (pata_ali) explode
+ * if status register is read more than once when
+ * there's no device attached.
+ */
+ if (status == 0xff) {
+ if (ata_link_online(link))
+ status = ATA_BUSY;
+ else if ((link->ap->flags & ATA_FLAG_SATA) &&
+ !ata_link_offline(link) &&
+ time_before(now, nodev_deadline))
+ status = ATA_BUSY;
+ if (status == 0xff)
+ return -ENODEV;
+ }
+
if (time_after(now, deadline))
return -EBUSY;
if (!warned && time_after(now, start + 5 * HZ) &&
(deadline - now > 3 * HZ)) {
- ata_port_printk(ap, KERN_WARNING,
- "port is slow to respond, please be patient "
+ ata_link_printk(link, KERN_WARNING,
+ "link is slow to respond, please be patient "
"(Status 0x%x)\n", status);
warned = 1;
}
@@ -1625,7 +1651,6 @@ void ata_sff_thaw(struct ata_port *ap)
*/
int ata_sff_prereset(struct ata_link *link, unsigned long deadline)
{
- struct ata_port *ap = link->ap;
struct ata_eh_context *ehc = &link->eh_context;
int rc;
@@ -1639,7 +1664,7 @@ int ata_sff_prereset(struct ata_link *link, unsigned long deadline)
/* wait for !BSY if we don't know that no device is attached */
if (!ata_link_offline(link)) {
- rc = ata_sff_wait_ready(ap, deadline);
+ rc = ata_sff_wait_ready(link, deadline);
if (rc && rc != -ENODEV) {
ata_link_printk(link, KERN_WARNING, "device not ready "
"(errno=%d), forcing hardreset\n", rc);
@@ -1762,25 +1787,41 @@ unsigned int ata_sff_dev_classify(struct ata_device *dev, int present,
return class;
}
-static int ata_bus_post_reset(struct ata_port *ap, unsigned int devmask,
- unsigned long deadline)
+/**
+ * ata_sff_wait_after_reset - wait for devices to become ready after reset
+ * @link: SFF link which is just reset
+ * @devmask: mask of present devices
+ * @deadline: deadline jiffies for the operation
+ *
+ * Wait devices attached to SFF @link to become ready after
+ * reset. It contains preceding 150ms wait to avoid accessing TF
+ * status register too early.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * 0 on success, -ENODEV if some or all of devices in @devmask
+ * don't seem to exist. -errno on other errors.
+ */
+int ata_sff_wait_after_reset(struct ata_link *link, unsigned int devmask,
+ unsigned long deadline)
{
+ struct ata_port *ap = link->ap;
struct ata_ioports *ioaddr = &ap->ioaddr;
unsigned int dev0 = devmask & (1 << 0);
unsigned int dev1 = devmask & (1 << 1);
int rc, ret = 0;
- /* if device 0 was found in ata_devchk, wait for its
- * BSY bit to clear
+ msleep(ATA_WAIT_AFTER_RESET_MSECS);
+
+ /* always check readiness of the master device */
+ rc = ata_sff_wait_ready(link, deadline);
+ /* -ENODEV means the odd clown forgot the D7 pulldown resistor
+ * and TF status is 0xff, bail out on it too.
*/
- if (dev0) {
- rc = ata_sff_wait_ready(ap, deadline);
- if (rc) {
- if (rc != -ENODEV)
- return rc;
- ret = rc;
- }
- }
+ if (rc)
+ return rc;
/* if device 1 was found in ata_devchk, wait for register
* access briefly, then wait for BSY to clear.
@@ -1804,7 +1845,7 @@ static int ata_bus_post_reset(struct ata_port *ap, unsigned int devmask,
msleep(50); /* give drive a breather */
}
- rc = ata_sff_wait_ready(ap, deadline);
+ rc = ata_sff_wait_ready(link, deadline);
if (rc) {
if (rc != -ENODEV)
return rc;
@@ -1822,61 +1863,6 @@ static int ata_bus_post_reset(struct ata_port *ap, unsigned int devmask,
return ret;
}
-/**
- * ata_sff_wait_after_reset - wait before checking status after reset
- * @ap: port containing status register to be polled
- * @deadline: deadline jiffies for the operation
- *
- * After reset, we need to pause a while before reading status.
- * Also, certain combination of controller and device report 0xff
- * for some duration (e.g. until SATA PHY is up and running)
- * which is interpreted as empty port in ATA world. This
- * function also waits for such devices to get out of 0xff
- * status.
- *
- * LOCKING:
- * Kernel thread context (may sleep).
- */
-void ata_sff_wait_after_reset(struct ata_port *ap, unsigned long deadline)
-{
- unsigned long until = jiffies + ATA_TMOUT_FF_WAIT;
-
- if (time_before(until, deadline))
- deadline = until;
-
- /* Spec mandates ">= 2ms" before checking status. We wait
- * 150ms, because that was the magic delay used for ATAPI
- * devices in Hale Landis's ATADRVR, for the period of time
- * between when the ATA command register is written, and then
- * status is checked. Because waiting for "a while" before
- * checking status is fine, post SRST, we perform this magic
- * delay here as well.
- *
- * Old drivers/ide uses the 2mS rule and then waits for ready.
- */
- msleep(150);
-
- /* Wait for 0xff to clear. Some SATA devices take a long time
- * to clear 0xff after reset. For example, HHD424020F7SV00
- * iVDR needs >= 800ms while. Quantum GoVault needs even more
- * than that.
- *
- * Note that some PATA controllers (pata_ali) explode if
- * status register is read more than once when there's no
- * device attached.
- */
- if (ap->flags & ATA_FLAG_SATA) {
- while (1) {
- u8 status = ap->ops->sff_check_status(ap);
-
- if (status != 0xff || time_after(jiffies, deadline))
- return;
-
- msleep(50);
- }
- }
-}
-
static int ata_bus_softreset(struct ata_port *ap, unsigned int devmask,
unsigned long deadline)
{
@@ -1891,17 +1877,8 @@ static int ata_bus_softreset(struct ata_port *ap, unsigned int devmask,
udelay(20); /* FIXME: flush */
iowrite8(ap->ctl, ioaddr->ctl_addr);
- /* wait a while before checking status */
- ata_sff_wait_after_reset(ap, deadline);
-
- /* Before we perform post reset processing we want to see if
- * the bus shows 0xFF because the odd clown forgets the D7
- * pulldown resistor.
- */
- if (ap->ops->sff_check_status(ap) == 0xFF)
- return -ENODEV;
-
- return ata_bus_post_reset(ap, devmask, deadline);
+ /* wait the port to become ready */
+ return ata_sff_wait_after_reset(&ap->link, devmask, deadline);
}
/**
@@ -2003,20 +1980,18 @@ int sata_sff_hardreset(struct ata_link *link, unsigned int *class,
return 0;
}
- /* wait a while before checking status */
- ata_sff_wait_after_reset(ap, deadline);
-
/* If PMP is supported, we have to do follow-up SRST. Note
* that some PMPs don't send D2H Reg FIS after hardreset at
* all if the first port is empty. Wait for it just for a
* second and request follow-up SRST.
*/
if (ap->flags & ATA_FLAG_PMP) {
- ata_sff_wait_ready(ap, jiffies + HZ);
+ ata_sff_wait_after_reset(link, 1, jiffies + HZ);
return -EAGAIN;
}
- rc = ata_sff_wait_ready(ap, deadline);
+ /* wait for the link to become online */
+ rc = ata_sff_wait_after_reset(link, 1, deadline);
/* link occupied, -ENODEV too is an error */
if (rc) {
ata_link_printk(link, KERN_ERR,
@@ -2024,8 +1999,6 @@ int sata_sff_hardreset(struct ata_link *link, unsigned int *class,
return rc;
}
- ap->ops->sff_dev_select(ap, 0); /* probably unnecessary */
-
*class = ata_sff_dev_classify(link->device, 1, NULL);
DPRINTK("EXIT, class=%u\n", *class);