diff options
author | Eric Yilun Lin <yllin@google.com> | 2022-08-10 07:33:10 +0000 |
---|---|---|
committer | Chromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-02-22 04:45:15 +0000 |
commit | fbf9b0a2f59b6cf282ee3aae8dbc7b3fbf34e481 (patch) | |
tree | 4a045661fdc19b09bf53ec92b3208fd95053e2ab | |
parent | 981fbf3f2b537b19e910f84d43e260d2dd963d6e (diff) | |
download | chrome-ec-fbf9b0a2f59b6cf282ee3aae8dbc7b3fbf34e481.tar.gz |
mt8186,mt8188: force turning off PMIC at S3S5
This CL ensures that before going to S5, the PMIC has turned off
the power source to AP, and we can move SHUTDOWN_COMPLETE to S3S5.
It did this by asserting EC_PMIC_EN_ODL at S3S5.
For pressing button shutdown, the flow becomes:
S0 -> hold powerkey 8 seconds -> S3 -> S3S5 -> hold EC_PMIC_EN_ODL for
8 seconds -> S5 -> G3
For the other shutdowns:
S0 -> S3S5 -> hold EC_PMIC_EN_ODL for 8 seconds -> S5 -> G3
Also, the AP won't boot when it's turning off the PMIC (S3S5) until
it goes to S5.
BUG=b:242012415 b:267268982
TEST=On Steelix, Tentacruel and Geralt:
* Cold reset:
$ dut-control cold_reset:on sleep:0.2 cold_reset:off
Result: G3 -> S0
* Long power press to shutdown:
$ dut-control dut-control power_key:8.2
Result: S0 -> S5 -> G3
* Long power press to power-on but then shutdown:
$ dut-control dut-control power_key:9.2
Result: G3 -> S0 -> S5 -> G3
* Short power press to power-on:
$ dut-control dut-control power_key:tab
Result: G3 -> S0
* Console command: apreset
Result: S0 -> S0, AP reboots
* Console command: apshutdown
Result: S0 -> S5 -> G3
* Lid open to power-on:
$ dut-control lid_open:no sleep:0.2 lid_open:yes
Result: G3 -> S0
* AP console: reboots
Reulst: S0 -> S0
* AP console: poweroff
Reulst: S0 -> G3
* Short power press to power-on:
$ dut-control dut-control power_key:tab
Result: G3 -> S0
BRANCH=none
Change-Id: Iacaa3dbcdafd61b2f3371e2ba376ebdcf29659ff
Signed-off-by: Eric Yilun Lin <yllin@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4269797
Reviewed-by: Ting Shen <phoenixshen@chromium.org>
Tested-by: Eric Yilun Lin <yllin@google.com>
Commit-Queue: Eric Yilun Lin <yllin@google.com>
-rw-r--r-- | power/mt8186.c | 134 | ||||
-rw-r--r-- | zephyr/test/krabby/src/power_seq.c | 46 |
2 files changed, 84 insertions, 96 deletions
diff --git a/power/mt8186.c b/power/mt8186.c index 16345ae2a8..1851b211bc 100644 --- a/power/mt8186.c +++ b/power/mt8186.c @@ -50,15 +50,13 @@ /* Long power key press to force shutdown in S0. go/crosdebug */ #define FORCED_SHUTDOWN_DELAY (8 * SECOND) -/* Long power key press to boot from S5/G3 state. */ -#define POWERBTN_BOOT_DELAY (10 * MSEC) -#define PMIC_EN_PULSE_MS 50 - /* PG4200 S5 ready delay */ #define PG_PP4200_S5_DELAY (100 * MSEC) /* Maximum time it should for PMIC to turn on after toggling PMIC_EN_ODL. */ #define PMIC_EN_TIMEOUT (300 * MSEC) +#define PMIC_EN_PULSE_MS 50 +#define PMIC_HARD_OFF_DELAY (8 * SECOND) /* 30 ms for hard reset, we hold it longer to prevent TPM false alarm. */ #define SYS_RST_PULSE_LENGTH (50 * MSEC) @@ -77,7 +75,7 @@ /* indicate MT8186 is processing a chipset reset. */ static bool is_resetting; -/* indicate MT8186 is processing a AP shutdown. */ +/* indicate MT8186 is processing a AP forcing shutdown. */ static bool is_shutdown; /* * indicate exiting off state, and don't respect the power signals until chipset @@ -85,6 +83,38 @@ static bool is_shutdown; */ static bool is_exiting_off = true; +/* Turn on the PMIC power source to AP, this also boots AP. */ +static void set_pmic_pwron(void) +{ + GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 1); + msleep(PMIC_EN_PULSE_MS); + GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 0); + msleep(PMIC_EN_PULSE_MS); + GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 1); +} + +/* Turn off the PMIC power source to AP (forcely), this could take up to 8 + * seconds + */ +static void set_pmic_pwroff(void) +{ + timestamp_t pmic_off_timeout; + + /* We don't have a PMIC PG signal, so we can only blindly assert + * the PMIC EN for a long delay time that the spec requiring. + */ + GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 0); + + pmic_off_timeout = get_time(); + pmic_off_timeout.val += PMIC_HARD_OFF_DELAY; + + while (!timestamp_expired(pmic_off_timeout, NULL)) { + msleep(100); + }; + + GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 1); +} + static void reset_request_interrupt_deferred(void) { chipset_reset(CHIPSET_RESET_AP_REQ); @@ -120,30 +150,12 @@ void chipset_watchdog_interrupt(enum gpio_signal signal) NORMAL_SHUTDOWN_DELAY); } -static void release_power_button(void) -{ - CPRINTS("release power button"); - GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 1); -} -DECLARE_DEFERRED(release_power_button); - void chipset_force_shutdown(enum chipset_shutdown_reason reason) { CPRINTS("%s: 0x%x", __func__, reason); report_ap_reset(reason); is_shutdown = true; - /* - * Force power off. This condition will reset once the state machine - * transitions to G3. - */ - GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 0); - if (reason != CHIPSET_SHUTDOWN_BUTTON) { - CPRINTS("Forcing pmic off with long press."); - GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 0); - hook_call_deferred(&release_power_button_data, - FORCED_SHUTDOWN_DELAY + SECOND); - } task_wake(TASK_ID_CHIPSET); } @@ -160,19 +172,6 @@ static void mt8186_exit_off(void) chipset_exit_hard_off(); } -void chipset_exit_hard_off_button(void) -{ - /* - * release power button in case we are in the 8 seconds long hold - * period - */ - hook_call_deferred(&release_power_button_data, -1); - release_power_button(); - /* Power up from off */ - mt8186_exit_off(); -} -DECLARE_DEFERRED(chipset_exit_hard_off_button); - static void reset_flag_deferred(void) { if (!is_resetting) @@ -217,7 +216,7 @@ static void power_reset_host_sleep_state(void) * * 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. + * is_shutdown flag indicates it's shutting down the AP, it goes for S5. */ static enum power_state power_get_signal_state(void) { @@ -229,14 +228,8 @@ static enum power_state power_get_signal_state(void) */ if (is_resetting) return POWER_S0; - if (is_shutdown) { - /* We are in S5 and pressing the powerkey to shutdown PMIC. */ - if (!gpio_get_level(GPIO_EC_PMIC_EN_ODL)) - return POWER_S5; - /* Powerkey released, PMIC full off. */ - else - return POWER_G3; - } + if (is_shutdown) + return POWER_S5; if (power_get_signals() & IN_AP_RST) return POWER_G3; if (power_get_signals() & IN_SUSPEND_ASSERTED) @@ -304,7 +297,6 @@ 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; @@ -312,8 +304,6 @@ enum power_state power_handle_state(enum power_state state) case POWER_S5: if (is_exiting_off) return POWER_S5S3; - else if (next_state == POWER_S5) - return POWER_S5; else if (next_state == POWER_G3) return POWER_S5G3; else @@ -352,12 +342,9 @@ enum power_state power_handle_state(enum power_state state) PG_PP4200_S5_DELAY)) return POWER_S5G3; #endif + set_pmic_pwron(); GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 1); - msleep(PMIC_EN_PULSE_MS); - GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 0); - msleep(PMIC_EN_PULSE_MS); - GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 1); if (power_wait_mask_signals_timeout(0, IN_AP_RST, PMIC_EN_TIMEOUT)) @@ -414,21 +401,6 @@ enum power_state power_handle_state(enum power_state state) */ enable_sleep(SLEEP_MASK_AP_RUN); - /* - * In case the power button is held awaiting power-off timeout, - * power off immediately now that we're entering S3. - */ - if (power_button_is_pressed()) { - hook_call_deferred(&chipset_force_shutdown_button_data, - -1); - /* - * if the ap is shutting down, but it doesn't report - * the reason, report it now. - */ - if (!is_shutdown) - chipset_force_shutdown_button(); - } - hook_notify(HOOK_CHIPSET_SUSPEND_COMPLETE); return POWER_S3; @@ -437,29 +409,27 @@ enum power_state power_handle_state(enum power_state state) power_signal_disable_interrupt(GPIO_AP_IN_SLEEP_L); power_signal_disable_interrupt(GPIO_AP_EC_WDTRST_L); power_signal_disable_interrupt(GPIO_AP_EC_WARM_RST_REQ); + GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 0); /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SHUTDOWN); + /* If this is a forcing shutdown, power off the PMIC now, + * and can wait at most up to 8 seconds. + */ + if (is_shutdown) + set_pmic_pwroff(); + #if DT_NODE_EXISTS(DT_NODELABEL(en_pp4200_s5)) gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(en_pp4200_s5), 0); +#endif hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); -#endif + + is_shutdown = false; return POWER_S5; case POWER_S5G3: -#if !DT_NODE_EXISTS(DT_NODELABEL(en_pp4200_s5)) - /* - * Normally, this is called in S3S5, but if it's a shutdown - * triggered by EC side, then EC is unable to set up PMIC - * registers for a graceful shutdown. What we can do instead - * is a force shutdown by asserting EC_PMIC_EN_ODL for 8 - * seconds, and all the rails are forced off, and the system - * will enter G3 after EC_PMIC_EN_ODL is released. - */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); -#endif return POWER_G3; default: CPRINTS("Unexpected power state %d", state); @@ -473,15 +443,11 @@ static void power_button_changed(void) { if (power_button_is_pressed()) { if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - hook_call_deferred(&chipset_exit_hard_off_button_data, - POWERBTN_BOOT_DELAY); - + mt8186_exit_off(); /* Delayed power down from S0/S3, cancel on PB release */ hook_call_deferred(&chipset_force_shutdown_button_data, FORCED_SHUTDOWN_DELAY); } else { - /* Power button released, cancel deferred shutdown/boot */ - hook_call_deferred(&chipset_exit_hard_off_button_data, -1); hook_call_deferred(&chipset_force_shutdown_button_data, -1); } } diff --git a/zephyr/test/krabby/src/power_seq.c b/zephyr/test/krabby/src/power_seq.c index 6931a3395d..03e5d85990 100644 --- a/zephyr/test/krabby/src/power_seq.c +++ b/zephyr/test/krabby/src/power_seq.c @@ -179,7 +179,8 @@ ZTEST(power_seq, test_host_sleep_hang) zassert_true(host_is_event_set(EC_HOST_EVENT_HANG_DETECT)); } -/* Shutdown from EC, S0 -> S5 (8 secs) -> G3 */ +/* Shutdown from EC, S0 -> power key press (8 secs) -> S3S5 (8 secs) -> S5 -> G3 + */ ZTEST(power_seq, test_force_shutdown) { const struct gpio_dt_spec *sys_rst_odl = @@ -196,19 +197,20 @@ ZTEST(power_seq, test_force_shutdown) /* Verify that ec resets ap and holds power button */ chipset_force_shutdown(CHIPSET_SHUTDOWN_CONSOLE_CMD); + k_sleep(K_SECONDS(1)); zassert_equal(gpio_emul_output_get(sys_rst_odl->port, sys_rst_odl->pin), 0); - zassert_equal(gpio_emul_output_get(ec_pmic_en_odl->port, - ec_pmic_en_odl->pin), - 0); /* Emulate AP power down (hw state G3, sw state unchanged), * Verify power state stops at S5 */ set_signal_state(POWER_G3); - zassert_equal(power_get_state(), POWER_S5); + zassert_equal(power_get_state(), POWER_S3S5); + zassert_equal(gpio_emul_output_get(ec_pmic_en_odl->port, + ec_pmic_en_odl->pin), + 0); - /* Wait 10 seconds for power button release and drop to G3 */ + /* Wait 10 seconds for EC_PMIC_EN_ODL release and drop to G3 */ k_sleep(K_SECONDS(10)); zassert_equal(gpio_emul_output_get(sys_rst_odl->port, sys_rst_odl->pin), 0); @@ -218,7 +220,7 @@ ZTEST(power_seq, test_force_shutdown) zassert_equal(power_get_state(), POWER_G3); } -/* Shutdown from AP, S0 -> G3 */ +/* Shutdown from AP, S0 -> powerkey hold (8 secs) -> S3S5 (8 secs) -> G3 */ ZTEST(power_seq, test_force_shutdown_button) { const struct gpio_dt_spec *sys_rst_odl = @@ -234,16 +236,36 @@ ZTEST(power_seq, test_force_shutdown_button) zassert_equal(power_get_state(), POWER_S0); power_button_simulate_press(10000); /* 10 seconds */ - k_sleep(K_SECONDS(2)); /* AP off after 2 seconds */ - set_signal_state(POWER_G3); + zassert_equal(power_get_state(), POWER_S0); + k_sleep(K_SECONDS(9)); /* AP off after 8 seconds */ zassert_equal(gpio_emul_output_get(sys_rst_odl->port, sys_rst_odl->pin), 0); zassert_equal(gpio_emul_output_get(ec_pmic_en_odl->port, ec_pmic_en_odl->pin), - 1); - zassert_equal(power_get_state(), POWER_G3); + 0); - k_sleep(K_SECONDS(10)); /* Wait for power button release */ + zassert_equal(power_get_state(), POWER_S3S5); + zassert_equal(gpio_emul_output_get(sys_rst_odl->port, sys_rst_odl->pin), + 0); + zassert_equal(gpio_emul_output_get(ec_pmic_en_odl->port, + ec_pmic_en_odl->pin), + 0); + + k_sleep(K_SECONDS(5)); /* Wait for power button release */ + /* Signal has dropped, but PMIC_EN is still held */ + set_signal_state(POWER_G3); + zassert_equal(power_get_state(), POWER_S3S5); + zassert_equal(gpio_emul_output_get(ec_pmic_en_odl->port, + ec_pmic_en_odl->pin), + 0); + + k_sleep(K_SECONDS(3)); /* Wait for G3 */ + + /* PMIC_EN released */ + zassert_equal(power_get_state(), POWER_G3); + zassert_equal(gpio_emul_output_get(ec_pmic_en_odl->port, + ec_pmic_en_odl->pin), + 1); } /* AP reset (S0 -> S0). |