diff options
Diffstat (limited to 'drivers/scsi/isci')
-rw-r--r-- | drivers/scsi/isci/host.c | 22 | ||||
-rw-r--r-- | drivers/scsi/isci/host.h | 2 | ||||
-rw-r--r-- | drivers/scsi/isci/init.c | 58 |
3 files changed, 73 insertions, 9 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index b425ed523ccc..b0afa4486158 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1044,7 +1044,7 @@ static enum sci_status sci_controller_start(struct isci_host *ihost, return SCI_SUCCESS; } -void isci_host_scan_start(struct Scsi_Host *shost) +void isci_host_start(struct Scsi_Host *shost) { struct isci_host *ihost = SHOST_TO_SAS_HA(shost)->lldd_ha; unsigned long tmo = sci_controller_get_suggested_start_timeout(ihost); @@ -1079,7 +1079,6 @@ static void sci_controller_completion_handler(struct isci_host *ihost) void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_task *task) { - task->lldd_task = NULL; if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags) && !(task->task_state_flags & SAS_TASK_STATE_ABORTED)) { if (test_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags)) { @@ -1087,16 +1086,19 @@ void ireq_done(struct isci_host *ihost, struct isci_request *ireq, struct sas_ta dev_dbg(&ihost->pdev->dev, "%s: Normal - ireq/task = %p/%p\n", __func__, ireq, task); - + task->lldd_task = NULL; task->task_done(task); } else { dev_dbg(&ihost->pdev->dev, "%s: Error - ireq/task = %p/%p\n", __func__, ireq, task); - + if (sas_protocol_ata(task->task_proto)) + task->lldd_task = NULL; sas_task_abort(task); } - } + } else + task->lldd_task = NULL; + if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) wake_up_all(&ihost->eventq); @@ -1120,10 +1122,16 @@ void isci_host_completion_routine(unsigned long data) sci_controller_completion_handler(ihost); spin_unlock_irq(&ihost->scic_lock); - /* the coalesence timeout doubles at each encoding step, so + /* + * we subtract SCI_MAX_PORTS to account for the number of dummy TCs + * issued for hardware issue workaround + */ + active = isci_tci_active(ihost) - SCI_MAX_PORTS; + + /* + * the coalesence timeout doubles at each encoding step, so * update it based on the ilog2 value of the outstanding requests */ - active = isci_tci_active(ihost); writel(SMU_ICC_GEN_VAL(NUMBER, active) | SMU_ICC_GEN_VAL(TIMER, ISCI_COALESCE_BASE + ilog2(active)), &ihost->smu_registers->interrupt_coalesce_control); diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 9ab58e0540e7..4911310a38f5 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -473,7 +473,7 @@ void sci_controller_remote_device_stopped(struct isci_host *ihost, enum sci_status sci_controller_continue_io(struct isci_request *ireq); int isci_host_scan_finished(struct Scsi_Host *, unsigned long); -void isci_host_scan_start(struct Scsi_Host *); +void isci_host_start(struct Scsi_Host *); u16 isci_alloc_tag(struct isci_host *ihost); enum sci_status isci_free_tag(struct isci_host *ihost, u16 io_tag); void isci_tci_free(struct isci_host *ihost, u16 tci); diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 1286a8a787e2..5ed4e608f200 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -156,7 +156,7 @@ static struct scsi_host_template isci_sht = { .target_alloc = sas_target_alloc, .slave_configure = sas_slave_configure, .scan_finished = isci_host_scan_finished, - .scan_start = isci_host_scan_start, + .scan_start = isci_host_start, .change_queue_depth = sas_change_queue_depth, .change_queue_type = sas_change_queue_type, .bios_param = sas_bios_param, @@ -721,11 +721,67 @@ static void __devexit isci_pci_remove(struct pci_dev *pdev) } } +#ifdef CONFIG_PM +static int isci_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct isci_host *ihost; + int i; + + for_each_isci_host(i, ihost, pdev) { + sas_suspend_ha(&ihost->sas_ha); + isci_host_deinit(ihost); + } + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int isci_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct isci_host *ihost; + int rc, i; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + rc = pcim_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, + "enabling device failure after resume(%d)\n", rc); + return rc; + } + + pci_set_master(pdev); + + for_each_isci_host(i, ihost, pdev) { + sas_prep_resume_ha(&ihost->sas_ha); + + isci_host_init(ihost); + isci_host_start(ihost->sas_ha.core.shost); + wait_for_start(ihost); + + sas_resume_ha(&ihost->sas_ha); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(isci_pm_ops, isci_suspend, isci_resume); +#endif + static struct pci_driver isci_pci_driver = { .name = DRV_NAME, .id_table = isci_id_table, .probe = isci_pci_probe, .remove = __devexit_p(isci_pci_remove), +#ifdef CONFIG_PM + .driver.pm = &isci_pm_ops, +#endif }; static __init int isci_init(void) |