ata: add ata port system PM callbacks
Lin Ming [Mon, 5 Dec 2011 01:20:27 +0000 (09:20 +0800)]
Change ata_host_request_pm to ata_port_request_pm which performs
port suspend/resume.

Add ata port type driver which implements port PM callbacks.

Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>

drivers/ata/libata-core.c
drivers/ata/libata-transport.c
drivers/ata/libata.h

index c04ad68..04c208e 100644 (file)
@@ -5234,112 +5234,116 @@ bool ata_link_offline(struct ata_link *link)
 }
 
 #ifdef CONFIG_PM
-static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg,
+static int ata_port_request_pm(struct ata_port *ap, pm_message_t mesg,
                               unsigned int action, unsigned int ehi_flags,
                               int wait)
 {
+       struct ata_link *link;
        unsigned long flags;
-       int i, rc;
-
-       for (i = 0; i < host->n_ports; i++) {
-               struct ata_port *ap = host->ports[i];
-               struct ata_link *link;
+       int rc;
 
-               /* 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);
-               }
+       /* 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);
+       /* request PM ops to EH */
+       spin_lock_irqsave(ap->lock, flags);
 
-               ap->pm_mesg = mesg;
-               if (wait) {
-                       rc = 0;
-                       ap->pm_result = &rc;
-               }
+       ap->pm_mesg = mesg;
+       if (wait) {
+               rc = 0;
+               ap->pm_result = &rc;
+       }
 
-               ap->pflags |= ATA_PFLAG_PM_PENDING;
-               ata_for_each_link(link, ap, HOST_FIRST) {
-                       link->eh_info.action |= action;
-                       link->eh_info.flags |= ehi_flags;
-               }
+       ap->pflags |= ATA_PFLAG_PM_PENDING;
+       ata_for_each_link(link, ap, HOST_FIRST) {
+               link->eh_info.action |= action;
+               link->eh_info.flags |= ehi_flags;
+       }
 
-               ata_port_schedule_eh(ap);
+       ata_port_schedule_eh(ap);
 
-               spin_unlock_irqrestore(ap->lock, flags);
+       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;
-               }
+       /* wait and check result */
+       if (wait) {
+               ata_port_wait_eh(ap);
+               WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
        }
 
-       return 0;
+       return rc;
 }
 
+#define to_ata_port(d) container_of(d, struct ata_port, tdev)
+
+static int ata_port_suspend_common(struct device *dev)
+{
+       struct ata_port *ap = to_ata_port(dev);
+       int rc;
+
+       rc = ata_port_request_pm(ap, PMSG_SUSPEND, 0, ATA_EHI_QUIET, 1);
+       return rc;
+}
+
+static int ata_port_suspend(struct device *dev)
+{
+       if (pm_runtime_suspended(dev))
+               return 0;
+
+       return ata_port_suspend_common(dev);
+}
+
+static int ata_port_resume(struct device *dev)
+{
+       struct ata_port *ap = to_ata_port(dev);
+       int rc;
+
+       rc = ata_port_request_pm(ap, PMSG_ON, ATA_EH_RESET,
+               ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 1);
+       return rc;
+}
+
+static const struct dev_pm_ops ata_port_pm_ops = {
+       .suspend = ata_port_suspend,
+       .resume = ata_port_resume,
+};
+
 /**
  *     ata_host_suspend - suspend host
  *     @host: host to suspend
  *     @mesg: PM message
  *
- *     Suspend @host.  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.
+ *     Suspend @host.  Actual operation is performed by port suspend.
  */
 int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
 {
-       unsigned int ehi_flags = ATA_EHI_QUIET;
-       int rc;
-
-       /*
-        * On some hardware, device fails to respond after spun down
-        * for suspend.  As the device won't be used before being
-        * resumed, we don't need to touch the device.  Ask EH to skip
-        * the usual stuff and proceed directly to suspend.
-        *
-        * http://thread.gmane.org/gmane.linux.ide/46764
-        */
-       if (mesg.event == PM_EVENT_SUSPEND)
-               ehi_flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_NO_RECOVERY;
-
-       rc = ata_host_request_pm(host, mesg, 0, ehi_flags, 1);
-       if (rc == 0)
-               host->dev->power.power_state = mesg;
-       return rc;
+       host->dev->power.power_state = mesg;
+       return 0;
 }
 
 /**
  *     ata_host_resume - resume host
  *     @host: host to resume
  *
- *     Resume @host.  Actual operation is performed by EH.  This
- *     function requests EH to perform PM operations and returns.
- *     Note that all resume operations are performed parallelly.
- *
- *     LOCKING:
- *     Kernel thread context (may sleep).
+ *     Resume @host.  Actual operation is performed by port resume.
  */
 void ata_host_resume(struct ata_host *host)
 {
-       ata_host_request_pm(host, PMSG_ON, ATA_EH_RESET,
-                           ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0);
        host->dev->power.power_state = PMSG_ON;
 }
 #endif
 
+struct device_type ata_port_type = {
+       .name = "ata_port",
+#ifdef CONFIG_PM
+       .pm = &ata_port_pm_ops,
+#endif
+};
+
 /**
  *     ata_dev_init - Initialize an ata_device structure
  *     @dev: Device structure to initialize
index ce9dc62..3ceb3d9 100644 (file)
@@ -279,6 +279,7 @@ int ata_tport_add(struct device *parent,
        struct device *dev = &ap->tdev;
 
        device_initialize(dev);
+       dev->type = &ata_port_type;
 
        dev->parent = get_device(parent);
        dev->release = ata_tport_release;
index 773de97..814486d 100644 (file)
@@ -58,6 +58,7 @@ extern int atapi_passthru16;
 extern int libata_fua;
 extern int libata_noacpi;
 extern int libata_allow_tpm;
+extern struct device_type ata_port_type;
 extern struct ata_link *ata_dev_phys_link(struct ata_device *dev);
 extern void ata_force_cbl(struct ata_port *ap);
 extern u64 ata_tf_to_lba(const struct ata_taskfile *tf);