Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-block.git] / drivers / scsi / hisi_sas / hisi_sas_main.c
index 13ca5a0bdf6be336cd6f3754c15632440995d765..3c3cf89f713fbfaf8a7c15fbca17120af609fe87 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include "hisi_sas.h"
+#include "../libsas/sas_internal.h"
 #define DRV_NAME "hisi_sas"
 
 #define DEV_IS_GONE(dev) \
@@ -707,6 +708,7 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device)
 
                        hisi_hba->devices[i].device_id = i;
                        sas_dev = &hisi_hba->devices[i];
+                       sas_dev->dev_status = HISI_SAS_DEV_INIT;
                        sas_dev->dev_type = device->dev_type;
                        sas_dev->hisi_hba = hisi_hba;
                        sas_dev->sas_device = device;
@@ -731,6 +733,8 @@ static int hisi_sas_init_device(struct domain_device *device)
        struct hisi_sas_tmf_task tmf_task;
        int retry = HISI_SAS_SRST_ATA_DISK_CNT;
        struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+       struct device *dev = hisi_hba->dev;
+       struct sas_phy *local_phy;
 
        switch (device->dev_type) {
        case SAS_END_DEVICE:
@@ -746,6 +750,31 @@ static int hisi_sas_init_device(struct domain_device *device)
        case SAS_SATA_PM:
        case SAS_SATA_PM_PORT:
        case SAS_SATA_PENDING:
+               /*
+                * send HARD RESET to clear previous affiliation of
+                * STP target port
+                */
+               local_phy = sas_get_local_phy(device);
+               if (!scsi_is_sas_phy_local(local_phy)) {
+                       unsigned long deadline = ata_deadline(jiffies, 20000);
+                       struct sata_device *sata_dev = &device->sata_dev;
+                       struct ata_host *ata_host = sata_dev->ata_host;
+                       struct ata_port_operations *ops = ata_host->ops;
+                       struct ata_port *ap = sata_dev->ap;
+                       struct ata_link *link;
+                       unsigned int classes;
+
+                       ata_for_each_link(link, ap, EDGE)
+                               rc = ops->hardreset(link, &classes,
+                                                   deadline);
+               }
+               sas_put_local_phy(local_phy);
+               if (rc) {
+                       dev_warn(dev, "SATA disk hardreset fail: 0x%x\n",
+                                rc);
+                       return rc;
+               }
+
                while (retry-- > 0) {
                        rc = hisi_sas_softreset_ata_disk(device);
                        if (!rc)
@@ -808,6 +837,7 @@ static int hisi_sas_dev_found(struct domain_device *device)
        rc = hisi_sas_init_device(device);
        if (rc)
                goto err_out;
+       sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
        return 0;
 
 err_out:
@@ -980,7 +1010,8 @@ static void hisi_sas_do_release_task(struct hisi_hba *hisi_hba, struct sas_task
                spin_lock_irqsave(&task->task_state_lock, flags);
                task->task_state_flags &=
                        ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR);
-               task->task_state_flags |= SAS_TASK_STATE_DONE;
+               if (!slot->is_internal && task->task_proto != SAS_PROTOCOL_SMP)
+                       task->task_state_flags |= SAS_TASK_STATE_DONE;
                spin_unlock_irqrestore(&task->task_state_lock, flags);
        }
 
@@ -1713,20 +1744,23 @@ static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun)
 static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device)
 {
        struct sas_phy *local_phy = sas_get_local_phy(device);
-       int rc, reset_type = (device->dev_type == SAS_SATA_DEV ||
-                       (device->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
+       struct hisi_sas_device *sas_dev = device->lldd_dev;
        struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
        struct sas_ha_struct *sas_ha = &hisi_hba->sha;
        struct asd_sas_phy *sas_phy = sas_ha->sas_phy[local_phy->number];
        struct hisi_sas_phy *phy = container_of(sas_phy,
                        struct hisi_sas_phy, sas_phy);
        DECLARE_COMPLETION_ONSTACK(phyreset);
+       int rc, reset_type;
 
        if (scsi_is_sas_phy_local(local_phy)) {
                phy->in_reset = 1;
                phy->reset_completion = &phyreset;
        }
 
+       reset_type = (sas_dev->dev_status == HISI_SAS_DEV_INIT ||
+                     !dev_is_sata(device)) ? 1 : 0;
+
        rc = sas_phy_reset(local_phy, reset_type);
        sas_put_local_phy(local_phy);
 
@@ -1742,8 +1776,13 @@ static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device)
                /* report PHY down if timed out */
                if (!ret)
                        hisi_sas_phy_down(hisi_hba, sas_phy->id, 0);
-       } else
+       } else if (sas_dev->dev_status != HISI_SAS_DEV_INIT) {
+               /*
+                * If in init state, we rely on caller to wait for link to be
+                * ready; otherwise, delay.
+                */
                msleep(2000);
+       }
 
        return rc;
 }
@@ -2125,9 +2164,18 @@ static int hisi_sas_write_gpio(struct sas_ha_struct *sha, u8 reg_type,
 
 static void hisi_sas_phy_disconnected(struct hisi_sas_phy *phy)
 {
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+       struct sas_phy *sphy = sas_phy->phy;
+       struct sas_phy_data *d = sphy->hostdata;
+
        phy->phy_attached = 0;
        phy->phy_type = 0;
        phy->port = NULL;
+
+       if (d->enable)
+               sphy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
+       else
+               sphy->negotiated_linkrate = SAS_PHY_DISABLED;
 }
 
 void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy)
@@ -2253,6 +2301,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba)
        for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
                hisi_hba->devices[i].dev_type = SAS_PHY_UNUSED;
                hisi_hba->devices[i].device_id = i;
+               hisi_hba->devices[i].dev_status = HISI_SAS_DEV_INIT;
        }
 
        for (i = 0; i < hisi_hba->queue_count; i++) {