mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	PCI / PM: Avoid resuming PCI devices during system suspend
Commitf25c0ae2b4(ACPI / PM: Avoid resuming devices in ACPI PM domain during system suspend) modified the ACPI PM domain's system suspend callbacks to allow devices attached to it to be left in the runtime-suspended state during system suspend so as to optimize the suspend process. This was based on the general mechanism introduced by commitaae4518b31(PM / sleep: Mechanism to avoid resuming runtime-suspended devices unnecessarily). Extend that approach to PCI devices by modifying the PCI bus type's ->prepare callback to return 1 for devices that are runtime-suspended when it is being executed and that are in a suitable power state and need not be resumed going forward. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
		
							parent
							
								
									ec6f34e5b5
								
							
						
					
					
						commit
						bac2a909a0
					
				
					 4 changed files with 55 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -501,12 +501,29 @@ static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool acpi_pci_need_resume(struct pci_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
 | 
			
		||||
 | 
			
		||||
	if (!adev || !acpi_device_power_manageable(adev))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (device_may_wakeup(&dev->dev) != !!adev->wakeup.prepare_count)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	if (acpi_target_system_state() == ACPI_STATE_S0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return !!adev->power.flags.dsw_present;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct pci_platform_pm_ops acpi_pci_platform_pm = {
 | 
			
		||||
	.is_manageable = acpi_pci_power_manageable,
 | 
			
		||||
	.set_state = acpi_pci_set_power_state,
 | 
			
		||||
	.choose_state = acpi_pci_choose_state,
 | 
			
		||||
	.sleep_wake = acpi_pci_sleep_wake,
 | 
			
		||||
	.run_wake = acpi_pci_run_wake,
 | 
			
		||||
	.need_resume = acpi_pci_need_resume,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void acpi_pci_add_bus(struct pci_bus *bus)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -653,7 +653,6 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)
 | 
			
		|||
static int pci_pm_prepare(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct device_driver *drv = dev->driver;
 | 
			
		||||
	int error = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Devices having power.ignore_children set may still be necessary for
 | 
			
		||||
| 
						 | 
				
			
			@ -662,10 +661,12 @@ static int pci_pm_prepare(struct device *dev)
 | 
			
		|||
	if (dev->power.ignore_children)
 | 
			
		||||
		pm_runtime_resume(dev);
 | 
			
		||||
 | 
			
		||||
	if (drv && drv->pm && drv->pm->prepare)
 | 
			
		||||
		error = drv->pm->prepare(dev);
 | 
			
		||||
 | 
			
		||||
	if (drv && drv->pm && drv->pm->prepare) {
 | 
			
		||||
		int error = drv->pm->prepare(dev);
 | 
			
		||||
		if (error)
 | 
			
		||||
			return error;
 | 
			
		||||
	}
 | 
			
		||||
	return pci_dev_keep_suspended(to_pci_dev(dev));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -521,6 +521,11 @@ static inline int platform_pci_run_wake(struct pci_dev *dev, bool enable)
 | 
			
		|||
			pci_platform_pm->run_wake(dev, enable) : -ENODEV;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool platform_pci_need_resume(struct pci_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pci_raw_set_power_state - Use PCI PM registers to set the power state of
 | 
			
		||||
 *                           given PCI device
 | 
			
		||||
| 
						 | 
				
			
			@ -1999,6 +2004,27 @@ bool pci_dev_run_wake(struct pci_dev *dev)
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(pci_dev_run_wake);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pci_dev_keep_suspended - Check if the device can stay in the suspended state.
 | 
			
		||||
 * @pci_dev: Device to check.
 | 
			
		||||
 *
 | 
			
		||||
 * Return 'true' if the device is runtime-suspended, it doesn't have to be
 | 
			
		||||
 * reconfigured due to wakeup settings difference between system and runtime
 | 
			
		||||
 * suspend and the current power state of it is suitable for the upcoming
 | 
			
		||||
 * (system) transition.
 | 
			
		||||
 */
 | 
			
		||||
bool pci_dev_keep_suspended(struct pci_dev *pci_dev)
 | 
			
		||||
{
 | 
			
		||||
	struct device *dev = &pci_dev->dev;
 | 
			
		||||
 | 
			
		||||
	if (!pm_runtime_suspended(dev)
 | 
			
		||||
	    || (device_can_wakeup(dev) && !device_may_wakeup(dev))
 | 
			
		||||
	    || platform_pci_need_resume(pci_dev))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return pci_target_state(pci_dev) == pci_dev->current_state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void pci_config_pm_runtime_get(struct pci_dev *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct device *dev = &pdev->dev;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,10 @@ int pci_probe_reset_function(struct pci_dev *dev);
 | 
			
		|||
 *		for given device (the device's wake-up capability has to be
 | 
			
		||||
 *		enabled by @sleep_wake for this feature to work)
 | 
			
		||||
 *
 | 
			
		||||
 * @need_resume: returns 'true' if the given device (which is currently
 | 
			
		||||
 *		suspended) needs to be resumed to be configured for system
 | 
			
		||||
 *		wakeup.
 | 
			
		||||
 *
 | 
			
		||||
 * If given platform is generally capable of power managing PCI devices, all of
 | 
			
		||||
 * these callbacks are mandatory.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +63,7 @@ struct pci_platform_pm_ops {
 | 
			
		|||
	pci_power_t (*choose_state)(struct pci_dev *dev);
 | 
			
		||||
	int (*sleep_wake)(struct pci_dev *dev, bool enable);
 | 
			
		||||
	int (*run_wake)(struct pci_dev *dev, bool enable);
 | 
			
		||||
	bool (*need_resume)(struct pci_dev *dev);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +72,7 @@ void pci_power_up(struct pci_dev *dev);
 | 
			
		|||
void pci_disable_enabled_device(struct pci_dev *dev);
 | 
			
		||||
int pci_finish_runtime_suspend(struct pci_dev *dev);
 | 
			
		||||
int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
 | 
			
		||||
bool pci_dev_keep_suspended(struct pci_dev *dev);
 | 
			
		||||
void pci_config_pm_runtime_get(struct pci_dev *dev);
 | 
			
		||||
void pci_config_pm_runtime_put(struct pci_dev *dev);
 | 
			
		||||
void pci_pm_init(struct pci_dev *dev);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue