Merge suspend-to-idle rework material for v5.4.
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 17 Sep 2019 07:35:35 +0000 (09:35 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 17 Sep 2019 07:35:35 +0000 (09:35 +0200)
* pm-s2idle-rework: (21 commits)
  ACPI: PM: s2idle: Always set up EC GPE for system wakeup
  ACPI: PM: s2idle: Avoid rearming SCI for wakeup unnecessarily
  PM: suspend: Fix platform_suspend_prepare_noirq()
  intel-hid: Disable button array during suspend-to-idle
  intel-hid: intel-vbtn: Avoid leaking wakeup_mode set
  ACPI: PM: s2idle: Execute LPS0 _DSM functions with suspended devices
  ACPI: EC: PM: Make acpi_ec_dispatch_gpe() print debug message
  ACPI: EC: PM: Consolidate some code depending on PM_SLEEP
  ACPI: PM: s2idle: Eliminate acpi_sleep_no_ec_events()
  ACPI: PM: s2idle: Switch EC over to polling during "noirq" suspend
  ACPI: PM: s2idle: Add acpi.sleep_no_lps0 module parameter
  ACPI: PM: s2idle: Rearrange lps0_device_attach()
  ACPI: PM: Set up EC GPE for system wakeup from drivers that need it
  PM: sleep: Drop dpm_noirq_begin() and dpm_noirq_end()
  PM: sleep: Integrate suspend-to-idle with generig suspend flow
  PM: sleep: Simplify suspend-to-idle control flow
  ACPI: PM: Set s2idle_wakeup earlier and clear it later
  PM: sleep: Fix possible overflow in pm_system_cancel_wakeup()
  ACPI: EC: Return bool from acpi_ec_dispatch_gpe()
  ACPICA: Return u32 from acpi_dispatch_gpe()
  ...

15 files changed:
drivers/acpi/acpica/evxfgpe.c
drivers/acpi/ec.c
drivers/acpi/internal.h
drivers/acpi/sleep.c
drivers/base/power/main.c
drivers/base/power/wakeup.c
drivers/platform/x86/intel-hid.c
drivers/platform/x86/intel-vbtn.c
include/acpi/acpixf.h
include/linux/acpi.h
include/linux/interrupt.h
include/linux/pm.h
include/linux/suspend.h
kernel/irq/pm.c
kernel/power/suspend.c

index 710488ec59e99f7819a7de1d685f87fe6a87b500..04a40d563dd6771edb5bcd7ad12c14eba8f3259a 100644 (file)
@@ -644,17 +644,17 @@ ACPI_EXPORT_SYMBOL(acpi_get_gpe_status)
  * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
  *              gpe_number          - GPE level within the GPE block
  *
- * RETURN:      None
+ * RETURN:      INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
  *
  * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function
  *              (e.g. EC) or method (e.g. _Lxx/_Exx) handler.
  *
  ******************************************************************************/
-void acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)
+u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)
 {
        ACPI_FUNCTION_TRACE(acpi_dispatch_gpe);
 
-       acpi_ev_detect_gpe(gpe_device, NULL, gpe_number);
+       return acpi_ev_detect_gpe(gpe_device, NULL, gpe_number);
 }
 
 ACPI_EXPORT_SYMBOL(acpi_dispatch_gpe)
index c33756ed33049cd9e9eced123177c556f76874ee..da1e5c5ce15065dbddac0e6c0b6abe1bde43fe2d 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
+#include <linux/suspend.h>
 #include <linux/acpi.h>
 #include <linux/dmi.h>
 #include <asm/io.h>
@@ -1048,24 +1049,6 @@ void acpi_ec_unblock_transactions(void)
                acpi_ec_start(first_ec, true);
 }
 
-void acpi_ec_mark_gpe_for_wake(void)
-{
-       if (first_ec && !ec_no_wakeup)
-               acpi_mark_gpe_for_wake(NULL, first_ec->gpe);
-}
-
-void acpi_ec_set_gpe_wake_mask(u8 action)
-{
-       if (first_ec && !ec_no_wakeup)
-               acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action);
-}
-
-void acpi_ec_dispatch_gpe(void)
-{
-       if (first_ec)
-               acpi_dispatch_gpe(NULL, first_ec->gpe);
-}
-
 /* --------------------------------------------------------------------------
                                 Event Management
    -------------------------------------------------------------------------- */
