summaryrefslogtreecommitdiff
path: root/power
diff options
context:
space:
mode:
authorEric Yilun Lin <yllin@chromium.org>2022-02-11 16:09:05 +0800
committerCommit Bot <commit-bot@chromium.org>2022-02-21 03:27:25 +0000
commitff0106d30a5511531d08407e507b7f1a10518947 (patch)
treee44b01c7aac1477e129e413cfcdb833690706bac /power
parent2f0fc1db10f20a015baad5db5d741c8505f76c3d (diff)
downloadchrome-ec-ff0106d30a5511531d08407e507b7f1a10518947.tar.gz
mt8186: enable AP watchdog interrupt
Reset AP (S0->S0) when AP watchdog interrupt is raised. Note that we should only do watchdog reset when it's a real WDT. A real WDT is that AP_WDTRST_L is the first power signal asserted, otherwise it's a fake WDT. Warm resets, AP shutdown will cause a fake WDT because when they are resetting the chipset, the WDT signal will be asserted in the process. BUG=b:218420108 TEST=in EC console apshutdown : S0->G3 apreset : S0->S0 lid open : G3->S0 toggle cold reset: G3->S0 powerbtn 8.2s : S0->G3 in AP console ectool apreset : S0->S0 shutdown : S0->G3 stop daisydog; echo > /dev/watchdog: S0->S0 reboot : S0->S0 BRANCH=none Change-Id: Ic0360c1eff1cf25d7a28974d76af41dd0c2984cd Signed-off-by: Eric Yilun Lin <yllin@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3459589 Reviewed-by: Ting Shen <phoenixshen@chromium.org> Tested-by: Eric Yilun Lin <yllin@google.com> Auto-Submit: Eric Yilun Lin <yllin@google.com> Commit-Queue: Eric Yilun Lin <yllin@google.com>
Diffstat (limited to 'power')
-rw-r--r--power/mt8186.c77
1 files changed, 75 insertions, 2 deletions
diff --git a/power/mt8186.c b/power/mt8186.c
index 75ab6fd170..17beb48256 100644
--- a/power/mt8186.c
+++ b/power/mt8186.c
@@ -61,6 +61,13 @@
/* 30 ms for hard reset, we hold it longer to prevent TPM false alarm. */
#define SYS_RST_PULSE_LENGTH (50 * MSEC)
+/*
+ * A delay for distinguish a WDT reset or a normal shutdown. It usually takes
+ * 90ms to pull AP_IN_SLEEP_L low in a normal shutdown.
+ */
+#define NORMAL_SHUTDOWN_DELAY (150 * MSEC)
+#define RESET_FLAG_TIMEOUT (2 * SECOND)
+
#ifndef CONFIG_ZEPHYR
/* power signal list. Must match order of enum power_signal. */
const struct power_signal_info power_signal_list[] = {
@@ -72,6 +79,11 @@ const struct power_signal_info power_signal_list[] = {
BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT);
#endif /* CONFIG_ZEPHYR */
+/* indicate MT8186 is processing a chipset reset. */
+static bool is_resetting;
+/* indicate MT8186 is processing a AP shutdown. */
+static bool is_shutdown;
+
static void reset_request_interrupt_deferred(void)
{
chipset_reset(CHIPSET_RESET_AP_REQ);
@@ -80,9 +92,36 @@ DECLARE_DEFERRED(reset_request_interrupt_deferred);
void chipset_reset_request_interrupt(enum gpio_signal signal)
{
+ power_signal_interrupt(signal);
hook_call_deferred(&reset_request_interrupt_deferred_data, 0);
}
+static void watchdog_interrupt_deferred(void)
+{
+ /*
+ * If this is a real WDT, AP_IN_SLEEP_L should keep high after
+ * the WDT interrupt is fired. Otherwise, it's a normal shutdown.
+ */
+ if (gpio_get_level(GPIO_AP_IN_SLEEP_L))
+ chipset_reset(CHIPSET_RESET_AP_WATCHDOG);
+}
+DECLARE_DEFERRED(watchdog_interrupt_deferred);
+
+void chipset_watchdog_interrupt(enum gpio_signal signal)
+{
+ power_signal_interrupt(signal);
+
+ /*
+ * We need this guard in that:
+ * 1. AP_EC_WDTRST_L will recursively toggle until the AP is reset.
+ * 2. If a warm reset request or AP shutdown is processing, then this
+ * interrupt tirgger is a fake WDT interrupt, we should skip it.
+ */
+ if (!is_resetting && !is_shutdown)
+ hook_call_deferred(&watchdog_interrupt_deferred_data,
+ NORMAL_SHUTDOWN_DELAY);
+}
+
static void release_power_button(void)
{
CPRINTS("release power button after 8 seconds.");
@@ -95,6 +134,7 @@ void chipset_force_shutdown(enum chipset_shutdown_reason reason)
CPRINTS("%s(%d)", __func__, reason);
report_ap_reset(reason);
+ is_shutdown = true;
/*
* Force power off. This condition will reset once the state machine
* transitions to G3.
@@ -127,11 +167,24 @@ void chipset_exit_hard_off_button(void)
}
DECLARE_DEFERRED(chipset_exit_hard_off_button);
+static void reset_flag_deferred(void)
+{
+ if (!is_resetting)
+ return;
+
+ CPRINTS("chipset_reset failed");
+ is_resetting = false;
+ task_wake(TASK_ID_CHIPSET);
+}
+DECLARE_DEFERRED(reset_flag_deferred);
+
void chipset_reset(enum chipset_shutdown_reason reason)
{
CPRINTS("%s: %d", __func__, reason);
report_ap_reset(reason);
+ is_resetting = true;
+ hook_call_deferred(&reset_flag_deferred_data, RESET_FLAG_TIMEOUT);
GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 0);
usleep(SYS_RST_PULSE_LENGTH);
GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 1);
@@ -167,9 +220,21 @@ DECLARE_HOOK(HOOK_CHIPSET_RESET, handle_chipset_reset, HOOK_PRIO_FIRST);
* G3 | 1 | x |
*
* S5 is only used when exit from G3 in power_common_state().
+ * is_resetting flag indicate it's resetting chipset, and it's always S0.
+ * is_shutdown flag indicates it's shutting down the AP, it goes for G3.
*/
static enum power_state power_get_signal_state(void)
{
+ /*
+ * We are processing a chipset reset(S0->S0), so we don't check the
+ * power signals until the reset is finished. This is because
+ * while the chipset is resetting, the intermediate power signal state
+ * is not reflecting the current power state.
+ */
+ if (is_resetting)
+ return POWER_S0;
+ if (is_shutdown)
+ return POWER_G3;
if (power_get_signals() & IN_AP_RST)
return POWER_G3;
if (power_get_signals() & IN_SUSPEND_ASSERTED)
@@ -183,13 +248,13 @@ enum power_state power_chipset_init(void)
enum power_state init_state = power_get_signal_state();
/* Enable reboot / sleep control inputs from AP */
- gpio_enable_interrupt(GPIO_AP_EC_WARM_RST_REQ);
gpio_enable_interrupt(GPIO_AP_IN_SLEEP_L);
gpio_enable_interrupt(GPIO_AP_EC_SYSRST_ODL);
- gpio_enable_interrupt(GPIO_AP_EC_WDTRST_L);
if (system_jumped_late()) {
if (init_state == POWER_S0) {
+ gpio_enable_interrupt(GPIO_AP_EC_WDTRST_L);
+ gpio_enable_interrupt(GPIO_AP_EC_WARM_RST_REQ);
disable_sleep(SLEEP_MASK_AP_RUN);
CPRINTS("already in S0");
}
@@ -232,6 +297,7 @@ enum power_state power_handle_state(enum power_state state)
switch (state) {
case POWER_G3:
+ is_shutdown = false;
if (next_state != POWER_G3)
return POWER_G3S5;
break;
@@ -249,6 +315,7 @@ enum power_state power_handle_state(enum power_state state)
case POWER_S0:
if (next_state != POWER_S0)
return POWER_S0S3;
+ is_resetting = false;
break;
@@ -258,6 +325,9 @@ enum power_state power_handle_state(enum power_state state)
case POWER_S5S3:
hook_notify(HOOK_CHIPSET_PRE_INIT);
+ gpio_enable_interrupt(GPIO_AP_EC_WARM_RST_REQ);
+ gpio_enable_interrupt(GPIO_AP_EC_WDTRST_L);
+
GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 1);
msleep(PMIC_EN_PULSE_MS);
GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 0);
@@ -329,6 +399,9 @@ enum power_state power_handle_state(enum power_state state)
return POWER_S3;
case POWER_S3S5:
+ gpio_disable_interrupt(GPIO_AP_EC_WDTRST_L);
+ gpio_disable_interrupt(GPIO_AP_EC_WARM_RST_REQ);
+
/* Call hooks before we remove power rails */
hook_notify(HOOK_CHIPSET_SHUTDOWN);
hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE);