diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2019-09-17 09:35:35 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2019-09-17 09:35:35 +0200 |
commit | 1b531e55c52bbe2927e2c3490d47c82113d303e7 (patch) | |
tree | 9e0f3cc50d552165fc146057e6bea87db31d47c1 /drivers/acpi | |
parent | 78c0f050847cac4c43dbfc1916a63d1557c74ac0 (diff) | |
parent | b90ff3554aa3e123bb7e6d08789f6fd92d86ddde (diff) | |
download | linux-1b531e55c52bbe2927e2c3490d47c82113d303e7.tar.bz2 |
Merge suspend-to-idle rework material for v5.4.
* 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()
...
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/acpica/evxfgpe.c | 6 | ||||
-rw-r--r-- | drivers/acpi/ec.c | 57 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 6 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 165 |
4 files changed, 125 insertions, 109 deletions
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 710488ec59e9..04a40d563dd6 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -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) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index c33756ed3304..da1e5c5ce150 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -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) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index f4c2fe6be4f2..afe6636f9ad3 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -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 diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index f0fe7c15d657..9fa77d72ef27 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -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; |