diff mbox series

[2/3] PM: sleep: Don't always assume s2idle will be enabled

Message ID 20220105193910.25678-3-mario.limonciello@amd.com
State New
Headers show
Series On AMD platforms only offer s2idle w/ proper FW | expand

Commit Message

Mario Limonciello Jan. 5, 2022, 7:39 p.m. UTC
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@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
---
 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 mbox series

Patch

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;