diff options
author | Evan Green <evgreen@chromium.org> | 2021-11-05 14:18:20 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-12-01 19:02:09 +0000 |
commit | d89e49b2016c69d1762fae2e2786d4867934b4d6 (patch) | |
tree | f8bc657c7b70aeb8f9fd842eb2281b4c907d7291 /power | |
parent | ba8a3c9c05af48c22e7ef3c9427da1cac92bbe8f (diff) | |
download | chrome-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.c | 25 | ||||
-rw-r--r-- | power/intel_x86.c | 38 |
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); |