diff options
-rw-r--r-- | zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h | 17 | ||||
-rw-r--r-- | zephyr/subsys/ap_pwrseq/power_host_sleep.c | 121 |
2 files changed, 138 insertions, 0 deletions
diff --git a/zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h b/zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h index e16816d185..d3cbc8c15f 100644 --- a/zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h +++ b/zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h @@ -26,10 +26,27 @@ int ap_power_get_lazy_wake_mask( enum power_states_ndsx state, host_event_t *mask); #if CONFIG_AP_PWRSEQ_S0IX +/* For S0ix path, flag to notify sleep change */ +enum ap_power_sleep_type { + AP_POWER_SLEEP_NONE, + AP_POWER_SLEEP_SUSPEND, + AP_POWER_SLEEP_RESUME, +}; + /* * Reset host sleep state and clean up */ void ap_power_reset_host_sleep_state(void); + +/* + * Check if the sleep type current power transition indicates is the same + * as what is notified. If same, means host sleep event notified by AP + * through Host Command and SLP_S0 are consistent. Process + * the transition. Otherwise, no action. + * + * @param check_state Sleep type which is going to transit to. + */ +void ap_power_sleep_notify_transition(enum ap_power_sleep_type check_state); #endif /* CONFIG_AP_PWRSEQ_S0IX */ #endif /* __AP_PWRSEQ_HOST_SLEEP_H */ diff --git a/zephyr/subsys/ap_pwrseq/power_host_sleep.c b/zephyr/subsys/ap_pwrseq/power_host_sleep.c index dcaa3a55d1..1803971620 100644 --- a/zephyr/subsys/ap_pwrseq/power_host_sleep.c +++ b/zephyr/subsys/ap_pwrseq/power_host_sleep.c @@ -85,6 +85,94 @@ void ap_power_set_active_wake_mask(void) static void ap_power_set_active_wake_mask(void) { } #endif /* CONFIG_PLATFORM_EC_HOST_INTERFACE_ESPI */ +#if CONFIG_AP_PWRSEQ_S0IX +/* + * Backup copies of SCI and SMI mask to preserve across S0ix suspend/resume + * cycle. If the host uses S0ix, BIOS is not involved during suspend and resume + * operations and hence SCI/SMI masks are programmed only once during boot-up. + * + * These backup variables are set whenever host expresses its interest to + * enter S0ix and then lpc_host_event_mask for SCI and SMI are cleared. When + * host resumes from S0ix, masks from backup variables are copied over to + * lpc_host_event_mask for SCI and SMI. + */ +static host_event_t backup_sci_mask; +static host_event_t backup_smi_mask; + +/* Flag to notify listeners about suspend/resume events. */ +enum ap_power_sleep_type sleep_state = AP_POWER_SLEEP_NONE; + +/* + * Clear host event masks for SMI and SCI when host is entering S0ix. This is + * done to prevent any SCI/SMI interrupts when the host is in suspend. Since + * BIOS is not involved in the suspend path, EC needs to take care of clearing + * these masks. + */ +static void power_s0ix_suspend_clear_masks(void) +{ + backup_sci_mask = lpc_get_host_event_mask(LPC_HOST_EVENT_SCI); + backup_smi_mask = lpc_get_host_event_mask(LPC_HOST_EVENT_SMI); + lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, 0); + lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, 0); +} + +/* + * Restore host event masks for SMI and SCI when host exits S0ix. This is done + * because BIOS is not involved in the resume path and so EC needs to restore + * the masks from backup variables. + */ +static void power_s0ix_resume_restore_masks(void) +{ + /* + * No need to restore SCI/SMI masks if both backup_sci_mask and + * backup_smi_mask are zero. This indicates that there was a failure to + * enter S0ix(SLP_S0# assertion) and hence SCI/SMI masks were never + * backed up. + */ + if (!backup_sci_mask && !backup_smi_mask) + return; + lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, backup_sci_mask); + lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, backup_smi_mask); + backup_sci_mask = backup_smi_mask = 0; +} + +/* + * Following functions are called in the S0ix path, not S3 path. + */ + +/* + * Notify the sleep type that is going to transit to; this is a token to + * ensure both host sleep event passed by Host Command and SLP_S0 satisfy + * the conditions to suspend or resume. + * + * @param new_state Notified sleep type + */ +static void ap_power_sleep_set_notify(enum ap_power_sleep_type new_state) +{ + sleep_state = new_state; +} + +void ap_power_sleep_notify_transition(enum ap_power_sleep_type check_state) +{ + if (sleep_state != check_state) + return; + + if (check_state == AP_POWER_SLEEP_SUSPEND) { + /* + * Transition to S0ix; + * clear mask before others running suspend. + */ + power_s0ix_suspend_clear_masks(); + ap_power_ev_send_callbacks(AP_POWER_SUSPEND); + } else if (check_state == AP_POWER_SLEEP_RESUME) { + ap_power_ev_send_callbacks(AP_POWER_RESUME); + } + + /* Transition is done; reset sleep state. */ + ap_power_sleep_set_notify(AP_POWER_SLEEP_NONE); +} +#endif /* CONFIG_AP_PWRSEQ_S0IX */ + #if CONFIG_AP_PWRSEQ_HOST_SLEEP #define HOST_SLEEP_EVENT_DEFAULT_RESET 0 @@ -107,5 +195,38 @@ void ap_power_chipset_handle_host_sleep_event( struct host_sleep_event_context *ctx) { LOG_DBG("host sleep event = %d!", state); +#if CONFIG_AP_PWRSEQ_S0IX + if (state == HOST_SLEEP_EVENT_S0IX_SUSPEND) { + + /* + * Indicate to power state machine that a new host event for + * s0ix/s3 suspend has been received and so chipset suspend + * notification needs to be sent to listeners. + */ + ap_power_sleep_set_notify(AP_POWER_SLEEP_SUSPEND); + power_signal_enable_interrupt(PWR_SLP_S0); + + } else if (state == HOST_SLEEP_EVENT_S0IX_RESUME) { + /* + * Set sleep state to resume; restore SCI/SMI masks; + * SLP_S0 should be de-asserted already, disable interrupt. + */ + ap_power_sleep_set_notify(AP_POWER_SLEEP_RESUME); + power_s0ix_resume_restore_masks(); + power_signal_disable_interrupt(PWR_SLP_S0); + + /* + * If the sleep signal timed out and never transitioned, then + * the wake mask was modified to its suspend state (S0ix), so + * that the event wakes the system. Explicitly restore the wake + * mask to its S0 state now. + */ + power_update_wake_mask(); + + } else if (state == HOST_SLEEP_EVENT_DEFAULT_RESET) { + power_signal_disable_interrupt(PWR_SLP_S0); + } +#endif /* CONFIG_AP_PWRSEQ_S0IX */ } + #endif /* CONFIG_AP_PWRSEQ_HOST_SLEEP */ |