summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zephyr/subsys/ap_pwrseq/include/ap_power_host_sleep.h17
-rw-r--r--zephyr/subsys/ap_pwrseq/power_host_sleep.c121
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 */