summaryrefslogtreecommitdiff
path: root/power
diff options
context:
space:
mode:
authorEvan Green <evgreen@chromium.org>2021-11-05 14:18:20 -0700
committerCommit Bot <commit-bot@chromium.org>2021-12-01 19:02:09 +0000
commitd89e49b2016c69d1762fae2e2786d4867934b4d6 (patch)
treef8bc657c7b70aeb8f9fd842eb2281b4c907d7291 /power
parentba8a3c9c05af48c22e7ef3c9427da1cac92bbe8f (diff)
downloadchrome-ec-d89e49b2016c69d1762fae2e2786d4867934b4d6.tar.gz
power: Introduce S4 as a real power state
In order to support hibernate (suspend to disk) on some systems, there are normally two choices for hibernate's destination power state: shutdown and S4. On most systems, shutdown is the logical choice for Chrome OS, since the wake sources are the same, and device state is properly saved/restored across an S5/G3 transition. However on Brya devices with Intel Keylocker technology, there is an IWKey (intermediate wrapping key) which software by design is not allowed to read. Intel, being no stranger to the concept of hibernate, provisioned support to save and restore this register in a platform area, while still keeping its contents inaccessible to software. However, architecturally they only guarantee this mechanism works down to S3/S4. This means in order to preserve keylocker contents during hibernation, shutdown is no longer an option. We must camp out in the architecturally designated S4 state during hibernation on devices with Intel Keylocker. The EC has long ignored this as a state since the OS doesn't support entering it. This needs to change. This patch introduces a POWER_S4 state. It's modeled after the S3 state, but represents itself as a "chipset soft off" state, like S5. Now, on Intel platforms, we (almost) always transition through S4 on our way up and down. For example, where we would normally go G3->S5->S3->S0, we now go G3->S5->S4->S3->S0. The "almost" refers to unusual error cases, where if power signals are totally wonky we may go from S3 straight to S5. The S3 <-> S5 state transitions also still exist because non-Intel platforms transition directly without going through S4. This bit of consistency was sacrificed to avoid retrofitting a bunch of ARM EC code to transition though a completely phony state. The "almost" refers to unusual error cases, where if power signals are totally wonky we may go from S3 straight to S5. The common Intel code used to look at SLP_S4 as a signal to transition between S5 and S3. Now, we look at SLP_S4 as the signal to transition to S4, and use the SLP_S5 signal to transition deeper, into S5. On platforms with virtual wire support, we should have access to the virtual SLP_S5 line already. On platforms that haven't explicitly set the config for VW_SLP_S5, we merge SLP_S5 and SLP_S4 by making them the same GPIO, so that the transition through S4 simply slides on through. This effectively disables S4 residency, so we disallow advertising S4 residency to the AP unless CONFIG_HOSTCMD_ESPI_VW_SLP_S5 is also enabled. We should then enable this on all new Intel platforms. Signed-off-by: Evan Green <evgreen@chromium.org> BRANCH=None BUG=b:204947672 TEST=hiberman hibernate --test-keys on volteer Change-Id: Icf4798fa517d40ad652a278bbea2051e4c9fb118 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3265286 Commit-Queue: Evan Green <evgreen@chromium.org> Tested-by: Evan Green <evgreen@chromium.org> Reviewed-by: Keith Short <keithshort@chromium.org> Reviewed-by: Jack Rosenthal <jrosenth@chromium.org> Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
Diffstat (limited to 'power')
-rw-r--r--power/common.c25
-rw-r--r--power/intel_x86.c38
2 files changed, 52 insertions, 11 deletions
diff --git a/power/common.c b/power/common.c
index 7e1f4bcb2b..0285ca75a7 100644
--- a/power/common.c
+++ b/power/common.c
@@ -46,6 +46,7 @@ static const int s5_inactivity_timeout = 10;
static const char * const state_names[] = {
"G3",
"S5",
+ "S4",
"S3",
"S0",
#ifdef CONFIG_POWER_S0IX
@@ -57,6 +58,10 @@ static const char * const state_names[] = {
"S0->S3",
"S3->S5",
"S5->G3",
+ "S3->S4",
+ "S4->S3",
+ "S4->S5",
+ "S5->S4",
#ifdef CONFIG_POWER_S0IX
"S0ix->S0",
"S0->S0ix",
@@ -246,11 +251,11 @@ void power_set_state(enum power_state new_state)
/*
* Reset want_g3_exit flag here to prevent the situation that if the
- * error handler in POWER_S5S3 decides to force shutdown the system and
+ * error handler in POWER_S5S4 decides to force shutdown the system and
* the flag is set, the system will go to G3 and then immediately exit
* G3 again.
*/
- if (state == POWER_S5S3)
+ if ((state == POWER_S5S4) || (state == POWER_S5S3))
want_g3_exit = 0;
}
@@ -525,6 +530,8 @@ static enum power_state power_common_state(enum power_state state)
}
break;
+ case POWER_S4:
+ /* fallthrough */
case POWER_S3:
/* fallthrough */
case POWER_S0:
@@ -570,10 +577,15 @@ int chipset_in_state(int state_mask)
need_mask = CHIPSET_STATE_HARD_OFF | CHIPSET_STATE_SOFT_OFF;
break;
case POWER_S5:
+ case POWER_S5S4:
+ case POWER_S4S5:
+ case POWER_S4:
need_mask = CHIPSET_STATE_SOFT_OFF;
break;
case POWER_S5S3:
case POWER_S3S5:
+ case POWER_S4S3:
+ case POWER_S3S4:
need_mask = CHIPSET_STATE_SOFT_OFF | CHIPSET_STATE_SUSPEND;
break;
case POWER_S3:
@@ -608,11 +620,16 @@ int chipset_in_or_transitioning_to_state(int state_mask)
case POWER_S5G3:
return state_mask & CHIPSET_STATE_HARD_OFF;
case POWER_S5:
- case POWER_G3S5:
+ case POWER_S4:
case POWER_S3S5:
+ case POWER_G3S5:
+ case POWER_S4S5:
+ case POWER_S5S4:
+ case POWER_S3S4:
return state_mask & CHIPSET_STATE_SOFT_OFF;
- case POWER_S3:
case POWER_S5S3:
+ case POWER_S3:
+ case POWER_S4S3:
case POWER_S0S3:
return state_mask & CHIPSET_STATE_SUSPEND;
#ifdef CONFIG_POWER_S0IX
diff --git a/power/intel_x86.c b/power/intel_x86.c
index c682b3f078..e020874de6 100644
--- a/power/intel_x86.c
+++ b/power/intel_x86.c
@@ -29,6 +29,7 @@
enum sys_sleep_state {
SYS_SLEEP_S3,
SYS_SLEEP_S4,
+ SYS_SLEEP_S5,
#ifdef CONFIG_POWER_S0IX
SYS_SLEEP_S0IX,
#endif
@@ -37,6 +38,7 @@ enum sys_sleep_state {
static const int sleep_sig[] = {
[SYS_SLEEP_S3] = SLP_S3_SIGNAL_L,
[SYS_SLEEP_S4] = SLP_S4_SIGNAL_L,
+ [SYS_SLEEP_S5] = SLP_S5_SIGNAL_L,
#ifdef CONFIG_POWER_S0IX
[SYS_SLEEP_S0IX] = GPIO_PCH_SLP_S0_L,
#endif
@@ -121,7 +123,7 @@ static enum power_state power_wait_s5_rtc_reset(void)
}
s5_exit_tries = 0;
- return POWER_S5S3; /* Power up to next state */
+ return POWER_S5S4; /* Power up to next state */
}
#endif
@@ -279,22 +281,34 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
return power_wait_s5_rtc_reset();
#endif
- if (chipset_get_sleep_signal(SYS_SLEEP_S4) == 1)
- return POWER_S5S3; /* Power up to next state */
+ if (chipset_get_sleep_signal(SYS_SLEEP_S5) == 1)
+ return POWER_S5S4; /* Power up to next state */
+ break;
+
+ case POWER_S4:
+ if (chipset_get_sleep_signal(SYS_SLEEP_S5) == 0) {
+ /* Power down to next state */
+ return POWER_S4S5;
+ } else if (chipset_get_sleep_signal(SYS_SLEEP_S4) == 1) {
+ /* Power up to the next level */
+ return POWER_S4S3;
+ }
+
break;
case POWER_S3:
if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
- /* Required rail went away */
+ /* Required rail went away, go straight to S5 */
chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S3S5;
} else if (chipset_get_sleep_signal(SYS_SLEEP_S3) == 1) {
/* Power up to next state */
return POWER_S3S0;
} else if (chipset_get_sleep_signal(SYS_SLEEP_S4) == 0) {
- /* Power down to next state */
- return POWER_S3S5;
+ /* Power down to the next state */
+ return POWER_S3S4;
}
+
break;
case POWER_S0:
@@ -359,7 +373,15 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
power_s5_up = 1;
return POWER_S5;
+ case POWER_S5S4:
+ return POWER_S4; /* Power up to next state */
+
+ case POWER_S3S4:
+ return POWER_S4; /* Power down to the next state */
+
case POWER_S5S3:
+ /* fallthrough */
+ case POWER_S4S3:
if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
/* Required rail went away */
chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
@@ -380,7 +402,7 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
case POWER_S3S0:
if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
- /* Required rail went away */
+ /* Required rail went away, go straight back to S5 */
chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S3S5;
}
@@ -477,6 +499,8 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
#endif
case POWER_S3S5:
+ /* fallthrough */
+ case POWER_S4S5:
/* Call hooks before we remove power rails */
hook_notify(HOOK_CHIPSET_SHUTDOWN);