@@ -66,6 +66,7 @@
#include <asm/byteorder.h>
#include <linux/cdrom.h>
#include <linux/ratelimit.h>
+#include <linux/pm_runtime.h>
#include "libata.h"
#include "libata-transport.h"
@@ -5234,51 +5235,62 @@ 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;
+ int rc;
- for (i = 0; i < host->n_ports; i++) {
- struct ata_port *ap = host->ports[i];
- struct ata_link *link;
+ /* 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);
+ }
- /* wait and check result */
- if (wait) {
- ata_port_wait_eh(ap);
- WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
- if (rc)
- return rc;
- }
+ return rc;
+}
+
+static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg,
+ unsigned int action, unsigned int ehi_flags,
+ int wait)
+{
+ int i, rc;
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+
+ rc = ata_port_request_pm(ap, mesg, action, ehi_flags, wait);
+ if (rc)
+ return rc;
}
return 0;
@@ -5874,6 +5886,45 @@ void ata_host_init(struct ata_host *host, struct device *dev,
host->ops = ops;
}
+#define to_ata_port(d) container_of(d, struct ata_port, tdev)
+
+static int ata_port_runtime_suspend(struct device *dev)
+{
+ struct ata_port *ap = to_ata_port(dev);
+ struct Scsi_Host *shost = ap->scsi_host;
+ int rc;
+
+ /* TODO: sync with hardware access */
+
+ if (shost->host_busy)
+ return -EBUSY;
+
+ rc = ata_port_request_pm(ap, PMSG_SUSPEND, 0, ATA_EHI_QUIET, 1);
+ return rc;
+}
+
+static int ata_port_runtime_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 = {
+ .runtime_suspend = ata_port_runtime_suspend,
+ .runtime_resume = ata_port_runtime_resume,
+};
+
+static struct device_type ata_port_type = {
+ .name = "ata_port",
+#ifdef CONFIG_PM
+ .pm = &ata_port_pm_ops,
+#endif
+};
+
int ata_port_probe(struct ata_port *ap)
{
int rc = 0;
@@ -5903,6 +5954,15 @@ int ata_port_probe(struct ata_port *ap)
rc = ata_bus_probe(ap);
DPRINTK("ata%u: bus probe end\n", ap->print_id);
}
+
+ ap->tdev.type = &ata_port_type;
+ pm_runtime_set_active(&ap->tdev);
+ pm_runtime_use_autosuspend(&ap->tdev);
+ /* 3 minutes idle to auto suspend */
+ pm_runtime_set_autosuspend_delay(&ap->tdev, 180*1000);
+ pm_runtime_enable(&ap->tdev);
+ pm_request_autosuspend(&ap->tdev);
+
return rc;
}
@@ -48,6 +48,7 @@
#include <linux/hdreg.h>
#include <linux/uaccess.h>
#include <linux/suspend.h>
+#include <linux/pm_runtime.h>
#include <asm/unaligned.h>
#include "libata.h"
@@ -3208,6 +3209,11 @@ int ata_scsi_queuecmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
ap = ata_shost_to_port(shost);
+ if (pm_runtime_suspended(&ap->tdev))
+ pm_runtime_resume(&ap->tdev);
+ pm_runtime_mark_last_busy(&ap->tdev);
+ pm_request_autosuspend(&ap->tdev);
+
spin_lock_irqsave(ap->lock, irq_flags);
ata_scsi_dump_cdb(ap, cmd);