summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2019-08-01 13:31:10 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2019-08-08 05:26:01 -0400
commitac9eafbe930abb589e9289842a99cc575cadb854 (patch)
treefb47026135efac89f5fd65bd6cf173fa6b6afb02
parent29113f2f0a7d8d5332bfdfdfca995c06d0896e83 (diff)
ACPI: PM: s2idle: Execute LPS0 _DSM functions with suspended devices
According to Section 3.5 of the "Intel Low Power S0 Idle" document [1], Function 5 of the LPS0 _DSM is expected to be invoked when the system configuration matches the criteria for entering the target low-power state of the platform. In particular, this means that all devices should be suspended and in low-power states already when that function is invoked. This is not the case currently, however, because Function 5 of the LPS0 _DSM is invoked by it before the "noirq" phase of device suspend, which means that some devices may not have been put into low-power states yet at that point. That is a consequence of the previous design of the suspend-to-idle flow that allowed the "noirq" phase of device suspend and the "noirq" phase of device resume to be carried out for multiple times while "suspended" (if any spurious wakeup events were detected) and the point of the LPS0 _DSM Function 5 invocation was chosen so as to call it (and LPS0 _DSM Function 6 analogously) once per suspend-resume cycle (regardless of how many times the "noirq" phases of device suspend and resume were carried out while "suspended"). Now that the suspend-to-idle flow has been redesigned to carry out the "noirq" phases of device suspend and resume once in each cycle, the code can be reordered to follow the specification that it is based on more closely. For this purpose, add ->prepare_late and ->restore_early platform callbacks for suspend-to-idle, to be executed, respectively, after the "noirq" phase of suspending devices and before the "noirq" phase of resuming them and make ACPI use them for the invocation of LPS0 _DSM functions as appropriate. While at it, move the LPS0 entry requirements check to be made before invoking Functions 3 and 5 of the LPS0 _DSM (also once per cycle) as follows from the specification [1]. Link: https://uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf # [1] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Tested-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
-rw-r--r--drivers/acpi/sleep.c36
-rw-r--r--include/linux/suspend.h2
-rw-r--r--kernel/power/suspend.c12
3 files changed, 35 insertions, 15 deletions
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 864bb18d3a5d..8f7e95f97e1f 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -954,11 +954,6 @@ static int acpi_s2idle_begin(void)
954 954
955static int acpi_s2idle_prepare(void) 955static int acpi_s2idle_prepare(void)
956{ 956{
957 if (lps0_device_handle && !sleep_no_lps0) {
958 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
959 acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);
960 }
961
962 if (acpi_sci_irq_valid()) 957 if (acpi_sci_irq_valid())
963 enable_irq_wake(acpi_sci_irq); 958 enable_irq_wake(acpi_sci_irq);
964 959
@@ -972,11 +967,22 @@ static int acpi_s2idle_prepare(void)
972 return 0; 967 return 0;
973} 968}
974 969
975static void acpi_s2idle_wake(void) 970static int acpi_s2idle_prepare_late(void)
976{ 971{
977 if (lps0_device_handle && !sleep_no_lps0 && pm_debug_messages_on) 972 if (!lps0_device_handle || sleep_no_lps0)
973 return 0;
974
975 if (pm_debug_messages_on)
978 lpi_check_constraints(); 976 lpi_check_constraints();
979 977
978 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF);
979 acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY);
980
981 return 0;
982}
983
984static void acpi_s2idle_wake(void)
985{
980 /* 986 /*
981 * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the SCI has 987 * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the SCI has
982 * not triggered while suspended, so bail out. 988 * not triggered while suspended, so bail out.
@@ -1011,6 +1017,15 @@ static void acpi_s2idle_wake(void)
1011 rearm_wake_irq(acpi_sci_irq); 1017 rearm_wake_irq(acpi_sci_irq);
1012} 1018}
1013 1019
1020static void acpi_s2idle_restore_early(void)
1021{
1022 if (!lps0_device_handle || sleep_no_lps0)
1023 return;
1024
1025 acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT);
1026 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON);
1027}
1028
1014static void acpi_s2idle_restore(void) 1029static void acpi_s2idle_restore(void)
1015{ 1030{
1016 s2idle_wakeup = false; 1031 s2idle_wakeup = false;
@@ -1021,11 +1036,6 @@ static void acpi_s2idle_restore(void)
1021 1036
1022 if (acpi_sci_irq_valid()) 1037 if (acpi_sci_irq_valid())
1023 disable_irq_wake(acpi_sci_irq); 1038 disable_irq_wake(acpi_sci_irq);
1024
1025 if (lps0_device_handle && !sleep_no_lps0) {
1026 acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT);
1027 acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON);
1028 }
1029} 1039}
1030 1040
1031static void acpi_s2idle_end(void) 1041static void acpi_s2idle_end(void)
@@ -1036,7 +1046,9 @@ static void acpi_s2idle_end(void)
1036static const struct platform_s2idle_ops acpi_s2idle_ops = { 1046static const struct platform_s2idle_ops acpi_s2idle_ops = {
1037 .begin = acpi_s2idle_begin, 1047 .begin = acpi_s2idle_begin,
1038 .prepare = acpi_s2idle_prepare, 1048 .prepare = acpi_s2idle_prepare,
1049 .prepare_late = acpi_s2idle_prepare_late,
1039 .wake = acpi_s2idle_wake, 1050 .wake = acpi_s2idle_wake,
1051 .restore_early = acpi_s2idle_restore_early,
1040 .restore = acpi_s2idle_restore, 1052 .restore = acpi_s2idle_restore,
1041 .end = acpi_s2idle_end, 1053 .end = acpi_s2idle_end,
1042}; 1054};
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index f0c4a8445140..6fc8843f1c9e 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -190,7 +190,9 @@ struct platform_suspend_ops {
190struct platform_s2idle_ops { 190struct platform_s2idle_ops {
191 int (*begin)(void); 191 int (*begin)(void);
192 int (*prepare)(void); 192 int (*prepare)(void);
193 int (*prepare_late)(void);
193 void (*wake)(void); 194 void (*wake)(void);
195 void (*restore_early)(void);
194 void (*restore)(void); 196 void (*restore)(void);
195 void (*end)(void); 197 void (*end)(void);
196}; 198};
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 2b6057853b33..ed9ddef12b13 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -253,13 +253,19 @@ static int platform_suspend_prepare_late(suspend_state_t state)
253 253
254static int platform_suspend_prepare_noirq(suspend_state_t state) 254static int platform_suspend_prepare_noirq(suspend_state_t state)
255{ 255{
256 return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare_late ? 256 if (state == PM_SUSPEND_TO_IDLE) {
257 suspend_ops->prepare_late() : 0; 257 if (s2idle_ops && s2idle_ops->prepare_late)
258 return s2idle_ops->prepare_late();
259 }
260 return suspend_ops->prepare_late ? suspend_ops->prepare_late() : 0;
258} 261}
259 262
260static void platform_resume_noirq(suspend_state_t state) 263static void platform_resume_noirq(suspend_state_t state)
261{ 264{
262 if (state != PM_SUSPEND_TO_IDLE && suspend_ops->wake) 265 if (state == PM_SUSPEND_TO_IDLE) {
266 if (s2idle_ops && s2idle_ops->restore_early)
267 s2idle_ops->restore_early();
268 } else if (suspend_ops->wake)
263 suspend_ops->wake(); 269 suspend_ops->wake();
264} 270}
265 271