libata: add deadline support to prereset and reset methods
[powerpc.git] / drivers / ata / libata-core.c
index ca67484..84e9448 100644 (file)
@@ -2979,23 +2979,68 @@ int ata_busy_sleep(struct ata_port *ap,
        return 0;
 }
 
-static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask)
+/**
+ *     ata_wait_ready - sleep until BSY clears, or timeout
+ *     @ap: port containing status register to be polled
+ *     @deadline: deadline jiffies for the operation
+ *
+ *     Sleep until ATA Status register bit BSY clears, or timeout
+ *     occurs.
+ *
+ *     LOCKING:
+ *     Kernel thread context (may sleep).
+ *
+ *     RETURNS:
+ *     0 on success, -errno otherwise.
+ */
+int ata_wait_ready(struct ata_port *ap, unsigned long deadline)
+{
+       unsigned long start = jiffies;
+       int warned = 0;
+
+       while (1) {
+               u8 status = ata_chk_status(ap);
+               unsigned long now = jiffies;
+
+               if (!(status & ATA_BUSY))
+                       return 0;
+               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 "
+                               "(Status 0x%x)\n", status);
+                       warned = 1;
+               }
+
+               msleep(50);
+       }
+}
+
+static int ata_bus_post_reset(struct ata_port *ap, unsigned int devmask,
+                             unsigned long deadline)
 {
        struct ata_ioports *ioaddr = &ap->ioaddr;
        unsigned int dev0 = devmask & (1 << 0);
        unsigned int dev1 = devmask & (1 << 1);
-       unsigned long timeout;
+       int rc;
 
        /* if device 0 was found in ata_devchk, wait for its
         * BSY bit to clear
         */
-       if (dev0)
-               ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+       if (dev0) {
+               rc = ata_wait_ready(ap, deadline);
+               if (rc && rc != -ENODEV)
+                       return rc;
+       }
 
        /* if device 1 was found in ata_devchk, wait for
         * register access, then wait for BSY to clear
         */
-       timeout = jiffies + ATA_TMOUT_BOOT;
        while (dev1) {
                u8 nsect, lbal;
 
@@ -3004,14 +3049,15 @@ static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask)
                lbal = ioread8(ioaddr->lbal_addr);
                if ((nsect == 1) && (lbal == 1))
                        break;
-               if (time_after(jiffies, timeout)) {
-                       dev1 = 0;
-                       break;
-               }
+               if (time_after(jiffies, deadline))
+                       return -EBUSY;
                msleep(50);     /* give drive a breather */
        }
-       if (dev1)
-               ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+       if (dev1) {
+               rc = ata_wait_ready(ap, deadline);
+               if (rc && rc != -ENODEV)
+                       return rc;
+       }
 
        /* is all this really necessary? */
        ap->ops->dev_select(ap, 0);
@@ -3019,10 +3065,12 @@ static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask)
                ap->ops->dev_select(ap, 1);
        if (dev0)
                ap->ops->dev_select(ap, 0);
+
+       return 0;
 }
 