@@ -1931,7 +1914,7 @@ static int acpi_ec_suspend(struct device *dev)
        struct acpi_ec *ec =
                acpi_driver_data(to_acpi_device(dev));
 
-       if (acpi_sleep_no_ec_events() && ec_freeze_events)
+       if (!pm_suspend_no_platform() && ec_freeze_events)
                acpi_ec_disable_event(ec);
        return 0;
 }
@@ -1948,8 +1931,7 @@ static int acpi_ec_suspend_noirq(struct device *dev)
            ec->reference_count >= 1)
                acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE);
 
-       if (acpi_sleep_no_ec_events())
-               acpi_ec_enter_noirq(ec);
+       acpi_ec_enter_noirq(ec);
 
        return 0;
 }
@@ -1958,8 +1940,7 @@ static int acpi_ec_resume_noirq(struct device *dev)
 {
        struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev));
 
-       if (acpi_sleep_no_ec_events())
-               acpi_ec_leave_noirq(ec);
+       acpi_ec_leave_noirq(ec);
 
        if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) &&
            ec->reference_count >= 1)
@@ -1976,7 +1957,35 @@ static int acpi_ec_resume(struct device *dev)
        acpi_ec_enable_event(ec);
        return 0;
 }
-#endif
+
+void acpi_ec_mark_gpe_for_wake(void)
+{
+       if (first_ec && !ec_no_wakeup)
+               acpi_mark_gpe_for_wake(NULL, first_ec->gpe);
+}
+EXPORT_SYMBOL_GPL(acpi_ec_mark_gpe_for_wake);
+
+void acpi_ec_set_gpe_wake_mask(u8 action)
+{
+       if (pm_suspend_no_platform() && first_ec && !ec_no_wakeup)
+               acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action);
+}
+
+bool acpi_ec_dispatch_gpe(void)
+{
+       u32 ret;
+
+       if (!first_ec)
+               return false;
+
+       ret = acpi_dispatch_gpe(NULL, first_ec->gpe);
+       if (ret == ACPI_INTERRUPT_HANDLED) {
+               pm_pr_dbg("EC GPE dispatched\n");
+               return true;
+       }
+       return false;
+}
+#endif /* CONFIG_PM_SLEEP */
 
 static const struct dev_pm_ops acpi_ec_pm = {
        SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq)
index f4c2fe6be4f2d8a9bfe48fb880755771bbe823cd..afe6636f9ad39d33333c2aae9e3e0467b70f9b64 100644 (file)
@@ -194,9 +194,6 @@ void acpi_ec_ecdt_probe(void);
 void acpi_ec_dsdt_probe(void);
 void acpi_ec_block_transactions(void);
 void acpi_ec_unblock_transactions(void);
-void acpi_ec_mark_gpe_for_wake(void);
-void acpi_ec_set_gpe_wake_mask(u8 action);
-void acpi_ec_dispatch_gpe(void);
 int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
                              acpi_handle handle, acpi_ec_query_func func,
                              void *data);
@@ -204,6 +201,7 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
 
 #ifdef CONFIG_PM_SLEEP
 void acpi_ec_flush_work(void);
+bool acpi_ec_dispatch_gpe(void);
 #endif
 
 
@@ -212,11 +210,9 @@ void acpi_ec_flush_work(void);
   -------------------------------------------------------------------------- */
 #ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
 extern bool acpi_s2idle_wakeup(void);
-extern bool acpi_sleep_no_ec_events(void);
 extern int acpi_sleep_init(void);
 #else
 static inline bool acpi_s2idle_wakeup(void) { return false; }
-static inline bool acpi_sleep_no_ec_events(void) { return true; }
 static inline int acpi_sleep_init(void) { return -ENXIO; }
 #endif
 
index f0fe7c15d657288d28a20f6b906c62f955c5fcda..9fa77d72ef27f52369a23f9ed99788705661bc3a 100644 (file)
@@ -89,6 +89,10 @@ bool acpi_sleep_state_supported(u8 sleep_state)
 }
 
 #ifdef CONFIG_ACPI_SLEEP
+static bool sleep_no_lps0 __read_mostly;
+module_param(sleep_no_lps0, bool, 0644);
+MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
+
 static u32 acpi_target_sleep_state = ACPI_STATE_S0;
 
 u32 acpi_target_system_state(void)
