mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									54f5758846
								
							
						
					
					
						commit
						5ef4108291
					
				
					 3 changed files with 82 additions and 76 deletions
				
			
		| 
						 | 
				
			
			@ -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;
 | 
			
		||||
	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
 | 
			
		||||
		 * 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);
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
		}
 | 
			
		||||
	/* 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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
 *	@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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -279,6 +279,7 @@ int ata_tport_add(struct device *parent,
 | 
			
		|||
	struct device *dev = &ap->tdev;
 | 
			
		||||
 | 
			
		||||
	device_initialize(dev);
 | 
			
		||||
	dev->type = &ata_port_type;
 | 
			
		||||
 | 
			
		||||
	dev->parent = get_device(parent);
 | 
			
		||||
	dev->release = ata_tport_release;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,6 +58,7 @@ extern int atapi_passthru16;
 | 
			
		|||
extern int libata_fua;
 | 
			
		||||
extern int libata_noacpi;
 | 
			
		||||
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 void ata_force_cbl(struct ata_port *ap);
 | 
			
		||||
extern u64 ata_tf_to_lba(const struct ata_taskfile *tf);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue