static int ahci_hardreset(struct ata_link *link, unsigned int *class,
                          unsigned long deadline)
 {
+       const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
        struct ata_port *ap = link->ap;
        struct ahci_port_priv *pp = ap->private_data;
        u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
        struct ata_taskfile tf;
+       bool online;
        int rc;
 
        DPRINTK("ENTER\n");
        tf.command = 0x80;
        ata_tf_to_fis(&tf, 0, 0, d2h_fis);
 
-       rc = sata_sff_hardreset(link, class, deadline);
+       rc = sata_link_hardreset(link, timing, deadline, &online,
+                                ahci_check_ready);
 
        ahci_start_engine(ap);
 
-       if (rc == 0 && ata_link_online(link))
+       *class = ATA_DEV_NONE;
+       if (online)
                *class = ahci_dev_classify(ap);
-       if (rc != -EAGAIN && *class == ATA_DEV_UNKNOWN)
-               *class = ATA_DEV_NONE;
 
        DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class);
        return rc;
 {
        struct ata_port *ap = link->ap;
        u32 serror;
+       bool online;
        int rc;
 
        DPRINTK("ENTER\n");
        ahci_stop_engine(ap);
 
        rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
-                                deadline);
+                                deadline, &online, NULL);
 
        /* vt8251 needs SError cleared for the port to operate */
        ahci_scr_read(ap, SCR_ERROR, &serror);
        /* vt8251 doesn't clear BSY on signature FIS reception,
         * request follow-up softreset.
         */
-       return rc ?: -EAGAIN;
+       *class = ATA_DEV_NONE;
+       return online ? -EAGAIN : rc;
 }
 
 static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class,
        struct ahci_port_priv *pp = ap->private_data;
        u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
        struct ata_taskfile tf;
+       bool online;
        int rc;
 
        ahci_stop_engine(ap);
        ata_tf_to_fis(&tf, 0, 0, d2h_fis);
 
        rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context),
-                                deadline);
+                                deadline, &online, NULL);
 
        ahci_start_engine(ap);
 
-       if (rc || ata_link_offline(link))
-               return rc;
-
        /* The pseudo configuration device on SIMG4726 attached to
         * ASUS P5W-DH Deluxe doesn't send signature FIS after
         * hardreset if no device is attached to the first downstream
         * have to be reset again.  For most cases, this should
         * suffice while making probing snappish enough.
         */
-       rc = ata_wait_after_reset(link, jiffies + 2 * HZ, ahci_check_ready);
-       if (rc)
-               ahci_kick_engine(ap, 0);
-
-       return 0;
+       if (online) {
+               rc = ata_wait_after_reset(link, jiffies + 2 * HZ,
+                                         ahci_check_ready);
+               if (rc)
+                       ahci_kick_engine(ap, 0);
+       }
+       *class = ATA_DEV_NONE;
+       return rc;
 }
 
 static void ahci_postreset(struct ata_link *link, unsigned int *class)
 
        int rc;
 
        /* do hardreset */
