diff options
Diffstat (limited to 'power/mt8183.c')
-rw-r--r-- | power/mt8183.c | 639 |
1 files changed, 0 insertions, 639 deletions
diff --git a/power/mt8183.c b/power/mt8183.c deleted file mode 100644 index bdbd319601..0000000000 --- a/power/mt8183.c +++ /dev/null @@ -1,639 +0,0 @@ -/* Copyright 2018 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. - */ - -/* mt8183 chipset power control module for Chrome EC */ - -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.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" -#include "util.h" - -/* - * mt8183 has two different power sequence versions - * 0: for normal tablet and detachable form factor - * 1: for boards have GPIO_EN_PP1800_S5_L - * CONFIG_CHIPSET_POWER_SEQ_VERSION defaults to 0, re-define the power seq - * version if needed. - */ - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -/* Input state flags */ -#define IN_PGOOD_PMIC POWER_SIGNAL_MASK(PMIC_PWR_GOOD) -#define IN_SUSPEND_ASSERTED POWER_SIGNAL_MASK(AP_IN_S3_L) - -/* Rails required for S3 and S0 */ -#define IN_PGOOD_S0 (IN_PGOOD_PMIC) -#define IN_PGOOD_S3 (IN_PGOOD_PMIC) - -/* All inputs in the right state for S0 */ -#define IN_ALL_S0 (IN_PGOOD_S0 & ~IN_SUSPEND_ASSERTED) - -/* Long power key press to force shutdown in S0. go/crosdebug */ -#ifdef VARIANT_KUKUI_JACUZZI -#define FORCED_SHUTDOWN_DELAY (8 * SECOND) -#else -#define FORCED_SHUTDOWN_DELAY (10 * SECOND) -#endif - -/* Long power key press to boot from S5/G3 state. */ -#ifndef POWERBTN_BOOT_DELAY -#define POWERBTN_BOOT_DELAY (1 * SECOND) -#endif - -#define CHARGER_INITIALIZED_DELAY_MS 100 -#define CHARGER_INITIALIZED_TRIES 40 - -#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) - -/* - * Amount of time we need to hold PMIC_FORCE_RESET_ODL to ensure PMIC is really - * off and will not restart on its own. - */ -#define PMIC_FORCE_RESET_TIME (10 * SECOND) - -/* Time delay in G3 to deassert EN_PP1800_S5_L */ -#define EN_PP1800_S5_L_DEASSERT_TIME (20 * MSEC) - -/* Data structure for a GPIO operation for power sequencing */ -struct power_seq_op { - /* enum gpio_signal in 8 bits */ - uint8_t signal; - uint8_t level; - /* Number of milliseconds to delay after setting signal to level */ - uint8_t delay; -}; -BUILD_ASSERT(GPIO_COUNT < 256); - -/* - * This is the power sequence for POWER_S5S3. - * The entries in the table are handled sequentially from the top - * to the bottom. - */ - -static const struct power_seq_op s5s3_power_seq[] = { - /* Release PMIC watchdog. */ - { GPIO_PMIC_WATCHDOG_L, 1, 0 }, - /* Turn on AP. */ - { GPIO_AP_SYS_RST_L, 1, 2 }, -}; - -/* The power sequence for POWER_S3S0 */ -static const struct power_seq_op s3s0_power_seq[] = { -}; - -/* The power sequence for POWER_S0S3 */ -static const struct power_seq_op s0s3_power_seq[] = { -}; - -/* The power sequence for POWER_S3S5 */ -static const struct power_seq_op s3s5_power_seq[] = { - /* Turn off AP. */ - { GPIO_AP_SYS_RST_L, 0, 0 }, - /* Assert watchdog to PMIC (there may be a 1.6ms debounce) */ - { GPIO_PMIC_WATCHDOG_L, 0, 3 }, -}; - -static int forcing_shutdown; -static int boot_from_cutoff; - -void chipset_reset_request_interrupt(enum gpio_signal signal) -{ - chipset_reset(CHIPSET_RESET_AP_REQ); -} - -/* - * Triggers on falling edge of AP watchdog line only. The falling edge can - * happen in these 3 cases: - * - AP asserts watchdog while the AP is on: this is a real AP-initiated reset. - * - EC asserted GPIO_AP_SYS_RST_L, so the AP is in reset and AP watchdog falls - * as well. This is _not_ a watchdog reset. We mask these cases by disabling - * the interrupt just before shutting down the AP, and re-enabling it just - * after starting the AP. - * - PMIC has shut down (e.g. the AP powered off by itself), this is not a - * watchdog reset either. This should be covered by the case above if the - * EC reacts quickly enough, but we mask those cases as well by testing if - * the PMIC is still on when the watchdog line falls. - */ -void chipset_watchdog_interrupt(enum gpio_signal signal) -{ - if (power_get_signals() & IN_PGOOD_PMIC) - chipset_reset(CHIPSET_RESET_AP_WATCHDOG); -} - -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. - */ - forcing_shutdown = 1; - 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) -{ - /* Power up from off */ - forcing_shutdown = 0; - chipset_exit_hard_off(); -} -DECLARE_DEFERRED(chipset_exit_hard_off_button); - -#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 */ - -/* If chipset needs to be reset, EC also reboots to RO. */ -void chipset_reset(enum chipset_reset_reason reason) -{ - int flags = SYSTEM_RESET_HARD; - - CPRINTS("%s: %d", __func__, reason); - report_ap_reset(reason); - - cflush(); - if (reason == CHIPSET_RESET_AP_WATCHDOG) - flags |= SYSTEM_RESET_AP_WATCHDOG; - - system_reset(flags); - - /* This should not be reachable. */ - while (1) - ; -} - -enum power_state power_chipset_init(void) -{ - /* Enable reboot / sleep control inputs from AP */ - gpio_enable_interrupt(GPIO_WARM_RESET_REQ); - gpio_enable_interrupt(GPIO_AP_IN_SLEEP_L); - - if (system_jumped_to_this_image()) { - if ((power_get_signals() & IN_ALL_S0) == IN_ALL_S0) { - disable_sleep(SLEEP_MASK_AP_RUN); - gpio_enable_interrupt(GPIO_AP_EC_WATCHDOG_L); - CPRINTS("already in S0"); - return POWER_S0; - } - } else if (system_get_reset_flags() & EC_RESET_FLAG_AP_OFF) { - /* Force shutdown from S5 if the PMIC is already up. */ - if (power_get_signals() & IN_PGOOD_PMIC) { - forcing_shutdown = 1; - return POWER_S5; - } - } else { - /* Auto-power on */ - chipset_exit_hard_off(); - - if (system_get_reset_flags() == EC_RESET_FLAG_RESET_PIN) - boot_from_cutoff = 1; - } - - /* Start from S5 if the PMIC is already up. */ - if (power_get_signals() & IN_PGOOD_PMIC) - return POWER_S5; - - return POWER_G3; -} - -/* - * If we have to force reset the PMIC, we only need to do so for a few seconds, - * then we need to release the GPIO to prevent leakage in G3. - */ -static void release_pmic_force_reset(void) -{ - CPRINTS("Releasing PMIC force reset"); - gpio_set_level(GPIO_PMIC_FORCE_RESET_ODL, 1); -} -DECLARE_DEFERRED(release_pmic_force_reset); - -/** - * Step through the power sequence table and do corresponding GPIO operations. - * - * @param power_seq_ops The pointer to the power sequence table. - * @param op_count The number of entries of power_seq_ops. - */ -static void power_seq_run(const struct power_seq_op *power_seq_ops, - int op_count) -{ - int i; - - for (i = 0; i < op_count; i++) { - gpio_set_level(power_seq_ops[i].signal, - power_seq_ops[i].level); - if (!power_seq_ops[i].delay) - continue; - msleep(power_seq_ops[i].delay); - } -} - -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 -static void deassert_en_pp1800_s5_l(void) -{ - gpio_set_level(GPIO_EN_PP1800_S5_L, 1); -} -DECLARE_DEFERRED(deassert_en_pp1800_s5_l); -#endif - -enum power_state power_handle_state(enum power_state state) -{ - /* - * Set if we already had a rising edge on AP_SYS_RST_L. If so, any - * subsequent boot attempt will require an EC reset. - */ - static int booted; - - /* Retry S5->S3 transition, if not zero. */ - static int s5s3_retry; - - /* - * PMIC power went away (AP most likely decided to shut down): - * transition to S5, G3. - */ - static int ap_shutdown; - uint16_t tries = 0; - - switch (state) { - case POWER_G3: - -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 - hook_call_deferred(&deassert_en_pp1800_s5_l_data, - EN_PP1800_S5_L_DEASSERT_TIME); -#endif - - /* Go back to S5->G3 if the PMIC unexpectedly starts again. */ - if (power_get_signals() & IN_PGOOD_PMIC) - return POWER_S5G3; - break; - - case POWER_S5: - boot_from_cutoff = 0; - - /* - * If AP initiated shutdown, PMIC is off, and we can transition - * to G3 immediately. - */ - if (ap_shutdown) { - ap_shutdown = 0; - return POWER_S5G3; - } else if (!forcing_shutdown) { - /* Powering up. */ - s5s3_retry = 1; - return POWER_S5S3; - } - - /* Forcing shutdown */ - - /* Long press has worked, transition to G3. */ - if (!(power_get_signals() & IN_PGOOD_PMIC)) - return POWER_S5G3; - - /* - * Try to force PMIC shutdown with a long press. This takes 8s, - * shorter than the common code S5->G3 timeout (10s). - */ - CPRINTS("Forcing shutdown with long press."); - gpio_set_level(GPIO_PMIC_EN_ODL, 0); - - /* - * Stay in S5, common code will drop to G3 after timeout - * if the long press does not work. - */ - return POWER_S5; - - case POWER_S3: - if (!power_has_signals(IN_PGOOD_S3) || forcing_shutdown) - return POWER_S3S5; - else if (!(power_get_signals() & IN_SUSPEND_ASSERTED)) - return POWER_S3S0; - break; - - case POWER_S0: - if (!power_has_signals(IN_PGOOD_S0) || - forcing_shutdown || - power_get_signals() & IN_SUSPEND_ASSERTED) - return POWER_S0S3; - - break; - - case POWER_G3S5: - forcing_shutdown = 0; - -#ifdef CONFIG_BATTERY_SMART - /* - * b:148045048: With the adapter to activate the smart battery - * which is shutdown mode, will enable PMIC during activation - * and have heavy loading, which will prevent the system from - * powering on. Delay to boot system until the smart battry - * is ready. - */ - if (battery_hw_present() && boot_from_cutoff) { - static int total_sleep_ms; - - if (total_sleep_ms < 4000) { - msleep(10); - total_sleep_ms += 10; - return POWER_G3S5; - } - } -#endif - - /* - * Allow time for charger to be initialized, in case we're - * trying to boot the AP with no battery. - */ - while (charge_prevent_power_on(0) && - tries++ < CHARGER_INITIALIZED_TRIES) { - msleep(CHARGER_INITIALIZED_DELAY_MS); - } - - /* Return to G3 if battery level is too low. */ - if (charge_want_shutdown() || - tries > CHARGER_INITIALIZED_TRIES) { - CPRINTS("power-up inhibited"); - chipset_force_shutdown( - CHIPSET_SHUTDOWN_BATTERY_INHIBIT); - return POWER_G3; - } - -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 - hook_call_deferred(&deassert_en_pp1800_s5_l_data, -1); -#endif - hook_call_deferred(&release_pmic_force_reset_data, -1); - gpio_set_level(GPIO_PMIC_FORCE_RESET_ODL, 1); -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 - gpio_set_level(GPIO_EN_PP1800_S5_L, 0); -#endif - - /* Power up to next state */ - return POWER_S5; - - case POWER_S5S3: - hook_notify(HOOK_CHIPSET_PRE_INIT); - - /* - * Release power button in case it was pressed by force shutdown - * sequence. - */ - gpio_set_level(GPIO_PMIC_EN_ODL, 1); - - /* If PMIC is off, switch it on by pulsing PMIC enable. */ - if (!(power_get_signals() & IN_PGOOD_PMIC)) { - msleep(PMIC_EN_PULSE_MS); - gpio_set_level(GPIO_PMIC_EN_ODL, 0); - msleep(PMIC_EN_PULSE_MS); - gpio_set_level(GPIO_PMIC_EN_ODL, 1); - } - - /* If EC jumped, or has already booted once, reboot to RO. */ - if (system_jumped_to_this_image() || booted) { - /* - * TODO(b:109850749): How quickly does the EC come back - * up? Would IN_PGOOD_PMIC be ready by the time we are - * back? According to PMIC spec, it should take ~158 ms - * after debounce (32 ms), minus PMIC_EN_PULSE_MS above. - * It would be good to avoid another _EN pulse above. - */ - chipset_reset(CHIPSET_RESET_AP_REQ); - } - - /* - * Wait for PMIC to bring up rails. Retry if it fails - * (it may take 2 attempts on restart after we use - * force reset). - */ - if (power_wait_signals_timeout(IN_PGOOD_PMIC, - PMIC_EN_TIMEOUT)) { - if (s5s3_retry) { - s5s3_retry = 0; - return POWER_S5S3; - } - /* Give up, go back to G3. */ - return POWER_S5G3; - } - - booted = 1; - /* Enable S3 power supplies, release AP reset. */ - power_seq_run(s5s3_power_seq, ARRAY_SIZE(s5s3_power_seq)); - gpio_enable_interrupt(GPIO_AP_EC_WATCHDOG_L); - - /* 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: - power_seq_run(s3s0_power_seq, ARRAY_SIZE(s3s0_power_seq)); - - if (power_wait_signals(IN_PGOOD_S0)) { - 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 */ - - /* - * TODO(b:109850749): Check if we need some delay here to - * "debounce" entering suspend (rk3399 uses 20ms delay). - */ - - power_seq_run(s0s3_power_seq, ARRAY_SIZE(s0s3_power_seq)); - - /* - * 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()) { - forcing_shutdown = 1; - hook_call_deferred(&chipset_force_shutdown_button_data, - -1); - } - - return POWER_S3; - - case POWER_S3S5: - /* PMIC has shutdown, transition to G3. */ - if (!(power_get_signals() & IN_PGOOD_PMIC)) - ap_shutdown = 1; - - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - gpio_disable_interrupt(GPIO_AP_EC_WATCHDOG_L); - power_seq_run(s3s5_power_seq, ARRAY_SIZE(s3s5_power_seq)); - - /* Call hooks after we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); - - /* Start shutting down */ - return POWER_S5; - - case POWER_S5G3: - /* Release the power button, in case it was long pressed. */ - if (forcing_shutdown) - gpio_set_level(GPIO_PMIC_EN_ODL, 1); - - /* - * If PMIC is still not off, assert PMIC_FORCE_RESET_ODL. - * This should only happen for forced shutdown where the AP is - * not able to send a command to the PMIC, and where the long - * power+home press did not work (if the PMIC is misconfigured). - * Also, PMIC will lose RTC state, in that case. - */ - if (power_get_signals() & IN_PGOOD_PMIC) { - CPRINTS("Forcing PMIC off"); - gpio_set_level(GPIO_PMIC_FORCE_RESET_ODL, 0); - msleep(5); - hook_call_deferred(&release_pmic_force_reset_data, - PMIC_FORCE_RESET_TIME); - - return POWER_S5G3; - } - - return POWER_G3; - } - - return state; -} - -#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 */ - -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_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 |