summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Green <evgreen@chromium.org>2019-05-01 16:46:36 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-05-08 19:35:34 -0700
commitfb2a7efc26a598fa2697300d72827a285261b913 (patch)
treea9bb9d4dd9ae6b25e091554ecc9cfa29423c5595
parent65fde9d10e35890766e279183ae061b72170299d (diff)
downloadchrome-ec-fb2a7efc26a598fa2697300d72827a285261b913.tar.gz
power: Manipulate wake mask during s0ix timeouts
When S0ix failure detection is enabled and a timeout occurs such that the SLP_S0 line never actually toggles, then s0ix_transition_timeout() sets the HANG_DETECT event bit. This doesn't quite work in this scenario, since the wake mask is only enabled when the power state transitions to S0ix, which happens when the SLP_S0 line toggles. So the AP never sees the event, since it's not in the wake mask and so never causes the EC->AP interrupt line to change. Detect this situation in the timeout function, and explicitly move the wake mask to its S0ix value so that when the event bit is set, (if it is in the wake mask), the system will wake up. Doing this forcefully gets the wake mask out of sync with the power state. So upon resume, explicitly restore the wake mask to its S0 state. BUG=b:131434497 BRANCH=none TEST=suspend_stress_test -c1 --suspend_min=60 with a firmware where S0ix fails. Change-Id: Id2e67c6933a7895fba85ccfdff9b336629eabf24 Signed-off-by: Evan Green <evgreen@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1592469 Reviewed-by: Furquan Shaikh <furquan@chromium.org>
-rw-r--r--include/power.h5
-rw-r--r--power/common.c28
-rw-r--r--power/intel_x86.c18
3 files changed, 39 insertions, 12 deletions
diff --git a/include/power.h b/include/power.h
index 2c57209e84..2688852e44 100644
--- a/include/power.h
+++ b/include/power.h
@@ -145,6 +145,11 @@ void power_set_state(enum power_state new_state);
*/
enum power_state power_get_state(void);
+/*
+ * Set the wake mask according to the current power state.
+ */
+void power_update_wake_mask(void);
+
/**
* Chipset-specific initialization
*
diff --git a/power/common.c b/power/common.c
index 2e2e9e3cc5..6d7fb7869d 100644
--- a/power/common.c
+++ b/power/common.c
@@ -221,30 +221,22 @@ enum power_state power_get_state(void)
/* If host doesn't program s0ix lazy wake mask, use default s0ix mask */
#define DEFAULT_WAKE_MASK_S0IX (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) | \
EC_HOST_EVENT_MASK(EC_HOST_EVENT_MODE_CHANGE))
+
/*
- * Set wake mask after power state has stabilized (5ms after power state
- * change):
+ * Set the wake mask according to the current power state:
* 1. On transition to S0, wake mask is reset.
* 2. In non-S0 states, active mask set by host gets a higher preference.
* 3. If host has not set any active mask, then check if a lazy mask exists
* for the current power state.
* 4. If state is S0ix and no lazy or active wake mask is set, then use default
* S0ix mask to be compatible with older BIOS versions.
- *
- * Reason for making this a deferred call is to avoid race conditions occurring
- * from S0ix periodic wakes on the SoC.
*/
-static void power_update_wake_mask_deferred(void);
-DECLARE_DEFERRED(power_update_wake_mask_deferred);
-
-static void power_update_wake_mask_deferred(void)
+void power_update_wake_mask(void)
{
host_event_t wake_mask;
enum power_state state;
- hook_call_deferred(&power_update_wake_mask_deferred_data, -1);
-
state = power_get_state();
if (state == POWER_S0)
@@ -260,6 +252,20 @@ static void power_update_wake_mask_deferred(void)
lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, wake_mask);
}
+ /*
+ * Set wake mask after power state has stabilized, 5ms after power state
+ * change. The reason for making this a deferred call is to avoid race
+ * conditions occurring from S0ix periodic wakes on the SoC.
+ */
+
+static void power_update_wake_mask_deferred(void);
+DECLARE_DEFERRED(power_update_wake_mask_deferred);
+
+static void power_update_wake_mask_deferred(void)
+{
+ hook_call_deferred(&power_update_wake_mask_deferred_data, -1);
+ power_update_wake_mask();
+}
static void power_set_active_wake_mask(void)
{
diff --git a/power/intel_x86.c b/power/intel_x86.c
index 4e7230d395..838935777f 100644
--- a/power/intel_x86.c
+++ b/power/intel_x86.c
@@ -271,8 +271,17 @@ static void s0ix_transition_timeout(void)
/*
* Wake up the AP so they don't just chill in a non-suspended state and
* burn power. Overload a vaguely related event bit since event bits are
- * at a premium.
+ * at a premium. If the system never entered S0ix, then manually set the
+ * wake mask to pretend it did, so that the hang detect event wakes the
+ * system.
*/
+ if (power_get_state() == POWER_S0) {
+ host_event_t s0ix_wake_mask;
+
+ get_lazy_wake_mask(POWER_S0ix, &s0ix_wake_mask);
+ lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, s0ix_wake_mask);
+ }
+
host_set_single_event(EC_HOST_EVENT_HANG_DETECT);
}
@@ -300,6 +309,13 @@ static void s0ix_complete_resume(struct host_sleep_event_context *ctx)
{
hook_call_deferred(&s0ix_transition_timeout_data, -1);
ctx->sleep_transitions = slp_s0ix_transitions;
+
+ /*
+ * If s0ix timed out and never transitioned, then the wake mask was
+ * modified to its s0ix state, so that the event wakes the system.
+ * Explicitly restore the wake mask to its S0 state now.
+ */
+ power_update_wake_mask();
}
static void s0ix_reset_tracking(void)