diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/acpi_lpss.c | 2 | ||||
-rw-r--r-- | drivers/acpi/device_pm.c | 19 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 3 | ||||
-rw-r--r-- | drivers/base/power/generic_ops.c | 23 | ||||
-rw-r--r-- | drivers/base/power/wakeup.c | 16 | ||||
-rw-r--r-- | drivers/input/serio/i8042.c | 32 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 7 | ||||
-rw-r--r-- | drivers/pci/pci.c | 70 | ||||
-rw-r--r-- | drivers/pci/pci.h | 1 |
9 files changed, 133 insertions, 40 deletions
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index f51bd0d0bc17..f9e0d09f7c66 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -664,7 +664,7 @@ static struct dev_pm_domain acpi_lpss_pm_domain = { #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, + .complete = pm_complete_with_resume_check, .suspend = acpi_subsys_suspend, .suspend_late = acpi_lpss_suspend_late, .resume_early = acpi_lpss_resume_early, diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4806b7f856c4..08a02cdc737c 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -963,23 +963,6 @@ int acpi_subsys_prepare(struct device *dev) EXPORT_SYMBOL_GPL(acpi_subsys_prepare); /** - * acpi_subsys_complete - Finalize device's resume during system resume. - * @dev: Device to handle. - */ -void acpi_subsys_complete(struct device *dev) -{ - pm_generic_complete(dev); - /* - * If the device had been runtime-suspended before the system went into - * the sleep state it is going out of and it has never been resumed till - * now, resume it in case the firmware powered it up. - */ - if (dev->power.direct_complete) - pm_request_resume(dev); -} -EXPORT_SYMBOL_GPL(acpi_subsys_complete); - -/** * acpi_subsys_suspend - Run the device driver's suspend callback. * @dev: Device to handle. * @@ -1047,7 +1030,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { .runtime_resume = acpi_subsys_runtime_resume, #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, - .complete = acpi_subsys_complete, + .complete = pm_complete_with_resume_check, .suspend = acpi_subsys_suspend, .suspend_late = acpi_subsys_suspend_late, .resume_early = acpi_subsys_resume_early, diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 3fe1fbec7677..0d94621dc856 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -487,6 +487,8 @@ static int acpi_suspend_begin(suspend_state_t pm_state) pr_err("ACPI does not support sleep state S%u\n", acpi_state); return -ENOSYS; } + if (acpi_state > ACPI_STATE_S1) + pm_set_suspend_via_firmware(); acpi_pm_start(acpi_state); return 0; @@ -522,6 +524,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state) if (error) return error; pr_info(PREFIX "Low-level resume complete\n"); + pm_set_resume_via_firmware(); break; } trace_suspend_resume(TPS("acpi_suspend"), acpi_state, false); diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index 96a92db83cad..07c3c4a9522d 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -9,6 +9,7 @@ #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/export.h> +#include <linux/suspend.h> #ifdef CONFIG_PM /** @@ -296,11 +297,27 @@ void pm_generic_complete(struct device *dev) if (drv && drv->pm && drv->pm->complete) drv->pm->complete(dev); +} +/** + * pm_complete_with_resume_check - Complete a device power transition. + * @dev: Device to handle. + * + * Complete a device power transition during a system-wide power transition and + * optionally schedule a runtime resume of the device if the system resume in + * progress has been initated by the platform firmware and the device had its + * power.direct_complete flag set. + */ +void pm_complete_with_resume_check(struct device *dev) +{ + pm_generic_complete(dev); /* - * Let runtime PM try to suspend devices that haven't been in use before - * going into the system-wide sleep state we're resuming from. + * If the device had been runtime-suspended before the system went into + * the sleep state it is going out of and it has never been resumed till + * now, resume it in case the firmware powered it up. */ - pm_request_idle(dev); + if (dev->power.direct_complete && pm_resume_via_firmware()) + pm_request_resume(dev); } +EXPORT_SYMBOL_GPL(pm_complete_with_resume_check); #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 51f15bc15774..a1e0b9ab847a 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -25,6 +25,9 @@ */ bool events_check_enabled __read_mostly; +/* First wakeup IRQ seen by the kernel in the last cycle. */ +unsigned int pm_wakeup_irq __read_mostly; + /* If set and the system is suspending, terminate the suspend. */ static bool pm_abort_suspend __read_mostly; @@ -91,7 +94,7 @@ struct wakeup_source *wakeup_source_create(const char *name) if (!ws) return NULL; - wakeup_source_prepare(ws, name ? kstrdup(name, GFP_KERNEL) : NULL); + wakeup_source_prepare(ws, name ? kstrdup_const(name, GFP_KERNEL) : NULL); return ws; } EXPORT_SYMBOL_GPL(wakeup_source_create); @@ -154,7 +157,7 @@ void wakeup_source_destroy(struct wakeup_source *ws) wakeup_source_drop(ws); wakeup_source_record(ws); - kfree(ws->name); + kfree_const(ws->name); kfree(ws); } EXPORT_SYMBOL_GPL(wakeup_source_destroy); @@ -868,6 +871,15 @@ EXPORT_SYMBOL_GPL(pm_system_wakeup); void pm_wakeup_clear(void) { pm_abort_suspend = false; + pm_wakeup_irq = 0; +} + +void pm_system_irq_wakeup(unsigned int irq_number) +{ + if (pm_wakeup_irq == 0) { + pm_wakeup_irq = irq_number; + pm_system_wakeup(); + } } /** diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index db91de539ee3..454195709a82 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -24,6 +24,7 @@ #include <linux/platform_device.h> #include <linux/i8042.h> #include <linux/slab.h> +#include <linux/suspend.h> #include <asm/io.h> @@ -1170,7 +1171,8 @@ static int i8042_pm_suspend(struct device *dev) { int i; - i8042_controller_reset(true); + if (pm_suspend_via_firmware()) + i8042_controller_reset(true); /* Set up serio interrupts for system wakeup. */ for (i = 0; i < I8042_NUM_PORTS; i++) { @@ -1183,8 +1185,17 @@ static int i8042_pm_suspend(struct device *dev) return 0; } +static int i8042_pm_resume_noirq(struct device *dev) +{ + if (!pm_resume_via_firmware()) + i8042_interrupt(0, NULL); + + return 0; +} + static int i8042_pm_resume(struct device *dev) { + bool force_reset; int i; for (i = 0; i < I8042_NUM_PORTS; i++) { @@ -1195,11 +1206,21 @@ static int i8042_pm_resume(struct device *dev) } /* - * On resume from S2R we always try to reset the controller - * to bring it in a sane state. (In case of S2D we expect - * BIOS to reset the controller for us.) + * If platform firmware was not going to be involved in suspend, we did + * not restore the controller state to whatever it had been at boot + * time, so we do not need to do anything. */ - return i8042_controller_resume(true); + if (!pm_suspend_via_firmware()) + return 0; + + /* + * We only need to reset the controller if we are resuming after handing + * off control to the platform firmware, otherwise we can simply restore + * the mode. + */ + force_reset = pm_resume_via_firmware(); + + return i8042_controller_resume(force_reset); } static int i8042_pm_thaw(struct device *dev) @@ -1223,6 +1244,7 @@ static int i8042_pm_restore(struct device *dev) static const struct dev_pm_ops i8042_pm_ops = { .suspend = i8042_pm_suspend, + .resume_noirq = i8042_pm_resume_noirq, .resume = i8042_pm_resume, .thaw = i8042_pm_thaw, .poweroff = i8042_pm_reset, diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 108a3118ace7..306124bba61e 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -684,10 +684,16 @@ static int pci_pm_prepare(struct device *dev) return pci_dev_keep_suspended(to_pci_dev(dev)); } +static void pci_pm_complete(struct device *dev) +{ + pci_dev_complete_resume(to_pci_dev(dev)); + pm_complete_with_resume_check(dev); +} #else /* !CONFIG_PM_SLEEP */ #define pci_pm_prepare NULL +#define pci_pm_complete NULL #endif /* !CONFIG_PM_SLEEP */ @@ -1218,6 +1224,7 @@ static int pci_pm_runtime_idle(struct device *dev) static const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, + .complete = pci_pm_complete, .suspend = pci_pm_suspend, .resume = pci_pm_resume, .freeze = pci_pm_freeze, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6a9a1116f1eb..78693fc5dbe9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1710,15 +1710,7 @@ static void pci_pme_list_scan(struct work_struct *work) mutex_unlock(&pci_pme_list_mutex); } -/** - * pci_pme_active - enable or disable PCI device's PME# function - * @dev: PCI device to handle. - * @enable: 'true' to enable PME# generation; 'false' to disable it. - * - * The caller must verify that the device is capable of generating PME# before - * calling this function with @enable equal to 'true'. - */ -void pci_pme_active(struct pci_dev *dev, bool enable) +static void __pci_pme_active(struct pci_dev *dev, bool enable) { u16 pmcsr; @@ -1732,6 +1724,19 @@ void pci_pme_active(struct pci_dev *dev, bool enable) pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); +} + +/** + * pci_pme_active - enable or disable PCI device's PME# function + * @dev: PCI device to handle. + * @enable: 'true' to enable PME# generation; 'false' to disable it. + * + * The caller must verify that the device is capable of generating PME# before + * calling this function with @enable equal to 'true'. + */ +void pci_pme_active(struct pci_dev *dev, bool enable) +{ + __pci_pme_active(dev, enable); /* * PCI (as opposed to PCIe) PME requires that the device have @@ -2032,17 +2037,60 @@ EXPORT_SYMBOL_GPL(pci_dev_run_wake); * 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. + * + * If the device is not configured for system wakeup, disable PME for it before + * returning 'true' to prevent it from waking up the system unnecessarily. */ 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)) + || pci_target_state(pci_dev) != pci_dev->current_state || platform_pci_need_resume(pci_dev)) return false; - return pci_target_state(pci_dev) == pci_dev->current_state; + /* + * At this point the device is good to go unless it's been configured + * to generate PME at the runtime suspend time, but it is not supposed + * to wake up the system. In that case, simply disable PME for it + * (it will have to be re-enabled on exit from system resume). + * + * If the device's power state is D3cold and the platform check above + * hasn't triggered, the device's configuration is suitable and we don't + * need to manipulate it at all. + */ + spin_lock_irq(&dev->power.lock); + + if (pm_runtime_suspended(dev) && pci_dev->current_state < PCI_D3cold && + !device_may_wakeup(dev)) + __pci_pme_active(pci_dev, false); + + spin_unlock_irq(&dev->power.lock); + return true; +} + +/** + * pci_dev_complete_resume - Finalize resume from system sleep for a device. + * @pci_dev: Device to handle. + * + * If the device is runtime suspended and wakeup-capable, enable PME for it as + * it might have been disabled during the prepare phase of system suspend if + * the device was not configured for system wakeup. + */ +void pci_dev_complete_resume(struct pci_dev *pci_dev) +{ + struct device *dev = &pci_dev->dev; + + if (!pci_dev_run_wake(pci_dev)) + return; + + spin_lock_irq(&dev->power.lock); + + if (pm_runtime_suspended(dev) && pci_dev->current_state < PCI_D3cold) + __pci_pme_active(pci_dev, true); + + spin_unlock_irq(&dev->power.lock); } void pci_config_pm_runtime_get(struct pci_dev *pdev) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 24ba9dc8910a..037e787a3ad5 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -75,6 +75,7 @@ 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_dev_complete_resume(struct pci_dev *pci_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); |