@@ -158,11 +162,11 @@ static int __init init_nvs_nosave(const struct dmi_system_id *d)
        return 0;
 }
 
-static bool acpi_sleep_no_lps0;
+static bool acpi_sleep_default_s3;
 
-static int __init init_no_lps0(const struct dmi_system_id *d)
+static int __init init_default_s3(const struct dmi_system_id *d)
 {
-       acpi_sleep_no_lps0 = true;
+       acpi_sleep_default_s3 = true;
        return 0;
 }
 
@@ -363,7 +367,7 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
         * S0 Idle firmware interface.
         */
        {
-       .callback = init_no_lps0,
+       .callback = init_default_s3,
        .ident = "Dell XPS13 9360",
        .matches = {
                DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
@@ -376,7 +380,7 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
         * https://bugzilla.kernel.org/show_bug.cgi?id=199057).
         */
        {
-       .callback = init_no_lps0,
+       .callback = init_default_s3,
        .ident = "ThinkPad X1 Tablet(2016)",
        .matches = {
                DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -524,8 +528,9 @@ static void acpi_pm_end(void)
        acpi_sleep_tts_switch(acpi_target_sleep_state);
 }
 #else /* !CONFIG_ACPI_SLEEP */
+#define sleep_no_lps0  (1)
 #define acpi_target_sleep_state        ACPI_STATE_S0
-#define acpi_sleep_no_lps0     (false)
+#define acpi_sleep_default_s3  (1)
 static inline void acpi_sleep_dmi_check(void) {}
 #endif /* CONFIG_ACPI_SLEEP */
 
@@ -691,7 +696,6 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
        .recover = acpi_pm_finish,
 };
 
-static bool s2idle_in_progress;
 static bool s2idle_wakeup;
 
 /*
@@ -904,42 +908,43 @@ static int lps0_device_attach(struct acpi_device *adev,
        if (lps0_device_handle)
                return 0;
 
-       if (acpi_sleep_no_lps0) {
-               acpi_handle_info(adev->handle,
-                                "Low Power S0 Idle interface disabled\n");
-               return 0;
-       }
-
        if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
                return 0;
 
        guid_parse(ACPI_LPS0_DSM_UUID, &lps0_dsm_guid);
        /* Check if the _DSM is present and as expected. */
        out_obj = acpi_evaluate_dsm(adev->handle, &lps0_dsm_guid, 1, 0, NULL);
-       if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) {
-               char bitmask = *(char *)out_obj->buffer.pointer;
-
-               lps0_dsm_func_mask = bitmask;
-               lps0_device_handle = adev->handle;
-               /*
-                * Use suspend-to-idle by default if the default
-                * suspend mode was not set from the command line.
-                */
-               if (mem_sleep_default > PM_SUSPEND_MEM)
-                       mem_sleep_current = PM_SUSPEND_TO_IDLE;
-
-               acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n",
-                                 bitmask);
-
-               acpi_ec_mark_gpe_for_wake();
-       } else {
+       if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER) {
                acpi_handle_debug(adev->handle,
                                  "_DSM function 0 evaluation failed\n");
+               return 0;
        }
+
+       lps0_dsm_func_mask = *(char *)out_obj->buffer.pointer;
+
        ACPI_FREE(out_obj);
 
+       acpi_handle_debug(adev->handle, "_DSM function mask: 0x%x\n",
+                         lps0_dsm_func_mask);
+
+       lps0_device_handle = adev->handle;
+
        lpi_device_get_constraints();
 
+       /*
+        * Use suspend-to-idle by default if the default suspend mode was not
+        * set from the command line.
+        */
+       if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3)
+               mem_sleep_current = PM_SUSPEND_TO_IDLE;
+
+       /*
+        * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the
+        * EC GPE to be enabled while suspended for certain wakeup devices to
+        * work, so mark it as wakeup-capable.
+        */
+       acpi_ec_mark_gpe_for_wake();
+
        return 0;
 }
 
