return to_sas_internal(dev->port->ha->core.shost->transportt);
 }
 
+static void sas_get_ata_command_set(struct domain_device *dev);
+
+int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy)
+{
+       if (phy->attached_tproto & SAS_PROTOCOL_STP)
+               dev->tproto = phy->attached_tproto;
+       if (phy->attached_sata_dev)
+               dev->tproto |= SATA_DEV;
+
+       if (phy->attached_dev_type == SATA_PENDING)
+               dev->dev_type = SATA_PENDING;
+       else {
+               int res;
+
+               dev->dev_type = SATA_DEV;
+               res = sas_get_report_phy_sata(dev->parent, phy->phy_id,
+                                             &dev->sata_dev.rps_resp);
+               if (res) {
+                       SAS_DPRINTK("report phy sata to %016llx:0x%x returned "
+                                   "0x%x\n", SAS_ADDR(dev->parent->sas_addr),
+                                   phy->phy_id, res);
+                       return res;
+               }
+               memcpy(dev->frame_rcvd, &dev->sata_dev.rps_resp.rps.fis,
+                      sizeof(struct dev_to_host_fis));
+               /* TODO switch to ata_dev_classify() */
+               sas_get_ata_command_set(dev);
+       }
+       return 0;
+}
+
+static int sas_ata_clear_pending(struct domain_device *dev, struct ex_phy *phy)
+{
+       int res;
+
+       /* we weren't pending, so successfully end the reset sequence now */
+       if (dev->dev_type != SATA_PENDING)
+               return 1;
+
+       /* hmmm, if this succeeds do we need to repost the domain_device to the
+        * lldd so it can pick up new parameters?
+        */
+       res = sas_get_ata_info(dev, phy);
+       if (res)
+               return 0; /* retry */
+       else
+               return 1;
+}
+
 static int smp_ata_check_ready(struct ata_link *link)
 {
        int res;
-       u8 addr[8];
        struct ata_port *ap = link->ap;
        struct domain_device *dev = ap->private_data;
        struct domain_device *ex_dev = dev->parent;
        struct sas_phy *phy = sas_get_local_phy(dev);
+       struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy->number];
 
-       res = sas_get_phy_attached_sas_addr(ex_dev, phy->number, addr);
+       res = sas_ex_phy_discover(ex_dev, phy->number);
        sas_put_local_phy(phy);
+
        /* break the wait early if the expander is unreachable,
         * otherwise keep polling
         */
        if (res == -ECOMM)
                return res;
-       if (res != SMP_RESP_FUNC_ACC || SAS_ADDR(addr) == 0)
+       if (res != SMP_RESP_FUNC_ACC)
                return 0;
-       else
-               return 1;
+
+       switch (ex_phy->attached_dev_type) {
+       case SATA_PENDING:
+               return 0;
+       case SAS_END_DEV:
+               if (ex_phy->attached_sata_dev)
+                       return sas_ata_clear_pending(dev, ex_phy);
+       default:
+               return -ENODEV;
+       }
 }
 
 static int local_ata_check_ready(struct ata_link *link)
        struct dev_to_host_fis *fis =
                (struct dev_to_host_fis *) dev->frame_rcvd;
 
+       if (dev->dev_type == SATA_PENDING)
+               return;
+
        if ((fis->sector_count == 1 && /* ATA */
             fis->lbal         == 1 &&
             fis->lbam         == 0 &&
 
        }
 }
 
-static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
-                          void *disc_resp)
+static enum sas_dev_type to_dev_type(struct discover_resp *dr)
 {
+       /* This is detecting a failure to transmit initial dev to host
+        * FIS as described in section J.5 of sas-2 r16
+        */
+       if (dr->attached_dev_type == NO_DEVICE && dr->attached_sata_dev &&
+           dr->linkrate >= SAS_LINK_RATE_1_5_GBPS)
+               return SATA_PENDING;
+       else
+               return dr->attached_dev_type;
+}
+
+static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
+{
+       enum sas_dev_type dev_type;
+       enum sas_linkrate linkrate;
+       u8 sas_addr[SAS_ADDR_SIZE];
+       struct smp_resp *resp = rsp;
+       struct discover_resp *dr = &resp->disc;
        struct expander_device *ex = &dev->ex_dev;
        struct ex_phy *phy = &ex->ex_phy[phy_id];
-       struct smp_resp *resp = disc_resp;
-       struct discover_resp *dr = &resp->disc;
        struct sas_rphy *rphy = dev->rphy;
        bool new_phy = !phy->phy;
        char *type;
                break;
        }
 
