Merge branch 'pm-sleep'
* pm-sleep: (29 commits) ACPI: PM: s2idle: Always set up EC GPE for system wakeup ACPI: PM: s2idle: Avoid rearming SCI for wakeup unnecessarily PM / wakeup: Unexport wakeup_source_sysfs_{add,remove}() PM / wakeup: Register wakeup class kobj after device is added PM / wakeup: Fix sysfs registration error path PM / wakeup: Show wakeup sources stats in sysfs PM / wakeup: Use wakeup_source_register() in wakelock.c PM / wakeup: Drop wakeup_source_init(), wakeup_source_prepare() PM: sleep: Replace strncmp() with str_has_prefix() 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() PM/sleep: Expose suspend stats in sysfs ...
This commit is contained in:
commit
d281706369
76
Documentation/ABI/testing/sysfs-class-wakeup
Normal file
76
Documentation/ABI/testing/sysfs-class-wakeup
Normal file
@ -0,0 +1,76 @@
|
||||
What: /sys/class/wakeup/
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
The /sys/class/wakeup/ directory contains pointers to all
|
||||
wakeup sources in the kernel at that moment in time.
|
||||
|
||||
What: /sys/class/wakeup/.../name
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the name of the wakeup source.
|
||||
|
||||
What: /sys/class/wakeup/.../active_count
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the number of times the wakeup source was
|
||||
activated.
|
||||
|
||||
What: /sys/class/wakeup/.../event_count
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the number of signaled wakeup events
|
||||
associated with the wakeup source.
|
||||
|
||||
What: /sys/class/wakeup/.../wakeup_count
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the number of times the wakeup source might
|
||||
abort suspend.
|
||||
|
||||
What: /sys/class/wakeup/.../expire_count
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the number of times the wakeup source's
|
||||
timeout has expired.
|
||||
|
||||
What: /sys/class/wakeup/.../active_time_ms
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the amount of time the wakeup source has
|
||||
been continuously active, in milliseconds. If the wakeup
|
||||
source is not active, this file contains '0'.
|
||||
|
||||
What: /sys/class/wakeup/.../total_time_ms
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the total amount of time this wakeup source
|
||||
has been active, in milliseconds.
|
||||
|
||||
What: /sys/class/wakeup/.../max_time_ms
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the maximum amount of time this wakeup
|
||||
source has been continuously active, in milliseconds.
|
||||
|
||||
What: /sys/class/wakeup/.../last_change_ms
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
This file contains the monotonic clock time when the wakeup
|
||||
source was touched last time, in milliseconds.
|
||||
|
||||
What: /sys/class/wakeup/.../prevent_suspend_time_ms
|
||||
Date: June 2019
|
||||
Contact: Tri Vo <trong@android.com>
|
||||
Description:
|
||||
The file contains the total amount of time this wakeup source
|
||||
has been preventing autosleep, in milliseconds.
|
@ -301,3 +301,109 @@ Description:
|
||||
|
||||
Using this sysfs file will override any values that were
|
||||
set using the kernel command line for disk offset.
|
||||
|
||||
What: /sys/power/suspend_stats
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats directory contains suspend related
|
||||
statistics.
|
||||
|
||||
What: /sys/power/suspend_stats/success
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/success file contains the number
|
||||
of times entering system sleep state succeeded.
|
||||
|
||||
What: /sys/power/suspend_stats/fail
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/fail file contains the number
|
||||
of times entering system sleep state failed.
|
||||
|
||||
What: /sys/power/suspend_stats/failed_freeze
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/failed_freeze file contains the
|
||||
number of times freezing processes failed.
|
||||
|
||||
What: /sys/power/suspend_stats/failed_prepare
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/failed_prepare file contains the
|
||||
number of times preparing all non-sysdev devices for
|
||||
a system PM transition failed.
|
||||
|
||||
What: /sys/power/suspend_stats/failed_resume
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/failed_resume file contains the
|
||||
number of times executing "resume" callbacks of
|
||||
non-sysdev devices failed.
|
||||
|
||||
What: /sys/power/suspend_stats/failed_resume_early
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/failed_resume_early file contains
|
||||
the number of times executing "early resume" callbacks
|
||||
of devices failed.
|
||||
|
||||
What: /sys/power/suspend_stats/failed_resume_noirq
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/failed_resume_noirq file contains
|
||||
the number of times executing "noirq resume" callbacks
|
||||
of devices failed.
|
||||
|
||||
What: /sys/power/suspend_stats/failed_suspend
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/failed_suspend file contains
|
||||
the number of times executing "suspend" callbacks
|
||||
of all non-sysdev devices failed.
|
||||
|
||||
What: /sys/power/suspend_stats/failed_suspend_late
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/failed_suspend_late file contains
|
||||
the number of times executing "late suspend" callbacks
|
||||
of all devices failed.
|
||||
|
||||
What: /sys/power/suspend_stats/failed_suspend_noirq
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/failed_suspend_noirq file contains
|
||||
the number of times executing "noirq suspend" callbacks
|
||||
of all devices failed.
|
||||
|
||||
What: /sys/power/suspend_stats/last_failed_dev
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/last_failed_dev file contains
|
||||
the last device for which a suspend/resume callback failed.
|
||||
|
||||
What: /sys/power/suspend_stats/last_failed_errno
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/last_failed_errno file contains
|
||||
the errno of the last failed attempt at entering
|
||||
system sleep state.
|
||||
|
||||
What: /sys/power/suspend_stats/last_failed_step
|
||||
Date: July 2019
|
||||
Contact: Kalesh Singh <kaleshsingh96@gmail.com>
|
||||
Description:
|
||||
The /sys/power/suspend_stats/last_failed_step file contains
|
||||
the last failed step in the suspend/resume path.
|
||||
|
@ -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)
|
||||
|
@ -497,7 +497,8 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
|
||||
goto out;
|
||||
|
||||
mutex_lock(&acpi_pm_notifier_lock);
|
||||
adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));
|
||||
adev->wakeup.ws = wakeup_source_register(&adev->dev,
|
||||
dev_name(&adev->dev));
|
||||
adev->wakeup.context.dev = dev;
|
||||
adev->wakeup.context.func = func;
|
||||
adev->wakeup.flags.notifier_present = true;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
|
||||
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
|
||||
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o wakeup_stats.o
|
||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
|
||||
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
|
||||
|
@ -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));
|
||||
|
@ -149,3 +149,21 @@ static inline void device_pm_init(struct device *dev)
|
||||
device_pm_sleep_init(dev);
|
||||
pm_runtime_init(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
/* drivers/base/power/wakeup_stats.c */
|
||||
extern int wakeup_source_sysfs_add(struct device *parent,
|
||||
struct wakeup_source *ws);
|
||||
extern void wakeup_source_sysfs_remove(struct wakeup_source *ws);
|
||||
|
||||
extern int pm_wakeup_source_sysfs_add(struct device *parent);
|
||||
|
||||
#else /* !CONFIG_PM_SLEEP */
|
||||
|
||||
static inline int pm_wakeup_source_sysfs_add(struct device *parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include "power.h"
|
||||
@ -667,8 +668,13 @@ int dpm_sysfs_add(struct device *dev)
|
||||
if (rc)
|
||||
goto err_wakeup;
|
||||
}
|
||||
rc = pm_wakeup_source_sysfs_add(dev);
|
||||
if (rc)
|
||||
goto err_latency;
|
||||
return 0;
|
||||
|
||||
err_latency:
|
||||
sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_tolerance_attr_group);
|
||||
err_wakeup:
|
||||
sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
|
||||
err_runtime:
|
||||
|
@ -72,22 +72,7 @@ static struct wakeup_source deleted_ws = {
|
||||
.lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock),
|
||||
};
|
||||
|
||||
/**
|
||||
* wakeup_source_prepare - Prepare a new wakeup source for initialization.
|
||||
* @ws: Wakeup source to prepare.
|
||||
* @name: Pointer to the name of the new wakeup source.
|
||||
*
|
||||
* Callers must ensure that the @name string won't be freed when @ws is still in
|
||||
* use.
|
||||
*/
|
||||
void wakeup_source_prepare(struct wakeup_source *ws, const char *name)
|
||||
{
|
||||
if (ws) {
|
||||
memset(ws, 0, sizeof(*ws));
|
||||
ws->name = name;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_source_prepare);
|
||||
static DEFINE_IDA(wakeup_ida);
|
||||
|
||||
/**
|
||||
* wakeup_source_create - Create a struct wakeup_source object.
|
||||
@ -96,13 +81,31 @@ EXPORT_SYMBOL_GPL(wakeup_source_prepare);
|
||||
struct wakeup_source *wakeup_source_create(const char *name)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
const char *ws_name;
|
||||
int id;
|
||||
|
||||
ws = kmalloc(sizeof(*ws), GFP_KERNEL);
|
||||
ws = kzalloc(sizeof(*ws), GFP_KERNEL);
|
||||
if (!ws)
|
||||
return NULL;
|
||||
goto err_ws;
|
||||
|
||||
ws_name = kstrdup_const(name, GFP_KERNEL);
|
||||
if (!ws_name)
|
||||
goto err_name;
|
||||
ws->name = ws_name;
|
||||
|
||||
id = ida_alloc(&wakeup_ida, GFP_KERNEL);
|
||||
if (id < 0)
|
||||
goto err_id;
|
||||
ws->id = id;
|
||||
|
||||
wakeup_source_prepare(ws, name ? kstrdup_const(name, GFP_KERNEL) : NULL);
|
||||
return ws;
|
||||
|
||||
err_id:
|
||||
kfree_const(ws->name);
|
||||
err_name:
|
||||
kfree(ws);
|
||||
err_ws:
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_source_create);
|
||||
|
||||
@ -134,6 +137,13 @@ static void wakeup_source_record(struct wakeup_source *ws)
|
||||
spin_unlock_irqrestore(&deleted_ws.lock, flags);
|
||||
}
|
||||
|
||||
static void wakeup_source_free(struct wakeup_source *ws)
|
||||
{
|
||||
ida_free(&wakeup_ida, ws->id);
|
||||
kfree_const(ws->name);
|
||||
kfree(ws);
|
||||
}
|
||||
|
||||
/**
|
||||
* wakeup_source_destroy - Destroy a struct wakeup_source object.
|
||||
* @ws: Wakeup source to destroy.
|
||||
@ -147,8 +157,7 @@ void wakeup_source_destroy(struct wakeup_source *ws)
|
||||
|
||||
__pm_relax(ws);
|
||||
wakeup_source_record(ws);
|
||||
kfree_const(ws->name);
|
||||
kfree(ws);
|
||||
wakeup_source_free(ws);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_source_destroy);
|
||||
|
||||
@ -200,16 +209,26 @@ EXPORT_SYMBOL_GPL(wakeup_source_remove);
|
||||
|
||||
/**
|
||||
* wakeup_source_register - Create wakeup source and add it to the list.
|
||||
* @dev: Device this wakeup source is associated with (or NULL if virtual).
|
||||
* @name: Name of the wakeup source to register.
|
||||
*/
|
||||
struct wakeup_source *wakeup_source_register(const char *name)
|
||||
struct wakeup_source *wakeup_source_register(struct device *dev,
|
||||
const char *name)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
int ret;
|
||||
|
||||
ws = wakeup_source_create(name);
|
||||
if (ws)
|
||||
if (ws) {
|
||||
if (!dev || device_is_registered(dev)) {
|
||||
ret = wakeup_source_sysfs_add(dev, ws);
|
||||
if (ret) {
|
||||
wakeup_source_free(ws);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
wakeup_source_add(ws);
|
||||
|
||||
}
|
||||
return ws;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wakeup_source_register);
|
||||
@ -222,6 +241,7 @@ void wakeup_source_unregister(struct wakeup_source *ws)
|
||||
{
|
||||
if (ws) {
|
||||
wakeup_source_remove(ws);
|
||||
wakeup_source_sysfs_remove(ws);
|
||||
wakeup_source_destroy(ws);
|
||||
}
|
||||
}
|
||||
@ -265,7 +285,7 @@ int device_wakeup_enable(struct device *dev)
|
||||
if (pm_suspend_target_state != PM_SUSPEND_ON)
|
||||
dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__);
|
||||
|
||||
ws = wakeup_source_register(dev_name(dev));
|
||||
ws = wakeup_source_register(dev, dev_name(dev));
|
||||
if (!ws)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -859,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)
|
||||
|
214
drivers/base/power/wakeup_stats.c
Normal file
214
drivers/base/power/wakeup_stats.c
Normal file
@ -0,0 +1,214 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Wakeup statistics in sysfs
|
||||
*
|
||||
* Copyright (c) 2019 Linux Foundation
|
||||
* Copyright (c) 2019 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
* Copyright (c) 2019 Google Inc.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
static struct class *wakeup_class;
|
||||
|
||||
#define wakeup_attr(_name) \
|
||||
static ssize_t _name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct wakeup_source *ws = dev_get_drvdata(dev); \
|
||||
\
|
||||
return sprintf(buf, "%lu\n", ws->_name); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(_name)
|
||||
|
||||
wakeup_attr(active_count);
|
||||
wakeup_attr(event_count);
|
||||
wakeup_attr(wakeup_count);
|
||||
wakeup_attr(expire_count);
|
||||
|
||||
static ssize_t active_time_ms_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct wakeup_source *ws = dev_get_drvdata(dev);
|
||||
ktime_t active_time =
|
||||
ws->active ? ktime_sub(ktime_get(), ws->last_time) : 0;
|
||||
|
||||
return sprintf(buf, "%lld\n", ktime_to_ms(active_time));
|
||||
}
|
||||
static DEVICE_ATTR_RO(active_time_ms);
|
||||
|
||||
static ssize_t total_time_ms_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct wakeup_source *ws = dev_get_drvdata(dev);
|
||||
ktime_t active_time;
|
||||
ktime_t total_time = ws->total_time;
|
||||
|
||||
if (ws->active) {
|
||||
active_time = ktime_sub(ktime_get(), ws->last_time);
|
||||
total_time = ktime_add(total_time, active_time);
|
||||
}
|
||||
return sprintf(buf, "%lld\n", ktime_to_ms(total_time));
|
||||
}
|
||||
static DEVICE_ATTR_RO(total_time_ms);
|
||||
|
||||
static ssize_t max_time_ms_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct wakeup_source *ws = dev_get_drvdata(dev);
|
||||
ktime_t active_time;
|
||||
ktime_t max_time = ws->max_time;
|
||||
|
||||
if (ws->active) {
|
||||
active_time = ktime_sub(ktime_get(), ws->last_time);
|
||||
if (active_time > max_time)
|
||||
max_time = active_time;
|
||||
}
|
||||
return sprintf(buf, "%lld\n", ktime_to_ms(max_time));
|
||||
}
|
||||
static DEVICE_ATTR_RO(max_time_ms);
|
||||
|
||||
static ssize_t last_change_ms_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct wakeup_source *ws = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%lld\n", ktime_to_ms(ws->last_time));
|
||||
}
|
||||
static DEVICE_ATTR_RO(last_change_ms);
|
||||
|
||||
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wakeup_source *ws = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", ws->name);
|
||||
}
|
||||
static DEVICE_ATTR_RO(name);
|
||||
|
||||
static ssize_t prevent_suspend_time_ms_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wakeup_source *ws = dev_get_drvdata(dev);
|
||||
ktime_t prevent_sleep_time = ws->prevent_sleep_time;
|
||||
|
||||
if (ws->active && ws->autosleep_enabled) {
|
||||
prevent_sleep_time = ktime_add(prevent_sleep_time,
|
||||
ktime_sub(ktime_get(), ws->start_prevent_time));
|
||||
}
|
||||
return sprintf(buf, "%lld\n", ktime_to_ms(prevent_sleep_time));
|
||||
}
|
||||
static DEVICE_ATTR_RO(prevent_suspend_time_ms);
|
||||
|
||||
static struct attribute *wakeup_source_attrs[] = {
|
||||
&dev_attr_name.attr,
|
||||
&dev_attr_active_count.attr,
|
||||
&dev_attr_event_count.attr,
|
||||
&dev_attr_wakeup_count.attr,
|
||||
&dev_attr_expire_count.attr,
|
||||
&dev_attr_active_time_ms.attr,
|
||||
&dev_attr_total_time_ms.attr,
|
||||
&dev_attr_max_time_ms.attr,
|
||||
&dev_attr_last_change_ms.attr,
|
||||
&dev_attr_prevent_suspend_time_ms.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(wakeup_source);
|
||||
|
||||
static void device_create_release(struct device *dev)
|
||||
{
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
static struct device *wakeup_source_device_create(struct device *parent,
|
||||
struct wakeup_source *ws)
|
||||
{
|
||||
struct device *dev = NULL;
|
||||
int retval = -ENODEV;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
device_initialize(dev);
|
||||
dev->devt = MKDEV(0, 0);
|
||||
dev->class = wakeup_class;
|
||||
dev->parent = parent;
|
||||
dev->groups = wakeup_source_groups;
|
||||
dev->release = device_create_release;
|
||||
dev_set_drvdata(dev, ws);
|
||||
device_set_pm_not_required(dev);
|
||||
|
||||
retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
retval = device_add(dev);
|
||||
if (retval)
|
||||
goto error;
|
||||
|
||||
return dev;
|
||||
|
||||
error:
|
||||
put_device(dev);
|
||||
return ERR_PTR(retval);
|
||||
}
|
||||
|
||||
/**
|
||||
* wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs.
|
||||
* @parent: Device given wakeup source is associated with (or NULL if virtual).
|
||||
* @ws: Wakeup source to be added in sysfs.
|
||||
*/
|
||||
int wakeup_source_sysfs_add(struct device *parent, struct wakeup_source *ws)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = wakeup_source_device_create(parent, ws);
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
ws->dev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_wakeup_source_sysfs_add - Add wakeup_source attributes to sysfs
|
||||
* for a device if they're missing.
|
||||
* @parent: Device given wakeup source is associated with
|
||||
*/
|
||||
int pm_wakeup_source_sysfs_add(struct device *parent)
|
||||
{
|
||||
if (!parent->power.wakeup || parent->power.wakeup->dev)
|
||||
return 0;
|
||||
|
||||
return wakeup_source_sysfs_add(parent, parent->power.wakeup);
|
||||
}
|
||||
|
||||
/**
|
||||
* wakeup_source_sysfs_remove - Remove wakeup_source attributes from sysfs.
|
||||
* @ws: Wakeup source to be removed from sysfs.
|
||||
*/
|
||||
void wakeup_source_sysfs_remove(struct wakeup_source *ws)
|
||||
{
|
||||
device_unregister(ws->dev);
|
||||
}
|
||||
|
||||
static int __init wakeup_sources_sysfs_init(void)
|
||||
{
|
||||
wakeup_class = class_create(THIS_MODULE, "wakeup");
|
||||
|
||||
return PTR_ERR_OR_ZERO(wakeup_class);
|
||||
}
|
||||
postcore_initcall(wakeup_sources_sysfs_init);
|
@ -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,
|
||||
|
@ -1459,13 +1459,13 @@ static int ep_create_wakeup_source(struct epitem *epi)
|
||||
struct wakeup_source *ws;
|
||||
|
||||
if (!epi->ep->ws) {
|
||||
epi->ep->ws = wakeup_source_register("eventpoll");
|
||||
epi->ep->ws = wakeup_source_register(NULL, "eventpoll");
|
||||
if (!epi->ep->ws)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
name = epi->ffd.file->f_path.dentry->d_name.name;
|
||||
ws = wakeup_source_register(name);
|
||||
ws = wakeup_source_register(NULL, name);
|
||||
|
||||
if (!ws)
|
||||
return -ENOMEM;
|
||||
|
@ -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);
|
||||
|
@ -21,6 +21,7 @@ struct wake_irq;
|
||||
* struct wakeup_source - Representation of wakeup sources
|
||||
*
|
||||
* @name: Name of the wakeup source
|
||||
* @id: Wakeup source id
|
||||
* @entry: Wakeup source list entry
|
||||
* @lock: Wakeup source lock
|
||||
* @wakeirq: Optional device specific wakeirq
|
||||
@ -35,11 +36,13 @@ struct wake_irq;
|
||||
* @relax_count: Number of times the wakeup source was deactivated.
|
||||
* @expire_count: Number of times the wakeup source's timeout has expired.
|
||||
* @wakeup_count: Number of times the wakeup source might abort suspend.
|
||||
* @dev: Struct device for sysfs statistics about the wakeup source.
|
||||
* @active: Status of the wakeup source.
|
||||
* @autosleep_enabled: Autosleep is active, so update @prevent_sleep_time.
|
||||
*/
|
||||
struct wakeup_source {
|
||||
const char *name;
|
||||
int id;
|
||||
struct list_head entry;
|
||||
spinlock_t lock;
|
||||
struct wake_irq *wakeirq;
|
||||
@ -55,6 +58,7 @@ struct wakeup_source {
|
||||
unsigned long relax_count;
|
||||
unsigned long expire_count;
|
||||
unsigned long wakeup_count;
|
||||
struct device *dev;
|
||||
bool active:1;
|
||||
bool autosleep_enabled:1;
|
||||
};
|
||||
@ -81,12 +85,12 @@ static inline void device_set_wakeup_path(struct device *dev)
|
||||
}
|
||||
|
||||
/* drivers/base/power/wakeup.c */
|
||||
extern void wakeup_source_prepare(struct wakeup_source *ws, const char *name);
|
||||
extern struct wakeup_source *wakeup_source_create(const char *name);
|
||||
extern void wakeup_source_destroy(struct wakeup_source *ws);
|
||||
extern void wakeup_source_add(struct wakeup_source *ws);
|
||||
extern void wakeup_source_remove(struct wakeup_source *ws);
|
||||
extern struct wakeup_source *wakeup_source_register(const char *name);
|
||||
extern struct wakeup_source *wakeup_source_register(struct device *dev,
|
||||
const char *name);
|
||||
extern void wakeup_source_unregister(struct wakeup_source *ws);
|
||||
extern int device_wakeup_enable(struct device *dev);
|
||||
extern int device_wakeup_disable(struct device *dev);
|
||||
@ -112,9 +116,6 @@ static inline bool device_can_wakeup(struct device *dev)
|
||||
return dev->power.can_wakeup;
|
||||
}
|
||||
|
||||
static inline void wakeup_source_prepare(struct wakeup_source *ws,
|
||||
const char *name) {}
|
||||
|
||||
static inline struct wakeup_source *wakeup_source_create(const char *name)
|
||||
{
|
||||
return NULL;
|
||||
@ -126,7 +127,8 @@ static inline void wakeup_source_add(struct wakeup_source *ws) {}
|
||||
|
||||
static inline void wakeup_source_remove(struct wakeup_source *ws) {}
|
||||
|
||||
static inline struct wakeup_source *wakeup_source_register(const char *name)
|
||||
static inline struct wakeup_source *wakeup_source_register(struct device *dev,
|
||||
const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@ -181,13 +183,6 @@ static inline void pm_wakeup_dev_event(struct device *dev, unsigned int msec,
|
||||
|
||||
#endif /* !CONFIG_PM_SLEEP */
|
||||
|
||||
static inline void wakeup_source_init(struct wakeup_source *ws,
|
||||
const char *name)
|
||||
{
|
||||
wakeup_source_prepare(ws, name);
|
||||
wakeup_source_add(ws);
|
||||
}
|
||||
|
||||
static inline void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec)
|
||||
{
|
||||
return pm_wakeup_ws_event(ws, msec, false);
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -116,7 +116,7 @@ int pm_autosleep_set_state(suspend_state_t state)
|
||||
|
||||
int __init pm_autosleep_init(void)
|
||||
{
|
||||
autosleep_ws = wakeup_source_register("autosleep");
|
||||
autosleep_ws = wakeup_source_register(NULL, "autosleep");
|
||||
if (!autosleep_ws)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -254,7 +254,6 @@ static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
power_attr(pm_test);
|
||||
#endif /* CONFIG_PM_SLEEP_DEBUG */
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static char *suspend_step_name(enum suspend_stat_step step)
|
||||
{
|
||||
switch (step) {
|
||||
@ -275,6 +274,92 @@ static char *suspend_step_name(enum suspend_stat_step step)
|
||||
}
|
||||
}
|
||||
|
||||
#define suspend_attr(_name) \
|
||||
static ssize_t _name##_show(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, "%d\n", suspend_stats._name); \
|
||||
} \
|
||||
static struct kobj_attribute _name = __ATTR_RO(_name)
|
||||
|
||||
suspend_attr(success);
|
||||
suspend_attr(fail);
|
||||
suspend_attr(failed_freeze);
|
||||
suspend_attr(failed_prepare);
|
||||
suspend_attr(failed_suspend);
|
||||
suspend_attr(failed_suspend_late);
|
||||
suspend_attr(failed_suspend_noirq);
|
||||
suspend_attr(failed_resume);
|
||||
suspend_attr(failed_resume_early);
|
||||
suspend_attr(failed_resume_noirq);
|
||||
|
||||
static ssize_t last_failed_dev_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int index;
|
||||
char *last_failed_dev = NULL;
|
||||
|
||||
index = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
|
||||
index %= REC_FAILED_NUM;
|
||||
last_failed_dev = suspend_stats.failed_devs[index];
|
||||
|
||||
return sprintf(buf, "%s\n", last_failed_dev);
|
||||
}
|
||||
static struct kobj_attribute last_failed_dev = __ATTR_RO(last_failed_dev);
|
||||
|
||||
static ssize_t last_failed_errno_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int index;
|
||||
int last_failed_errno;
|
||||
|
||||
index = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1;
|
||||
index %= REC_FAILED_NUM;
|
||||
last_failed_errno = suspend_stats.errno[index];
|
||||
|
||||
return sprintf(buf, "%d\n", last_failed_errno);
|
||||
}
|
||||
static struct kobj_attribute last_failed_errno = __ATTR_RO(last_failed_errno);
|
||||
|
||||
static ssize_t last_failed_step_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int index;
|
||||
enum suspend_stat_step step;
|
||||
char *last_failed_step = NULL;
|
||||
|
||||
index = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;
|
||||
index %= REC_FAILED_NUM;
|
||||
step = suspend_stats.failed_steps[index];
|
||||
last_failed_step = suspend_step_name(step);
|
||||
|
||||
return sprintf(buf, "%s\n", last_failed_step);
|
||||
}
|
||||
static struct kobj_attribute last_failed_step = __ATTR_RO(last_failed_step);
|
||||
|
||||
static struct attribute *suspend_attrs[] = {
|
||||
&success.attr,
|
||||
&fail.attr,
|
||||
&failed_freeze.attr,
|
||||
&failed_prepare.attr,
|
||||
&failed_suspend.attr,
|
||||
&failed_suspend_late.attr,
|
||||
&failed_suspend_noirq.attr,
|
||||
&failed_resume.attr,
|
||||
&failed_resume_early.attr,
|
||||
&failed_resume_noirq.attr,
|
||||
&last_failed_dev.attr,
|
||||
&last_failed_errno.attr,
|
||||
&last_failed_step.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group suspend_attr_group = {
|
||||
.name = "suspend_stats",
|
||||
.attrs = suspend_attrs,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int suspend_stats_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int i, index, last_dev, last_errno, last_step;
|
||||
@ -495,7 +580,7 @@ static suspend_state_t decode_state(const char *buf, size_t n)
|
||||
len = p ? p - buf : n;
|
||||
|
||||
/* Check hibernation first. */
|
||||
if (len == 4 && !strncmp(buf, "disk", len))
|
||||
if (len == 4 && str_has_prefix(buf, "disk"))
|
||||
return PM_SUSPEND_MAX;
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
@ -794,6 +879,14 @@ static const struct attribute_group attr_group = {
|
||||
.attrs = g,
|
||||
};
|
||||
|
||||
static const struct attribute_group *attr_groups[] = {
|
||||
&attr_group,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
&suspend_attr_group,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct workqueue_struct *pm_wq;
|
||||
EXPORT_SYMBOL_GPL(pm_wq);
|
||||
|
||||
@ -815,7 +908,7 @@ static int __init pm_init(void)
|
||||
power_kobj = kobject_create_and_add("power", NULL);
|
||||
if (!power_kobj)
|
||||
return -ENOMEM;
|
||||
error = sysfs_create_group(power_kobj, &attr_group);
|
||||
error = sysfs_create_groups(power_kobj, attr_groups);
|
||||
if (error)
|
||||
return error;
|
||||
pm_print_times_init();
|
||||
|
@ -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;
|
||||
|
@ -27,7 +27,7 @@ static DEFINE_MUTEX(wakelocks_lock);
|
||||
struct wakelock {
|
||||
char *name;
|
||||
struct rb_node node;
|
||||
struct wakeup_source ws;
|
||||
struct wakeup_source *ws;
|
||||
#ifdef CONFIG_PM_WAKELOCKS_GC
|
||||
struct list_head lru;
|
||||
#endif
|
||||
@ -46,7 +46,7 @@ ssize_t pm_show_wakelocks(char *buf, bool show_active)
|
||||
|
||||
for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
|
||||
wl = rb_entry(node, struct wakelock, node);
|
||||
if (wl->ws.active == show_active)
|
||||
if (wl->ws->active == show_active)
|
||||
str += scnprintf(str, end - str, "%s ", wl->name);
|
||||
}
|
||||
if (str > buf)
|
||||
@ -112,16 +112,16 @@ static void __wakelocks_gc(struct work_struct *work)
|
||||
u64 idle_time_ns;
|
||||
bool active;
|
||||
|
||||
spin_lock_irq(&wl->ws.lock);
|
||||
idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
|
||||
active = wl->ws.active;
|
||||
spin_unlock_irq(&wl->ws.lock);
|
||||
spin_lock_irq(&wl->ws->lock);
|
||||
idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws->last_time));
|
||||
active = wl->ws->active;
|
||||
spin_unlock_irq(&wl->ws->lock);
|
||||
|
||||
if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
|
||||
break;
|
||||
|
||||
if (!active) {
|
||||
wakeup_source_remove(&wl->ws);
|
||||
wakeup_source_unregister(wl->ws);
|
||||
rb_erase(&wl->node, &wakelocks_tree);
|
||||
list_del(&wl->lru);
|
||||
kfree(wl->name);
|
||||
@ -187,9 +187,15 @@ static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
|
||||
kfree(wl);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
wl->ws.name = wl->name;
|
||||
wl->ws.last_time = ktime_get();
|
||||
wakeup_source_add(&wl->ws);
|
||||
|
||||
wl->ws = wakeup_source_register(NULL, wl->name);
|
||||
if (!wl->ws) {
|
||||
kfree(wl->name);
|
||||
kfree(wl);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
wl->ws->last_time = ktime_get();
|
||||
|
||||
rb_link_node(&wl->node, parent, node);
|
||||
rb_insert_color(&wl->node, &wakelocks_tree);
|
||||
wakelocks_lru_add(wl);
|
||||
@ -233,9 +239,9 @@ int pm_wake_lock(const char *buf)
|
||||
u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
|
||||
|
||||
do_div(timeout_ms, NSEC_PER_MSEC);
|
||||
__pm_wakeup_event(&wl->ws, timeout_ms);
|
||||
__pm_wakeup_event(wl->ws, timeout_ms);
|
||||
} else {
|
||||
__pm_stay_awake(&wl->ws);
|
||||
__pm_stay_awake(wl->ws);
|
||||
}
|
||||
|
||||
wakelocks_lru_most_recent(wl);
|
||||
@ -271,7 +277,7 @@ int pm_wake_unlock(const char *buf)
|
||||
ret = PTR_ERR(wl);
|
||||
goto out;
|
||||
}
|
||||
__pm_relax(&wl->ws);
|
||||
__pm_relax(wl->ws);
|
||||
|
||||
wakelocks_lru_most_recent(wl);
|
||||
wakelocks_gc();
|
||||
|
@ -97,7 +97,7 @@ static int alarmtimer_rtc_add_device(struct device *dev,
|
||||
if (!device_may_wakeup(rtc->dev.parent))
|
||||
return -1;
|
||||
|
||||
__ws = wakeup_source_register("alarmtimer");
|
||||
__ws = wakeup_source_register(dev, "alarmtimer");
|
||||
|
||||
spin_lock_irqsave(&rtcdev_lock, flags);
|
||||
if (!rtcdev) {
|
||||
|
Loading…
Reference in New Issue
Block a user