@@ -951,98 +956,110 @@ static struct acpi_scan_handler lps0_handler = {
 static int acpi_s2idle_begin(void)
 {
        acpi_scan_lock_acquire();
-       s2idle_in_progress = true;
        return 0;
 }
 
 static int acpi_s2idle_prepare(void)
 {
-       if (lps0_device_handle) {
-               acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
-               acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);
-
+       if (acpi_sci_irq_valid()) {
+               enable_irq_wake(acpi_sci_irq);
                acpi_ec_set_gpe_wake_mask(ACPI_GPE_ENABLE);
        }
 
-       if (acpi_sci_irq_valid())
-               enable_irq_wake(acpi_sci_irq);
-
        acpi_enable_wakeup_devices(ACPI_STATE_S0);
 
        /* Change the configuration of GPEs to avoid spurious wakeup. */
        acpi_enable_all_wakeup_gpes();
        acpi_os_wait_events_complete();
+
+       s2idle_wakeup = true;
        return 0;
 }
 
-static void acpi_s2idle_wake(void)
+static int acpi_s2idle_prepare_late(void)
 {
-       if (!lps0_device_handle)
-               return;
+       if (!lps0_device_handle || sleep_no_lps0)
+               return 0;
 
        if (pm_debug_messages_on)
                lpi_check_constraints();
 
+       acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
+       acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);
+
+       return 0;
+}
+
+static void acpi_s2idle_wake(void)
+{
+       /*
+        * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the SCI has
+        * not triggered while suspended, so bail out.
+        */
+       if (!acpi_sci_irq_valid() ||
+           irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq)))
+               return;
+
        /*
-        * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
-        * that the SCI has triggered while suspended, so cancel the wakeup in
-        * case it has not been a wakeup event (the GPEs will be checked later).
+        * If there are EC events to process, the wakeup may be a spurious one
+        * coming from the EC.
         */
-       if (acpi_sci_irq_valid() &&
-           !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
+       if (acpi_ec_dispatch_gpe()) {
+               /*
+                * Cancel the wakeup and process all pending events in case
+                * there are any wakeup ones in there.
+                *
+                * Note that if any non-EC GPEs are active at this point, the
+                * SCI will retrigger after the rearming below, so no events
+                * should be missed by canceling the wakeup here.
+                */
                pm_system_cancel_wakeup();
-               s2idle_wakeup = true;
                /*
-                * On some platforms with the LPS0 _DSM device noirq resume
-                * takes too much time for EC wakeup events to survive, so look
-                * for them now.
+                * The EC driver uses the system workqueue and an additional
+                * special one, so those need to be flushed too.
                 */
-               acpi_ec_dispatch_gpe();
+               acpi_os_wait_events_complete(); /* synchronize EC GPE processing */
+               acpi_ec_flush_work();
+               acpi_os_wait_events_complete(); /* synchronize Notify handling */
+
+               rearm_wake_irq(acpi_sci_irq);
        }
 }
 
-static void acpi_s2idle_sync(void)
+static void acpi_s2idle_restore_early(void)
 {
-       /*
-        * Process all pending events in case there are any wakeup ones.
-        *
-        * The EC driver uses the system workqueue and an additional special
-        * one, so those need to be flushed too.
-        */
-       acpi_os_wait_events_complete(); /* synchronize SCI IRQ handling */
-       acpi_ec_flush_work();
-       acpi_os_wait_events_complete(); /* synchronize Notify handling */
-       s2idle_wakeup = false;
+       if (!lps0_device_handle || sleep_no_lps0)
+               return;
+
+       acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT);
+       acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON);
 }
 
 static void acpi_s2idle_restore(void)
 {
+       s2idle_wakeup = false;
+
        acpi_enable_all_runtime_gpes();
 
        acpi_disable_wakeup_devices(ACPI_STATE_S0);
 
-       if (acpi_sci_irq_valid())
-               disable_irq_wake(acpi_sci_irq);
-
-       if (lps0_device_handle) {
+       if (acpi_sci_irq_valid()) {
                acpi_ec_set_gpe_wake_mask(ACPI_GPE_DISABLE);
-
-               acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT);
-               acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON);
+               disable_irq_wake(acpi_sci_irq);
        }
 }
 
 static void acpi_s2idle_end(void)
 {
-       s2idle_in_progress = false;
        acpi_scan_lock_release();
 }
 
 static const struct platform_s2idle_ops acpi_s2idle_ops = {
        .begin = acpi_s2idle_begin,
        .prepare = acpi_s2idle_prepare,
+       .prepare_late = acpi_s2idle_prepare_late,
        .wake = acpi_s2idle_wake,
-       .sync = acpi_s2idle_sync,
+       .restore_early = acpi_s2idle_restore_early,
        .restore = acpi_s2idle_restore,
        .end = acpi_s2idle_end,
 };
