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>
This commit is contained in:
Lin Ming 2011-12-05 09:20:27 +08:00 committed by Jeff Garzik
parent 54f5758846
commit 5ef4108291
3 changed files with 82 additions and 76 deletions

View File

@ -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++) { /* Previous resume operation might still be in
struct ata_port *ap = host->ports[i]; * progress. Wait for PM_PENDING to clear.
struct ata_link *link; */
if (ap->pflags & ATA_PFLAG_PM_PENDING) {
/* Previous resume operation might still be in ata_port_wait_eh(ap);
* progress. Wait for PM_PENDING to clear. WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING);
*/
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);
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;
}
ata_port_schedule_eh(ap);
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;
}
} }
return 0; /* request PM ops to EH */
spin_lock_irqsave(ap->lock, flags);
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;
}
ata_port_schedule_eh(ap);
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);
}
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

View File

@ -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;

View File

@ -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);