+       /* check if anything important changed to squelch debug */
+       dev_type = phy->attached_dev_type;
+       linkrate  = phy->linkrate;
+       memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE);
+
+       phy->attached_dev_type = to_dev_type(dr);
        phy->phy_id = phy_id;
-       phy->attached_dev_type = dr->attached_dev_type;
        phy->linkrate = dr->linkrate;
        phy->attached_sata_host = dr->attached_sata_host;
        phy->attached_sata_dev  = dr->attached_sata_dev;
        phy->last_da_index = -1;
 
        phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr);
-       phy->phy->identify.device_type = phy->attached_dev_type;
+       phy->phy->identify.device_type = dr->attached_dev_type;
        phy->phy->identify.initiator_port_protocols = phy->attached_iproto;
        phy->phy->identify.target_port_protocols = phy->attached_tproto;
        phy->phy->identify.phy_identifier = phy_id;
                }
 
        switch (phy->attached_dev_type) {
+       case SATA_PENDING:
+               type = "stp pending";
+               break;
        case NO_DEVICE:
                type = "no device";
                break;
                type = "unknown";
        }
 
+       /* this routine is polled by libata error recovery so filter
+        * unimportant messages
+        */
+       if (new_phy || phy->attached_dev_type != dev_type ||
+           phy->linkrate != linkrate ||
+           SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr))
+               /* pass */;
+       else
+               return;
+
        SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n",
                    SAS_ADDR(dev->sas_addr), phy->phy_id,
                    sas_route_char(dev, phy), phy->linkrate,
 static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
                                      u8 *disc_resp, int single)
 {
-       struct domain_device *ata_dev = sas_ex_to_ata(dev, single);
-       int i, res;
+       struct discover_resp *dr;
+       int res;
 
        disc_req[9] = single;
-       for (i = 1 ; i < 3; i++) {
-               struct discover_resp *dr;
 
-               res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE,
-                                      disc_resp, DISCOVER_RESP_SIZE);
-               if (res)
-                       return res;
-               dr = &((struct smp_resp *)disc_resp)->disc;
-               if (memcmp(dev->sas_addr, dr->attached_sas_addr,
-                         SAS_ADDR_SIZE) == 0) {
-                       sas_printk("Found loopback topology, just ignore it!\n");
-                       return 0;
-               }
-
-               /* This is detecting a failure to transmit initial
-                * dev to host FIS as described in section J.5 of
-                * sas-2 r16
-                */
-               if (!(dr->attached_dev_type == 0 &&
-                     dr->attached_sata_dev))
-                       break;
-
-               /* In order to generate the dev to host FIS, we send a
-                * link reset to the expander port.  If a device was
-                * previously detected on this port we ask libata to
-                * manage the reset and link recovery.
-                */
-               if (ata_dev) {
-                       sas_ata_schedule_reset(ata_dev);
-                       break;
-               }
-               sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL);
-               /* Wait for the reset to trigger the negotiation */
-               msleep(500);
+       res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE,
+                              disc_resp, DISCOVER_RESP_SIZE);
+       if (res)
+               return res;
+       dr = &((struct smp_resp *)disc_resp)->disc;
+       if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) {
+               sas_printk("Found loopback topology, just ignore it!\n");
+               return 0;
        }
        sas_set_ex_phy(dev, single, disc_resp);
        return 0;
 }
 
