diff options
Diffstat (limited to 'power')
-rw-r--r-- | power/mt8186.c | 77 |
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); |