|
@@ -5009,6 +5009,122 @@ int ata_flush_cache(struct ata_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int ata_host_set_request_pm(struct ata_host_set *host_set,
|
|
|
+ pm_message_t mesg, unsigned int action,
|
|
|
+ unsigned int ehi_flags, int wait)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ int i, rc;
|
|
|
+
|
|
|
+ for (i = 0; i < host_set->n_ports; i++) {
|
|
|
+ struct ata_port *ap = host_set->ports[i];
|
|
|
+
|
|
|
+ /* Previous resume operation might still be in
|
|
|
+ * progress. Wait for PM_PENDING to clear.
|
|
|
+ */
|
|
|
+ if (ap->pflags & ATA_PFLAG_PM_PENDING) {
|
|
|
+ ata_port_wait_eh(ap);
|
|
|
+ WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* request PM ops to EH */
|
|
|
+ spin_lock_irqsave(ap->lock, flags);
|
|
|
+
|
|
|
+ ap->pm_mesg = mesg;
|
|
|
+ if (wait) {
|
|
|
+ rc = 0;
|
|
|
+ ap->pm_result = &rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ ap->pflags |= ATA_PFLAG_PM_PENDING;
|
|
|
+ ap->eh_info.action |= action;
|
|
|
+ ap->eh_info.flags |= ehi_flags;
|
|
|
+
|
|
|
+ ata_port_schedule_eh(ap);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(ap->lock, flags);
|
|
|
+
|
|
|
+ /* wait and check result */
|
|
|
+ if (wait) {
|
|
|
+ ata_port_wait_eh(ap);
|
|
|
+ WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ata_host_set_suspend - suspend host_set
|
|
|
+ * @host_set: host_set to suspend
|
|
|
+ * @mesg: PM message
|
|
|
+ *
|
|
|
+ * Suspend @host_set. Actual operation is performed by EH. This
|
|
|
+ * function requests EH to perform PM operations and waits for EH
|
|
|
+ * to finish.
|
|
|
+ *
|
|
|
+ * LOCKING:
|
|
|
+ * Kernel thread context (may sleep).
|
|
|
+ *
|
|
|
+ * RETURNS:
|
|
|
+ * 0 on success, -errno on failure.
|
|
|
+ */
|
|
|
+int ata_host_set_suspend(struct ata_host_set *host_set, pm_message_t mesg)
|
|
|
+{
|
|
|
+ int i, j, rc;
|
|
|
+
|
|
|
+ rc = ata_host_set_request_pm(host_set, mesg, 0, ATA_EHI_QUIET, 1);
|
|
|
+ if (rc)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ /* EH is quiescent now. Fail if we have any ready device.
|
|
|
+ * This happens if hotplug occurs between completion of device
|
|
|
+ * suspension and here.
|
|
|
+ */
|
|
|
+ for (i = 0; i < host_set->n_ports; i++) {
|
|
|
+ struct ata_port *ap = host_set->ports[i];
|
|
|
+
|
|
|
+ for (j = 0; j < ATA_MAX_DEVICES; j++) {
|
|
|
+ struct ata_device *dev = &ap->device[j];
|
|
|
+
|
|
|
+ if (ata_dev_ready(dev)) {
|
|
|
+ ata_port_printk(ap, KERN_WARNING,
|
|
|
+ "suspend failed, device %d "
|
|
|
+ "still active\n", dev->devno);
|
|
|
+ rc = -EBUSY;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ host_set->dev->power.power_state = mesg;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ fail:
|
|
|
+ ata_host_set_resume(host_set);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * ata_host_set_resume - resume host_set
|
|
|
+ * @host_set: host_set to resume
|
|
|
+ *
|
|
|
+ * Resume @host_set. Actual operation is performed by EH. This
|
|
|
+ * function requests EH to perform PM operations and returns.
|
|
|
+ * Note that all resume operations are performed parallely.
|
|
|
+ *
|
|
|
+ * LOCKING:
|
|
|
+ * Kernel thread context (may sleep).
|
|
|
+ */
|
|
|
+void ata_host_set_resume(struct ata_host_set *host_set)
|
|
|
+{
|
|
|
+ ata_host_set_request_pm(host_set, PMSG_ON, ATA_EH_SOFTRESET,
|
|
|
+ ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
|
|
|
+ host_set->dev->power.power_state = PMSG_ON;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* ata_port_start - Set port up for dma.
|
|
|
* @ap: Port to initialize
|
|
@@ -5651,20 +5767,55 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits)
|
|
|
return (tmp == bits->val) ? 1 : 0;
|
|
|
}
|
|
|
|
|
|
-int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
+void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
{
|
|
|
pci_save_state(pdev);
|
|
|
- pci_disable_device(pdev);
|
|
|
- pci_set_power_state(pdev, PCI_D3hot);
|
|
|
- return 0;
|
|
|
+
|
|
|
+ if (state.event == PM_EVENT_SUSPEND) {
|
|
|
+ pci_disable_device(pdev);
|
|
|
+ pci_set_power_state(pdev, PCI_D3hot);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-int ata_pci_device_resume(struct pci_dev *pdev)
|
|
|
+void ata_pci_device_do_resume(struct pci_dev *pdev)
|
|
|
{
|
|
|
pci_set_power_state(pdev, PCI_D0);
|
|
|
pci_restore_state(pdev);
|
|
|
pci_enable_device(pdev);
|
|
|
pci_set_master(pdev);
|
|
|
+}
|
|
|
+
|
|
|
+int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
+{
|
|
|
+ struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
|
|
|
+ int rc = 0;
|
|
|
+
|
|
|
+ rc = ata_host_set_suspend(host_set, state);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ if (host_set->next) {
|
|
|
+ rc = ata_host_set_suspend(host_set->next, state);
|
|
|
+ if (rc) {
|
|
|
+ ata_host_set_resume(host_set);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ata_pci_device_do_suspend(pdev, state);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int ata_pci_device_resume(struct pci_dev *pdev)
|
|
|
+{
|
|
|
+ struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev);
|
|
|
+
|
|
|
+ ata_pci_device_do_resume(pdev);
|
|
|
+ ata_host_set_resume(host_set);
|
|
|
+ if (host_set->next)
|
|
|
+ ata_host_set_resume(host_set->next);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
#endif /* CONFIG_PCI */
|
|
@@ -5844,6 +5995,8 @@ EXPORT_SYMBOL_GPL(sata_scr_write);
|
|
|
EXPORT_SYMBOL_GPL(sata_scr_write_flush);
|
|
|
EXPORT_SYMBOL_GPL(ata_port_online);
|
|
|
EXPORT_SYMBOL_GPL(ata_port_offline);
|
|
|
+EXPORT_SYMBOL_GPL(ata_host_set_suspend);
|
|
|
+EXPORT_SYMBOL_GPL(ata_host_set_resume);
|
|
|
EXPORT_SYMBOL_GPL(ata_id_string);
|
|
|
EXPORT_SYMBOL_GPL(ata_id_c_string);
|
|
|
EXPORT_SYMBOL_GPL(ata_scsi_simulate);
|
|
@@ -5858,6 +6011,8 @@ EXPORT_SYMBOL_GPL(ata_pci_host_stop);
|
|
|
EXPORT_SYMBOL_GPL(ata_pci_init_native_mode);
|
|
|
EXPORT_SYMBOL_GPL(ata_pci_init_one);
|
|
|
EXPORT_SYMBOL_GPL(ata_pci_remove_one);
|
|
|
+EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend);
|
|
|
+EXPORT_SYMBOL_GPL(ata_pci_device_do_resume);
|
|
|
EXPORT_SYMBOL_GPL(ata_pci_device_suspend);
|
|
|
EXPORT_SYMBOL_GPL(ata_pci_device_resume);
|
|
|
EXPORT_SYMBOL_GPL(ata_pci_default_filter);
|