diff options
author | Lin Ming <ming.m.lin@intel.com> | 2011-12-05 09:20:27 +0800 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2012-01-08 19:14:58 -0500 |
commit | 5ef41082912bdfcb33fa53b8dba2ad17dea2ef90 (patch) | |
tree | b63aff744994b75af6c3e5e5e70e01312027992e /drivers/ata/libata-core.c | |
parent | 54f57588463db1105f4a93b2902a6f95cb8f796a (diff) |
ata: add ata port system PM callbacks
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>
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 144 |
1 files changed, 74 insertions, 70 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c04ad68cb60..04c208e3cec 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -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 |