@@ -1063,7 +1080,6 @@ static void acpi_sleep_suspend_setup(void)
 }
 
 #else /* !CONFIG_SUSPEND */
-#define s2idle_in_progress     (false)
 #define s2idle_wakeup          (false)
 #define lps0_device_handle     (NULL)
 static inline void acpi_sleep_suspend_setup(void) {}
@@ -1074,11 +1090,6 @@ bool acpi_s2idle_wakeup(void)
        return s2idle_wakeup;
 }
 
-bool acpi_sleep_no_ec_events(void)
-{
-       return !s2idle_in_progress || !lps0_device_handle;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static u32 saved_bm_rld;
 
index 7fb2c39bc7254f308f7bb170ffabe6427aa448a1..134a8af51511f604ee3ec5d33f58b9fb4fed01f6 100644 (file)
@@ -716,7 +716,7 @@ static void async_resume_noirq(void *data, async_cookie_t cookie)
        put_device(dev);
 }
 
-void dpm_noirq_resume_devices(pm_message_t state)
+static void dpm_noirq_resume_devices(pm_message_t state)
 {
        struct device *dev;
        ktime_t starttime = ktime_get();
@@ -760,13 +760,6 @@ void dpm_noirq_resume_devices(pm_message_t state)
        trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
 
-void dpm_noirq_end(void)
-{
-       resume_device_irqs();
-       device_wakeup_disarm_wake_irqs();
-       cpuidle_resume();
-}
-
 /**
  * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices.
  * @state: PM transition of the system being carried out.
@@ -777,7 +770,11 @@ void dpm_noirq_end(void)
 void dpm_resume_noirq(pm_message_t state)
 {
        dpm_noirq_resume_devices(state);
-       dpm_noirq_end();
+
+       resume_device_irqs();
+       device_wakeup_disarm_wake_irqs();
+
+       cpuidle_resume();
 }
 
 static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev,
@@ -1291,11 +1288,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
        if (async_error)
                goto Complete;
 
-       if (pm_wakeup_pending()) {
-               async_error = -EBUSY;
-               goto Complete;
-       }
-
        if (dev->power.syscore || dev->power.direct_complete)
                goto Complete;
 
@@ -1362,14 +1354,7 @@ static int device_suspend_noirq(struct device *dev)
        return __device_suspend_noirq(dev, pm_transition, false);
 }
 
-void dpm_noirq_begin(void)
-{
-       cpuidle_pause();
-       device_wakeup_arm_wake_irqs();
-       suspend_device_irqs();
-}
-
-int dpm_noirq_suspend_devices(pm_message_t state)
+static int dpm_noirq_suspend_devices(pm_message_t state)
 {
        ktime_t starttime = ktime_get();
        int error = 0;
@@ -1426,7 +1411,11 @@ int dpm_suspend_noirq(pm_message_t state)
 {
        int ret;
 
-       dpm_noirq_begin();
+       cpuidle_pause();
+
+       device_wakeup_arm_wake_irqs();
+       suspend_device_irqs();
+
        ret = dpm_noirq_suspend_devices(state);
        if (ret)
                dpm_resume_noirq(resume_event(state));
index e58b070985b14eb5eaf5c8a69453fa046785d448..5817b51d2b1537017320926312f46c3f029ff50b 100644 (file)
@@ -879,7 +879,7 @@ EXPORT_SYMBOL_GPL(pm_system_wakeup);
 
 void pm_system_cancel_wakeup(void)
 {
-       atomic_dec(&pm_abort_suspend);
+       atomic_dec_if_positive(&pm_abort_suspend);
 }
 
 void pm_wakeup_clear(bool reset)
index bc0d55a5901594727d62c9676b8480d8c2fba7c7..ef6d4bd77b1a10dcf42728a654a58a5e1534374e 100644 (file)
@@ -253,35 +253,45 @@ static void intel_button_array_enable(struct device *device, bool enable)
 
 static int intel_hid_pm_prepare(struct device *device)
 {
-       struct intel_hid_priv *priv = dev_get_drvdata(device);
+       if (device_may_wakeup(device)) {
+               struct intel_hid_priv *priv = dev_get_drvdata(device);
 
-       priv->wakeup_mode = true;
+               priv->wakeup_mode = true;
+       }
        return 0;
 }
 
+static void intel_hid_pm_complete(struct device *device)
+{
+       struct intel_hid_priv *priv = dev_get_drvdata(device);
+
+       priv->wakeup_mode = false;
+}
+
 static int intel_hid_pl_suspend_handler(struct device *device)
 {
-       if (pm_suspend_via_firmware()) {
+       intel_button_array_enable(device, false);
+
+       if (!pm_suspend_no_platform())
                intel_hid_set_enable(device, false);
-               intel_button_array_enable(device, false);
-       }
+
        return 0;
 }
 
 static int intel_hid_pl_resume_handler(struct device *device)
 {
-       struct intel_hid_priv *priv = dev_get_drvdata(device);
+       intel_hid_pm_complete(device);
 
-       priv->wakeup_mode = false;
-       if (pm_resume_via_firmware()) {
+       if (!pm_suspend_no_platform())
                intel_hid_set_enable(device, true);
-               intel_button_array_enable(device, true);
-       }
+
+       intel_button_array_enable(device, true);
        return 0;
 }
 
 static const struct dev_pm_ops intel_hid_pl_pm_ops = {
        .prepare = intel_hid_pm_prepare,
+       .complete = intel_hid_pm_complete,
        .freeze  = intel_hid_pl_suspend_handler,
        .thaw  = intel_hid_pl_resume_handler,
        .restore  = intel_hid_pl_resume_handler,
@@ -491,6 +501,12 @@ static int intel_hid_probe(struct platform_device *device)
        }
 
        device_init_wakeup(&device->dev, true);
+       /*
+        * In order for system wakeup to work, the EC GPE has to be marked as
+        * a wakeup one, so do that here (this setting will persist, but it has
+        * no effect until the wakeup mask is set for the EC GPE).
+        */
+       acpi_ec_mark_gpe_for_wake();
        return 0;
 
 err_remove_notify:
index a0d0cecff55fd908a3b3096238c529a2ed657217..b74932307d69b292effdce8283aad646f251e2f8 100644 (file)
@@ -176,6 +176,12 @@ static int intel_vbtn_probe(struct platform_device *device)
                return -EBUSY;
 
        device_init_wakeup(&device->dev, true);
+       /*
+        * In order for system wakeup to work, the EC GPE has to be marked as
+        * a wakeup one, so do that here (this setting will persist, but it has
+        * no effect until the wakeup mask is set for the EC GPE).
+        */
+       acpi_ec_mark_gpe_for_wake();
        return 0;
 }
 
