summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)