summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Yilun Lin <yllin@google.com>2022-08-10 07:33:10 +0000
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-02-22 04:45:15 +0000
commitfbf9b0a2f59b6cf282ee3aae8dbc7b3fbf34e481 (patch)
tree4a045661fdc19b09bf53ec92b3208fd95053e2ab
parent981fbf3f2b537b19e910f84d43e260d2dd963d6e (diff)
downloadchrome-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.c134
-rw-r--r--zephyr/test/krabby/src/power_seq.c46
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).