@@ -195,22 +201,30 @@ static int intel_vbtn_remove(struct platform_device *device)
 
 static int intel_vbtn_pm_prepare(struct device *dev)
 {
-       struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
+       if (device_may_wakeup(dev)) {
+               struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
 
-       priv->wakeup_mode = true;
+               priv->wakeup_mode = true;
+       }
        return 0;
 }
 
-static int intel_vbtn_pm_resume(struct device *dev)
+static void intel_vbtn_pm_complete(struct device *dev)
 {
        struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
 
        priv->wakeup_mode = false;
+}
+
+static int intel_vbtn_pm_resume(struct device *dev)
+{
+       intel_vbtn_pm_complete(dev);
        return 0;
 }
 
 static const struct dev_pm_ops intel_vbtn_pm_ops = {
        .prepare = intel_vbtn_pm_prepare,
+       .complete = intel_vbtn_pm_complete,
        .resume = intel_vbtn_pm_resume,
        .restore = intel_vbtn_pm_resume,
        .thaw = intel_vbtn_pm_resume,
index 3845c8fcc94e5de64d09b679f61f5ce0eca5b45c..4ed603a3b4487af284036db2a404af948998867d 100644 (file)
@@ -297,6 +297,9 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running);
 #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
        ACPI_EXTERNAL_RETURN_OK(prototype)
 
+#define ACPI_HW_DEPENDENT_RETURN_UINT32(prototype) \
+       ACPI_EXTERNAL_RETURN_UINT32(prototype)
+
 #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
        ACPI_EXTERNAL_RETURN_VOID(prototype)
 
@@ -307,6 +310,9 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_and_running);
 #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
        static ACPI_INLINE prototype {return(AE_OK);}
 
+#define ACPI_HW_DEPENDENT_RETURN_UINT32(prototype) \
+       static ACPI_INLINE prototype {return(0);}
+
 #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
        static ACPI_INLINE prototype {return;}
 
@@ -738,7 +744,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                                     u32 gpe_number,
                                                     acpi_event_status
                                                     *event_status))
-ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number))
+ACPI_HW_DEPENDENT_RETURN_UINT32(u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void))
index 9426b9aaed86f7db346aa4b0cd7010b7b75be62d..e65a4c5bbeaefcd1b0c84eb7a062976a080cbf7c 100644 (file)
@@ -931,6 +931,8 @@ int acpi_subsys_suspend_noirq(struct device *dev);
 int acpi_subsys_suspend(struct device *dev);
 int acpi_subsys_freeze(struct device *dev);
 int acpi_subsys_poweroff(struct device *dev);
+void acpi_ec_mark_gpe_for_wake(void);
+void acpi_ec_set_gpe_wake_mask(u8 action);
 #else
 static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
 static inline void acpi_subsys_complete(struct device *dev) {}
@@ -939,6 +941,8 @@ static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; }
 static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
 static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
 static inline int acpi_subsys_poweroff(struct device *dev) { return 0; }
+static inline void acpi_ec_mark_gpe_for_wake(void) {}
+static inline void acpi_ec_set_gpe_wake_mask(u8 action) {}
 #endif
 
 #ifdef CONFIG_ACPI
index 5b8328a99b2aaa4295f4eadf763657c69c0fee24..0e9cdb3efda742b4f72d09fbd8b21f14f87c0969 100644 (file)
@@ -238,6 +238,7 @@ extern void teardown_percpu_nmi(unsigned int irq);
 /* The following three functions are for the core kernel use only. */
 extern void suspend_device_irqs(void);
 extern void resume_device_irqs(void);
+extern void rearm_wake_irq(unsigned int irq);
 
 /**
  * struct irq_affinity_notify - context for notification of IRQ affinity changes
index 3619a870eaa489fbfd45a28c256af288ece278ee..4c441be03079d5ddef729ec9b486d690ca2af960 100644 (file)
@@ -712,8 +712,6 @@ struct dev_pm_domain {
 extern void device_pm_lock(void);
 extern void dpm_resume_start(pm_message_t state);
 extern void dpm_resume_end(pm_message_t state);
-extern void dpm_noirq_resume_devices(pm_message_t state);
-extern void dpm_noirq_end(void);
 extern void dpm_resume_noirq(pm_message_t state);
 extern void dpm_resume_early(pm_message_t state);
 extern void dpm_resume(pm_message_t state);
@@ -722,8 +720,6 @@ extern void dpm_complete(pm_message_t state);
 extern void device_pm_unlock(void);
 extern int dpm_suspend_end(pm_message_t state);
 extern int dpm_suspend_start(pm_message_t state);
-extern void dpm_noirq_begin(void);
-extern int dpm_noirq_suspend_devices(pm_message_t state);
 extern int dpm_suspend_noirq(pm_message_t state);
 extern int dpm_suspend_late(pm_message_t state);
 extern int dpm_suspend(pm_message_t state);
index 9c0ad1a3a7271995f13d57ba30729bdf99929f42..6fc8843f1c9e93624302b5f28ab112df1a7ae0e7 100644 (file)
@@ -190,8 +190,9 @@ struct platform_suspend_ops {
 struct platform_s2idle_ops {
        int (*begin)(void);
        int (*prepare)(void);
+       int (*prepare_late)(void);
        void (*wake)(void);
-       void (*sync)(void);
+       void (*restore_early)(void);
        void (*restore)(void);
        void (*end)(void);
 };
@@ -336,6 +337,7 @@ static inline void pm_set_suspend_via_firmware(void) {}
 static inline void pm_set_resume_via_firmware(void) {}
 static inline bool pm_suspend_via_firmware(void) { return false; }
 static inline bool pm_resume_via_firmware(void) { return false; }
+static inline bool pm_suspend_no_platform(void) { return false; }
 static inline bool pm_suspend_default_s2idle(void) { return false; }
 
 static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {}
index d6961d3c6f9e2673b8ecf0b9d4e610b77e42750a..8f557fa1f4fe47642dd0782ebf55a7be5a3a0f55 100644 (file)
@@ -176,6 +176,26 @@ static void resume_irqs(bool want_early)
        }
 }
 
+/**
+ * rearm_wake_irq - rearm a wakeup interrupt line after signaling wakeup
+ * @irq: Interrupt to rearm
+ */
+void rearm_wake_irq(unsigned int irq)
+{
+       unsigned long flags;
+       struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
+
+       if (!desc || !(desc->istate & IRQS_SUSPENDED) ||
+           !irqd_is_wakeup_set(&desc->irq_data))
+               return;
+
+       desc->istate &= ~IRQS_SUSPENDED;
+       irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
+       __enable_irq(desc);
+
+       irq_put_desc_busunlock(desc, flags);
+}
+
 /**
  * irq_pm_syscore_ops - enable interrupt lines early
  *
index c874a7026e249f9b8578896849de1438e8de891a..f3b7239f1892fd501ca6a4412e9a4f7f6086bc3b 100644 (file)
@@ -121,43 +121,25 @@ static void s2idle_loop(void)
 {
        pm_pr_dbg("suspend-to-idle\n");
 
+       /*
+        * Suspend-to-idle equals:
+        * frozen processes + suspended devices + idle processors.
+        * Thus s2idle_enter() should be called right after all devices have
+        * been suspended.
+        *
+        * Wakeups during the noirq suspend of devices may be spurious, so try
+        * to avoid them upfront.
+        */
        for (;;) {
-               int error;
-
-               dpm_noirq_begin();
-
-               /*
-                * Suspend-to-idle equals
-                * frozen processes + suspended devices + idle processors.
-                * Thus s2idle_enter() should be called right after
-                * all devices have been suspended.
-                *
-                * Wakeups during the noirq suspend of devices may be spurious,
-                * so prevent them from terminating the loop right away.
-                */
-               error = dpm_noirq_suspend_devices(PMSG_SUSPEND);
-               if (!error)
-                       s2idle_enter();
-               else if (error == -EBUSY && pm_wakeup_pending())
-                       error = 0;
-
-               if (!error && s2idle_ops && s2idle_ops->wake)
+               if (s2idle_ops && s2idle_ops->wake)
                        s2idle_ops->wake();
 
-               dpm_noirq_resume_devices(PMSG_RESUME);
-
-               dpm_noirq_end();
-
-               if (error)
-                       break;
-
-               if (s2idle_ops && s2idle_ops->sync)
-                       s2idle_ops->sync();
-
                if (pm_wakeup_pending())
                        break;
 
                pm_wakeup_clear(false);
+
+               s2idle_enter();
        }
 
        pm_pr_dbg("resume from suspend-to-idle\n");
