diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4cd4bdab053d..112fd6c55c2c 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -684,6 +684,40 @@ static void acpi_pm_notify_work_func(struct acpi_device_wakeup_context *context) static DEFINE_MUTEX(acpi_wakeup_lock); +static int __acpi_device_wakeup_enable(struct acpi_device *adev, + u32 target_state, int max_count) +{ + struct acpi_device_wakeup *wakeup = &adev->wakeup; + acpi_status status; + int error = 0; + + mutex_lock(&acpi_wakeup_lock); + + if (wakeup->enable_count >= max_count) + goto out; + + if (wakeup->enable_count > 0) + goto inc; + + error = acpi_enable_wakeup_device_power(adev, target_state); + if (error) + goto out; + + status = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number); + if (ACPI_FAILURE(status)) { + acpi_disable_wakeup_device_power(adev); + error = -EIO; + goto out; + } + +inc: + wakeup->enable_count++; + +out: + mutex_unlock(&acpi_wakeup_lock); + return error; +} + /** * acpi_device_wakeup_enable - Enable wakeup functionality for device. * @adev: ACPI device to enable wakeup functionality for. @@ -698,31 +732,7 @@ static DEFINE_MUTEX(acpi_wakeup_lock); */ static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state) { - struct acpi_device_wakeup *wakeup = &adev->wakeup; - acpi_status status; - int error = 0; - - mutex_lock(&acpi_wakeup_lock); - - if (wakeup->enable_count > 0) - goto out; - - error = acpi_enable_wakeup_device_power(adev, target_state); - if (error) - goto out; - - status = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number); - if (ACPI_FAILURE(status)) { - acpi_disable_wakeup_device_power(adev); - error = -EIO; - goto out; - } - - wakeup->enable_count++; - -out: - mutex_unlock(&acpi_wakeup_lock); - return error; + return __acpi_device_wakeup_enable(adev, target_state, 1); } /** @@ -752,12 +762,8 @@ out: mutex_unlock(&acpi_wakeup_lock); } -/** - * acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device. - * @dev: Device to enable/disable to generate wakeup events. - * @enable: Whether to enable or disable the wakeup functionality. - */ -int acpi_pm_set_device_wakeup(struct device *dev, bool enable) +static int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, + int max_count) { struct acpi_device *adev; int error; @@ -777,13 +783,35 @@ int acpi_pm_set_device_wakeup(struct device *dev, bool enable) return 0; } - error = acpi_device_wakeup_enable(adev, acpi_target_system_state()); + error = __acpi_device_wakeup_enable(adev, acpi_target_system_state(), + max_count); if (!error) dev_dbg(dev, "Wakeup enabled by ACPI\n"); return error; } -EXPORT_SYMBOL(acpi_pm_set_device_wakeup); + +/** + * acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device. + * @dev: Device to enable/disable to generate wakeup events. + * @enable: Whether to enable or disable the wakeup functionality. + */ +int acpi_pm_set_device_wakeup(struct device *dev, bool enable) +{ + return __acpi_pm_set_device_wakeup(dev, enable, 1); +} +EXPORT_SYMBOL_GPL(acpi_pm_set_device_wakeup); + +/** + * acpi_pm_set_bridge_wakeup - Enable/disable remote wakeup for given bridge. + * @dev: Bridge device to enable/disable to generate wakeup events. + * @enable: Whether to enable or disable the wakeup functionality. + */ +int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable) +{ + return __acpi_pm_set_device_wakeup(dev, enable, INT_MAX); +} +EXPORT_SYMBOL_GPL(acpi_pm_set_bridge_wakeup); /** * acpi_dev_pm_low_power - Put ACPI device into a low-power state. diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index e70c1c7ba1bf..a8da543b3814 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -573,7 +573,7 @@ static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable) { while (bus->parent) { if (acpi_pm_device_can_wakeup(&bus->self->dev)) - return acpi_pm_set_device_wakeup(&bus->self->dev, enable); + return acpi_pm_set_bridge_wakeup(&bus->self->dev, enable); bus = bus->parent; } @@ -581,7 +581,7 @@ static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable) /* We have reached the root bus. */ if (bus->bridge) { if (acpi_pm_device_can_wakeup(bus->bridge)) - return acpi_pm_set_device_wakeup(bus->bridge, enable); + return acpi_pm_set_bridge_wakeup(bus->bridge, enable); } return 0; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index b7df95dbe7e9..de7e8ac9d806 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -606,6 +606,7 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev); bool acpi_pm_device_can_wakeup(struct device *dev); int acpi_pm_device_sleep_state(struct device *, int *, int); int acpi_pm_set_device_wakeup(struct device *dev, bool enable); +int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable); #else static inline void acpi_pm_wakeup_event(struct device *dev) { @@ -636,6 +637,10 @@ static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable) { return -ENODEV; } +static inline int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable) +{ + return -ENODEV; +} #endif #ifdef CONFIG_ACPI_SLEEP