forked from Minki/linux
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() ...
This commit is contained in:
commit
1b531e55c5
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 int acpi_s2idle_prepare_late(void)
|
||||
{
|
||||
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 (!lps0_device_handle)
|
||||
/*
|
||||
* 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 (pm_debug_messages_on)
|
||||
lpi_check_constraints();
|
||||
|
||||
/*
|
||||
* 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))) {
|
||||
pm_system_cancel_wakeup();
|
||||
s2idle_wakeup = true;
|
||||
if (acpi_ec_dispatch_gpe()) {
|
||||
/*
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
acpi_ec_dispatch_gpe();
|
||||
pm_system_cancel_wakeup();
|
||||
/*
|
||||
* 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 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;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -252,36 +252,46 @@ static void intel_button_array_enable(struct device *device, bool enable)
|
||||
}
|
||||
|
||||
static int intel_hid_pm_prepare(struct device *device)
|
||||
{
|
||||
if (device_may_wakeup(device)) {
|
||||
struct intel_hid_priv *priv = dev_get_drvdata(device);
|
||||
|
||||
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 = true;
|
||||
return 0;
|
||||
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:
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {}
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user