Commit 5ef41082 authored by Lin Ming's avatar Lin Ming Committed by Jeff Garzik

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: default avatarLin Ming <ming.m.lin@intel.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 54f57588
...@@ -5234,112 +5234,116 @@ bool ata_link_offline(struct ata_link *link) ...@@ -5234,112 +5234,116 @@ bool ata_link_offline(struct ata_link *link)
} }
#ifdef CONFIG_PM #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, unsigned int action, unsigned int ehi_flags,
int wait) int wait)
{ {
struct ata_link *link;
unsigned long flags; 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 /* Previous resume operation might still be in
* progress. Wait for PM_PENDING to clear. * progress. Wait for PM_PENDING to clear.
*/ */
if (ap->pflags & ATA_PFLAG_PM_PENDING) { if (ap->pflags & ATA_PFLAG_PM_PENDING) {
ata_port_wait_eh(ap); ata_port_wait_eh(ap);
WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
} }
/* request PM ops to EH */ /* request PM ops to EH */
spin_lock_irqsave(ap->lock, flags); spin_lock_irqsave(ap->lock, flags);
ap->pm_mesg = mesg; ap->pm_mesg = mesg;
if (wait) { if (wait) {
rc = 0; rc = 0;
ap->pm_result = &rc; ap->pm_result = &rc;
} }
ap->pflags |= ATA_PFLAG_PM_PENDING; ap->pflags |= ATA_PFLAG_PM_PENDING;
ata_for_each_link(link, ap, HOST_FIRST) { ata_for_each_link(link, ap, HOST_FIRST) {
link->eh_info.action |= action; link->eh_info.action |= action;
link->eh_info.flags |= ehi_flags; 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 */ /* wait and check result */
if (wait) { if (wait) {
ata_port_wait_eh(ap); ata_port_wait_eh(ap);
WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
if (rc)
return rc;
}
} }
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 * ata_host_suspend - suspend host
* @host: host to suspend * @host: host to suspend
* @mesg: PM message * @mesg: PM message
* *
* Suspend @host. Actual operation is performed by EH. This * Suspend @host. Actual operation is performed by port suspend.
* 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) int ata_host_suspend(struct ata_host *host, pm_message_t mesg)
{ {
unsigned int ehi_flags = ATA_EHI_QUIET; host->dev->power.power_state = mesg;
int rc; return 0;
/*
* 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;
} }
/** /**
* ata_host_resume - resume host * ata_host_resume - resume host
* @host: host to resume * @host: host to resume
* *
* Resume @host. Actual operation is performed by EH. This * Resume @host. Actual operation is performed by port resume.
* function requests EH to perform PM operations and returns.
* Note that all resume operations are performed parallelly.
*
* LOCKING:
* Kernel thread context (may sleep).
*/ */
void ata_host_resume(struct ata_host *host) 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; host->dev->power.power_state = PMSG_ON;
} }
#endif #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 * ata_dev_init - Initialize an ata_device structure
* @dev: Device structure to initialize * @dev: Device structure to initialize
......
...@@ -279,6 +279,7 @@ int ata_tport_add(struct device *parent, ...@@ -279,6 +279,7 @@ int ata_tport_add(struct device *parent,
struct device *dev = &ap->tdev; struct device *dev = &ap->tdev;
device_initialize(dev); device_initialize(dev);
dev->type = &ata_port_type;
dev->parent = get_device(parent); dev->parent = get_device(parent);
dev->release = ata_tport_release; dev->release = ata_tport_release;
......
...@@ -58,6 +58,7 @@ extern int atapi_passthru16; ...@@ -58,6 +58,7 @@ extern int atapi_passthru16;
extern int libata_fua; extern int libata_fua;
extern int libata_noacpi; extern int libata_noacpi;
extern int libata_allow_tpm; 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 struct ata_link *ata_dev_phys_link(struct ata_device *dev);
extern void ata_force_cbl(struct ata_port *ap); extern void ata_force_cbl(struct ata_port *ap);
extern u64 ata_tf_to_lba(const struct ata_taskfile *tf); extern u64 ata_tf_to_lba(const struct ata_taskfile *tf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment