summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Feng <li1.feng@intel.com>2022-03-31 14:09:16 -0700
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-04-13 02:30:58 +0000
commit05e483d637cae2a903c09d22340e32baa3457493 (patch)
tree76081fb91a1946ecc6c06222000aac7618885b8a
parentd12fb951bc0efbd8fc1ef366cbc835e4147faf92 (diff)
downloadchrome-ec-05e483d637cae2a903c09d22340e32baa3457493.tar.gz
zephyr: ap_pwrseq: support host sleep for S0ix
S0ix enablement has multiple patches, this is the third one of the series. Clear SCI/SMI masks when host enters S0ix and restores SCI/SMI masks on resuming from S0ix. BIOS does not get involved in S0ix, the communication is directly between AP and EC. So it's EC's responsibility to preserve SCI/SMI masks across S0ix suspend/resume. Host and EC uses eSPI for communication. If host enters S0ix but SCI/SMI masks are not cleared, then EC could generate SCI/SMI depends on the value of masks. Since host is in sleep, it won't respond to eSPI virtual wire event from the EC. This causes EC to wait forever and so can't go to low power idle mode. To avoid this, EC saves SCI/SMI masks before S0ix and clears in EC driver, and restore the masks after resume. Refer to CL:1099968. S0ix entry: host event HOST_SLEEP_EVENT_S0IX_SUSPEND, then SLP_S0 assertion; S0ix exit: SLP_S0 de-assertion, then host event HOST_SLEEP_EVENT_S0IX_RESUME. EC tracks sleep states and enable/disable SLP_S0 interrupt on the fly. BUG=b:203446068 b:203446865 BRANCH=none TEST=zmake testall Signed-off-by: Li Feng <li1.feng@intel.com> Change-Id: I35ace43e331fd6348618e8a0a1ee1479c5de65f6 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3563507 Reviewed-by: Vijay P Hiremath <vijay.p.hiremath@intel.com> Reviewed-by: Andrew McRae <amcrae@google.com>
-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 */