-static int sas_ex_phy_discover(struct domain_device *dev, int single)
+int sas_ex_phy_discover(struct domain_device *dev, int single)
 {
        struct expander_device *ex = &dev->ex_dev;
        int  res = 0;
 #define RPS_REQ_SIZE  16
 #define RPS_RESP_SIZE 60
 
-static int sas_get_report_phy_sata(struct domain_device *dev,
-                                         int phy_id,
-                                         struct smp_resp *rps_resp)
+int sas_get_report_phy_sata(struct domain_device *dev, int phy_id,
+                           struct smp_resp *rps_resp)
 {
        int res;
        u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE);
 
 #ifdef CONFIG_SCSI_SAS_ATA
        if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) {
-               child->dev_type = SATA_DEV;
-               if (phy->attached_tproto & SAS_PROTOCOL_STP)
-                       child->tproto = phy->attached_tproto;
-               if (phy->attached_sata_dev)
-                       child->tproto |= SATA_DEV;
-               res = sas_get_report_phy_sata(parent, phy_id,
-                                             &child->sata_dev.rps_resp);
-               if (res) {
-                       SAS_DPRINTK("report phy sata to %016llx:0x%x returned "
-                                   "0x%x\n", SAS_ADDR(parent->sas_addr),
-                                   phy_id, res);
+               res = sas_get_ata_info(child, phy);
+               if (res)
                        goto out_free;
-               }
-               memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis,
-                      sizeof(struct dev_to_host_fis));
 
                rphy = sas_end_device_alloc(phy->port);
                if (unlikely(!rphy))
 
        if (ex_phy->attached_dev_type != SAS_END_DEV &&
            ex_phy->attached_dev_type != FANOUT_DEV &&
-           ex_phy->attached_dev_type != EDGE_DEV) {
+           ex_phy->attached_dev_type != EDGE_DEV &&
+           ex_phy->attached_dev_type != SATA_PENDING) {
                SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx "
                            "phy 0x%x\n", ex_phy->attached_dev_type,
                            SAS_ADDR(dev->sas_addr),
 
        switch (ex_phy->attached_dev_type) {
        case SAS_END_DEV:
+       case SATA_PENDING:
                child = sas_ex_discover_end_dev(dev, phy_id);
                break;
        case FANOUT_DEV:
        return res;
 }
 
-int sas_get_phy_attached_sas_addr(struct domain_device *dev, int phy_id,
-                                 u8 *attached_sas_addr)
+static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id,
+                                   u8 *sas_addr, enum sas_dev_type *type)
 {
        int res;
        struct smp_resp *disc_resp;
        dr = &disc_resp->disc;
 
        res = sas_get_phy_discover(dev, phy_id, disc_resp);
-       if (!res) {
-               memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8);
-               if (dr->attached_dev_type == 0)
-                       memset(attached_sas_addr, 0, 8);
+       if (res == 0) {
+               memcpy(sas_addr, disc_resp->disc.attached_sas_addr, 8);
+               *type = to_dev_type(dr);
+               if (*type == 0)
+                       memset(sas_addr, 0, 8);
        }
        kfree(disc_resp);
        return res;
        return res;
 }
 
+static bool dev_type_flutter(enum sas_dev_type new, enum sas_dev_type old)
+{
+       if (old == new)
+               return true;
+
+       /* treat device directed resets as flutter, if we went
+        * SAS_END_DEV to SATA_PENDING the link needs recovery
+        */
+       if ((old == SATA_PENDING && new == SAS_END_DEV) ||
+           (old == SAS_END_DEV && new == SATA_PENDING))
+               return true;
+
+       return false;
+}
+
 static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)
 {
        struct expander_device *ex = &dev->ex_dev;
        struct ex_phy *phy = &ex->ex_phy[phy_id];
-       u8 attached_sas_addr[8];
+       enum sas_dev_type type = NO_DEVICE;
+       u8 sas_addr[8];
        int res;
 
-       res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr);
+       res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type);
        switch (res) {
        case SMP_RESP_NO_PHY:
                phy->phy_state = PHY_NOT_PRESENT;
                sas_unregister_devs_sas_addr(dev, phy_id, last);
-               goto out; break;
+               return res;
        case SMP_RESP_PHY_VACANT:
                phy->phy_state = PHY_VACANT;
                sas_unregister_devs_sas_addr(dev, phy_id, last);
-               goto out; break;
+               return res;
        case SMP_RESP_FUNC_ACC:
                break;
        }
 
-       if (SAS_ADDR(attached_sas_addr) == 0) {
+       if (SAS_ADDR(sas_addr) == 0) {
                phy->phy_state = PHY_EMPTY;
                sas_unregister_devs_sas_addr(dev, phy_id, last);
-       } else if (SAS_ADDR(attached_sas_addr) ==
-                  SAS_ADDR(phy->attached_sas_addr)) {
-               SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
-                           SAS_ADDR(dev->sas_addr), phy_id);
+               return res;
+       } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) &&
+                  dev_type_flutter(type, phy->attached_dev_type)) {
+               struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id);
+               char *action = "";
+
                sas_ex_phy_discover(dev, phy_id);
-       } else
-               res = sas_discover_new(dev, phy_id);
-out:
-       return res;
+
+               if (ata_dev && phy->attached_dev_type == SATA_PENDING)
+                       action = ", needs recovery";
+               SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n",
+                           SAS_ADDR(dev->sas_addr), phy_id, action);
+               return res;
+       }
+
+       return sas_discover_new(dev, phy_id);
 }
 
 /**