-static unsigned int ata_bus_softreset(struct ata_port *ap,
-                                     unsigned int devmask)
+static int ata_bus_softreset(struct ata_port *ap, unsigned int devmask,
+                            unsigned long deadline)
 {
        struct ata_ioports *ioaddr = &ap->ioaddr;
 
@@ -3054,9 +3102,7 @@ static unsigned int ata_bus_softreset(struct ata_port *ap,
        if (ata_check_status(ap) == 0xFF)
                return 0;
 
-       ata_bus_post_reset(ap, devmask);
-
-       return 0;
+       return ata_bus_post_reset(ap, devmask, deadline);
 }
 
 /**
@@ -3107,7 +3153,7 @@ void ata_bus_reset(struct ata_port *ap)
 
        /* issue bus reset */
        if (ap->flags & ATA_FLAG_SRST)
-               if (ata_bus_softreset(ap, devmask))
+               if (ata_bus_softreset(ap, devmask, jiffies + 40 * HZ))
                        goto err_out;
 
        /*
@@ -3150,29 +3196,37 @@ err_out:
  *     sata_phy_debounce - debounce SATA phy status
  *     @ap: ATA port to debounce SATA phy status for
  *     @params: timing parameters { interval, duratinon, timeout } in msec
+ *     @deadline: deadline jiffies for the operation
  *
  *     Make sure SStatus of @ap reaches stable state, determined by
  *     holding the same value where DET is not 1 for @duration polled
  *     every @interval, before @timeout.  Timeout constraints the
- *     beginning of the stable state.  Because, after hot unplugging,
- *     DET gets stuck at 1 on some controllers, this functions waits
+ *     beginning of the stable state.  Because DET gets stuck at 1 on
+ *     some controllers after hot unplugging, this functions waits
  *     until timeout then returns 0 if DET is stable at 1.
  *
+ *     @timeout is further limited by @deadline.  The sooner of the
+ *     two is used.
+ *
  *     LOCKING:
  *     Kernel thread context (may sleep)
  *
  *     RETURNS:
  *     0 on success, -errno on failure.
  */
-int sata_phy_debounce(struct ata_port *ap, const unsigned long *params)
+int sata_phy_debounce(struct ata_port *ap, const unsigned long *params,
+                     unsigned long deadline)
 {
        unsigned long interval_msec = params[0];
-       unsigned long duration = params[1] * HZ / 1000;
-       unsigned long timeout = jiffies + params[2] * HZ / 1000;
-       unsigned long last_jiffies;
+       unsigned long duration = msecs_to_jiffies(params[1]);
+       unsigned long last_jiffies, t;
        u32 last, cur;
        int rc;
 
+       t = jiffies + msecs_to_jiffies(params[2]);
+       if (time_before(t, deadline))
+               deadline = t;
+
        if ((rc = sata_scr_read(ap, SCR_STATUS, &cur)))
                return rc;
        cur &= 0xf;
@@ -3188,7 +3242,7 @@ int sata_phy_debounce(struct ata_port *ap, const unsigned long *params)
 
                /* DET stable? */
                if (cur == last) {
-                       if (cur == 1 && time_before(jiffies, timeout))
+                       if (cur == 1 && time_before(jiffies, deadline))
                                continue;
                        if (time_after(jiffies, last_jiffies + duration))
                                return 0;
@@ -3199,8 +3253,8 @@ int sata_phy_debounce(struct ata_port *ap, const unsigned long *params)
                last = cur;
                last_jiffies = jiffies;
 
-               /* check timeout */
-               if (time_after(jiffies, timeout))
+               /* check deadline */
+               if (time_after(jiffies, deadline))
                        return -EBUSY;
        }
 }
@@ -3209,6 +3263,7 @@ int sata_phy_debounce(struct ata_port *ap, const unsigned long *params)
  *     sata_phy_resume - resume SATA phy
  *     @ap: ATA port to resume SATA phy for
  *     @params: timing parameters { interval, duratinon, timeout } in msec
+ *     @deadline: deadline jiffies for the operation
  *
  *     Resume SATA phy of @ap and debounce it.
  *
@@ -3218,7 +3273,8 @@ int sata_phy_debounce(struct ata_port *ap, const unsigned long *params)
  *     RETURNS:
  *     0 on success, -errno on failure.
  */
-int sata_phy_resume(struct ata_port *ap, const unsigned long *params)
+int sata_phy_resume(struct ata_port *ap, const unsigned long *params,
+                   unsigned long deadline)
 {
        u32 scontrol;
        int rc;
@@ -3236,10 +3292,10 @@ int sata_phy_resume(struct ata_port *ap, const unsigned long *params)
         */
        msleep(200);
 
-       return sata_phy_debounce(ap, params);
+       return sata_phy_debounce(ap, params, deadline);
 }
 
-static void ata_wait_spinup(struct ata_port *ap)
+static void ata_wait_spinup(struct ata_port *ap, unsigned long deadline)
 {
        struct ata_eh_context *ehc = &ap->eh_context;
        unsigned long end, secs;
@@ -3247,7 +3303,7 @@ static void ata_wait_spinup(struct ata_port *ap)
 
        /* first, debounce phy if SATA */
        if (ap->cbl == ATA_CBL_SATA) {
-               rc = sata_phy_debounce(ap, sata_deb_timing_hotplug);
+               rc = sata_phy_debounce(ap, sata_deb_timing_hotplug, deadline);
 
                /* if debounced successfully and offline, no need to wait */
                if ((rc == 0 || rc == -EOPNOTSUPP) && ata_port_offline(ap))
@@ -3271,6 +3327,7 @@ static void ata_wait_spinup(struct ata_port *ap)
 /**
  *     ata_std_prereset - prepare for reset
  *     @ap: ATA port to be reset
+ *     @deadline: deadline jiffies for the operation
  *
  *     @ap is about to be reset.  Initialize it.
  *
@@ -3280,7 +3337,7 @@ static void ata_wait_spinup(struct ata_port *ap)
  *     RETURNS:
  *     0 on success, -errno otherwise.
  */
-int ata_std_prereset(struct ata_port *ap)
+int ata_std_prereset(struct ata_port *ap, unsigned long deadline)
 {
        struct ata_eh_context *ehc = &ap->eh_context;
        const unsigned long *timing = sata_ehc_deb_timing(ehc);
@@ -3293,7 +3350,7 @@ int ata_std_prereset(struct ata_port *ap)
 
        if ((ehc->i.flags & ATA_EHI_HOTPLUGGED) &&
            (ap->flags & ATA_FLAG_SKIP_D2H_BSY))
-               ata_wait_spinup(ap);
+               ata_wait_spinup(ap, deadline);
 
        /* if we're about to do hardreset, nothing more to do */
        if (ehc->i.action & ATA_EH_HARDRESET)
@@ -3301,7 +3358,7 @@ int ata_std_prereset(struct ata_port *ap)
 
        /* if SATA, resume phy */
        if (ap->cbl == ATA_CBL_SATA) {
-               rc = sata_phy_resume(ap, timing);
+               rc = sata_phy_resume(ap, timing, deadline);
                if (rc && rc != -EOPNOTSUPP) {
                        /* phy resume failed */
                        ata_port_printk(ap, KERN_WARNING, "failed to resume "
@@ -3314,7 +3371,7 @@ int ata_std_prereset(struct ata_port *ap)
         * Reg FIS and we don't know that no device is attached.
         */
        if (!(ap->flags & ATA_FLAG_SKIP_D2H_BSY) && !ata_port_offline(ap))
-               ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
+               ata_wait_ready(ap, deadline);
 
        return 0;
 }
@@ -3323,6 +3380,7 @@ int ata_std_prereset(struct ata_port *ap)
  *     ata_std_softreset - reset host port via ATA SRST
  *     @ap: port to reset
  *     @classes: resulting classes of attached devices
+ *     @deadline: deadline jiffies for the operation
  *
  *     Reset host port using ATA SRST.
  *
@@ -3332,10 +3390,12 @@ int ata_std_prereset(struct ata_port *ap)
  *     RETURNS:
  *     0 on success, -errno otherwise.
  */
-int ata_std_softreset(struct ata_port *ap, unsigned int *classes)
+int ata_std_softreset(struct ata_port *ap, unsigned int *classes,
+                     unsigned long deadline)
 {
        unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
-       unsigned int devmask = 0, err_mask;
+       unsigned int devmask = 0;
+       int rc;
        u8 err;
 
        DPRINTK("ENTER\n");
@@ -3356,11 +3416,10 @@ int ata_std_softreset(struct ata_port *ap, unsigned int *classes)
 
        /* issue bus reset */
        DPRINTK("about to softreset, devmask=%x\n", devmask);
-       err_mask = ata_bus_softreset(ap, devmask);
-       if (err_mask) {
-               ata_port_printk(ap, KERN_ERR, "SRST failed (err_mask=0x%x)\n",
-                               err_mask);
-               return -EIO;
+       rc = ata_bus_softreset(ap, devmask, deadline);
+       if (rc) {
+               ata_port_printk(ap, KERN_ERR, "SRST failed (errno=%d)\n", rc);
+               return rc;
        }
 
        /* determine by signature whether we have ATA or ATAPI devices */
@@ -3377,6 +3436,7 @@ int ata_std_softreset(struct ata_port *ap, unsigned int *classes)
  *     sata_port_hardreset - reset port via SATA phy reset
  *     @ap: port to reset
  *     @timing: timing parameters { interval, duratinon, timeout } in msec
+ *     @deadline: deadline jiffies for the operation
  *
  *     SATA phy-reset host port using DET bits of SControl register.
  *
@@ -3386,7 +3446,8 @@ int ata_std_softreset(struct ata_port *ap, unsigned int *classes)
  *     RETURNS:
  *     0 on success, -errno otherwise.
  */
-int sata_port_hardreset(struct ata_port *ap, const unsigned long *timing)
+int sata_port_hardreset(struct ata_port *ap, const unsigned long *timing,
+                       unsigned long deadline)
 {
        u32 scontrol;
        int rc;
@@ -3425,7 +3486,7 @@ int sata_port_hardreset(struct ata_port *ap, const unsigned long *timing)
        msleep(1);
 
        /* bring phy back */
-       rc = sata_phy_resume(ap, timing);
+       rc = sata_phy_resume(ap, timing, deadline);
  out:
        DPRINTK("EXIT, rc=%d\n", rc);
        return rc;
@@ -3435,6 +3496,7 @@ int sata_port_hardreset(struct ata_port *ap, const unsigned long *timing)
  *     sata_std_hardreset - reset host port via SATA phy reset
  *     @ap: port to reset
  *     @class: resulting class of attached device
+ *     @deadline: deadline jiffies for the operation
  *
  *     SATA phy-reset host port using DET bits of SControl register,
  *     wait for !BSY and classify the attached device.
@@ -3445,7 +3507,8 @@ int sata_port_hardreset(struct ata_port *ap, const unsigned long *timing)
  *     RETURNS:
  *     0 on success, -errno otherwise.
  */
-int sata_std_hardreset(struct ata_port *ap, unsigned int *class)
+int sata_std_hardreset(struct ata_port *ap, unsigned int *class,
+                      unsigned long deadline)
 {
        const unsigned long *timing = sata_ehc_deb_timing(&ap->eh_context);
        int rc;
@@ -3453,7 +3516,7 @@ int sata_std_hardreset(struct ata_port *ap, unsigned int *class)
        DPRINTK("ENTER\n");
 
        /* do hardreset */
-       rc = sata_port_hardreset(ap, timing);
+       rc = sata_port_hardreset(ap, timing, deadline);
        if (rc) {
                ata_port_printk(ap, KERN_ERR,
                                "COMRESET failed (errno=%d)\n", rc);
@@ -3470,10 +3533,11 @@ int sata_std_hardreset(struct ata_port *ap, unsigned int *class)
        /* wait a while before checking status, see SRST for more info */
        msleep(150);
 
-       if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) {
+       rc = ata_wait_ready(ap, deadline);
+       if (rc && rc != -ENODEV) {
                ata_port_printk(ap, KERN_ERR,
-                               "COMRESET failed (device not ready)\n");
-               return -EIO;
+                               "COMRESET failed (errno=%d)\n", rc);
+               return rc;
        }
 
        ap->ops->dev_select(ap, 0);     /* probably unnecessary */
@@ -6793,6 +6857,7 @@ EXPORT_SYMBOL_GPL(ata_port_disable);
 EXPORT_SYMBOL_GPL(ata_ratelimit);
 EXPORT_SYMBOL_GPL(ata_wait_register);
 EXPORT_SYMBOL_GPL(ata_busy_sleep);
+EXPORT_SYMBOL_GPL(ata_wait_ready);
 EXPORT_SYMBOL_GPL(ata_port_queue_task);
 EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
 EXPORT_SYMBOL_GPL(ata_scsi_queuecmd);