diff options
Diffstat (limited to 'drivers/scsi/pm8001/pm8001_sas.c')
-rw-r--r-- | drivers/scsi/pm8001/pm8001_sas.c | 77 |
1 files changed, 74 insertions, 3 deletions
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index a7626a851b62..c991a185724b 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -1166,13 +1166,16 @@ int pm8001_abort_task(struct sas_task *task) struct scsi_lun lun; struct pm8001_device *pm8001_dev; struct pm8001_tmf_task tmf_task; - int rc = TMF_RESP_FUNC_FAILED; + int rc = TMF_RESP_FUNC_FAILED, ret; + u32 phy_id; + struct sas_task_slow slow_task; if (unlikely(!task || !task->lldd_task || !task->dev)) return TMF_RESP_FUNC_FAILED; dev = task->dev; pm8001_dev = dev->lldd_dev; pm8001_ha = pm8001_find_ha_by_dev(dev); device_id = pm8001_dev->device_id; + phy_id = pm8001_dev->attached_phy; rc = pm8001_find_tag(task, &tag); if (rc == 0) { pm8001_printk("no tag for task:%p\n", task); @@ -1183,6 +1186,11 @@ int pm8001_abort_task(struct sas_task *task) spin_unlock_irqrestore(&task->task_state_lock, flags); return TMF_RESP_FUNC_COMPLETE; } + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + if (task->slow_task == NULL) { + init_completion(&slow_task.completion); + task->slow_task = &slow_task; + } spin_unlock_irqrestore(&task->task_state_lock, flags); if (task->task_proto & SAS_PROTOCOL_SSP) { struct scsi_cmnd *cmnd = task->uldd_task; @@ -1194,14 +1202,77 @@ int pm8001_abort_task(struct sas_task *task) pm8001_dev->sas_device, 0, tag); } else if (task->task_proto & SAS_PROTOCOL_SATA || task->task_proto & SAS_PROTOCOL_STP) { - rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev, - pm8001_dev->sas_device, 0, tag); + if (pm8001_ha->chip_id == chip_8006) { + DECLARE_COMPLETION_ONSTACK(completion_reset); + DECLARE_COMPLETION_ONSTACK(completion); + struct pm8001_phy *phy = pm8001_ha->phy + phy_id; + + /* 1. Set Device state as Recovery */ + pm8001_dev->setds_completion = &completion; + PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, + pm8001_dev, 0x03); + wait_for_completion(&completion); + + /* 2. Send Phy Control Hard Reset */ + reinit_completion(&completion); + phy->reset_success = false; + phy->enable_completion = &completion; + phy->reset_completion = &completion_reset; + ret = PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id, + PHY_HARD_RESET); + if (ret) + goto out; + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Waiting for local phy ctl\n")); + wait_for_completion(&completion); + if (!phy->reset_success) + goto out; + + /* 3. Wait for Port Reset complete / Port reset TMO */ + PM8001_MSG_DBG(pm8001_ha, + pm8001_printk("Waiting for Port reset\n")); + wait_for_completion(&completion_reset); + if (phy->port_reset_status) + goto out; + + /* + * 4. SATA Abort ALL + * we wait for the task to be aborted so that the task + * is removed from the ccb. on success the caller is + * going to free the task. + */ + ret = pm8001_exec_internal_task_abort(pm8001_ha, + pm8001_dev, pm8001_dev->sas_device, 1, tag); + if (ret) + goto out; + ret = wait_for_completion_timeout( + &task->slow_task->completion, + PM8001_TASK_TIMEOUT * HZ); + if (!ret) + goto out; + + /* 5. Set Device State as Operational */ + reinit_completion(&completion); + pm8001_dev->setds_completion = &completion; + PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha, + pm8001_dev, 0x01); + wait_for_completion(&completion); + } else { + rc = pm8001_exec_internal_task_abort(pm8001_ha, + pm8001_dev, pm8001_dev->sas_device, 0, tag); + } + rc = TMF_RESP_FUNC_COMPLETE; } else if (task->task_proto & SAS_PROTOCOL_SMP) { /* SMP */ rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev, pm8001_dev->sas_device, 0, tag); } +out: + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->slow_task == &slow_task) + task->slow_task = NULL; + spin_unlock_irqrestore(&task->task_state_lock, flags); if (rc != TMF_RESP_FUNC_COMPLETE) pm8001_printk("rc= %d\n", rc); return rc; |