@@ -271,14 +253,21 @@ static int platform_suspend_prepare_late(suspend_state_t state)
 
 static int platform_suspend_prepare_noirq(suspend_state_t state)
 {
-       return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare_late ?
-               suspend_ops->prepare_late() : 0;
+       if (state == PM_SUSPEND_TO_IDLE)
+               return s2idle_ops && s2idle_ops->prepare_late ?
+                       s2idle_ops->prepare_late() : 0;
+
+       return suspend_ops->prepare_late ? suspend_ops->prepare_late() : 0;
 }
 
 static void platform_resume_noirq(suspend_state_t state)
 {
-       if (state != PM_SUSPEND_TO_IDLE && suspend_ops->wake)
+       if (state == PM_SUSPEND_TO_IDLE) {
+               if (s2idle_ops && s2idle_ops->restore_early)
+                       s2idle_ops->restore_early();
+       } else if (suspend_ops->wake) {
                suspend_ops->wake();
+       }
 }
 
 static void platform_resume_early(suspend_state_t state)
@@ -415,11 +404,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        if (error)
                goto Devices_early_resume;
 
-       if (state == PM_SUSPEND_TO_IDLE && pm_test_level != TEST_PLATFORM) {
-               s2idle_loop();
-               goto Platform_early_resume;
-       }
-
        error = dpm_suspend_noirq(PMSG_SUSPEND);
        if (error) {
                pr_err("noirq suspend of devices failed\n");
@@ -432,6 +416,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
        if (suspend_test(TEST_PLATFORM))
                goto Platform_wake;
 
+       if (state == PM_SUSPEND_TO_IDLE) {
+               s2idle_loop();
+               goto Platform_wake;
+       }
+
        error = suspend_disable_secondary_cpus();
        if (error || suspend_test(TEST_CPUS))
                goto Enable_cpus;