Some platforms may require firmware support for s2idle to work properly, so allow other drivers that set s2idle ops to decide whether to offer it on a given platform. This should be no functional change for any platform. Reviewed-by: Basavaraj Natikar <Basavaraj.Natikar@xxxxxxx> Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxx> --- drivers/acpi/sleep.c | 6 ++++++ drivers/acpi/x86/s2idle.c | 6 ++++++ include/linux/suspend.h | 1 + kernel/power/suspend.c | 27 ++++++++++++++------------- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index eaa47753b758..9dafdaafa5dc 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -800,12 +800,18 @@ void acpi_s2idle_end(void) acpi_scan_lock_release(); } +static bool acpi_s2idle_valid(void) +{ + return true; +} + static const struct platform_s2idle_ops acpi_s2idle_ops = { .begin = acpi_s2idle_begin, .prepare = acpi_s2idle_prepare, .wake = acpi_s2idle_wake, .restore = acpi_s2idle_restore, .end = acpi_s2idle_end, + .valid = acpi_s2idle_valid, }; void __weak acpi_s2idle_setup(void) diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index 32ab4ba5adb9..a1626737e5e0 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -361,6 +361,11 @@ static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *d return ret; } +static bool acpi_s2idle_valid(void) +{ + return true; +} + static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { .begin = acpi_s2idle_begin, .prepare = acpi_s2idle_prepare, @@ -369,6 +374,7 @@ static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = { .restore_early = acpi_s2idle_restore_early, .restore = acpi_s2idle_restore, .end = acpi_s2idle_end, + .valid = acpi_s2idle_valid, }; static int lps0_device_attach(struct acpi_device *adev, diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 8af13ba60c7e..51e5bdd9be23 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -195,6 +195,7 @@ struct platform_s2idle_ops { void (*restore_early)(void); void (*restore)(void); void (*end)(void); + bool (*valid)(void); }; #ifdef CONFIG_SUSPEND diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 597c5e65aa45..29d6f1738359 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -46,7 +46,7 @@ static const char * const mem_sleep_labels[] = { }; const char *mem_sleep_states[PM_SUSPEND_MAX]; -suspend_state_t mem_sleep_current = PM_SUSPEND_TO_IDLE; +suspend_state_t mem_sleep_current = PM_SUSPEND_MAX; suspend_state_t mem_sleep_default = PM_SUSPEND_MAX; suspend_state_t pm_suspend_target_state; EXPORT_SYMBOL_GPL(pm_suspend_target_state); @@ -152,6 +152,10 @@ EXPORT_SYMBOL_GPL(s2idle_wake); static bool valid_state(suspend_state_t state) { + /* PM_SUSPEND_TO_IDLE may require low-level support on some platforms */ + if (state == PM_SUSPEND_TO_IDLE) + return s2idle_ops && s2idle_ops->valid && s2idle_ops->valid(); + /* * The PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states require low-level * support and need to be valid to the low-level implementation. @@ -166,6 +170,13 @@ void s2idle_set_ops(const struct platform_s2idle_ops *ops) { lock_system_sleep(); s2idle_ops = ops; + if (valid_state(PM_SUSPEND_TO_IDLE)) { + mem_sleep_states[PM_SUSPEND_TO_IDLE] = mem_sleep_labels[PM_SUSPEND_TO_IDLE]; + /* if supported, update to suspend to idle for default when ops set */ + if (mem_sleep_current == PM_SUSPEND_MAX || + mem_sleep_default == PM_SUSPEND_TO_IDLE) + mem_sleep_current = PM_SUSPEND_TO_IDLE; + } unlock_system_sleep(); } @@ -174,11 +185,6 @@ void __init pm_states_init(void) /* "mem" and "freeze" are always present in /sys/power/state. */ pm_states[PM_SUSPEND_MEM] = pm_labels[PM_SUSPEND_MEM]; pm_states[PM_SUSPEND_TO_IDLE] = pm_labels[PM_SUSPEND_TO_IDLE]; - /* - * Suspend-to-idle should be supported even without any suspend_ops, - * initialize mem_sleep_states[] accordingly here. - */ - mem_sleep_states[PM_SUSPEND_TO_IDLE] = mem_sleep_labels[PM_SUSPEND_TO_IDLE]; } static int __init mem_sleep_default_setup(char *str) @@ -236,11 +242,6 @@ int suspend_valid_only_mem(suspend_state_t state) } EXPORT_SYMBOL_GPL(suspend_valid_only_mem); -static bool sleep_state_supported(suspend_state_t state) -{ - return state == PM_SUSPEND_TO_IDLE || valid_state(state); -} - static int platform_suspend_prepare(suspend_state_t state) { return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare ? @@ -346,7 +347,7 @@ static int suspend_prepare(suspend_state_t state) { int error; - if (!sleep_state_supported(state)) + if (!valid_state(state)) return -EPERM; pm_prepare_console(); @@ -478,7 +479,7 @@ int suspend_devices_and_enter(suspend_state_t state) int error; bool wakeup = false; - if (!sleep_state_supported(state)) + if (!valid_state(state)) return -ENOSYS; pm_suspend_target_state = state; -- 2.25.1