diff options
Diffstat (limited to 'power/intel_x86.c')
-rw-r--r-- | power/intel_x86.c | 117 |
1 files changed, 115 insertions, 2 deletions
diff --git a/power/intel_x86.c b/power/intel_x86.c index 758f8c2ed5..4e7230d395 100644 --- a/power/intel_x86.c +++ b/power/intel_x86.c @@ -39,6 +39,7 @@ /* Console output macros */ #define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) enum sys_sleep_state { SYS_SLEEP_S3, @@ -224,7 +225,109 @@ static void handle_chipset_reset(void) } DECLARE_HOOK(HOOK_CHIPSET_RESET, handle_chipset_reset, HOOK_PRIO_FIRST); -#endif +#ifdef CONFIG_POWER_TRACK_HOST_SLEEP_STATE +#ifdef CONFIG_POWER_S0IX_FAILURE_DETECTION + +static uint16_t slp_s0ix_timeout; +static uint32_t slp_s0ix_transitions; + +static void s0ix_transition_timeout(void); +DECLARE_DEFERRED(s0ix_transition_timeout); + +static void s0ix_increment_transition(void) +{ + if ((slp_s0ix_transitions & EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK) < + EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK) + slp_s0ix_transitions += 1; +} + +static void s0ix_suspend_transition(void) +{ + s0ix_increment_transition(); + hook_call_deferred(&s0ix_transition_timeout_data, -1); +} + +static void s0ix_resume_transition(void) +{ + s0ix_increment_transition(); + + /* + * Start the timer again to ensure the AP doesn't get itself stuck in + * a state where it's no longer in S0ix, but from the Linux perspective + * is still suspended. Perhaps a bug in the SoC-internal periodic + * housekeeping code might result in a situation like this. + */ + if (slp_s0ix_timeout) + hook_call_deferred(&s0ix_transition_timeout_data, + (uint32_t)slp_s0ix_timeout * 1000); +} + +static void s0ix_transition_timeout(void) +{ + /* Mark the timeout. */ + slp_s0ix_transitions |= EC_HOST_RESUME_SLEEP_TIMEOUT; + hook_call_deferred(&s0ix_transition_timeout_data, -1); + + /* + * 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. + */ + host_set_single_event(EC_HOST_EVENT_HANG_DETECT); +} + +static void s0ix_start_suspend(struct host_sleep_event_context *ctx) +{ + uint16_t timeout = ctx->sleep_timeout_ms; + + slp_s0ix_transitions = 0; + + /* Use zero internally to indicate no timeout. */ + if (timeout == EC_HOST_SLEEP_TIMEOUT_DEFAULT) { + timeout = CONFIG_SLEEP_TIMEOUT_MS; + + } else if (timeout == EC_HOST_SLEEP_TIMEOUT_INFINITE) { + slp_s0ix_timeout = 0; + return; + } + + slp_s0ix_timeout = timeout; + hook_call_deferred(&s0ix_transition_timeout_data, + (uint32_t)timeout * 1000); +} + +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; +} + +static void s0ix_reset_tracking(void) +{ + slp_s0ix_transitions = 0; + slp_s0ix_timeout = 0; +} + +#else /* !CONFIG_POWER_S0IX_FAILURE_DETECTION */ + +#define s0ix_suspend_transition() +#define s0ix_resume_transition() +#define s0ix_start_suspend(_ctx) +#define s0ix_complete_resume(_ctx) +#define s0ix_reset_tracking() + +#endif /* CONFIG_POWER_S0IX_FAILURE_DETECTION */ + +void power_reset_host_sleep_state(void) +{ + power_set_host_sleep_state(HOST_SLEEP_EVENT_DEFAULT_RESET); + s0ix_reset_tracking(); + power_chipset_handle_host_sleep_event(HOST_SLEEP_EVENT_DEFAULT_RESET, + NULL); +} + +#endif /* CONFIG_POWER_TRACK_HOST_SLEEP_STATE */ +#endif /* CONFIG_POWER_S0IX */ void chipset_throttle_cpu(int throttle) { @@ -459,11 +562,13 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state) #ifdef CONFIG_POWER_S0IX case POWER_S0S0ix: + /* * Call hooks only if we haven't notified listeners of S0ix * suspend. */ s0ix_transition(S0IX_NOTIFY_SUSPEND, HOOK_CHIPSET_SUSPEND); + s0ix_suspend_transition(); /* * Enable idle task deep sleep. Allow the low power idle task @@ -479,6 +584,7 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state) */ disable_sleep(SLEEP_MASK_AP_RUN); + s0ix_resume_transition(); return POWER_S0; #endif @@ -549,7 +655,9 @@ power_board_handle_host_sleep_event(enum host_sleep_event state) /* Default weak implementation -- no action required. */ } -void power_chipset_handle_host_sleep_event(enum host_sleep_event state) +void +power_chipset_handle_host_sleep_event(enum host_sleep_event state, + struct host_sleep_event_context *ctx) { power_board_handle_host_sleep_event(state); @@ -561,6 +669,8 @@ void power_chipset_handle_host_sleep_event(enum host_sleep_event state) * notification needs to be sent to listeners. */ s0ix_notify = S0IX_NOTIFY_SUSPEND; + + s0ix_start_suspend(ctx); power_signal_enable_interrupt(sleep_sig[SYS_SLEEP_S0IX]); } else if (state == HOST_SLEEP_EVENT_S0IX_RESUME) { /* @@ -574,10 +684,13 @@ void power_chipset_handle_host_sleep_event(enum host_sleep_event state) ; lpc_s0ix_resume_restore_masks(); power_signal_disable_interrupt(sleep_sig[SYS_SLEEP_S0IX]); + s0ix_complete_resume(ctx); + } else if (state == HOST_SLEEP_EVENT_DEFAULT_RESET) { power_signal_disable_interrupt(sleep_sig[SYS_SLEEP_S0IX]); } #endif + } #endif |