-       rc = sata_link_hardreset(link, timing, deadline);
+       rc = sata_link_hardreset(link, timing, deadline, NULL, NULL);
        if (rc) {
                ata_link_printk(link, KERN_ERR,
                                "COMRESET failed (errno=%d)\n", rc);
 
  *     @link: link to reset
  *     @timing: timing parameters { interval, duratinon, timeout } in msec
  *     @deadline: deadline jiffies for the operation
+ *     @online: optional out parameter indicating link onlineness
+ *     @check_ready: optional callback to check link readiness
  *
  *     SATA phy-reset @link using DET bits of SControl register.
+ *     After hardreset, link readiness is waited upon using
+ *     ata_wait_ready() if @check_ready is specified.  LLDs are
+ *     allowed to not specify @check_ready and wait itself after this
+ *     function returns.  Device classification is LLD's
+ *     responsibility.
+ *
+ *     *@online is set to one iff reset succeeded and @link is online
+ *     after reset.
  *
  *     LOCKING:
  *     Kernel thread context (may sleep)
  *     0 on success, -errno otherwise.
  */
 int sata_link_hardreset(struct ata_link *link, const unsigned long *timing,
-                       unsigned long deadline)
+                       unsigned long deadline,
+                       bool *online, int (*check_ready)(struct ata_link *))
 {
        u32 scontrol;
        int rc;
 
        DPRINTK("ENTER\n");
 
+       if (online)
+               *online = false;
+
        if (sata_set_spd_needed(link)) {
                /* SATA spec says nothing about how to reconfigure
                 * spd.  To be on the safe side, turn off phy during
 
        /* bring link back */
        rc = sata_link_resume(link, timing, deadline);
+       if (rc)
+               goto out;
+       /* if link is offline nothing more to do */
+       if (ata_link_offline(link))
+               goto out;
+
+       /* Link is online.  From this point, -ENODEV too is an error. */
+       if (online)
+               *online = true;
+
+       if ((link->ap->flags & ATA_FLAG_PMP) && ata_is_host_link(link)) {
+               /* If PMP is supported, we have to do follow-up SRST.
+                * Some PMPs don't send D2H Reg FIS after hardreset if
+                * the first port is empty.  Wait only for
+                * ATA_TMOUT_PMP_SRST_WAIT.
+                */
+               if (check_ready) {
+                       unsigned long pmp_deadline;
+
+                       pmp_deadline = jiffies + ATA_TMOUT_PMP_SRST_WAIT;
+                       if (time_after(pmp_deadline, deadline))
+                               pmp_deadline = deadline;
+                       ata_wait_ready(link, pmp_deadline, check_ready);
+               }
+               rc = -EAGAIN;
+               goto out;
+       }
+
+       rc = 0;
+       if (check_ready)
+               rc = ata_wait_ready(link, deadline, check_ready);
  out:
+       if (rc && rc != -EAGAIN)
+               ata_link_printk(link, KERN_ERR,
+                               "COMRESET failed (errno=%d)\n", rc);
        DPRINTK("EXIT, rc=%d\n", rc);
        return rc;
 }
 
                           unsigned long deadline)
 {
        const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+       bool online;
        u32 tmp;
        int rc;
 
        DPRINTK("ENTER\n");
 
        /* do hardreset */
-       rc = sata_link_hardreset(link, timing, deadline);
+       rc = sata_link_hardreset(link, timing, deadline, &online, NULL);
        if (rc) {
                ata_link_printk(link, KERN_ERR,
                                "COMRESET failed (errno=%d)\n", rc);
        }
 
        /* if device is present, follow up with srst to wait for !BSY */
-       if (ata_link_online(link))
+       if (online)
                rc = -EAGAIN;
  out:
        /* if SCR isn't accessible, we need to reset the PMP */
                 * SError.N working.
                 */
                sata_link_hardreset(link, sata_deb_timing_normal,
-                                   jiffies + ATA_TMOUT_INTERNAL_QUICK);
+                               jiffies + ATA_TMOUT_INTERNAL_QUICK, NULL, NULL);
 
                /* unconditionally clear SError.N */
                rc = sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG);
 
 int sata_sff_hardreset(struct ata_link *link, unsigned int *class,
                       unsigned long deadline)
 {
-       struct ata_port *ap = link->ap;
-       const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+       struct ata_eh_context *ehc = &link->eh_context;
+       const unsigned long *timing = sata_ehc_deb_timing(ehc);
+       bool online;
        int rc;
 
-       DPRINTK("ENTER\n");
-
-       /* do hardreset */
-       rc = sata_link_hardreset(link, timing, deadline);
-       if (rc) {
-               ata_link_printk(link, KERN_ERR,
-                               "COMRESET failed (errno=%d)\n", rc);
-               return rc;
-       }
-
-       /* TODO: phy layer with polling, timeouts, etc. */
-       if (ata_link_offline(link)) {
-               *class = ATA_DEV_NONE;
-               DPRINTK("EXIT, link offline\n");
-               return 0;
-       }
-
-       /* 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_after_reset(link, 1, jiffies + HZ);
-               return -EAGAIN;
-       }
-
-       /* 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,
-                               "COMRESET failed (errno=%d)\n", rc);
-               return rc;
-       }
-
-       *class = ata_sff_dev_classify(link->device, 1, NULL);
+       rc = sata_link_hardreset(link, timing, deadline, &online,
+                                ata_sff_check_ready);
+       *class = ATA_DEV_NONE;
+       if (online)
+               *class = ata_sff_dev_classify(link->device, 1, NULL);
 
        DPRINTK("EXIT, class=%u\n", *class);
-       return 0;
+       return rc;
 }
 
 /**
 
         */
        ATA_WAIT_AFTER_RESET_MSECS = 150,
 
+       /* If PMP is supported, we have to do follow-up SRST.  As some
+        * PMPs don't send D2H Reg FIS after hardreset, LLDs are
+        * advised to wait only for the following duration before
+        * doing SRST.
+        */
+       ATA_TMOUT_PMP_SRST_WAIT = 1 * HZ,
+
        /* ATA bus states */
        BUS_UNKNOWN             = 0,
        BUS_DMA                 = 1,
 extern int sata_link_resume(struct ata_link *link, const unsigned long *params,
                            unsigned long deadline);
 extern int sata_link_hardreset(struct ata_link *link,
-                       const unsigned long *timing, unsigned long deadline);
+                       const unsigned long *timing, unsigned long deadline,
+                       bool *online, int (*check_ready)(struct ata_link *));
 extern void ata_std_postreset(struct ata_link *link, unsigned int *classes);
 extern void ata_port_disable(struct ata_port *);