diff options
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 188 |
1 files changed, 124 insertions, 64 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c04ad68cb60..11c9aea4f4f 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -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 |