summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-11-02 00:52:19 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-11-02 00:52:19 +0100
commit9bb4064ca3bd771f8df88529a3578f9bb5243911 (patch)
treea148f0ff6ee2f5bb6d556ac7a39e30311862eafd /drivers
parent66c44877242d37873df2abb10e4fce26ff4fc9f9 (diff)
parentd439e64f22ce0eea681ae90c71f584d3a0145ded (diff)
Merge branch 'pm-sleep'
* pm-sleep: PM / hibernate: fix a comment typo input: i8042: Avoid resetting controller on system suspend/resume PM / PCI / ACPI: Kick devices that might have been reset by firmware PM / sleep: Add flags to indicate platform firmware involvement PM / sleep: Drop pm_request_idle() from pm_generic_complete() PCI / PM: Avoid resuming more devices during system suspend PM / wakeup: wakeup_source_create: use kstrdup_const PM / sleep: Report interrupt that caused system wakeup
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpi_lpss.c2
-rw-r--r--drivers/acpi/device_pm.c19
-rw-r--r--drivers/acpi/sleep.c3
-rw-r--r--drivers/base/power/generic_ops.c23
-rw-r--r--drivers/base/power/wakeup.c16
-rw-r--r--drivers/input/serio/i8042.c32
-rw-r--r--drivers/pci/pci-driver.c7
-rw-r--r--drivers/pci/pci.c70
-rw-r--r--drivers/pci/pci.h1
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);