|
@@ -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"
|
|
@@ -3248,10 +3249,10 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev)
|
|
|
ata_force_xfermask(dev);
|
|
|
|
|
|
pio_mask = ata_pack_xfermask(dev->pio_mask, 0, 0);
|
|
|
- dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask);
|
|
|
|
|
|
if (libata_dma_mask & mode_mask)
|
|
|
- dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask);
|
|
|
+ dma_mask = ata_pack_xfermask(0, dev->mwdma_mask,
|
|
|
+ dev->udma_mask);
|
|
|
else
|
|
|
dma_mask = 0;
|
|
|
|
|
@@ -5234,73 +5235,55 @@ 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;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * 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.
|
|
|
- */
|
|
|
-int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
|
|
|
+#define to_ata_port(d) container_of(d, struct ata_port, tdev)
|
|
|
+
|
|
|
+static int ata_port_suspend_common(struct device *dev, pm_message_t mesg)
|
|
|
{
|
|
|
+ struct ata_port *ap = to_ata_port(dev);
|
|
|
unsigned int ehi_flags = ATA_EHI_QUIET;
|
|
|
int rc;
|
|
|
|
|
@@ -5315,31 +5298,108 @@ int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
|
|
|
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;
|
|
|
+ rc = ata_port_request_pm(ap, mesg, 0, ehi_flags, 1);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+static int ata_port_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ if (pm_runtime_suspended(dev))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return ata_port_suspend_common(dev, PMSG_SUSPEND);
|
|
|
+}
|
|
|
+
|
|
|
+static int ata_port_do_freeze(struct device *dev)
|
|
|
+{
|
|
|
+ if (pm_runtime_suspended(dev))
|
|
|
+ pm_runtime_resume(dev);
|
|
|
+
|
|
|
+ return ata_port_suspend_common(dev, PMSG_FREEZE);
|
|
|
+}
|
|
|
+
|
|
|
+static int ata_port_poweroff(struct device *dev)
|
|
|
+{
|
|
|
+ if (pm_runtime_suspended(dev))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return ata_port_suspend_common(dev, PMSG_HIBERNATE);
|
|
|
+}
|
|
|
+
|
|
|
+static int ata_port_resume_common(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 int ata_port_resume(struct device *dev)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ rc = ata_port_resume_common(dev);
|
|
|
+ if (!rc) {
|
|
|
+ pm_runtime_disable(dev);
|
|
|
+ pm_runtime_set_active(dev);
|
|
|
+ pm_runtime_enable(dev);
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static int ata_port_runtime_idle(struct device *dev)
|
|
|
+{
|
|
|
+ return pm_runtime_suspend(dev);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct dev_pm_ops ata_port_pm_ops = {
|
|
|
+ .suspend = ata_port_suspend,
|
|
|
+ .resume = ata_port_resume,
|
|
|
+ .freeze = ata_port_do_freeze,
|
|
|
+ .thaw = ata_port_resume,
|
|
|
+ .poweroff = ata_port_poweroff,
|
|
|
+ .restore = ata_port_resume,
|
|
|
+
|
|
|
+ .runtime_suspend = ata_port_suspend,
|
|
|
+ .runtime_resume = ata_port_resume_common,
|
|
|
+ .runtime_idle = ata_port_runtime_idle,
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * ata_host_suspend - suspend host
|
|
|
+ * @host: host to suspend
|
|
|
+ * @mesg: PM message
|
|
|
+ *
|
|
|
+ * Suspend @host. Actual operation is performed by port suspend.
|
|
|
+ */
|
|
|
+int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
|
|
|
+{
|
|
|
+ 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
|