summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTing Shen <phoenixshen@google.com>2021-11-16 16:06:37 +0800
committerCommit Bot <commit-bot@chromium.org>2022-01-07 04:31:36 +0000
commitafc427853f629e12e238dce780b7b13eabb3b299 (patch)
tree8e6687315010443a0c3a1a22bcd5f04124119054
parent229723e49cef20809c0ca0fca520d63c0cfd0e24 (diff)
downloadchrome-ec-afc427853f629e12e238dce780b7b13eabb3b299.tar.gz
power: add mt8186 power sequence
MT8186 power sequence is a simplified version of 8192/95. EC does not lie between AP and PMIC, so there's no need to forward the signals. Other logics are almost the same. BUG=b:206338930 TEST=Test following items on krabby CL:3233784 * Cold reset: $ dut-control cold_reset:on sleep:0.2 cold_reset:off Result: G3 -> S0 * Long power press to shutdown: $ dut-control 'ec_uart_cmd:powerbtn 8200' Result: S0 -> S5 -> G3 * Long power press to power-on but then shutdown: $ dut-control 'ec_uart_cmd:powerbtn 8200' Result: G3 -> S0 -> S5 -> G3 * Short power press to power-on: $ dut-control 'ec_uart_cmd:powerbtn 200' Result: G3 -> S0 * Console command: apreset Result: S0 -> S5 -> 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 BRANCH=none Cq-Depend: chromium:3366102 Signed-off-by: Ting Shen <phoenixshen@google.com> Change-Id: Iebfe77c8f6d127ee4d0685903b67afd215ca6682 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3233783 Reviewed-by: Eric Yilun Lin <yllin@google.com> Tested-by: Ting Shen <phoenixshen@chromium.org> Commit-Queue: Ting Shen <phoenixshen@chromium.org>
-rw-r--r--include/power/mt8186.h17
-rw-r--r--power/build.mk1
-rw-r--r--power/mt8186.c410
-rw-r--r--zephyr/CMakeLists.txt2
-rw-r--r--zephyr/Kconfig.powerseq7
-rw-r--r--zephyr/app/ec/soc/Kconfig6
-rw-r--r--zephyr/dts/bindings/cros_pwr_signal/mt8186,power-signal-list.yaml20
7 files changed, 463 insertions, 0 deletions
diff --git a/include/power/mt8186.h b/include/power/mt8186.h
new file mode 100644
index 0000000000..a2ad5648ed
--- /dev/null
+++ b/include/power/mt8186.h
@@ -0,0 +1,17 @@
+/* Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_POWER_MT8186_H_
+#define __CROS_EC_POWER_MT8186_H_
+
+enum power_signal {
+ AP_IN_RST,
+ AP_IN_S3,
+ AP_WDT_ASSERTED,
+ AP_WARM_RST_REQ,
+ POWER_SIGNAL_COUNT,
+};
+
+#endif /* __CROS_EC_POWER_MT8186_H_ */
diff --git a/power/build.mk b/power/build.mk
index 3e47167f0a..b04526c45f 100644
--- a/power/build.mk
+++ b/power/build.mk
@@ -17,6 +17,7 @@ power-$(CONFIG_CHIPSET_ECDRIVEN)+=ec_driven.o
power-$(CONFIG_CHIPSET_ICELAKE)+=icelake.o intel_x86.o
power-$(CONFIG_CHIPSET_MT817X)+=mt817x.o
power-$(CONFIG_CHIPSET_MT8183)+=mt8183.o
+power-$(CONFIG_CHIPSET_MT8186)+=mt8186.o
power-$(CONFIG_CHIPSET_MT8192)+=mt8192.o
power-$(CONFIG_CHIPSET_CEZANNE)+=amd_x86.o
power-$(CONFIG_CHIPSET_RK3288)+=rk3288.o
diff --git a/power/mt8186.c b/power/mt8186.c
new file mode 100644
index 0000000000..75ab6fd170
--- /dev/null
+++ b/power/mt8186.c
@@ -0,0 +1,410 @@
+/* Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * MT8186 SoC power sequencing module for Chrome EC
+ *
+ * This implements the following features:
+ *
+ * - Cold reset powers on the AP
+ *
+ * When powered off:
+ * - Press power button turns on the AP
+ * - Hold power button turns on the AP, and then 8s later turns it off and
+ * leaves it off until pwron is released and press again.
+ * - Lid open turns on the AP
+ *
+ * When powered on:
+ * - Holding power button for 8s powers off the AP
+ * - Pressing and releaseing power within that 8s is ignored
+ */
+
+#include "assert.h"
+#include "battery.h"
+#include "chipset.h"
+#include "common.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "lid_switch.h"
+#include "power.h"
+#include "power_button.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+
+#ifdef CONFIG_BRINGUP
+#define GPIO_SET_LEVEL(signal, value) \
+ gpio_set_level_verbose(CC_CHIPSET, signal, value)
+#else
+#define GPIO_SET_LEVEL(signal, value) gpio_set_level(signal, value)
+#endif
+
+/* Console output macros */
+#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ##args)
+
+/* Input state flags */
+#define IN_SUSPEND_ASSERTED POWER_SIGNAL_MASK(AP_IN_S3)
+#define IN_AP_RST POWER_SIGNAL_MASK(AP_IN_RST)
+
+/* 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
+
+/* Maximum time it should for PMIC to turn on after toggling PMIC_EN_ODL. */
+#define PMIC_EN_TIMEOUT (300 * MSEC)
+
+/* 30 ms for hard reset, we hold it longer to prevent TPM false alarm. */
+#define SYS_RST_PULSE_LENGTH (50 * MSEC)
+
+#ifndef CONFIG_ZEPHYR
+/* power signal list. Must match order of enum power_signal. */
+const struct power_signal_info power_signal_list[] = {
+ {GPIO_AP_EC_SYSRST_ODL, POWER_SIGNAL_ACTIVE_LOW, "AP_IN_RST"},
+ {GPIO_AP_IN_SLEEP_L, POWER_SIGNAL_ACTIVE_LOW, "AP_IN_S3"},
+ {GPIO_AP_EC_WDTRST_L, POWER_SIGNAL_ACTIVE_LOW, "AP_WDT_ASSERTED"},
+ {GPIO_AP_EC_WARM_RST_REQ, POWER_SIGNAL_ACTIVE_HIGH, "AP_WARM_RST_REQ"},
+};
+BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT);
+#endif /* CONFIG_ZEPHYR */
+
+static void reset_request_interrupt_deferred(void)
+{
+ chipset_reset(CHIPSET_RESET_AP_REQ);
+}
+DECLARE_DEFERRED(reset_request_interrupt_deferred);
+
+void chipset_reset_request_interrupt(enum gpio_signal signal)
+{
+ hook_call_deferred(&reset_request_interrupt_deferred_data, 0);
+}
+
+static void release_power_button(void)
+{
+ CPRINTS("release power button after 8 seconds.");
+ 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(%d)", __func__, reason);
+ report_ap_reset(reason);
+
+ /*
+ * Force power off. This condition will reset once the state machine
+ * transitions to G3.
+ */
+ GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 0);
+ 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);
+}
+
+void chipset_force_shutdown_button(void)
+{
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_BUTTON);
+}
+DECLARE_DEFERRED(chipset_force_shutdown_button);
+
+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 */
+ chipset_exit_hard_off();
+}
+DECLARE_DEFERRED(chipset_exit_hard_off_button);
+
+void chipset_reset(enum chipset_shutdown_reason reason)
+{
+ CPRINTS("%s: %d", __func__, reason);
+ report_ap_reset(reason);
+
+ GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 0);
+ usleep(SYS_RST_PULSE_LENGTH);
+ GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 1);
+}
+
+#ifdef CONFIG_POWER_TRACK_HOST_SLEEP_STATE
+static void power_reset_host_sleep_state(void)
+{
+ power_set_host_sleep_state(HOST_SLEEP_EVENT_DEFAULT_RESET);
+ sleep_reset_tracking();
+ power_chipset_handle_host_sleep_event(HOST_SLEEP_EVENT_DEFAULT_RESET,
+ NULL);
+}
+
+static void handle_chipset_reset(void)
+{
+ if (chipset_in_state(CHIPSET_STATE_SUSPEND)) {
+ CPRINTS("Chipset reset: exit s3");
+ power_reset_host_sleep_state();
+ task_wake(TASK_ID_CHIPSET);
+ }
+}
+DECLARE_HOOK(HOOK_CHIPSET_RESET, handle_chipset_reset, HOOK_PRIO_FIRST);
+#endif /* CONFIG_POWER_TRACK_HOST_SLEEP_STATE */
+
+/*
+ * Power state is determined from the following table:
+ *
+ * | IN_AP_RST | IN_SUSPEND_ASSERTED |
+ * ----------------------------------------------
+ * S0 | 0 | 0 |
+ * S3 | 0 | 1 |
+ * G3 | 1 | x |
+ *
+ * S5 is only used when exit from G3 in power_common_state().
+ */
+static enum power_state power_get_signal_state(void)
+{
+ if (power_get_signals() & IN_AP_RST)
+ return POWER_G3;
+ if (power_get_signals() & IN_SUSPEND_ASSERTED)
+ return POWER_S3;
+ return POWER_S0;
+}
+
+enum power_state power_chipset_init(void)
+{
+ int exit_hard_off = 1;
+ 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) {
+ disable_sleep(SLEEP_MASK_AP_RUN);
+ CPRINTS("already in S0");
+ }
+ } else if (system_get_reset_flags() & EC_RESET_FLAG_AP_OFF) {
+ exit_hard_off = 0;
+ } else if ((system_get_reset_flags() & EC_RESET_FLAG_HIBERNATE) &&
+ gpio_get_level(GPIO_AC_PRESENT)) {
+ /*
+ * If AC present, assume this is a wake-up by AC insert.
+ * Boot EC only.
+ *
+ * Note that extpower module is not initialized at this point,
+ * the only way is to ask GPIO_AC_PRESENT directly.
+ */
+ exit_hard_off = 0;
+ }
+
+ if (battery_is_present() == BP_YES)
+ /*
+ * (crosbug.com/p/28289): Wait battery stable.
+ * Some batteries use clock stretching feature, which requires
+ * more time to be stable.
+ */
+ battery_wait_for_stable();
+
+ if (exit_hard_off)
+ /* Auto-power on */
+ chipset_exit_hard_off();
+
+ if (init_state != POWER_G3 && !exit_hard_off)
+ /* Force shutdown from S5 if the PMIC is already up. */
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_INIT);
+
+ return init_state;
+}
+
+enum power_state power_handle_state(enum power_state state)
+{
+ enum power_state next_state = power_get_signal_state();
+
+ switch (state) {
+ case POWER_G3:
+ if (next_state != POWER_G3)
+ return POWER_G3S5;
+ break;
+
+ case POWER_S5:
+ return POWER_S5S3;
+
+ case POWER_S3:
+ if (next_state == POWER_G3)
+ return POWER_S3S5;
+ else if (next_state == POWER_S0)
+ return POWER_S3S0;
+ break;
+
+ case POWER_S0:
+ if (next_state != POWER_S0)
+ return POWER_S0S3;
+
+ break;
+
+ case POWER_G3S5:
+ return POWER_S5;
+
+ case POWER_S5S3:
+ hook_notify(HOOK_CHIPSET_PRE_INIT);
+
+ 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))
+ /* Give up, go back to G3. */
+ return POWER_S5G3;
+
+ msleep(500);
+ /* Call hooks now that rails are up */
+ hook_notify(HOOK_CHIPSET_STARTUP);
+ /*
+ * Clearing the sleep failure detection tracking on the path
+ * to S0 to handle any reset conditions.
+ */
+#ifdef CONFIG_POWER_SLEEP_FAILURE_DETECTION
+ power_reset_host_sleep_state();
+#endif /* CONFIG_POWER_SLEEP_FAILURE_DETECTION */
+ /* Power up to next state */
+ return POWER_S3;
+
+ case POWER_S3S0:
+ if (power_wait_mask_signals_timeout(0, IN_AP_RST, SECOND)) {
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
+ return POWER_S0S3;
+ }
+
+ /* Call hooks now that rails are up */
+ hook_notify(HOOK_CHIPSET_RESUME);
+
+#ifdef CONFIG_POWER_SLEEP_FAILURE_DETECTION
+ sleep_resume_transition();
+#endif /* CONFIG_POWER_SLEEP_FAILURE_DETECTION */
+
+ /*
+ * Disable idle task deep sleep. This means that the low
+ * power idle task will not go into deep sleep while in S0.
+ */
+ disable_sleep(SLEEP_MASK_AP_RUN);
+
+ /* Power up to next state */
+ return POWER_S0;
+
+ case POWER_S0S3:
+ /* Call hooks before we remove power rails */
+ hook_notify(HOOK_CHIPSET_SUSPEND);
+
+#ifdef CONFIG_POWER_SLEEP_FAILURE_DETECTION
+ sleep_suspend_transition();
+#endif /* CONFIG_POWER_SLEEP_FAILURE_DETECTION */
+
+ /*
+ * Enable idle task deep sleep. Allow the low power idle task
+ * to go into deep sleep in S3 or lower.
+ */
+ 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);
+
+ return POWER_S3;
+
+ case POWER_S3S5:
+ /* Call hooks before we remove power rails */
+ hook_notify(HOOK_CHIPSET_SHUTDOWN);
+ hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE);
+
+ /* Skip S5 */
+ return POWER_S5G3;
+
+ case POWER_S5G3:
+ return POWER_G3;
+ default:
+ CPRINTS("Unexpected power state %d", state);
+ break;
+ }
+
+ return state;
+}
+
+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);
+
+ /* 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);
+ }
+}
+DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, power_button_changed, HOOK_PRIO_DEFAULT);
+
+#ifdef CONFIG_POWER_TRACK_HOST_SLEEP_STATE
+static void suspend_hang_detected(void)
+{
+ CPRINTS("Warning: Detected sleep hang! Waking host up!");
+ host_set_single_event(EC_HOST_EVENT_HANG_DETECT);
+}
+
+__override void power_chipset_handle_host_sleep_event(
+ enum host_sleep_event state,
+ struct host_sleep_event_context *ctx)
+{
+ CPRINTS("Handle sleep: %d", state);
+
+ if (state == HOST_SLEEP_EVENT_S3_SUSPEND) {
+ /*
+ * Indicate to power state machine that a new host event for
+ * S3 suspend has been received and so chipset suspend
+ * notification needs to be sent to listeners.
+ */
+ sleep_set_notify(SLEEP_NOTIFY_SUSPEND);
+ sleep_start_suspend(ctx, suspend_hang_detected);
+
+ } else if (state == HOST_SLEEP_EVENT_S3_RESUME) {
+ /*
+ * Wake up chipset task and indicate to power state machine that
+ * listeners need to be notified of chipset resume.
+ */
+ sleep_set_notify(SLEEP_NOTIFY_RESUME);
+ task_wake(TASK_ID_CHIPSET);
+ sleep_complete_resume(ctx);
+
+ }
+}
+#endif /* CONFIG_POWER_TRACK_HOST_SLEEP_STATE */
+
+#ifdef CONFIG_LID_SWITCH
+static void lid_changed(void)
+{
+ /* Power-up from off on lid open */
+ if (lid_is_open() && chipset_in_state(CHIPSET_STATE_ANY_OFF))
+ chipset_exit_hard_off();
+}
+DECLARE_HOOK(HOOK_LID_CHANGE, lid_changed, HOOK_PRIO_DEFAULT);
+#endif
diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt
index f9cf915613..f1d6dead11 100644
--- a/zephyr/CMakeLists.txt
+++ b/zephyr/CMakeLists.txt
@@ -299,6 +299,8 @@ zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_POWERSEQ_INTEL
"${PLATFORM_EC}/power/intel_x86.c")
zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_POWERSEQ_HOST_SLEEP
"${PLATFORM_EC}/power/host_sleep.c")
+zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_POWERSEQ_MT8186
+ "${PLATFORM_EC}/power/mt8186.c")
zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_POWERSEQ_MT8192
"${PLATFORM_EC}/power/mt8192.c")
zephyr_library_sources_ifdef(CONFIG_PLATFORM_EC_POWERSEQ_SC7180
diff --git a/zephyr/Kconfig.powerseq b/zephyr/Kconfig.powerseq
index d35caf6900..305fdf74b9 100644
--- a/zephyr/Kconfig.powerseq
+++ b/zephyr/Kconfig.powerseq
@@ -170,6 +170,13 @@ config PLATFORM_EC_POWERSEQ_MT8192
help
Use the MT8192 code for power sequencing.
+config PLATFORM_EC_POWERSEQ_MT8186
+ bool "Use common MT8186 code for power sequencing"
+ default y
+ depends on AP_ARM_MTK_MT8186
+ help
+ Use the MT8186 code for power sequencing.
+
config PLATFORM_EC_POWERSEQ_SC7180
bool "SC7180 power sequencing"
depends on AP_ARM_QUALCOMM_SC7180
diff --git a/zephyr/app/ec/soc/Kconfig b/zephyr/app/ec/soc/Kconfig
index efb9ab9949..1fd4e26e16 100644
--- a/zephyr/app/ec/soc/Kconfig
+++ b/zephyr/app/ec/soc/Kconfig
@@ -44,6 +44,12 @@ config AP_ARM_MTK_MT8192
help
The application processor is a MediaTek MT8192 processor.
+config AP_ARM_MTK_MT8186
+ bool "MediaTek MT8186"
+ select AP_AARCH64
+ help
+ The application processor is a MediaTek MT8186 processor.
+
config AP_ARM_QUALCOMM_SC7180
bool "Qualcomm Snapdragon SC7180"
select AP_AARCH64
diff --git a/zephyr/dts/bindings/cros_pwr_signal/mt8186,power-signal-list.yaml b/zephyr/dts/bindings/cros_pwr_signal/mt8186,power-signal-list.yaml
new file mode 100644
index 0000000000..6e9af9ccef
--- /dev/null
+++ b/zephyr/dts/bindings/cros_pwr_signal/mt8186,power-signal-list.yaml
@@ -0,0 +1,20 @@
+# Copyright 2021 Google LLC
+# SPDX-License-Identifier: Apache-2.0
+
+description: MediaTek MT8186, Power Signal List
+compatible: "mt8186,power-signal-list"
+
+include: power-signal-list.yaml
+
+properties:
+ power-signals-required:
+ default: 4
+
+child-binding:
+ properties:
+ power-enum-name:
+ enum:
+ - AP_IN_RST
+ - AP_IN_S3
+ - AP_WDT_ASSERTED
+ - AP_WARM_RST_REQ