diff options
Diffstat (limited to 'power')
-rw-r--r-- | power/alderlake_slg4bd44540.c | 277 | ||||
-rw-r--r-- | power/amd_x86.c | 524 | ||||
-rw-r--r-- | power/apollolake.c | 183 | ||||
-rw-r--r-- | power/braswell.c | 324 | ||||
-rw-r--r-- | power/build.mk | 30 | ||||
-rw-r--r-- | power/cannonlake.c | 139 | ||||
-rw-r--r-- | power/cometlake-discrete.c | 416 | ||||
-rw-r--r-- | power/cometlake.c | 211 | ||||
-rw-r--r-- | power/common.c | 1117 | ||||
-rw-r--r-- | power/ec_driven.c | 55 | ||||
-rw-r--r-- | power/host_sleep.c | 269 | ||||
-rw-r--r-- | power/icelake.c | 325 | ||||
-rw-r--r-- | power/intel_x86.c | 679 | ||||
-rw-r--r-- | power/mt817x.c | 818 | ||||
-rw-r--r-- | power/mt8183.c | 639 | ||||
-rw-r--r-- | power/mt8192.c | 543 | ||||
-rw-r--r-- | power/qcom.c | 1169 | ||||
-rw-r--r-- | power/rk3288.c | 577 | ||||
-rw-r--r-- | power/rk3399.c | 610 | ||||
-rw-r--r-- | power/sdm845.c | 882 | ||||
-rw-r--r-- | power/skylake.c | 194 |
21 files changed, 0 insertions, 9981 deletions
diff --git a/power/alderlake_slg4bd44540.c b/power/alderlake_slg4bd44540.c deleted file mode 100644 index e7ae9497a2..0000000000 --- a/power/alderlake_slg4bd44540.c +++ /dev/null @@ -1,277 +0,0 @@ -/* Copyright 2021 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. - */ - -/* - * This was originally copied form power/icelake.c (also used on TGL and - * ADL) and adapted to support ADL designs using the Silergy SLG4BD44540 - * power sequencer chip. - */ - -#include "board_config.h" -#include "chipset.h" -#include "console.h" -#include "gpio.h" -#include "power.h" -#include "power/alderlake_slg4bd44540.h" -#include "power/intel_x86.h" -#include "timer.h" - -/* - * These delays are used by the brya power sequence reference design and - * should be suitable for variants. - */ - -/* SEQ_EC_ALL_SYS_PG high to VCCST_PWRGD high delay */ -#define VCCST_PWRGD_DELAY_MS 2 - -/* IMVP9_VRRDY high to PCH_PWROK high delay */ -#define PCH_PWROK_DELAY_MS 2 - -/* SEQ_EC_ALL_SYS_PG high to EC_PCH_SYS_PWROK high delay */ -#define SYS_PWROK_DELAY_MS 45 - -/* IMVP9_VRRDY high timeout */ -#define VRRDY_TIMEOUT_MS 50 - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -#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 - -/* The wait time is ~150 msec, allow for safety margin. */ -#define IN_PCH_SLP_SUS_WAIT_TIME_USEC (250 * MSEC) - -/* Power signals list. Must match order of enum power_signal. */ -const struct power_signal_info power_signal_list[] = { - [X86_SLP_S0_DEASSERTED] = { - .gpio = GPIO_PCH_SLP_S0_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH | - POWER_SIGNAL_DISABLE_AT_BOOT, - .name = "SLP_S0_DEASSERTED", - }, - [X86_SLP_S3_DEASSERTED] = { - .gpio = SLP_S3_SIGNAL_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "SLP_S3_DEASSERTED", - }, - [X86_SLP_S4_DEASSERTED] = { - .gpio = SLP_S4_SIGNAL_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "SLP_S4_DEASSERTED", - }, - [X86_SLP_SUS_DEASSERTED] = { - .gpio = GPIO_SLP_SUS_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "SLP_SUS_DEASSERTED", - }, - [X86_RSMRST_L_PGOOD] = { - .gpio = GPIO_PG_EC_RSMRST_ODL, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "RSMRST_L_PGOOD", - }, - [X86_DSW_DPWROK] = { - .gpio = GPIO_PG_EC_DSW_PWROK, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "DSW_DPWROK", - }, - [X86_ALL_SYS_PGOOD] = { - .gpio = GPIO_PG_EC_ALL_SYS_PWRGD, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "ALL_SYS_PWRGD", - }, -}; -BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); - -__overridable int intel_x86_get_pg_ec_dsw_pwrok(void) -{ - return gpio_get_level(GPIO_PG_EC_DSW_PWROK); -} - -__overridable int intel_x86_get_pg_ec_all_sys_pwrgd(void) -{ - return gpio_get_level(GPIO_PG_EC_ALL_SYS_PWRGD); -} - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - int timeout_ms = 50; - - CPRINTS("%s() %d", __func__, reason); - report_ap_reset(reason); - - /* Turn off RMSRST_L to meet tPCH12 */ - board_before_rsmrst(0); - GPIO_SET_LEVEL(GPIO_PCH_RSMRST_L, 0); - board_after_rsmrst(0); - - /* Turn off S5 rails */ - GPIO_SET_LEVEL(GPIO_EN_S5_RAILS, 0); - - /* - * TODO(b/179519791): Replace this wait with - * power_wait_signals_timeout() - */ - /* Now wait for DSW_PWROK and RSMRST_ODL to go away. */ - while (intel_x86_get_pg_ec_dsw_pwrok() && - gpio_get_level(GPIO_PG_EC_RSMRST_ODL) && (timeout_ms > 0)) { - msleep(1); - timeout_ms--; - }; - - if (!timeout_ms) - CPRINTS("DSW_PWROK or RSMRST_ODL didn't go low! Assuming G3."); -} - -void chipset_handle_espi_reset_assert(void) -{ - /* No special handling needed. */ -} - -enum power_state chipset_force_g3(void) -{ - chipset_force_shutdown(CHIPSET_SHUTDOWN_G3); - - return POWER_G3; -} - -static void ap_off(void) -{ - GPIO_SET_LEVEL(GPIO_VCCST_PWRGD_OD, 0); - GPIO_SET_LEVEL(GPIO_PCH_PWROK, 0); - GPIO_SET_LEVEL(GPIO_EC_PCH_SYS_PWROK, 0); -} - -/* - * We have asserted VCCST_PWRGO_OD, now wait for the IMVP9.1 - * to assert IMVP9_VRRDY_OD. - * - * Returns state of VRRDY. - */ - -static int wait_for_vrrdy(void) -{ - int timeout_ms = VRRDY_TIMEOUT_MS; - int vrrdy; - - for (; timeout_ms > 0; --timeout_ms) { - vrrdy = gpio_get_level(GPIO_IMVP9_VRRDY_OD); - if (vrrdy != 0) - return 1; - msleep(1); - } - return 0; -} - -/* - * The relationship between these signals is described in - * Intel PDG #627205 rev. 0.81. - * - * tCPU16: >= 0 - * VCCST_PWRGD to PCH_PWROK - * tPLT05: >= 0 - * SYS_ALL_PWRGD to SYS_PWROK - * PCH_PWROK to SYS_PWROK - */ - -static void all_sys_pwrgd_pass_thru(void) -{ - int sys_pg; - int vccst_pg; - int pch_pok; - int sys_pok; - - sys_pg = gpio_get_level(GPIO_SEQ_EC_ALL_SYS_PG); - - if (IS_ENABLED(CONFIG_BRINGUP)) - CPRINTS("SEQ_EC_ALL_SYS_PG is %d", sys_pg); - - if (sys_pg == 0) { - ap_off(); - return; - } - - /* SEQ_EC_ALL_SYS_PG is asserted, enable VCCST_PWRGD_OD. */ - - vccst_pg = gpio_get_level(GPIO_VCCST_PWRGD_OD); - if (vccst_pg == 0) { - msleep(VCCST_PWRGD_DELAY_MS); - GPIO_SET_LEVEL(GPIO_VCCST_PWRGD_OD, 1); - } - - /* Enable PCH_PWROK, gated by VRRDY. */ - - pch_pok = gpio_get_level(GPIO_PCH_PWROK); - if (pch_pok == 0) { - if (wait_for_vrrdy() == 0) { - CPRINTS("Timed out waiting for VRRDY, " - "shutting AP off!"); - ap_off(); - return; - } - msleep(PCH_PWROK_DELAY_MS); - GPIO_SET_LEVEL(GPIO_PCH_PWROK, 1); - } - - /* Enable PCH_SYS_PWROK. */ - - sys_pok = gpio_get_level(GPIO_EC_PCH_SYS_PWROK); - if (sys_pok == 0) { - msleep(SYS_PWROK_DELAY_MS); - /* Check if we lost power while waiting. */ - sys_pg = gpio_get_level(GPIO_SEQ_EC_ALL_SYS_PG); - if (sys_pg == 0) { - CPRINTS("SEQ_EC_ALL_SYS_PG deasserted, " - "shutting AP off!"); - ap_off(); - return; - } - GPIO_SET_LEVEL(GPIO_EC_PCH_SYS_PWROK, 1); - /* PCH will now release PLT_RST */ - } -} - -enum power_state power_handle_state(enum power_state state) -{ - all_sys_pwrgd_pass_thru(); - - common_intel_x86_handle_rsmrst(state); - - switch (state) { - - case POWER_G3S5: - GPIO_SET_LEVEL(GPIO_EN_S5_RAILS, 1); - - if (power_wait_signals(IN_PGOOD_ALL_CORE)) - break; - - /* - * Now wait for SLP_SUS_L to go high based on tPCH32. If this - * signal doesn't go high within 250 msec then go back to G3. - */ - if (power_wait_signals_timeout(IN_PCH_SLP_SUS_DEASSERTED, - IN_PCH_SLP_SUS_WAIT_TIME_USEC) != EC_SUCCESS) { - CPRINTS("SLP_SUS_L didn't go high! Going back to G3."); - return POWER_S5G3; - } - break; - - case POWER_S5: - /* If SLP_SUS_L is asserted, we're no longer in S5. */ - if (!power_has_signals(IN_PCH_SLP_SUS_DEASSERTED)) - return POWER_S5G3; - break; - - default: - break; - } - - return common_intel_x86_power_handle_state(state); -} diff --git a/power/amd_x86.c b/power/amd_x86.c deleted file mode 100644 index c1b7b2d853..0000000000 --- a/power/amd_x86.c +++ /dev/null @@ -1,524 +0,0 @@ -/* Copyright 2017 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. - */ - -/* AMD x86 power sequencing module for Chrome EC */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "lid_switch.h" -#include "lpc.h" -#include "power.h" -#include "power_button.h" -#include "system.h" -#include "timer.h" -#include "usb_charge.h" -#include "util.h" -#include "wireless.h" -#include "registers.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ##args) - -#define IN_S5_PGOOD POWER_SIGNAL_MASK(X86_S5_PGOOD) - -static int forcing_shutdown; /* Forced shutdown in progress? */ - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s()", __func__); - - if (!chipset_in_or_transitioning_to_state(CHIPSET_STATE_ANY_OFF)) { - forcing_shutdown = 1; - power_button_pch_press(); - report_ap_reset(reason); - } -} - -static void chipset_force_g3(void) -{ - /* Disable system power ("*_A" rails) in G3. */ - gpio_set_level(GPIO_EN_PWR_A, 0); -} - -void chipset_reset(enum chipset_reset_reason reason) -{ - CPRINTS("%s: %d", __func__, reason); - - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { - CPRINTS("Can't reset: SOC is off"); - return; - } - - report_ap_reset(reason); - /* - * Send a pulse to SYS_RST to trigger a warm reset. - */ - gpio_set_level(GPIO_SYS_RESET_L, 0); - usleep(32 * MSEC); - gpio_set_level(GPIO_SYS_RESET_L, 1); -} - -void chipset_throttle_cpu(int throttle) -{ - CPRINTS("%s(%d)", __func__, throttle); - if (IS_ENABLED(CONFIG_CPU_PROCHOT_ACTIVE_LOW)) - throttle = !throttle; - - if (chipset_in_state(CHIPSET_STATE_ON)) - gpio_set_level(GPIO_CPU_PROCHOT, throttle); -} - -void chipset_handle_espi_reset_assert(void) -{ - /* - * eSPI_Reset# pin being asserted without RSMRST# being asserted - * means there is an unexpected power loss (global reset event). - * In this case, check if the shutdown is forced by the EC (due - * to battery, thermal, or console command). The forced shutdown - * initiates a power button press that we need to release. - * - * NOTE: S5_PGOOD input is passed through to the RSMRST# output to - * the AP. - */ - if ((power_get_signals() & IN_S5_PGOOD) && forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } -} - -enum power_state power_chipset_init(void) -{ - CPRINTS("%s: power_signal=0x%x", __func__, power_get_signals()); - - if (!system_jumped_to_this_image()) - return POWER_G3; - /* - * We are here as RW. We need to handle the following cases: - * - * 1. Late sysjump by software sync. AP is in S0. - * 2. Shutting down in recovery mode then sysjump by EFS2. AP is in S5 - * and expected to sequence down. - * 3. Rebooting from recovery mode then sysjump by EFS2. AP is in S5 - * and expected to sequence up. - * 4. RO jumps to RW from main() by EFS2. (a.k.a. power on reset, cold - * reset). AP is in G3. - */ - if (gpio_get_level(GPIO_S0_PGOOD)) { - /* case #1. Disable idle task deep sleep when in S0. */ - disable_sleep(SLEEP_MASK_AP_RUN); - CPRINTS("already in S0"); - return POWER_S0; - } - if (power_get_signals() & IN_S5_PGOOD) { - /* case #2 & #3 */ - CPRINTS("already in S5"); - return POWER_S5; - } - /* case #4 */ - chipset_force_g3(); - return POWER_G3; -} - -static void handle_pass_through(enum gpio_signal pin_in, - enum gpio_signal pin_out) -{ - /* - * Pass through asynchronously, as SOC may not react - * immediately to power changes. - */ - int in_level = gpio_get_level(pin_in); - int out_level = gpio_get_level(pin_out); - - /* - * Only pass through high S0_PGOOD (S0 power) when S5_PGOOD (S5 power) - * is also high (S0_PGOOD is pulled high in G3 when S5_PGOOD is low). - */ - if ((pin_in == GPIO_S0_PGOOD) && !gpio_get_level(GPIO_S5_PGOOD)) - in_level = 0; - - /* Nothing to do. */ - if (in_level == out_level) - return; - - /* - * SOC requires a delay of 1ms with stable power before - * asserting PWR_GOOD. - */ - if ((pin_in == GPIO_S0_PGOOD) && in_level) - msleep(1); - - if (IS_ENABLED(CONFIG_CHIPSET_X86_RSMRST_DELAY) && - (pin_out == GPIO_PCH_RSMRST_L) && in_level) - msleep(10); - - gpio_set_level(pin_out, in_level); - - CPRINTS("Pass through %s: %d", gpio_get_name(pin_in), in_level); -} - -#ifdef CONFIG_POWER_S0IX -/* - * Backup copies of SCI and SMI mask to preserve across S0ix suspend/resume - * cycle. If the host uses S0ix, BIOS is not involved during suspend and resume - * operations and hence SCI/SMI masks are programmed only once during boot-up. - * - * These backup variables are set whenever host expresses its interest to - * enter S0ix and then lpc_host_event_mask for SCI and SMI are cleared. When - * host resumes from S0ix, masks from backup variables are copied over to - * lpc_host_event_mask for SCI and SMI. - */ -static host_event_t backup_sci_mask; -static host_event_t backup_smi_mask; - -/* - * Clear host event masks for SMI and SCI when host is entering S0ix. This is - * done to prevent any SCI/SMI interrupts when the host is in suspend. Since - * BIOS is not involved in the suspend path, EC needs to take care of clearing - * these masks. - */ -static void lpc_s0ix_suspend_clear_masks(void) -{ - backup_sci_mask = lpc_get_host_event_mask(LPC_HOST_EVENT_SCI); - backup_smi_mask = lpc_get_host_event_mask(LPC_HOST_EVENT_SMI); - - lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, 0); - lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, 0); -} - -/* - * Restore host event masks for SMI and SCI when host exits S0ix. This is done - * because BIOS is not involved in the resume path and so EC needs to restore - * the masks from backup variables. - */ -static void lpc_s0ix_resume_restore_masks(void) -{ - /* - * No need to restore SCI/SMI masks if both backup_sci_mask and - * backup_smi_mask are zero. This indicates that there was a failure to - * enter S0ix(SLP_S0# assertion) and hence SCI/SMI masks were never - * backed up. - */ - if (!backup_sci_mask && !backup_smi_mask) - return; - - lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, backup_sci_mask); - lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, backup_smi_mask); - - backup_sci_mask = backup_smi_mask = 0; -} - -static void lpc_s0ix_hang_detected(void) -{ - /* - * Wake up the AP so they don't just chill in a non-suspended state and - * burn power. Overload a vaguely related event bit since event bits are - * at a premium. If the system never entered S0ix, then manually set the - * wake mask to pretend it did, so that the hang detect event wakes the - * system. - */ - if (power_get_state() == POWER_S0) { - host_event_t sleep_wake_mask; - - get_lazy_wake_mask(POWER_S0ix, &sleep_wake_mask); - lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, sleep_wake_mask); - } - - CPRINTS("Warning: Detected sleep hang! Waking host up!"); - host_set_single_event(EC_HOST_EVENT_HANG_DETECT); -} - -static void handle_chipset_suspend(void) -{ - /* Clear masks before any hooks are run for suspend. */ - lpc_s0ix_suspend_clear_masks(); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, handle_chipset_suspend, HOOK_PRIO_FIRST); - -static void handle_chipset_reset(void) -{ - if (chipset_in_state(CHIPSET_STATE_STANDBY)) { - CPRINTS("chipset reset: exit s0ix"); - power_reset_host_sleep_state(); - task_wake(TASK_ID_CHIPSET); - } -} -DECLARE_HOOK(HOOK_CHIPSET_RESET, handle_chipset_reset, HOOK_PRIO_FIRST); - -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); -} - -#endif /* CONFIG_POWER_S0IX */ - -#ifdef CONFIG_POWER_TRACK_HOST_SLEEP_STATE - -__overridable void power_board_handle_host_sleep_event( - enum host_sleep_event state) -{ - /* Default weak implementation -- no action required. */ -} - -__override void power_chipset_handle_host_sleep_event( - enum host_sleep_event state, - struct host_sleep_event_context *ctx) -{ - power_board_handle_host_sleep_event(state); - -#ifdef CONFIG_POWER_S0IX - if (state == HOST_SLEEP_EVENT_S0IX_SUSPEND) { - /* - * Indicate to power state machine that a new host event for - * s0ix/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, lpc_s0ix_hang_detected); - power_signal_enable_interrupt(GPIO_PCH_SLP_S0_L); - } else if (state == HOST_SLEEP_EVENT_S0IX_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); - lpc_s0ix_resume_restore_masks(); - power_signal_disable_interrupt(GPIO_PCH_SLP_S0_L); - sleep_complete_resume(ctx); - /* - * If the sleep signal timed out and never transitioned, then - * the wake mask was modified to its suspend state (S0ix), so - * that the event wakes the system. Explicitly restore the wake - * mask to its S0 state now. - */ - power_update_wake_mask(); - } else if (state == HOST_SLEEP_EVENT_DEFAULT_RESET) { - power_signal_disable_interrupt(GPIO_PCH_SLP_S0_L); - } -#endif /* CONFIG_POWER_S0IX */ -} -#endif /* CONFIG_POWER_TRACK_HOST_SLEEP_STATE */ - -enum power_state power_handle_state(enum power_state state) -{ - handle_pass_through(GPIO_S5_PGOOD, GPIO_PCH_RSMRST_L); - - handle_pass_through(GPIO_S0_PGOOD, GPIO_PCH_SYS_PWROK); - - if (state == POWER_S5 && forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } - - switch (state) { - case POWER_G3: - break; - - case POWER_G3S5: - /* Exit SOC G3 */ - /* Enable system power ("*_A" rails) in S5. */ - gpio_set_level(GPIO_EN_PWR_A, 1); - - /* - * Callback to do pre-initialization within the context of - * chipset task. - */ - if (IS_ENABLED(CONFIG_CHIPSET_HAS_PRE_INIT_CALLBACK)) - chipset_pre_init_callback(); - - if (power_wait_signals(IN_S5_PGOOD)) { - chipset_force_g3(); - return POWER_G3; - } - - CPRINTS("Exit SOC G3"); - - return POWER_S5; - - case POWER_S5: - if (!power_has_signals(IN_S5_PGOOD)) { - /* Required rail went away */ - return POWER_S5G3; - } else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1) { - /* Power up to next state */ - return POWER_S5S3; - } - break; - - case POWER_S5S3: - if (!power_has_signals(IN_S5_PGOOD)) { - /* Required rail went away */ - return POWER_S5G3; - } - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_STARTUP); - -#ifdef CONFIG_POWER_S0IX - /* - * Clearing the S0ix flag on the path to S0 - * to handle any reset conditions. - */ - power_reset_host_sleep_state(); -#endif - return POWER_S3; - - case POWER_S3: - if (!power_has_signals(IN_S5_PGOOD)) { - /* Required rail went away */ - return POWER_S5G3; - } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { - /* Power up to next state */ - return POWER_S3S0; - } else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) { - /* Power down to next state */ - return POWER_S3S5; - } - break; - - case POWER_S3S0: - if (!power_has_signals(IN_S5_PGOOD)) { - /* Required rail went away */ - return POWER_S5G3; - } - - /* Enable wireless */ - wireless_set_state(WIRELESS_ON); - - lpc_s3_resume_clear_masks(); - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_RESUME); - - /* - * 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); - - return POWER_S0; - - case POWER_S0: - if (!power_has_signals(IN_S5_PGOOD)) { - /* Required rail went away */ - return POWER_S5G3; - } -#ifdef CONFIG_POWER_S0IX - /* - * SLP_S0 may assert in system idle scenario without a kernel - * freeze call. This may cause interrupt storm since there is - * no freeze/unfreeze of threads/process in the idle scenario. - * Ignore the SLP_S0 assertions in idle scenario by checking - * the host sleep state. - */ - else if (power_get_host_sleep_state() - == HOST_SLEEP_EVENT_S0IX_SUSPEND && - gpio_get_level(GPIO_PCH_SLP_S0_L) == 0) { - return POWER_S0S0ix; - } -#endif - else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { - /* Power down to next state */ - return POWER_S0S3; - } -#ifdef CONFIG_POWER_S0IX - /* - * Call hooks only if we haven't notified listeners of S0ix - * resume. - */ - sleep_notify_transition(SLEEP_NOTIFY_RESUME, - HOOK_CHIPSET_RESUME); -#endif - break; - - case POWER_S0S3: - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SUSPEND); - - /* Suspend wireless */ - wireless_set_state(WIRELESS_SUSPEND); - - /* - * 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); - -#ifdef CONFIG_POWER_S0IX - /* re-init S0ix flag */ - power_reset_host_sleep_state(); -#endif - return POWER_S3; - - case POWER_S3S5: - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - /* Disable wireless */ - wireless_set_state(WIRELESS_OFF); - - /* Call hooks after we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); - - return POWER_S5; - - case POWER_S5G3: - chipset_force_g3(); - - return POWER_G3; - -#ifdef CONFIG_POWER_S0IX - case POWER_S0ix: - /* System in S0 only if SLP_S0 and SLP_S3 are de-asserted */ - if ((gpio_get_level(GPIO_PCH_SLP_S0_L) == 1) && - (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1)) { - return POWER_S0ixS0; - } else if (!power_has_signals(IN_S5_PGOOD)) { - /* Lost power, start transition to G3 */ - return POWER_S0; - } - - break; - - case POWER_S0S0ix: - /* - * Call hooks only if we haven't notified listeners of S0ix - * suspend. - */ - sleep_notify_transition(SLEEP_NOTIFY_SUSPEND, - HOOK_CHIPSET_SUSPEND); - sleep_suspend_transition(); - - /* - * Enable idle task deep sleep. Allow the low power idle task - * to go into deep sleep in S0ix. - */ - enable_sleep(SLEEP_MASK_AP_RUN); - return POWER_S0ix; - - case POWER_S0ixS0: - /* - * 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); - - sleep_resume_transition(); - return POWER_S0; -#endif /* CONFIG_POWER_S0IX */ - default: - break; - } - return state; -} diff --git a/power/apollolake.c b/power/apollolake.c deleted file mode 100644 index 2aaf7fc533..0000000000 --- a/power/apollolake.c +++ /dev/null @@ -1,183 +0,0 @@ -/* Copyright 2016 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. - */ - -/* Apollolake chipset power control module for Chrome EC */ - -#include "chipset.h" -#include "console.h" -#include "gpio.h" -#include "power/intel_x86.h" -#include "task.h" -#include "timer.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -/* - * force_shutdown is used to maintain chipset shutdown request. This request - * needs to be handled from within the chipset task. - */ -static int force_shutdown; - -/* Power signals list. Must match order of enum power_signal. */ -const struct power_signal_info power_signal_list[] = { -#ifdef CONFIG_POWER_S0IX - [X86_SLP_S0_N] = { - GPIO_PCH_SLP_S0_L, - POWER_SIGNAL_ACTIVE_HIGH | POWER_SIGNAL_DISABLE_AT_BOOT, - "SLP_S0_DEASSERTED", - }, -#endif - [X86_SLP_S3_N] = { - GPIO_PCH_SLP_S3_L, - POWER_SIGNAL_ACTIVE_HIGH, - "SLP_S3_DEASSERTED", - }, - [X86_SLP_S4_N] = { - GPIO_PCH_SLP_S4_L, - POWER_SIGNAL_ACTIVE_HIGH, - "SLP_S4_DEASSERTED", - }, - [X86_SUSPWRDNACK] = { - GPIO_SUSPWRDNACK, - POWER_SIGNAL_ACTIVE_HIGH, - "SUSPWRDNACK_DEASSERTED", - }, - [X86_ALL_SYS_PG] = { - GPIO_ALL_SYS_PGOOD, - POWER_SIGNAL_ACTIVE_HIGH, - "ALL_SYS_PGOOD", - }, - [X86_RSMRST_N] = { - GPIO_RSMRST_L_PGOOD, - POWER_SIGNAL_ACTIVE_HIGH, - "RSMRST_L", - }, - [X86_PGOOD_PP3300] = { - GPIO_PP3300_PG, - POWER_SIGNAL_ACTIVE_HIGH, - "PP3300_PG", - }, - [X86_PGOOD_PP5000] = { - GPIO_PP5000_PG, - POWER_SIGNAL_ACTIVE_HIGH, - "PP5000_PG", - }, -}; -BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); - -__attribute__((weak)) void chipset_do_shutdown(void) -{ - /* Need to implement board specific shutdown */ -} - -static void internal_chipset_shutdown(void) -{ - /* - * UART buffer gets overwritten by other tasks if it is not explicitly - * flushed before printing it on the console by same task. Hence, clean - * up the UART buffer so that all the debug messages are printed on the - * UART console before doing shutdown. - */ - cflush(); - - CPRINTS("%s()", __func__); - - force_shutdown = 0; - chipset_do_shutdown(); -} - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s: %d", __func__, reason); - report_ap_reset(reason); - - /* - * This function is called from multiple tasks and hence it is racy! But - * since things are going down hard, it does not matter if some task - * misses out. - */ - force_shutdown = 1; - task_wake(TASK_ID_CHIPSET); -} - -enum power_state chipset_force_g3(void) -{ - chipset_force_shutdown(CHIPSET_SHUTDOWN_G3); - - return POWER_G3; -} - -void chipset_handle_espi_reset_assert(void) -{ -} - -static void handle_all_sys_pgood(enum power_state state) -{ - /* - * Pass through asynchronously, as SOC may not react - * immediately to power changes. - */ - int in_level = gpio_get_level(GPIO_ALL_SYS_PGOOD); - int out_level = gpio_get_level(GPIO_PCH_SYS_PWROK); - - /* Nothing to do. */ - if (in_level == out_level) - return; - - gpio_set_level(GPIO_PCH_SYS_PWROK, in_level); - - CPRINTS("Pass through GPIO_ALL_SYS_PGOOD: %d", in_level); -} - -enum power_state power_handle_state(enum power_state state) -{ - enum power_state new_state; - - /* Process ALL_SYS_PGOOD state changes. */ - handle_all_sys_pgood(state); - - if (state == POWER_S5 && !power_has_signals(IN_PGOOD_ALL_CORE)) { - /* Required rail went away */ - internal_chipset_shutdown(); - - new_state = POWER_S5G3; - goto rsmrst_handle; - - } - - /* If force shutdown is requested, perform that. */ - if (force_shutdown) - internal_chipset_shutdown(); - - new_state = common_intel_x86_power_handle_state(state); - -rsmrst_handle: - - /* - * Process RSMRST_L state changes: - * RSMRST_L de-assertion is passed to SoC only on G3S5 to S5 transition. - * RSMRST_L is also checked in some states and, if asserted, will - * force shutdown. - */ - common_intel_x86_handle_rsmrst(new_state); - - return new_state; -} - -/** - * chipset check if PLTRST# is valid. - * - * @return non-zero if PLTRST# is valid, 0 if invalid. - */ -int chipset_pltrst_is_valid(void) -{ - /* - * Invalid PLTRST# from SOC unless RSMRST# - * from PMIC through EC to soc is deasserted. - */ - return (gpio_get_level(GPIO_RSMRST_L_PGOOD) && - gpio_get_level(GPIO_PCH_RSMRST_L)); -} diff --git a/power/braswell.c b/power/braswell.c deleted file mode 100644 index 288092c795..0000000000 --- a/power/braswell.c +++ /dev/null @@ -1,324 +0,0 @@ -/* Copyright 2014 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. - */ - -/* X86 braswell chipset power control module for Chrome EC */ - -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "lid_switch.h" -#include "lpc.h" -#include "power.h" -#include "power_button.h" -#include "system.h" -#include "timer.h" -#include "usb_charge.h" -#include "util.h" -#include "wireless.h" -#include "registers.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -/* Input state flags */ -#define IN_RSMRST_L_PWRGD POWER_SIGNAL_MASK(X86_RSMRST_L_PWRGD) -#define IN_ALL_SYS_PWRGD POWER_SIGNAL_MASK(X86_ALL_SYS_PWRGD) -#define IN_SLP_S3_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S3_DEASSERTED) -#define IN_SLP_S4_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S4_DEASSERTED) - -/* All always-on supplies */ -#define IN_PGOOD_ALWAYS_ON (IN_RSMRST_L_PWRGD) -/* All non-core power rails */ -#define IN_PGOOD_ALL_NONCORE (IN_ALL_SYS_PWRGD) -/* All core power rails */ -#define IN_PGOOD_ALL_CORE (IN_ALL_SYS_PWRGD) -/* Rails required for S5 */ -#define IN_PGOOD_S5 (IN_PGOOD_ALWAYS_ON) -/* Rails required for S3 */ -#define IN_PGOOD_S3 (IN_PGOOD_ALWAYS_ON) -/* Rails required for S0 */ -#define IN_PGOOD_S0 (IN_PGOOD_ALWAYS_ON | IN_PGOOD_ALL_NONCORE) - -/* All PM_SLP signals from PCH deasserted */ -#define IN_ALL_PM_SLP_DEASSERTED (IN_SLP_S3_DEASSERTED | IN_SLP_S4_DEASSERTED) -/* All inputs in the right state for S0 */ -#define IN_ALL_S0 (IN_PGOOD_S0 | IN_ALL_PM_SLP_DEASSERTED) - -static int throttle_cpu; /* Throttle CPU? */ -static int forcing_shutdown; /* Forced shutdown in progress? */ - -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. - */ -#ifndef CONFIG_PMIC - gpio_set_level(GPIO_PCH_SYS_PWROK, 0); -#endif - gpio_set_level(GPIO_PCH_RSMRST_L, 0); - forcing_shutdown = 1; -} - -void chipset_reset(enum chipset_reset_reason reason) -{ - CPRINTS("%s: %d", __func__, reason); - report_ap_reset(reason); - - /* - * Send a reset pulse to the PCH. This just causes it to - * assert INIT# to the CPU without dropping power or asserting - * PLTRST# to reset the rest of the system. The PCH uses a 16 - * ms debounce time, so assert the signal for twice that. - */ - gpio_set_level(GPIO_PCH_RCIN_L, 0); - usleep(32 * MSEC); - gpio_set_level(GPIO_PCH_RCIN_L, 1); -} - -void chipset_throttle_cpu(int throttle) -{ -#ifdef CONFIG_CPU_PROCHOT_ACTIVE_LOW - throttle = !throttle; -#endif /* CONFIG_CPU_PROCHOT_ACTIVE_LOW */ - if (chipset_in_state(CHIPSET_STATE_ON)) - gpio_set_level(GPIO_CPU_PROCHOT, throttle); -} - -enum power_state power_chipset_init(void) -{ - /* Pause in S5 when shutting down. */ - power_set_pause_in_s5(1); - - /* - * If we're switching between images without rebooting, see if the x86 - * is already powered on; if so, leave it there instead of cycling - * through G3. - */ - if (system_jumped_to_this_image()) { - if ((power_get_signals() & IN_PGOOD_S0) == IN_PGOOD_S0) { - /* Disable idle task deep sleep when in S0. */ - disable_sleep(SLEEP_MASK_AP_RUN); - - CPRINTS("already in S0"); - return POWER_S0; - } else { - /* Force all signals to their G3 states */ - CPRINTS("forcing G3"); - gpio_set_level(GPIO_PCH_SYS_PWROK, 0); - gpio_set_level(GPIO_PCH_RSMRST_L, 0); - - /*wireless_set_state(WIRELESS_OFF);*/ - } - } - - return POWER_G3; -} - -enum power_state power_handle_state(enum power_state state) -{ - switch (state) { - case POWER_G3: - break; - - case POWER_G3S5: - /* Exit SOC G3 */ -#ifdef CONFIG_PMIC - gpio_set_level(GPIO_PCH_SYS_PWROK, 1); -#else - gpio_set_level(GPIO_SUSPWRDNACK_SOC_EC, 0); -#endif - CPRINTS("Exit SOC G3"); - - if (power_wait_signals(IN_PGOOD_S5)) { - chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT); - return POWER_G3; - } - - /* Deassert RSMRST# */ - gpio_set_level(GPIO_PCH_RSMRST_L, 1); - return POWER_S5; - - case POWER_S5: - /* Check for SLP S4 */ - if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1) - return POWER_S5S3; /* Power up to next state */ - break; - - case POWER_S5S3: - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_STARTUP); - - return POWER_S3; - - - case POWER_S3: - - /* Check for state transitions */ - if (!power_has_signals(IN_PGOOD_S3)) { - /* Required rail went away */ - chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL); - return POWER_S3S5; - } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { - /* Power up to next state */ - return POWER_S3S0; - } else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 0) { - /* Power down to next state */ - return POWER_S3S5; - } - break; - - case POWER_S3S0: - /* Enable wireless */ - - /*wireless_set_state(WIRELESS_ON);*/ - - if (!power_has_signals(IN_PGOOD_S3)) { - chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL); - - /*wireless_set_state(WIRELESS_OFF);*/ - return POWER_S3S5; - } - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_RESUME); - - /* - * 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); - - /* - * Wait 15 ms after all voltages good. 100 ms is only needed - * for PCIe devices; mini-PCIe devices should need only 10 ms. - */ - msleep(15); - - /* - * Throttle CPU if necessary. This should only be asserted - * when +VCCP is powered (it is by now). - */ -#ifdef CONFIG_CPU_PROCHOT_ACTIVE_LOW - gpio_set_level(GPIO_CPU_PROCHOT, !throttle_cpu); -#else - gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu); -#endif /* CONFIG_CPU_PROCHOT_ACTIVE_LOW */ - - /* Set SYS and CORE PWROK */ - gpio_set_level(GPIO_PCH_SYS_PWROK, 1); - - return POWER_S0; - - - case POWER_S0: - - if (!power_has_signals(IN_PGOOD_ALWAYS_ON)) { - chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL); - return POWER_S0S3; - } - - if (!power_has_signals(IN_ALL_S0)) { - return POWER_S0S3; - } - - break; - case POWER_S0S3: - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SUSPEND); - -#ifndef CONFIG_PMIC - /* Clear SYS and CORE PWROK */ - gpio_set_level(GPIO_PCH_SYS_PWROK, 0); -#endif - /* Wait 40ns */ - udelay(1); - - /* Suspend wireless */ - - /*wireless_set_state(WIRELESS_SUSPEND);*/ - - /* - * 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); - - /* - * Deassert prochot since CPU is off and we're about to drop - * +VCCP. - */ - gpio_set_level(GPIO_CPU_PROCHOT, 0); - - return POWER_S3; - - case POWER_S3S5: - - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - /*wireless_set_state(WIRELESS_OFF);*/ - - /* Call hooks after we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); - - /* Start shutting down */ - return power_get_pause_in_s5() ? POWER_S5 : POWER_S5G3; - - case POWER_S5G3: - /* - * in case shutdown is already done by apshutdown - * (or chipset_force_shutdown()), SOC already lost - * power and can't assert PMC_SUSPWRDNACK any more. - */ - if (forcing_shutdown) { - /* Config pins for SOC G3 */ - gpio_config_module(MODULE_GPIO, 1); -#ifndef CONFIG_PMIC - gpio_set_level(GPIO_SUSPWRDNACK_SOC_EC, 1); -#else - gpio_set_level(GPIO_PCH_SYS_PWROK, 0); -#endif - - forcing_shutdown = 0; - - CPRINTS("Enter SOC G3"); - - return POWER_G3; - } - - if (gpio_get_level(GPIO_PCH_SUSPWRDNACK) == 1) { - /* Assert RSMRST# */ - gpio_set_level(GPIO_PCH_RSMRST_L, 0); - - /* Config pins for SOC G3 */ - gpio_config_module(MODULE_GPIO, 1); - - /* Enter SOC G3 */ -#ifdef CONFIG_PMIC - gpio_set_level(GPIO_PCH_SYS_PWROK, 0); - udelay(1); - gpio_set_level(GPIO_PCH_RSMRST_L, 0); -#else - gpio_set_level(GPIO_SUSPWRDNACK_SOC_EC, 1); -#endif - CPRINTS("Enter SOC G3"); - - return POWER_G3; - } else { - CPRINTS("waiting for PMC_SUSPWRDNACK to assert!"); - return POWER_S5; - } - } - return state; -} diff --git a/power/build.mk b/power/build.mk deleted file mode 100644 index e2b86a055e..0000000000 --- a/power/build.mk +++ /dev/null @@ -1,30 +0,0 @@ -# -*- makefile -*- -# Copyright 2013 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. -# -# Power management for application processor and peripherals -# - -power-$(CONFIG_CHIPSET_ALDERLAKE_SLG4BD44540)+=alderlake_slg4bd44540.o -power-$(CONFIG_CHIPSET_ALDERLAKE_SLG4BD44540)+=intel_x86.o -power-$(CONFIG_CHIPSET_APL_GLK)+=apollolake.o intel_x86.o -power-$(CONFIG_CHIPSET_BRASWELL)+=braswell.o -power-$(CONFIG_CHIPSET_CANNONLAKE)+=cannonlake.o intel_x86.o -power-$(CONFIG_CHIPSET_COMETLAKE)+=cometlake.o intel_x86.o -power-$(CONFIG_CHIPSET_COMETLAKE_DISCRETE)+=cometlake-discrete.o intel_x86.o -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_MT8192)+=mt8192.o -power-$(CONFIG_CHIPSET_CEZANNE)+=amd_x86.o -power-$(CONFIG_CHIPSET_RK3288)+=rk3288.o -power-$(CONFIG_CHIPSET_RK3399)+=rk3399.o -power-$(CONFIG_CHIPSET_SC7180)+=qcom.o -power-$(CONFIG_CHIPSET_SC7280)+=qcom.o -power-$(CONFIG_CHIPSET_SDM845)+=sdm845.o -power-$(CONFIG_CHIPSET_SKYLAKE)+=skylake.o intel_x86.o -power-$(CONFIG_CHIPSET_STONEY)+=amd_x86.o -power-$(CONFIG_POWER_COMMON)+=common.o -power-$(CONFIG_POWER_TRACK_HOST_SLEEP_STATE)+=host_sleep.o diff --git a/power/cannonlake.c b/power/cannonlake.c deleted file mode 100644 index 392db669df..0000000000 --- a/power/cannonlake.c +++ /dev/null @@ -1,139 +0,0 @@ -/* Copyright 2017 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. - */ - -/* Cannonlake chipset power control module for Chrome EC */ - -#include "chipset.h" -#include "console.h" -#include "gpio.h" -#include "power.h" -#include "power/cannonlake.h" -#include "power/intel_x86.h" -#include "power_button.h" -#include "task.h" -#include "timer.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -static int forcing_shutdown; /* Forced shutdown in progress? */ - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s(%d)", __func__, reason); - - /* - * Force off. Sending a reset command to the PMIC will power off - * the EC, so simulate a long power button press instead. This - * condition will reset once the state machine transitions to G3. - * Consider reducing the latency here by changing the power off - * hold time on the PMIC. - */ - if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) { - report_ap_reset(reason); - forcing_shutdown = 1; - power_button_pch_press(); - } -} - -void chipset_handle_espi_reset_assert(void) -{ - /* - * If eSPI_Reset# pin is asserted without SLP_SUS# being asserted, then - * it means that there is an unexpected power loss (global reset - * event). In this case, check if shutdown was being forced by pressing - * power button. If yes, release power button. - */ - if ((power_get_signals() & IN_PCH_SLP_SUS_DEASSERTED) && - forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } -} - -enum power_state chipset_force_g3(void) -{ - int timeout = 50; - chipset_force_shutdown(CHIPSET_SHUTDOWN_G3); - - /* Turn off DSW load switch. */ - gpio_set_level(GPIO_EN_PP3300_DSW, 0); - - /* Now wait for DSW_PWROK to go away. */ - while (gpio_get_level(GPIO_PMIC_DPWROK) && (timeout > 0)) { - msleep(1); - timeout--; - }; - - if (!timeout) - CPRINTS("DSW_PWROK didn't go low! Assuming G3."); - - return POWER_G3; -} - -enum power_state power_handle_state(enum power_state state) -{ - enum power_state new_state; - int dswpwrok_in = gpio_get_level(GPIO_PMIC_DPWROK); - static int dswpwrok_out = -1; - - /* Pass-through DSW_PWROK to CNL. */ - if (dswpwrok_in != dswpwrok_out) { - CPRINTS("Pass thru GPIO_DSW_PWROK: %d", dswpwrok_in); - gpio_set_level(GPIO_PCH_DSW_PWROK, dswpwrok_in); - dswpwrok_out = dswpwrok_in; - } - - common_intel_x86_handle_rsmrst(state); - - if (state == POWER_S5 && forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } - - switch (state) { - case POWER_G3: - /* If SLP_SUS_L is deasserted, we're no longer in G3. */ - if (power_has_signals(IN_PCH_SLP_SUS_DEASSERTED)) - return POWER_S5; - break; - - case POWER_G3S5: - /* Turn on the PP3300_DSW rail. */ - gpio_set_level(GPIO_EN_PP3300_DSW, 1); - if (power_wait_signals(IN_PGOOD_ALL_CORE)) - break; - - /* Pass thru DSWPWROK again since we changed it. */ - dswpwrok_in = gpio_get_level(GPIO_PMIC_DPWROK); - gpio_set_level(GPIO_PCH_DSW_PWROK, dswpwrok_in); - CPRINTS("Pass thru GPIO_DSW_PWROK: %d", dswpwrok_in); - dswpwrok_out = dswpwrok_in; - - /* Enable the 5V rail. */ -#ifdef CONFIG_POWER_PP5000_CONTROL - power_5v_enable(task_get_current(), 1); -#else /* !defined(CONFIG_POWER_PP5000_CONTROL) */ - gpio_set_level(GPIO_EN_PP5000, 1); -#endif /* defined(CONFIG_POWER_PP5000_CONTROL) */ - break; - - case POWER_S5G3: - /* Turn off the 5V rail. */ -#ifdef CONFIG_POWER_PP5000_CONTROL - power_5v_enable(task_get_current(), 0); -#else /* !defined(CONFIG_POWER_PP5000_CONTROL) */ - gpio_set_level(GPIO_EN_PP5000, 0); -#endif /* defined(CONFIG_POWER_PP5000_CONTROL) */ - break; - - default: - break; - }; - - new_state = common_intel_x86_power_handle_state(state); - - return new_state; -} diff --git a/power/cometlake-discrete.c b/power/cometlake-discrete.c deleted file mode 100644 index a22e32a69f..0000000000 --- a/power/cometlake-discrete.c +++ /dev/null @@ -1,416 +0,0 @@ -/* Copyright 2019 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. - */ - -/* - * Chrome EC chipset power control for Cometlake with platform-controlled - * discrete sequencing. - */ - -#include "adc.h" -#include "chipset.h" -#include "console.h" -#include "gpio.h" -#include "power.h" -#include "power/intel_x86.h" -#include "power_button.h" -#include "task.h" -#include "timer.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ##args) - -/* Power signals list. Must match order of enum power_signal. */ -const struct power_signal_info power_signal_list[] = { - [PP5000_A_PGOOD] = { - GPIO_PG_PP5000_A_OD, - POWER_SIGNAL_ACTIVE_HIGH, - "PP5000_A_PGOOD", - }, - [PP1800_A_PGOOD] = { - GPIO_PG_PP1800_A_OD, - POWER_SIGNAL_ACTIVE_HIGH, - "PP1800_A_PGOOD", - }, - [VPRIM_CORE_A_PGOOD] = { - GPIO_PG_VPRIM_CORE_A_OD, - POWER_SIGNAL_ACTIVE_HIGH, - "VPRIM_CORE_A_PGOOD", - }, - [PP1050_A_PGOOD] = { - GPIO_PG_PP1050_A_OD, - POWER_SIGNAL_ACTIVE_HIGH, - "PP1050_A_PGOOD", - }, - [OUT_PCH_RSMRST_DEASSERTED] = { - GPIO_PCH_RSMRST_L, - POWER_SIGNAL_ACTIVE_HIGH, - "OUT_PCH_RSMRST_DEASSERTED", - }, - [X86_SLP_S4_DEASSERTED] = { - SLP_S4_SIGNAL_L, - POWER_SIGNAL_ACTIVE_HIGH, - "SLP_S4_DEASSERTED", - }, - [PP2500_DRAM_PGOOD] = { - GPIO_PG_PP2500_DRAM_U_OD, - POWER_SIGNAL_ACTIVE_HIGH, - "PP2500_DRAM_PGOOD", - }, - [PP1200_DRAM_PGOOD] = { - GPIO_PG_PP1200_U_OD, - POWER_SIGNAL_ACTIVE_HIGH, - "PP1200_DRAM_PGOOD", - }, - [X86_SLP_S3_DEASSERTED] = { - SLP_S3_SIGNAL_L, - POWER_SIGNAL_ACTIVE_HIGH, - "SLP_S3_DEASSERTED", - }, - [PP950_VCCIO_PGOOD] = { - GPIO_PG_PP950_VCCIO_OD, - POWER_SIGNAL_ACTIVE_HIGH, - "PP950_VCCIO_PGOOD", - }, - [X86_SLP_S0_DEASSERTED] = { - GPIO_PCH_SLP_S0_L, - POWER_SIGNAL_ACTIVE_HIGH | POWER_SIGNAL_DISABLE_AT_BOOT, - "SLP_S0_DEASSERTED", - }, - [CPU_C10_GATE_DEASSERTED] = { - GPIO_CPU_C10_GATE_L, - POWER_SIGNAL_ACTIVE_HIGH, - "CPU_C10_GATE_DEASSERTED", - }, - [IMVP8_READY] = { - GPIO_IMVP8_VRRDY_OD, - POWER_SIGNAL_ACTIVE_HIGH, - "IMVP8_READY", - }, -}; -BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); - -/* - * The EC is responsible for most of the power-on sequence with this driver, - * enabling rails and waiting for power-good signals from regulators before - * continuing. The power sequencing works as follows. - * - * 1. From G3 (all-off), power is applied and EC power supplies come up. - * The power button task kicks off platform power-up as desired. - * 2. Power up the platform to reach S5 - * a. Enable PP5000_A and wait for PP5000_A_PGOOD. - * b. Enable PP3300_A (EN_ROA_RAILS). - * c. Wait for PP3300_A power good. This regulator doesn't provide a power - * good output, so the EC monitors ADC_SNS_PP3300. - * d. Enable PP1800_A and wait for PP1800_A_PGOOD. - * e. PP1800_A_PGOOD automatically enables PPVAR_VPRIM_CORE_A, which receives - * power from PP3300_A (hence PP3300_A must precede PP1800_A, even though - * PP1800_A draws power from PP3300_G which is guaranteed to already be on) - * f. PPVAR_VPRIM_CORE_A_PGOOD automatically enables PP1050_A - * g. Wait for PP1050_A_PGOOD, indicating that both PPVAR_VPRIM_CORE_A and - * PP1050_A are good. - * h. Wait 10ms to satisfy tPCH03, then bring the PCH out of reset by - * deasserting RSMRST. - * 3. The PCH controls transition from S5 up to S3 and higher-power states. - * a. PCH deasserts SLP_S4, automatically turning on PP2500_DRAM_U and - * PP1200_DRAM_U. - * b. Wait for PP2500_DRAM_PGOOD and PP1200_DRAM_PGOOD. - * 4. PCH deasserts SLP_S3 to switch to S0 - * a. SLP_S3 transition automatically enables PP1050_ST_S. - * b. Wait for PP1050_ST_S good. The power good output from this regulator is - * not connected, so the EC monitors ADC_SNS_PP1050_ST_S. - * c. Turn on EN_S0_RAILS (enabling PP1200_PLLOC and PP1050_STG). - * VCCIO must not ramp up before VCCST, VCCSTG and memory rails are good - * (PDG figure 424, note 14). - * d. Wait 2ms (for EN_S0_RAILS load switches to turn on). - * e. Enable PP950_VCCIO. - * f. Wait for PG_PP950_VCCIO. Although the PCH may be asserting CPU_C10_GATED - * which holds the VCCIO regulator in a low-power mode, the regulator will - * turn on normally and assert power good then drop into low power mode - * and continue asserting power good. - * 5. Transition fully to S0 following SLP_S0 - * a. Assert VCCST_PWRGD. This notionally tracks PP1050_ST_S but must be - * deasserted in S3 and lower. - * b. Enable IMVP8_VR. - * c. Wait 2ms. - * d. Assert SYS_PWROK. - * e. Wait for IMVP8_VRRDY. - * f. Wait 2ms. - * g. Assert PCH_PWROK. - * - * When CPU_C10_GATED is asserted, we are free to disable PP1200_PLLOC and - * PP1050_STG by deasserting EN_S0_RAILS to save some power. VCCIO is - * automatically placed in low-power mode by CPU_C10_GATED, and no further - * action is required- power-good signals will not change, just the relevant - * load switches (which are specified to meet the platform's minimum turn-on - * time when CPU_C10_GATED is deasserted again) are turned off. This gating is - * done asynchronously directly in the interrupt handler because its timing is - * very tight. - * - * For further reference, Figure 421 and Table 370 in the Comet Lake U PDG - * summarizes platform power rail requirements in a reasonably easy-to-digest - * manner, while section 12.11 (containing those diagrams) details the required - * operation. - */ - -/* - * Reverse of S0->S3 transition. - * - * This is a separate function so it can be reused when forcing shutdown due to - * power failure or other reasons. - * - * This function may be called from an ISR (slp_s3_interrupt) so must not - * assume that it's running in a regular task. - */ -static void shutdown_s0_rails(void) -{ - board_enable_s0_rails(0); - /* - * Deassert VCCST_PG as early as possible to satisfy tCPU22; VDDQ is - * derived directly from SLP_S3. - */ - gpio_set_level(GPIO_VCCST_PG_OD, 0); - gpio_set_level(GPIO_EC_PCH_PWROK, 0); - gpio_set_level(GPIO_EC_PCH_SYS_PWROK, 0); - gpio_set_level(GPIO_EN_IMVP8_VR, 0); - gpio_set_level(GPIO_EN_S0_RAILS, 0); - /* - * * tPCH10: PCH_PWROK to VCCIO off >400ns (but only on unexpected - * power-down) - * * tPLT18: SLP_S3_L to VCCIO disable <200us - * - * tPCH10 is only 7 CPU cycles at 16 MHz so we should satisfy that - * minimum time with no extra code, and sleeping is likely to cause - * a delay that exceeds tPLT18. - */ - gpio_set_level(GPIO_EN_PP950_VCCIO, 0); -} - -/* - * Reverse of G3->S5 transition. - * - * This is a separate function so it can be reused when forcing shutdown due to - * power failure or other reasons. - */ -static void shutdown_s5_rails(void) -{ - gpio_set_level(GPIO_PCH_RSMRST_L, 0); - /* tPCH12: RSMRST to VCCPRIM (PPVAR_VPRIM_CORE_A) off >400ns */ - usleep(1); - gpio_set_level(GPIO_EN_PP1800_A, 0); - gpio_set_level(GPIO_EN_ROA_RAILS, 0); -#ifdef CONFIG_POWER_PP5000_CONTROL - power_5v_enable(task_get_current(), 0); -#else - gpio_set_level(GPIO_EN_PP5000_A, 0); -#endif -} - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - - shutdown_s0_rails(); - /* S3->S5 is automatic based on SLP_S3 driving memory rails. */ - shutdown_s5_rails(); -} - -void chipset_handle_espi_reset_assert(void) {} - -enum power_state chipset_force_g3(void) -{ - chipset_force_shutdown(CHIPSET_SHUTDOWN_G3); - - return POWER_G3; -} - -/* - * Wait for a power rail on an analog channel to become good. - * - * @param channel ADC channel to read - * @param min_voltage Minimum required voltage for rail (in mV) - * - * @return EC_SUCCESS, or non-zero if error. - */ -static int power_wait_analog(enum adc_channel channel, int min_voltage) -{ - timestamp_t deadline; - int reading; - - /* One second timeout */ - deadline = get_time(); - deadline.val += SECOND; - - do { - reading = adc_read_channel(channel); - if (reading == ADC_READ_ERROR) - return EC_ERROR_HW_INTERNAL; - if (timestamp_expired(deadline, NULL)) - return EC_ERROR_TIMEOUT; - } while (reading < min_voltage); - - return EC_SUCCESS; -} - -/* - * Force system power state if we time out waiting for a power rail to become - * good. - * - * In general the new state is to transition down to the next lower-power state, - * so if we time out in G3->S5 we return POWER_G3 to turn things off again and - * if S3->S0 times out we return POWER_S3S5 for the same reason. - * - * Correct sequencing of rails that might already be enabled is handled by - * chipset_force_shutdown(), so the caller of this function doesn't need to - * clean up after itself. - */ -static enum power_state pgood_timeout(enum power_state new_state) -{ - chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT); - return new_state; -} - -/* - * Called in the chipset task when power signal inputs change state. - * If this doesn't request a different state, power_common_state handles it. - * - * @param state Current power state - * @return New power state - */ -enum power_state power_handle_state(enum power_state state) -{ - switch (state) { - case POWER_G3S5: - if (intel_x86_wait_power_up_ok() != EC_SUCCESS) { - chipset_force_shutdown( - CHIPSET_SHUTDOWN_BATTERY_INHIBIT); - return POWER_G3; - } - /* Power-up steps 2a-2h. */ -#ifdef CONFIG_POWER_PP5000_CONTROL - power_5v_enable(task_get_current(), 1); -#else - gpio_set_level(GPIO_EN_PP5000_A, 1); -#endif - if (power_wait_signals(POWER_SIGNAL_MASK(PP5000_A_PGOOD))) - return pgood_timeout(POWER_S5G3); - gpio_set_level(GPIO_EN_ROA_RAILS, 1); - if (power_wait_analog(ADC_SNS_PP3300, 3000) != EC_SUCCESS) - return pgood_timeout(POWER_S5G3); - gpio_set_level(GPIO_EN_PP1800_A, 1); - if (power_wait_signals(POWER_SIGNAL_MASK(PP1800_A_PGOOD) | - POWER_SIGNAL_MASK(PP1050_A_PGOOD))) - return pgood_timeout(POWER_S5G3); - msleep(10); /* tPCH03: VCCPRIM good -> RSMRST >10ms */ - gpio_set_level(GPIO_PCH_RSMRST_L, 1); - break; - - case POWER_S5G3: - shutdown_s5_rails(); - break; - - case POWER_S5S3: - /* Power-up steps 3a-3b. */ - if (power_wait_signals(POWER_SIGNAL_MASK(PP2500_DRAM_PGOOD) | - POWER_SIGNAL_MASK(PP1200_DRAM_PGOOD))) - return pgood_timeout(POWER_S3S5); - break; - - case POWER_S3S0: - /* Power-up steps 4a-4f. */ - if (power_wait_analog(ADC_SNS_PP1050, 1000) != EC_SUCCESS) - return pgood_timeout(POWER_S3S5); - gpio_set_level(GPIO_EN_S0_RAILS, 1); - msleep(2); - gpio_set_level(GPIO_EN_PP950_VCCIO, 1); - if (power_wait_signals(POWER_SIGNAL_MASK(PP950_VCCIO_PGOOD))) - return pgood_timeout(POWER_S3S5); - - /* Power-up steps 5a-5h */ - gpio_set_level(GPIO_VCCST_PG_OD, 1); - gpio_set_level(GPIO_EN_IMVP8_VR, 1); - msleep(2); - gpio_set_level(GPIO_EC_PCH_SYS_PWROK, 1); - if (power_wait_signals(POWER_SIGNAL_MASK(IMVP8_READY))) - return pgood_timeout(POWER_S3S5); - msleep(2); - gpio_set_level(GPIO_EC_PCH_PWROK, 1); - - board_enable_s0_rails(1); - break; - - case POWER_S0S3: - /* - * Handled in the slp_s3_interrupt fast path, but also run - * here in case we miss the interrupt somehow. - */ - shutdown_s0_rails(); - break; - - case POWER_S5: - /* - * Return to G3 if S5 rails are not on, probably because of - * a forced power-off. - */ - if ((power_get_signals() & CHIPSET_G3S5_POWERUP_SIGNAL) != - CHIPSET_G3S5_POWERUP_SIGNAL) - return POWER_S5G3; - break; - - default: - break; - } - - /* - * Power-up steps 3a-3b (S5->S3 via IN_PGOOD_ALL_CORE) plus general - * bookkeeping. - */ - return common_intel_x86_power_handle_state(state); -} - -#ifdef CONFIG_VBOOT_EFS -/* - * Called in main() to ensure chipset power is in a good state. - * - * This may be useful because EC reset could happen under unexpected - * conditions and we want to ensure that if the AP is wedged for some - * reason (for instance) we unwedge it before continuing. - * - * Because power sequencing here is all EC-controlled and this is called - * as part of the init sequence, we don't need to do anything- EC reset - * implies power sequencing is all-off and we don't have any external - * PMIC to synchronize state with. - */ -void chipset_handle_reboot(void) {} -#endif /* CONFIG_VBOOT_EFS */ - -void c10_gate_interrupt(enum gpio_signal signal) -{ - /* - * Per PDG, gate VccSTG and VCCIO on (SLP_S3_L && CPU_C10_GATE_L). - * - * When in S3 we let the state machine do it since timing is less - * critical; when in S0/S0ix we do it here because timing is very - * tight. - */ - if (board_is_c10_gate_enabled() && gpio_get_level(GPIO_SLP_S3_L)) { - int enable_core = gpio_get_level(GPIO_CPU_C10_GATE_L); - - gpio_set_level(GPIO_EN_S0_RAILS, enable_core); - } - - return power_signal_interrupt(signal); -} - -void slp_s3_interrupt(enum gpio_signal signal) -{ - if (!gpio_get_level(GPIO_SLP_S3_L) - && chipset_in_state(CHIPSET_STATE_ON)) { - /* Falling edge on SLP_S3_L means dropping to S3 from S0 */ - shutdown_s0_rails(); - } - - return power_signal_interrupt(signal); -} diff --git a/power/cometlake.c b/power/cometlake.c deleted file mode 100644 index 1b73bcc296..0000000000 --- a/power/cometlake.c +++ /dev/null @@ -1,211 +0,0 @@ -/* Copyright 2019 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. - */ - -/* Cometlake chipset power control module for Chrome EC */ - -#include "chipset.h" -#include "console.h" -#include "gpio.h" -#include "power.h" -#include "power/intel_x86.h" -#include "power_button.h" -#include "task.h" -#include "timer.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -/* Power signals list. Must match order of enum power_signal. */ -const struct power_signal_info power_signal_list[] = { - [X86_SLP_S0_DEASSERTED] = { - .gpio = GPIO_PCH_SLP_S0_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH | - POWER_SIGNAL_DISABLE_AT_BOOT, - .name = "SLP_S0_DEASSERTED", - }, - [X86_SLP_S3_DEASSERTED] = { - .gpio = SLP_S3_SIGNAL_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "SLP_S3_DEASSERTED", - }, - [X86_SLP_S4_DEASSERTED] = { - .gpio = SLP_S4_SIGNAL_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "SLP_S4_DEASSERTED", - }, - [X86_RSMRST_L_PGOOD] = { - .gpio = GPIO_RSMRST_L_PGOOD, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "RSMRST_L_PGOOD", - }, - [X86_PP5000_A_PGOOD] = { - .gpio = GPIO_PP5000_A_PG_OD, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "PP5000_A_PGOOD", - }, - [X86_ALL_SYS_PGOOD] = { - .gpio = GPIO_PG_EC_ALL_SYS_PWRGD, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "ALL_SYS_PWRGD", - }, -}; -BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); - -static int forcing_shutdown; /* Forced shutdown in progress? */ - -/* Default no action, overwrite it in board.c if necessary*/ -__overridable void board_chipset_forced_shutdown(void) -{ - return; -} - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - int timeout_ms = 50; - - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - - /* Turn off RSMRST_L to meet tPCH12 */ - gpio_set_level(GPIO_PCH_RSMRST_L, 0); - - /* Turn off A (except PP5000_A) rails*/ - gpio_set_level(GPIO_EN_A_RAILS, 0); - -#ifdef CONFIG_POWER_PP5000_CONTROL - /* Issue a request to turn off the rail. */ - power_5v_enable(task_get_current(), 0); -#else - /* Turn off PP5000_A rail */ - gpio_set_level(GPIO_EN_PP5000_A, 0); -#endif - - /* For b:143440730, stop checking GPIO_ALL_SYS_PGOOD if system is - * already force to G3. - */ - board_chipset_forced_shutdown(); - - /* Need to wait a min of 10 msec before check for power good */ - msleep(10); - - /* Now wait for PP5000_A and RSMRST_L to go low */ - while ((gpio_get_level(GPIO_PP5000_A_PG_OD) || - power_has_signals(IN_PGOOD_ALL_CORE)) && (timeout_ms > 0)) { - msleep(1); - timeout_ms--; - }; - - if (!timeout_ms) - CPRINTS("PP5000_A rail still up! Assuming G3."); -} - -void chipset_handle_espi_reset_assert(void) -{ - /* - * If eSPI_Reset# pin is asserted without SLP_SUS# being asserted, then - * it means that there is an unexpected power loss (global reset - * event). In this case, check if shutdown was being forced by pressing - * power button. If yes, release power button. - */ - if ((power_get_signals() & IN_PGOOD_ALL_CORE) && forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } -} - -enum power_state chipset_force_g3(void) -{ - chipset_force_shutdown(CHIPSET_SHUTDOWN_G3); - - return POWER_G3; -} - -/* Default no action, overwrite it in board.c if necessary*/ -__attribute__((weak)) void all_sys_pgood_check_reboot(void) -{ - return; -} - -/* Called by APL power state machine when transitioning from G3 to S5 */ -void chipset_pre_init_callback(void) -{ - /* Enable 5.0V and 3.3V rails, and wait for Power Good */ -#ifdef CONFIG_POWER_PP5000_CONTROL - power_5v_enable(task_get_current(), 1); -#else - /* Turn on PP5000_A rail */ - gpio_set_level(GPIO_EN_PP5000_A, 1); -#endif - /* Turn on A (except PP5000_A) rails*/ - gpio_set_level(GPIO_EN_A_RAILS, 1); - - /* - * The status of the 5000_A rail is verifed in the calling function via - * power_wait_signals() as PP5000_A_PGOOD is included in the - * CHIPSET_G3S5_POWERUP_SIGNAL macro. - */ - - /* For b:143440730, system might hang-up before enter S0/S3. Check - * GPIO_ALL_SYS_PGOOD here to make sure it will trigger every time. - */ - all_sys_pgood_check_reboot(); -} - -enum power_state power_handle_state(enum power_state state) -{ - - int all_sys_pwrgd_in; - int all_sys_pwrgd_out; - - /* - * Check if RSMRST_L signal state has changed and if so, pass the new - * value along to the PCH. However, if the new transition of RSMRST_L - * from the Sielgo is from low to high, then gate this transition to the - * AP by the PP5000_A rail. If the new transition is from high to low, - * then pass that through regardless of the PP5000_A value. - * - * The PP5000_A power good signal will float high if the - * regulator is not powered, so checking both that the EN and the PG - * signals are high. - */ - if ((gpio_get_level(GPIO_PP5000_A_PG_OD) && - gpio_get_level(GPIO_EN_PP5000_A)) || - gpio_get_level(GPIO_PCH_RSMRST_L)) - common_intel_x86_handle_rsmrst(state); - - switch (state) { - - case POWER_S5: - if (forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } - /* If RSMRST_L is asserted, we're no longer in S5. */ - if (!power_has_signals(IN_PGOOD_ALL_CORE)) - return POWER_S5G3; - break; - - case POWER_S0: - /* - * Check value of PG_EC_ALL_SYS_PWRGD to see if PCH_SYS_PWROK - * needs to be changed. If it's low->high transition, requires a - * 2msec delay. - */ - all_sys_pwrgd_in = gpio_get_level(GPIO_PG_EC_ALL_SYS_PWRGD); - all_sys_pwrgd_out = gpio_get_level(GPIO_PCH_SYS_PWROK); - - if (all_sys_pwrgd_in != all_sys_pwrgd_out) { - if (all_sys_pwrgd_in) - msleep(2); - gpio_set_level(GPIO_PCH_SYS_PWROK, all_sys_pwrgd_in); - } - break; - - default: - break; - } - - return common_intel_x86_power_handle_state(state); -} diff --git a/power/common.c b/power/common.c deleted file mode 100644 index 0f83a2ce61..0000000000 --- a/power/common.c +++ /dev/null @@ -1,1117 +0,0 @@ -/* Copyright 2013 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. - */ - -/* Common functionality across all chipsets */ - -#include "battery.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "display_7seg.h" -#include "espi.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "lpc.h" -#include "power.h" -#include "power/intel_x86.h" -#include "power/qcom.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) - -/* - * Default timeout in us; if we've been waiting this long for an input - * transition, just jump to the next state. - */ -#define DEFAULT_TIMEOUT SECOND - -/* Timeout for dropping back from S5 to G3 in seconds */ -#ifdef CONFIG_CMD_S5_TIMEOUT -static int s5_inactivity_timeout = 10; -#else -static const int s5_inactivity_timeout = 10; -#endif - -static const char * const state_names[] = { - "G3", - "S5", - "S3", - "S0", -#ifdef CONFIG_POWER_S0IX - "S0ix", -#endif - "G3->S5", - "S5->S3", - "S3->S0", - "S0->S3", - "S3->S5", - "S5->G3", -#ifdef CONFIG_POWER_S0IX - "S0ix->S0", - "S0->S0ix", -#endif -}; - -static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */ -static uint32_t in_want; /* Input signal state we're waiting for */ -static uint32_t in_debug; /* Signal values which print debug output */ - -static enum power_state state = POWER_G3; /* Current state */ -static int want_g3_exit; /* Should we exit the G3 state? */ -static uint64_t last_shutdown_time; /* When did we enter G3? */ - -#ifdef CONFIG_HIBERNATE -/* Delay before hibernating, in seconds */ -static uint32_t hibernate_delay = CONFIG_HIBERNATE_DELAY_SEC; -#endif - -#ifdef CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 -/* Pause in S5 on shutdown? */ -static int pause_in_s5; -#endif - -static bool want_reboot_ap_at_g3;/* Want to reboot AP from G3? */ -/* Want to reboot AP from G3 with delay? */ -static uint64_t reboot_ap_at_g3_delay; - -static enum ec_status -host_command_reboot_ap_on_g3(struct host_cmd_handler_args *args) -{ - const struct ec_params_reboot_ap_on_g3_v1 *cmd = args->params; - - /* Store request for processing at g3 */ - want_reboot_ap_at_g3 = true; - - switch (args->version) { - case 0: - break; - case 1: - /* Store user specified delay to wait in G3 state */ - reboot_ap_at_g3_delay = cmd->reboot_ap_at_g3_delay; - break; - default: - return EC_RES_INVALID_PARAM; - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_REBOOT_AP_ON_G3, - host_command_reboot_ap_on_g3, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -__overridable int power_signal_get_level(enum gpio_signal signal) -{ - if (IS_ENABLED(CONFIG_HOST_ESPI_VW_POWER_SIGNAL)) { - /* Check signal is from GPIOs or VWs */ - if (espi_signal_is_vw(signal)) - return espi_vw_get_wire(signal); - } - return gpio_get_level(signal); -} - -int power_signal_disable_interrupt(enum gpio_signal signal) -{ - if (IS_ENABLED(CONFIG_HOST_ESPI_VW_POWER_SIGNAL)) { - /* Check signal is from GPIOs or VWs */ - if (espi_signal_is_vw(signal)) - return espi_vw_disable_wire_int(signal); - } - return gpio_disable_interrupt(signal); -} - -int power_signal_enable_interrupt(enum gpio_signal signal) -{ - if (IS_ENABLED(CONFIG_HOST_ESPI_VW_POWER_SIGNAL)) { - /* Check signal is from GPIOs or VWs */ - if (espi_signal_is_vw(signal)) - return espi_vw_enable_wire_int(signal); - } - return gpio_enable_interrupt(signal); -} - -int power_signal_is_asserted(const struct power_signal_info *s) -{ - return power_signal_get_level(s->gpio) == - !!(s->flags & POWER_SIGNAL_ACTIVE_STATE); -} - -#ifdef CONFIG_BRINGUP -static const char *power_signal_get_name(enum gpio_signal signal) -{ - if (IS_ENABLED(CONFIG_HOSTCMD_ESPI)) { - /* Check signal is from GPIOs or VWs */ - if (espi_signal_is_vw(signal)) - return espi_vw_get_wire_name(signal); - } - return gpio_get_name(signal); -} -#endif - -/** - * Update input signals mask - */ -static void power_update_signals(void) -{ - uint32_t inew = 0; - const struct power_signal_info *s = power_signal_list; - int i; - - for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) { - if (power_signal_is_asserted(s)) - inew |= 1 << i; - } - - if ((in_signals & in_debug) != (inew & in_debug)) - CPRINTS("power in 0x%04x", inew); - - in_signals = inew; -} - -uint32_t power_get_signals(void) -{ - return in_signals; -} - -int power_has_signals(uint32_t want) -{ - if ((in_signals & want) == want) - return 1; - - CPRINTS("power lost input; wanted 0x%04x, got 0x%04x", - want, in_signals & want); - - return 0; -} - -int power_wait_signals(uint32_t want) -{ - int ret = power_wait_signals_timeout(want, DEFAULT_TIMEOUT); - - if (ret == EC_ERROR_TIMEOUT) - CPRINTS("power timeout on input; wanted 0x%04x, got 0x%04x", - want, in_signals & want); - return ret; -} - -int power_wait_signals_timeout(uint32_t want, int timeout) -{ - return power_wait_mask_signals_timeout(want, want, timeout); -} - -int power_wait_mask_signals_timeout(uint32_t want, uint32_t mask, int timeout) -{ - in_want = want; - if (!mask) - return EC_SUCCESS; - - while ((in_signals & mask) != in_want) { - if (task_wait_event(timeout) == TASK_EVENT_TIMER) { - power_update_signals(); - return EC_ERROR_TIMEOUT; - } - /* - * TODO(crosbug.com/p/23772): should really shrink the - * remaining timeout if we woke up but didn't have all the - * signals we wanted. Also need to handle aborts if we're no - * longer in the same state we were when we started waiting. - */ - } - return EC_SUCCESS; -} - -void power_set_state(enum power_state new_state) -{ - /* Record the time we go into G3 */ - if (new_state == POWER_G3) - last_shutdown_time = get_time().val; - - /* Print out the RTC value to help correlate EC and kernel logs. */ - print_system_rtc(CC_CHIPSET); - - state = new_state; - - /* - * Reset want_g3_exit flag here to prevent the situation that if the - * error handler in POWER_S5S3 decides to force shutdown the system and - * the flag is set, the system will go to G3 and then immediately exit - * G3 again. - */ - if (state == POWER_S5S3) - want_g3_exit = 0; -} - -enum power_state power_get_state(void) -{ - return state; -} - -#ifdef CONFIG_HOSTCMD_X86 - -/* If host doesn't program s0ix lazy wake mask, use default s0ix mask */ -#define DEFAULT_WAKE_MASK_S0IX (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) | \ - EC_HOST_EVENT_MASK(EC_HOST_EVENT_MODE_CHANGE)) - - /* - * Set the wake mask according to the current power state: - * 1. On transition to S0, wake mask is reset. - * 2. In non-S0 states, active mask set by host gets a higher preference. - * 3. If host has not set any active mask, then check if a lazy mask exists - * for the current power state. - * 4. If state is S0ix and no lazy or active wake mask is set, then use default - * S0ix mask to be compatible with older BIOS versions. - */ - -void power_update_wake_mask(void) -{ - host_event_t wake_mask; - enum power_state state; - - state = power_get_state(); - - if (state == POWER_S0) - wake_mask = 0; - else if (lpc_is_active_wm_set_by_host()) - return; - else if (get_lazy_wake_mask(state, &wake_mask)) - return; -#ifdef CONFIG_POWER_S0IX - if ((state == POWER_S0ix) && (wake_mask == 0)) - wake_mask = DEFAULT_WAKE_MASK_S0IX; -#endif - - lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, wake_mask); -} - /* - * Set wake mask after power state has stabilized, 5ms after power state - * change. The reason for making this a deferred call is to avoid race - * conditions occurring from S0ix periodic wakes on the SoC. - */ - -static void power_update_wake_mask_deferred(void); -DECLARE_DEFERRED(power_update_wake_mask_deferred); - -static void power_update_wake_mask_deferred(void) -{ - hook_call_deferred(&power_update_wake_mask_deferred_data, -1); - power_update_wake_mask(); -} - -static void power_set_active_wake_mask(void) -{ - /* - * Allow state machine to stabilize and update wake mask after 5msec. It - * was observed that on platforms where host wakes up periodically from - * S0ix for hardware book-keeping activities, there is a small window - * where host is not really up and running software, but still SLP_S0# - * is de-asserted and hence setting wake mask right away can cause user - * wake events to be missed. - * - * Time for deferred callback was chosen to be 5msec based on the fact - * that it takes ~2msec for the periodic wake cycle to complete on the - * host for KBL. - */ - hook_call_deferred(&power_update_wake_mask_deferred_data, - 5 * MSEC); -} - -#else -static void power_set_active_wake_mask(void) { } -#endif - -#ifdef CONFIG_HIBERNATE -#ifdef CONFIG_BATTERY -/* - * Smart discharge system - * - * EC controls how the system discharges differently depending on the remaining - * capacity and the expected hours to zero. - * - * 0 X1 X2 full - * |----------|-------------------|------------------------------------| - * cutoff stay-up safe - * - * EC cuts off the battery at X1 mAh and hibernates the system at X2 mAh. X1 and - * X2 are derived from the cutoff and hibernation discharge rate, respectively. - * - * TODO: Learn discharge rates dynamically. - * - * TODO: Save sdzone in non-volatile memory and restore it when waking up from - * cutoff or hibernation. - */ -static struct smart_discharge_zone sdzone; - -static enum ec_status hc_smart_discharge(struct host_cmd_handler_args *args) -{ - static uint16_t hours_to_zero; - static struct discharge_rate drate; - const struct ec_params_smart_discharge *p = args->params; - struct ec_response_smart_discharge *r = args->response; - - if (p->flags & EC_SMART_DISCHARGE_FLAGS_SET) { - int cap; - - if (battery_full_charge_capacity(&cap)) - return EC_RES_UNAVAILABLE; - - if (p->drate.hibern < p->drate.cutoff) - /* Hibernation discharge rate should be always higher */ - return EC_RES_INVALID_PARAM; - else if (p->drate.cutoff > 0 && p->drate.hibern > 0) - drate = p->drate; - else if (p->drate.cutoff == 0 && p->drate.hibern == 0) - ; /* no-op. use the current drate. */ - else - return EC_RES_INVALID_PARAM; - - /* Commit */ - hours_to_zero = p->hours_to_zero; - sdzone.stayup = MIN(hours_to_zero * drate.hibern / 1000, cap); - sdzone.cutoff = MIN(hours_to_zero * drate.cutoff / 1000, - sdzone.stayup); - } - - /* Return the effective values. */ - r->hours_to_zero = hours_to_zero; - r->dzone = sdzone; - r->drate = drate; - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_SMART_DISCHARGE, - hc_smart_discharge, - EC_VER_MASK(0)); - -__overridable enum critical_shutdown board_system_is_idle( - uint64_t last_shutdown_time, uint64_t *target, uint64_t now) -{ - int remain; - - if (now < *target) - return CRITICAL_SHUTDOWN_IGNORE; - - if (battery_remaining_capacity(&remain)) { - CPRINTS("SDC Failed to get remaining capacity"); - return CRITICAL_SHUTDOWN_HIBERNATE; - } - - if (remain < sdzone.cutoff) { - CPRINTS("SDC Cutoff"); - return CRITICAL_SHUTDOWN_CUTOFF; - } else if (remain < sdzone.stayup) { - CPRINTS("SDC Stay-up"); - return CRITICAL_SHUTDOWN_IGNORE; - } - - CPRINTS("SDC Safe"); - return CRITICAL_SHUTDOWN_HIBERNATE; -} -#else -/* Default implementation for battery-less systems */ -__overridable enum critical_shutdown board_system_is_idle( - uint64_t last_shutdown_time, uint64_t *target, uint64_t now) -{ - return now > *target ? - CRITICAL_SHUTDOWN_HIBERNATE : CRITICAL_SHUTDOWN_IGNORE; -} -#endif /* CONFIG_BATTERY */ -#endif /* CONFIG_HIBERNATE */ - -/** - * Common handler for steady states - * - * @param state Current power state - * @return Updated power state - */ -static enum power_state power_common_state(enum power_state state) -{ - switch (state) { - case POWER_G3: - if (want_g3_exit || want_reboot_ap_at_g3) { - uint64_t i; - - want_g3_exit = 0; - want_reboot_ap_at_g3 = false; - reboot_ap_at_g3_delay = reboot_ap_at_g3_delay * MSEC; - /* - * G3->S0 transition should happen only after the - * user specified delay. Hence, wait until the - * user specified delay times out. - */ - for (i = 0; i < reboot_ap_at_g3_delay; i += 100) - msleep(100); - reboot_ap_at_g3_delay = 0; - - return POWER_G3S5; - } - - in_want = 0; -#ifdef CONFIG_HIBERNATE - { - uint64_t target, now, wait; - if (extpower_is_present()) { - task_wait_event(-1); - break; - } - - now = get_time().val; - target = last_shutdown_time + - (uint64_t)hibernate_delay * SECOND; - switch (board_system_is_idle(last_shutdown_time, - &target, now)) { - case CRITICAL_SHUTDOWN_HIBERNATE: - CPRINTS("Hibernate due to G3 idle"); - system_hibernate(0, 0); - break; -#ifdef CONFIG_BATTERY_CUT_OFF - case CRITICAL_SHUTDOWN_CUTOFF: - CPRINTS("Cutoff due to G3 idle"); - /* Ensure logs are flushed. */ - cflush(); - board_cut_off_battery(); - break; -#endif - case CRITICAL_SHUTDOWN_IGNORE: - default: - break; - } - - wait = MIN(target - now, TASK_MAX_WAIT_US); - task_wait_event(wait); - } -#else /* !CONFIG_HIBERNATE */ - task_wait_event(-1); -#endif - break; - - case POWER_S5: - /* - * If the power button is pressed before S5 inactivity timer - * expires, the timer will be cancelled and the task of the - * power state machine will be back here again. Since we are - * here, which means the system has been waiting for CPU - * starting up, we don't need want_g3_exit flag to be set - * anymore. Therefore, we can reset the flag here to prevent - * the situation that the flag is still set after S5 inactivity - * timer expires, which can cause the system to exit G3 again. - */ - want_g3_exit = 0; - - power_wait_signals(0); - - /* Wait for inactivity timeout, if desired */ - if (s5_inactivity_timeout == 0) { - return POWER_S5G3; - } else if (s5_inactivity_timeout < 0) { - task_wait_event(-1); - } else if (task_wait_event(s5_inactivity_timeout * SECOND) == - TASK_EVENT_TIMER) { - /* Prepare to drop to G3; wake not requested yet */ - return POWER_S5G3; - } - break; - - case POWER_S3: - /* Wait for a message */ - power_wait_signals(0); - task_wait_event(-1); - break; - - case POWER_S0: - /* Wait for a message */ - power_wait_signals(0); - task_wait_event(-1); - break; -#ifdef CONFIG_POWER_S0IX - case POWER_S0ix: - /* Wait for a message */ - power_wait_signals(0); - task_wait_event(-1); - break; -#endif - default: - /* No common functionality for transition states */ - break; - } - - return state; -} - -/*****************************************************************************/ -/* Chipset interface */ - -int chipset_in_state(int state_mask) -{ - int need_mask = 0; - - /* - * TODO(crosbug.com/p/23773): what to do about state transitions? If - * the caller wants HARD_OFF|SOFT_OFF and we're in G3S5, we could still - * return non-zero. - */ - switch (state) { - case POWER_G3: - need_mask = CHIPSET_STATE_HARD_OFF; - break; - case POWER_G3S5: - case POWER_S5G3: - /* - * In between hard and soft off states. Match only if caller - * will accept both. - */ - need_mask = CHIPSET_STATE_HARD_OFF | CHIPSET_STATE_SOFT_OFF; - break; - case POWER_S5: - need_mask = CHIPSET_STATE_SOFT_OFF; - break; - case POWER_S5S3: - case POWER_S3S5: - need_mask = CHIPSET_STATE_SOFT_OFF | CHIPSET_STATE_SUSPEND; - break; - case POWER_S3: - need_mask = CHIPSET_STATE_SUSPEND; - break; - case POWER_S3S0: - case POWER_S0S3: - need_mask = CHIPSET_STATE_SUSPEND | CHIPSET_STATE_ON; - break; - case POWER_S0: - need_mask = CHIPSET_STATE_ON; - break; -#ifdef CONFIG_POWER_S0IX - case POWER_S0ixS0: - case POWER_S0S0ix: - need_mask = CHIPSET_STATE_ON | CHIPSET_STATE_STANDBY; - break; - case POWER_S0ix: - need_mask = CHIPSET_STATE_STANDBY; - break; -#endif - } - - /* Return non-zero if all needed bits are present */ - return (state_mask & need_mask) == need_mask; -} - -int chipset_in_or_transitioning_to_state(int state_mask) -{ - switch (state) { - case POWER_G3: - case POWER_S5G3: - return state_mask & CHIPSET_STATE_HARD_OFF; - case POWER_S5: - case POWER_G3S5: - case POWER_S3S5: - return state_mask & CHIPSET_STATE_SOFT_OFF; - case POWER_S3: - case POWER_S5S3: - case POWER_S0S3: - return state_mask & CHIPSET_STATE_SUSPEND; -#ifdef CONFIG_POWER_S0IX - case POWER_S0ix: - case POWER_S0S0ix: - return state_mask & CHIPSET_STATE_STANDBY; -#endif - case POWER_S0: - case POWER_S3S0: -#ifdef CONFIG_POWER_S0IX - case POWER_S0ixS0: -#endif - return state_mask & CHIPSET_STATE_ON; - } - - /* Unknown power state; return false. */ - return 0; -} - -void chipset_exit_hard_off(void) -{ - /* - * If not in the soft-off state, hard-off state, or headed there, - * nothing to do. - */ - if (state != POWER_G3 && state != POWER_S5G3 && state != POWER_S5) - return; - - /* - * Set a flag to leave G3, then wake the task. If the power state is - * POWER_S5G3, or is POWER_S5 but the S5 inactivity timer has - * expired, set this flag can let system go to G3 and then exit G3 - * immediately for powering on. - */ - want_g3_exit = 1; - - /* - * If the power state is in POWER_S5 and S5 inactivity timer is - * running, to wake the chipset task can cancel S5 inactivity timer and - * then restart the timer. This will give cpu a chance to start up if - * S5 inactivity timer is about to expire while power button is - * pressed. For other states here, to wake the chipset task to trigger - * the event for leaving G3 is necessary. - */ - task_wake(TASK_ID_CHIPSET); -} - -/*****************************************************************************/ -/* Task function */ - -void chipset_task(void *u) -{ - enum power_state new_state; - static enum power_state last_state; - uint32_t this_in_signals; - static uint32_t last_in_signals; - - while (1) { - /* - * In order to prevent repeated console spam, only print the - * current power state if something has actually changed. It's - * possible that one of the power signals goes away briefly and - * comes back by the time we update our in_signals. - */ - this_in_signals = in_signals; - if (this_in_signals != last_in_signals || state != last_state) { - CPRINTS("power state %d = %s, in 0x%04x", - state, state_names[state], this_in_signals); - if (IS_ENABLED(CONFIG_SEVEN_SEG_DISPLAY)) - display_7seg_write(SEVEN_SEG_EC_DISPLAY, state); - last_in_signals = this_in_signals; - last_state = state; - } - - /* Always let the specific chipset handle the state first */ - new_state = power_handle_state(state); - - /* - * If the state hasn't changed, run common steady-state - * handler. - */ - if (new_state == state) - new_state = power_common_state(state); - - /* Handle state changes */ - if (new_state != state) { - power_set_state(new_state); - power_set_active_wake_mask(); - - /* Call hooks before we enter G3 */ - if (new_state == POWER_G3) - hook_notify(HOOK_CHIPSET_HARD_OFF); - } - } -} - -/*****************************************************************************/ -/* Hooks */ - -static void power_common_init(void) -{ - const struct power_signal_info *s = power_signal_list; - int i; - - /* Update input state */ - power_update_signals(); - - /* Enable interrupts for input signals */ - for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) - if (s->flags & POWER_SIGNAL_DISABLE_AT_BOOT) - power_signal_disable_interrupt(s->gpio); - else - power_signal_enable_interrupt(s->gpio); - - /* Call chipset-specific init to set initial state */ - power_set_state(power_chipset_init()); - - /* - * Update input state again since there is a small window - * before GPIO is enabled. - */ - power_update_signals(); -} -DECLARE_HOOK(HOOK_INIT, power_common_init, HOOK_PRIO_INIT_CHIPSET); - -static void power_lid_change(void) -{ - /* Wake up the task to update power state */ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, power_lid_change, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_EXTPOWER -static void power_ac_change(void) -{ - if (extpower_is_present()) { - CPRINTS("AC on"); - } else { - CPRINTS("AC off"); - - if (state == POWER_G3) { - last_shutdown_time = get_time().val; - task_wake(TASK_ID_CHIPSET); - } - } -} -DECLARE_HOOK(HOOK_AC_CHANGE, power_ac_change, HOOK_PRIO_DEFAULT); -#endif - -/*****************************************************************************/ -/* Interrupts */ - -#ifdef CONFIG_BRINGUP -#define MAX_SIGLOG_ENTRIES 24 - -static unsigned int siglog_entries; -static unsigned int siglog_truncated; - -static struct { - timestamp_t time; - enum gpio_signal signal; - int level; -} siglog[MAX_SIGLOG_ENTRIES]; - -static void siglog_deferred(void) -{ - unsigned int i; - timestamp_t tdiff = {.val = 0}; - - /* Disable interrupts for input signals while we print stuff.*/ - for (i = 0; i < POWER_SIGNAL_COUNT; i++) - power_signal_disable_interrupt(power_signal_list[i].gpio); - - CPRINTF("%d signal changes:\n", siglog_entries); - for (i = 0; i < siglog_entries; i++) { - if (i) - tdiff.val = siglog[i].time.val - siglog[i-1].time.val; - CPRINTF(" %.6lld +%.6lld %s => %d\n", - siglog[i].time.val, tdiff.val, - power_signal_get_name(siglog[i].signal), - siglog[i].level); - } - if (siglog_truncated) - CPRINTF(" SIGNAL LOG TRUNCATED...\n"); - siglog_entries = siglog_truncated = 0; - - /* Okay, turn 'em on again. */ - for (i = 0; i < POWER_SIGNAL_COUNT; i++) - power_signal_enable_interrupt(power_signal_list[i].gpio); -} -DECLARE_DEFERRED(siglog_deferred); - -static void siglog_add(enum gpio_signal signal) -{ - if (siglog_entries >= MAX_SIGLOG_ENTRIES) { - siglog_truncated = 1; - return; - } - - siglog[siglog_entries].time = get_time(); - siglog[siglog_entries].signal = signal; - siglog[siglog_entries].level = power_signal_get_level(signal); - siglog_entries++; - - hook_call_deferred(&siglog_deferred_data, SECOND); -} - -#define SIGLOG(S) siglog_add(S) - -#else -#define SIGLOG(S) -#endif /* CONFIG_BRINGUP */ - -#ifdef CONFIG_POWER_SIGNAL_INTERRUPT_STORM_DETECT_THRESHOLD -/* - * Print an interrupt storm warning when we receive more than - * CONFIG_POWER_SIGNAL_INTERRUPT_STORM_DETECT_THRESHOLD interrupts of a - * single source within 1 second. - */ -static int power_signal_interrupt_count[POWER_SIGNAL_COUNT]; - -static void reset_power_signal_interrupt_count(void) -{ - int i; - - for (i = 0; i < POWER_SIGNAL_COUNT; ++i) - power_signal_interrupt_count[i] = 0; -} -DECLARE_HOOK(HOOK_SECOND, - reset_power_signal_interrupt_count, - HOOK_PRIO_DEFAULT); -#endif - -void power_signal_interrupt(enum gpio_signal signal) -{ -#ifdef CONFIG_POWER_SIGNAL_INTERRUPT_STORM_DETECT_THRESHOLD - int i; - - /* Tally our interrupts and print a warning if necessary. */ - for (i = 0; i < POWER_SIGNAL_COUNT; ++i) { - if (power_signal_list[i].gpio == signal) { - if (power_signal_interrupt_count[i]++ == - CONFIG_POWER_SIGNAL_INTERRUPT_STORM_DETECT_THRESHOLD) - CPRINTS("Interrupt storm! Signal %d", i); - break; - } - } -#endif - - SIGLOG(signal); - - /* Shadow signals and compare with our desired signal state. */ - power_update_signals(); - - /* Wake up the task */ - task_wake(TASK_ID_CHIPSET); -} - -#ifdef CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 -inline int power_get_pause_in_s5(void) -{ - return pause_in_s5; -} - -inline void power_set_pause_in_s5(int pause) -{ - pause_in_s5 = pause; -} -#endif - -/*****************************************************************************/ -/* Console commands */ - -static int command_powerinfo(int argc, char **argv) -{ - /* - * Print power state in same format as state machine. This is - * used by FAFT tests, so must match exactly. - */ - ccprintf("power state %d = %s, in 0x%04x\n", - state, state_names[state], in_signals); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo, - NULL, - "Show current power state"); - -#ifdef CONFIG_CMD_POWERINDEBUG -static int command_powerindebug(int argc, char **argv) -{ - const struct power_signal_info *s = power_signal_list; - int i; - char *e; - - /* If one arg, set the mask */ - if (argc == 2) { - int m = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - in_debug = m; - } - - /* Print the mask */ - ccprintf("power in: 0x%04x\n", in_signals); - ccprintf("debug mask: 0x%04x\n", in_debug); - - /* Print the decode */ - - ccprintf("bit meanings:\n"); - for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) { - int mask = 1 << i; - ccprintf(" 0x%04x %d %s\n", - mask, in_signals & mask ? 1 : 0, s->name); - } - - return EC_SUCCESS; -}; -DECLARE_CONSOLE_COMMAND(powerindebug, command_powerindebug, - "[mask]", - "Get/set power input debug mask"); -#endif - -#ifdef CONFIG_CMD_S5_TIMEOUT -/* Allow command-line access to configure our S5 delay for power testing */ -static int command_s5_timeout(int argc, char **argv) -{ - char *e; - - if (argc >= 2) { - uint32_t s = strtoi(argv[1], &e, 0); - - if (*e) - return EC_ERROR_PARAM1; - - s5_inactivity_timeout = s; - } - - /* Print the current setting */ - ccprintf("S5 inactivity timeout: %d s\n", s5_inactivity_timeout); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(s5_timeout, command_s5_timeout, - "[sec]", - "Set the timeout from S5 to G3 transition, " - "-1 to indicate no transition"); -#endif - -#ifdef CONFIG_HIBERNATE -static int command_hibernation_delay(int argc, char **argv) -{ - char *e; - uint32_t time_g3 = ((uint32_t)(get_time().val - last_shutdown_time)) - / SECOND; - - if (argc >= 2) { - uint32_t s = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - hibernate_delay = s; - } - - /* Print the current setting */ - ccprintf("Hibernation delay: %d s\n", hibernate_delay); - if (state == POWER_G3 && !extpower_is_present()) { - ccprintf("Time G3: %d s\n", time_g3); - ccprintf("Time left: %d s\n", hibernate_delay - time_g3); - } - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(hibdelay, command_hibernation_delay, - "[sec]", - "Set the delay before going into hibernation"); - -static enum ec_status -host_command_hibernation_delay(struct host_cmd_handler_args *args) -{ - const struct ec_params_hibernation_delay *p = args->params; - struct ec_response_hibernation_delay *r = args->response; - - uint32_t time_g3; - uint64_t t = get_time().val - last_shutdown_time; - - uint64divmod(&t, SECOND); - time_g3 = (uint32_t)t; - - /* Only change the hibernation delay if seconds is non-zero. */ - if (p->seconds) - hibernate_delay = p->seconds; - - if (state == POWER_G3 && !extpower_is_present()) - r->time_g3 = time_g3; - else - r->time_g3 = 0; - - if ((time_g3 != 0) && (time_g3 > hibernate_delay)) - r->time_remaining = 0; - else - r->time_remaining = hibernate_delay - time_g3; - r->hibernate_delay = hibernate_delay; - - args->response_size = sizeof(struct ec_response_hibernation_delay); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_HIBERNATION_DELAY, - host_command_hibernation_delay, - EC_VER_MASK(0)); -#endif /* CONFIG_HIBERNATE */ - -#ifdef CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 -static enum ec_status -host_command_pause_in_s5(struct host_cmd_handler_args *args) -{ - const struct ec_params_get_set_value *p = args->params; - struct ec_response_get_set_value *r = args->response; - - if (p->flags & EC_GSV_SET) - pause_in_s5 = p->value; - - r->value = pause_in_s5; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_GSV_PAUSE_IN_S5, - host_command_pause_in_s5, - EC_VER_MASK(0)); - -static int command_pause_in_s5(int argc, char **argv) -{ - if (argc > 1 && !parse_bool(argv[1], &pause_in_s5)) - return EC_ERROR_INVAL; - - ccprintf("pause_in_s5 = %s\n", pause_in_s5 ? "on" : "off"); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pause_in_s5, command_pause_in_s5, - "[on|off]", - "Should the AP pause in S5 during shutdown?"); -#endif /* CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 */ - -#ifdef CONFIG_POWER_PP5000_CONTROL -__overridable void board_power_5v_enable(int enable) -{ - if (enable) - gpio_set_level(GPIO_EN_PP5000, 1); - else - gpio_set_level(GPIO_EN_PP5000, 0); -} - -/* 5V enable request bitmask from various tasks. */ -static uint32_t pwr_5v_en_req; -K_MUTEX_DEFINE(pwr_5v_ctl_mtx); - -void power_5v_enable(task_id_t tid, int enable) -{ - mutex_lock(&pwr_5v_ctl_mtx); - - if (enable) /* Set the bit indicating the request. */ - pwr_5v_en_req |= 1 << tid; - else /* Clear the task's request bit. */ - pwr_5v_en_req &= ~(1 << tid); - - /* - * If there are any outstanding requests for the rail to be enabled, - * turn on the rail. Otherwise, turn it off. - */ - board_power_5v_enable(pwr_5v_en_req); - mutex_unlock(&pwr_5v_ctl_mtx); -} - -#define P5_SYSJUMP_TAG 0x5005 /* "P5" */ -static void restore_enable_5v_state(void) -{ - const uint32_t *state; - int size; - - state = (const uint32_t *) system_get_jump_tag(P5_SYSJUMP_TAG, 0, - &size); - if (state && size == sizeof(pwr_5v_en_req)) { - mutex_lock(&pwr_5v_ctl_mtx); - pwr_5v_en_req |= *state; - mutex_unlock(&pwr_5v_ctl_mtx); - } -} -DECLARE_HOOK(HOOK_INIT, restore_enable_5v_state, HOOK_PRIO_FIRST); - -static void preserve_enable_5v_state(void) -{ - mutex_lock(&pwr_5v_ctl_mtx); - system_add_jump_tag(P5_SYSJUMP_TAG, 0, sizeof(pwr_5v_en_req), - &pwr_5v_en_req); - mutex_unlock(&pwr_5v_ctl_mtx); -} -DECLARE_HOOK(HOOK_SYSJUMP, preserve_enable_5v_state, HOOK_PRIO_DEFAULT); -#endif /* defined(CONFIG_POWER_PP5000_CONTROL) */ diff --git a/power/ec_driven.c b/power/ec_driven.c deleted file mode 100644 index 282941b941..0000000000 --- a/power/ec_driven.c +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright 2013 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. - */ - -/* - * Mock power module for Sensor HUB. - * - * This implements the following features: - * when AP_IN_SUSPEND is low, in S0, otherwise S3. - * - */ - -#include "chipset.h" /* This module implements chipset functions too */ -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "power.h" -#include "task.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -#define IN_SUSPEND POWER_SIGNAL_MASK(ECDRIVEN_SUSPEND_ASSERTED) - -enum power_state power_chipset_init(void) -{ - return POWER_S3; -} - -enum power_state power_handle_state(enum power_state state) -{ - switch (state) { - case POWER_S3: - if (!(power_get_signals() & IN_SUSPEND)) { - hook_notify(HOOK_CHIPSET_RESUME); - return POWER_S0; - } - return state; - - case POWER_S0: - if (power_get_signals() & IN_SUSPEND) { - hook_notify(HOOK_CHIPSET_SUSPEND); - return POWER_S3; - } - return state; - default: - CPRINTS("Unexpected state: $d", state); - } - - return state; -} diff --git a/power/host_sleep.c b/power/host_sleep.c deleted file mode 100644 index bfa5cbd90a..0000000000 --- a/power/host_sleep.c +++ /dev/null @@ -1,269 +0,0 @@ -/* Copyright 2019 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. - */ - -#include "config.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "host_command.h" -#include "power.h" -#include "util.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) - -/* Track last reported sleep event */ -static enum host_sleep_event host_sleep_state; - -__overridable void power_chipset_handle_host_sleep_event( - enum host_sleep_event state, - struct host_sleep_event_context *ctx) -{ - /* Default weak implementation -- no action required. */ -} - -static enum ec_status -host_command_host_sleep_event(struct host_cmd_handler_args *args) -{ - const struct ec_params_host_sleep_event_v1 *p = args->params; - struct ec_response_host_sleep_event_v1 *r = args->response; - struct host_sleep_event_context ctx; - enum host_sleep_event state = p->sleep_event; - - host_sleep_state = state; - ctx.sleep_transitions = 0; - switch (state) { - case HOST_SLEEP_EVENT_S0IX_SUSPEND: - case HOST_SLEEP_EVENT_S3_SUSPEND: - case HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND: - ctx.sleep_timeout_ms = EC_HOST_SLEEP_TIMEOUT_DEFAULT; - - /* The original version contained only state. */ - if (args->version >= 1) - ctx.sleep_timeout_ms = - p->suspend_params.sleep_timeout_ms; - - break; - - default: - break; - } - - power_chipset_handle_host_sleep_event(host_sleep_state, &ctx); - switch (state) { - case HOST_SLEEP_EVENT_S0IX_RESUME: - case HOST_SLEEP_EVENT_S3_RESUME: - if (args->version >= 1) { - r->resume_response.sleep_transitions = - ctx.sleep_transitions; - - args->response_size = sizeof(*r); - } - - break; - - default: - break; - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_HOST_SLEEP_EVENT, - host_command_host_sleep_event, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -enum host_sleep_event power_get_host_sleep_state(void) -{ - return host_sleep_state; -} - -void power_set_host_sleep_state(enum host_sleep_event state) -{ - host_sleep_state = state; -} - -/* Flag to notify listeners about suspend/resume events. */ -enum sleep_notify_type sleep_notify = SLEEP_NOTIFY_NONE; - -/* - * Note: the following sleep_ functions do not get called in the S3 path on - * Intel devices. On Intel devices, they are called in the S0ix path. - */ -void sleep_set_notify(enum sleep_notify_type notify) -{ - sleep_notify = notify; -} - -void sleep_notify_transition(int check_state, int hook_id) -{ - if (sleep_notify != check_state) - return; - - hook_notify(hook_id); - sleep_set_notify(SLEEP_NOTIFY_NONE); -} - -#ifdef CONFIG_POWER_SLEEP_FAILURE_DETECTION - -static uint16_t sleep_signal_timeout; -static uint16_t host_sleep_timeout_default = CONFIG_SLEEP_TIMEOUT_MS; -static uint32_t sleep_signal_transitions; -static void (*sleep_timeout_callback)(void); - -static void sleep_transition_timeout(void); -DECLARE_DEFERRED(sleep_transition_timeout); - -static void sleep_increment_transition(void) -{ - if ((sleep_signal_transitions & EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK) < - EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK) - sleep_signal_transitions += 1; -} - -void sleep_suspend_transition(void) -{ - sleep_increment_transition(); - hook_call_deferred(&sleep_transition_timeout_data, -1); -} - -void sleep_resume_transition(void) -{ - sleep_increment_transition(); - - /* - * Start the timer again to ensure the AP doesn't get itself stuck in - * a state where it's no longer in a sleep state (S0ix/S3), but from - * the Linux perspective is still suspended. Perhaps a bug in the SoC- - * internal periodic housekeeping code might result in a situation - * like this. - */ - if (sleep_signal_timeout) - hook_call_deferred(&sleep_transition_timeout_data, - (uint32_t)sleep_signal_timeout * 1000); -} - -static void sleep_transition_timeout(void) -{ - /* Mark the timeout. */ - sleep_signal_transitions |= EC_HOST_RESUME_SLEEP_TIMEOUT; - hook_call_deferred(&sleep_transition_timeout_data, -1); - - /* Call the custom callback */ - if (sleep_timeout_callback) - sleep_timeout_callback(); -} - -void sleep_start_suspend(struct host_sleep_event_context *ctx, - void (*callback)(void)) -{ - uint16_t timeout = ctx->sleep_timeout_ms; - - sleep_timeout_callback = callback; - sleep_signal_transitions = 0; - - /* Use zero internally to indicate no timeout. */ - if (timeout == EC_HOST_SLEEP_TIMEOUT_DEFAULT) { - timeout = host_sleep_timeout_default; - } - - /* Use 0xFFFF to disable the timeout */ - if (timeout == EC_HOST_SLEEP_TIMEOUT_INFINITE) { - sleep_signal_timeout = 0; - return; - } - - sleep_signal_timeout = timeout; - hook_call_deferred(&sleep_transition_timeout_data, - (uint32_t)timeout * 1000); -} - -void sleep_complete_resume(struct host_sleep_event_context *ctx) -{ - /* - * Ensure we don't schedule another sleep_transition_timeout - * if the the HOST_SLEEP_EVENT_S0IX_RESUME message arrives before - * the CHIPSET task transitions to the POWER_S0ixS0 state. - */ - sleep_signal_timeout = 0; - hook_call_deferred(&sleep_transition_timeout_data, -1); - ctx->sleep_transitions = sleep_signal_transitions; -} - -void sleep_reset_tracking(void) -{ - sleep_signal_transitions = 0; - sleep_signal_timeout = 0; - sleep_timeout_callback = NULL; -} - -static int command_sleep_fail_timeout(int argc, char **argv) -{ - if (argc < 2) { - /* no arguments - just print the current timeout */ - } else if (!strcasecmp(argv[1], "default")) { - host_sleep_timeout_default = CONFIG_SLEEP_TIMEOUT_MS; - } else if (!strcasecmp(argv[1], "infinite")) { - host_sleep_timeout_default = EC_HOST_SLEEP_TIMEOUT_INFINITE; - } else { - char *e; - int val; - - val = strtoi(argv[1], &e, 10); - if (*e) - return EC_ERROR_PARAM1; - - if (val <= 0 || val >= EC_HOST_SLEEP_TIMEOUT_INFINITE) { - ccprintf("Error: timeout range is 1..%d [msec]\n", - EC_HOST_SLEEP_TIMEOUT_INFINITE - 1); - return EC_ERROR_PARAM1; - } - - host_sleep_timeout_default = val; - } - - if (host_sleep_timeout_default == EC_HOST_SLEEP_TIMEOUT_INFINITE) - ccprintf("Sleep failure detection timeout is disabled\n"); - else - ccprintf("Sleep failure detection timeout is %d [msec]\n", - host_sleep_timeout_default); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(sleeptimeout, command_sleep_fail_timeout, - "[default | infinite | <msec>]", - "Display or set host sleep failure detection timeout.\n" - "Valid arguments are:\n" - " default\n" - " infinite - disables the timeout\n" - " <msec> - custom length in milliseconds\n" - " <none> - prints the current setting"); - - -#else /* !CONFIG_POWER_SLEEP_FAILURE_DETECTION */ - -/* No action */ -void sleep_suspend_transition(void) -{ -} - -void sleep_resume_transition(void) -{ -} - -void sleep_start_suspend(struct host_sleep_event_context *ctx, - void (*callback)(void)) -{ -} - -void sleep_complete_resume(struct host_sleep_event_context *ctx) -{ -} - -void sleep_reset_tracking(void) -{ -} - -#endif /* CONFIG_POWER_SLEEP_FAILURE_DETECTION */ diff --git a/power/icelake.c b/power/icelake.c deleted file mode 100644 index c47f44c146..0000000000 --- a/power/icelake.c +++ /dev/null @@ -1,325 +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. - */ - -/* Icelake chipset power control module for Chrome EC */ - -#include "board_config.h" -#include "chipset.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "power.h" -#include "power/intel_x86.h" -#include "power_button.h" -#include "task.h" -#include "timer.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -#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 - -/* The wait time is ~150 msec, allow for safety margin. */ -#define IN_PCH_SLP_SUS_WAIT_TIME_USEC (250 * MSEC) - -static int forcing_shutdown; /* Forced shutdown in progress? */ - -/* Power signals list. Must match order of enum power_signal. */ -const struct power_signal_info power_signal_list[] = { - [X86_SLP_S0_DEASSERTED] = { - .gpio = GPIO_PCH_SLP_S0_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH | - POWER_SIGNAL_DISABLE_AT_BOOT, - .name = "SLP_S0_DEASSERTED", - }, - [X86_SLP_S3_DEASSERTED] = { - .gpio = SLP_S3_SIGNAL_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "SLP_S3_DEASSERTED", - }, - [X86_SLP_S4_DEASSERTED] = { - .gpio = SLP_S4_SIGNAL_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "SLP_S4_DEASSERTED", - }, - [X86_SLP_SUS_DEASSERTED] = { - .gpio = GPIO_SLP_SUS_L, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "SLP_SUS_DEASSERTED", - }, - [X86_RSMRST_L_PGOOD] = { - .gpio = GPIO_PG_EC_RSMRST_ODL, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "RSMRST_L_PGOOD", - }, - [X86_DSW_DPWROK] = { - .gpio = GPIO_PG_EC_DSW_PWROK, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "DSW_DPWROK", - }, - [X86_ALL_SYS_PGOOD] = { - .gpio = GPIO_PG_EC_ALL_SYS_PWRGD, - .flags = POWER_SIGNAL_ACTIVE_HIGH, - .name = "ALL_SYS_PWRGD", - }, -}; -BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); - -__overridable int intel_x86_get_pg_ec_dsw_pwrok(void) -{ - return gpio_get_level(GPIO_PG_EC_DSW_PWROK); -} - -__overridable int intel_x86_get_pg_ec_all_sys_pwrgd(void) -{ - return gpio_get_level(GPIO_PG_EC_ALL_SYS_PWRGD); -} - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - int timeout_ms = 50; - - CPRINTS("%s() %d", __func__, reason); - report_ap_reset(reason); - - /* Turn off RMSRST_L to meet tPCH12 */ - board_before_rsmrst(0); - GPIO_SET_LEVEL(GPIO_PCH_RSMRST_L, 0); - board_after_rsmrst(0); - - /* Turn off DSW_PWROK to meet tPCH14 */ - GPIO_SET_LEVEL(GPIO_PCH_DSW_PWROK, 0); - - /* Turn off DSW load switch. */ - GPIO_SET_LEVEL(GPIO_EN_PP3300_A, 0); - - /* - * For JSL, we need to wait 60ms before turning off PP5000_U to allow - * VCCIN_AUX time to discharge. - */ - if (IS_ENABLED(CONFIG_CHIPSET_JASPERLAKE)) - msleep(60); - - /* Turn off PP5000 rail */ - if (IS_ENABLED(CONFIG_POWER_PP5000_CONTROL)) - power_5v_enable(task_get_current(), 0); - else - GPIO_SET_LEVEL(GPIO_EN_PP5000, 0); - - /* - * TODO(b/111810925): Replace this wait with - * power_wait_signals_timeout() - */ - /* Now wait for DSW_PWROK and RSMRST_ODL to go away. */ - while (intel_x86_get_pg_ec_dsw_pwrok() && - gpio_get_level(GPIO_PG_EC_RSMRST_ODL) && (timeout_ms > 0)) { - msleep(1); - timeout_ms--; - }; - - if (!timeout_ms) - CPRINTS("DSW_PWROK or RSMRST_ODL didn't go low! Assuming G3."); -} - -void chipset_handle_espi_reset_assert(void) -{ - /* - * If eSPI_Reset# pin is asserted without SLP_SUS# being asserted, then - * it means that there is an unexpected power loss (global reset - * event). In this case, check if shutdown was being forced by pressing - * power button. If yes, release power button. - */ - if ((power_get_signals() & IN_PCH_SLP_SUS_DEASSERTED) && - forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } -} - -enum power_state chipset_force_g3(void) -{ - chipset_force_shutdown(CHIPSET_SHUTDOWN_G3); - - return POWER_G3; -} - -static void enable_pp5000_rail(void) -{ - if (IS_ENABLED(CONFIG_POWER_PP5000_CONTROL)) - power_5v_enable(task_get_current(), 1); - else - GPIO_SET_LEVEL(GPIO_EN_PP5000, 1); - -} - -static void dsw_pwrok_pass_thru(void) -{ - int dswpwrok_in = intel_x86_get_pg_ec_dsw_pwrok(); - - /* Pass-through DSW_PWROK to ICL. */ - if (dswpwrok_in != gpio_get_level(GPIO_PCH_DSW_PWROK)) { - if (IS_ENABLED(CONFIG_CHIPSET_SLP_S3_L_OVERRIDE) - && dswpwrok_in) { - /* - * Once DSW_PWROK is high, reconfigure SLP_S3_L back to - * an input after a short delay. - */ - msleep(1); - CPRINTS("Release SLP_S3_L"); - gpio_reset(SLP_S3_SIGNAL_L); - power_signal_enable_interrupt(SLP_S3_SIGNAL_L); - } - - CPRINTS("Pass thru GPIO_DSW_PWROK: %d", dswpwrok_in); - /* - * A minimum 10 msec delay is required between PP3300_A being - * stable and the DSW_PWROK signal being passed to the PCH. - */ - msleep(10); - GPIO_SET_LEVEL(GPIO_PCH_DSW_PWROK, dswpwrok_in); - } -} - -/* - * Set the PWROK signal state - * - * ¶m level 0 deasserts the signal, other values assert the signal - */ -static void pwrok_signal_set(const struct intel_x86_pwrok_signal *signal, - int level) -{ - GPIO_SET_LEVEL(signal->gpio, signal->active_low ? !level : level); -} - -/* - * Pass through the state of the ALL_SYS_PWRGD input to all the PWROK outputs - * defined by the board. - */ -static void all_sys_pwrgd_pass_thru(void) -{ - int all_sys_pwrgd_in = intel_x86_get_pg_ec_all_sys_pwrgd(); - const struct intel_x86_pwrok_signal *pwrok_signal; - int signal_count; - int i; - - if (all_sys_pwrgd_in) { - pwrok_signal = pwrok_signal_assert_list; - signal_count = pwrok_signal_assert_count; - } else { - pwrok_signal = pwrok_signal_deassert_list; - signal_count = pwrok_signal_deassert_count; - } - - /* - * Loop through all PWROK signals defined by the board and set - * to match the current ALL_SYS_PWRGD input. - */ - for (i = 0; i < signal_count; i++, pwrok_signal++) { - if (pwrok_signal->delay_ms > 0) - msleep(pwrok_signal->delay_ms); - - pwrok_signal_set(pwrok_signal, all_sys_pwrgd_in); - } -} - -enum power_state power_handle_state(enum power_state state) -{ -#ifdef CONFIG_CHIPSET_JASPERLAKE - int timeout_ms = 10; -#endif /* CONFIG_CHIPSET_JASPERLAKE */ - - dsw_pwrok_pass_thru(); - - all_sys_pwrgd_pass_thru(); - - common_intel_x86_handle_rsmrst(state); - - switch (state) { - - case POWER_G3S5: - if (IS_ENABLED(CONFIG_CHIPSET_SLP_S3_L_OVERRIDE)) { - /* - * Prevent glitches on the SLP_S3_L and PCH_PWROK - * signals while when the PP3300_A rail is turned on. - * Drive SLP_S3_L from the EC until DSW_PWROK is high. - */ - CPRINTS("Drive SLP_S3_L low during PP3300_A rampup"); - power_signal_disable_interrupt(SLP_S3_SIGNAL_L); - gpio_set_flags(SLP_S3_SIGNAL_L, GPIO_ODR_LOW); - } - - /* Default behavior - turn on PP5000 rail first */ - if (!IS_ENABLED(CONFIG_CHIPSET_PP3300_RAIL_FIRST)) - enable_pp5000_rail(); - - /* - * TODO(b/111121615): Should modify this to wait until the - * common power state machine indicates that it's ok to try an - * boot the AP prior to turning on the 3300_A rail. This could - * be done using chipset_pre_init_callback() - */ - /* Turn on the PP3300_DSW rail. */ - GPIO_SET_LEVEL(GPIO_EN_PP3300_A, 1); - if (power_wait_signals(IN_PGOOD_ALL_CORE)) - break; - - /* Pass thru DSWPWROK again since we changed it. */ - dsw_pwrok_pass_thru(); - - /* Turn on PP5000 after PP3300 and DSW PWROK when enabled */ - if (IS_ENABLED(CONFIG_CHIPSET_PP3300_RAIL_FIRST)) - enable_pp5000_rail(); - - /* - * Now wait for SLP_SUS_L to go high based on tPCH32. If this - * signal doesn't go high within 250 msec then go back to G3. - */ - if (power_wait_signals_timeout(IN_PCH_SLP_SUS_DEASSERTED, - IN_PCH_SLP_SUS_WAIT_TIME_USEC) != EC_SUCCESS) { - CPRINTS("SLP_SUS_L didn't go high! Going back to G3."); - return POWER_S5G3; - } - break; - - case POWER_S5: - if (forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } - /* If SLP_SUS_L is asserted, we're no longer in S5. */ - if (!power_has_signals(IN_PCH_SLP_SUS_DEASSERTED)) - return POWER_S5G3; - break; - -#ifdef CONFIG_CHIPSET_JASPERLAKE - case POWER_S3S0: - GPIO_SET_LEVEL(GPIO_EN_VCCIO_EXT, 1); - /* Now wait for ALL_SYS_PWRGD. */ - while (!intel_x86_get_pg_ec_all_sys_pwrgd() && - (timeout_ms > 0)) { - msleep(1); - timeout_ms--; - }; - if (!timeout_ms) - CPRINTS("ALL_SYS_PWRGD not received."); - break; - - case POWER_S0S3: - GPIO_SET_LEVEL(GPIO_EN_VCCIO_EXT, 0); - break; -#endif /* CONFIG_CHIPSET_JASPERLAKE */ - - default: - break; - } - - return common_intel_x86_power_handle_state(state); -} diff --git a/power/intel_x86.c b/power/intel_x86.c deleted file mode 100644 index c4aae9db81..0000000000 --- a/power/intel_x86.c +++ /dev/null @@ -1,679 +0,0 @@ -/* Copyright 2016 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. - */ - -/* Intel X86 chipset power control module for Chrome EC */ - -#include "board_config.h" -#include "charge_state.h" -#include "chipset.h" -#include "console.h" -#include "ec_commands.h" -#include "gpio.h" -#include "hooks.h" -#include "lpc.h" -#include "power.h" -#include "power/intel_x86.h" -#include "power_button.h" -#include "system.h" -#include "task.h" -#include "util.h" -#include "vboot.h" -#include "wireless.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) - -enum sys_sleep_state { - SYS_SLEEP_S3, - SYS_SLEEP_S4, -#ifdef CONFIG_POWER_S0IX - SYS_SLEEP_S0IX, -#endif -}; - -static const int sleep_sig[] = { - [SYS_SLEEP_S3] = SLP_S3_SIGNAL_L, - [SYS_SLEEP_S4] = SLP_S4_SIGNAL_L, -#ifdef CONFIG_POWER_S0IX - [SYS_SLEEP_S0IX] = GPIO_PCH_SLP_S0_L, -#endif -}; - -static int power_s5_up; /* Chipset is sequencing up or down */ - -#ifdef CONFIG_CHARGER -/* Flag to indicate if power up was inhibited due to low battery SOC level. */ -static int power_up_inhibited; - -/* - * Check if AP power up should be inhibited. - * 0 = Ok to boot up AP - * 1 = AP power up is inhibited. - */ -static int is_power_up_inhibited(void) -{ - /* Defaulting to power button not pressed. */ - const int power_button_pressed = 0; - - return charge_prevent_power_on(power_button_pressed) || - charge_want_shutdown(); -} - -static void power_up_inhibited_cb(void) -{ - if (!power_up_inhibited) - return; - - if (is_power_up_inhibited()) { - CPRINTS("power-up still inhibited"); - return; - } - - CPRINTS("Battery SOC ok to boot AP!"); - power_up_inhibited = 0; - - chipset_exit_hard_off(); -} -DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, power_up_inhibited_cb, HOOK_PRIO_DEFAULT); -#endif - -/* Get system sleep state through GPIOs or VWs */ -static inline int chipset_get_sleep_signal(enum sys_sleep_state state) -{ - return power_signal_get_level(sleep_sig[state]); -} - -#ifdef CONFIG_BOARD_HAS_RTC_RESET -static void intel_x86_rtc_reset(void) -{ - CPRINTS("Asserting RTCRST# to PCH"); - gpio_set_level(GPIO_PCH_RTCRST, 1); - udelay(100); - gpio_set_level(GPIO_PCH_RTCRST, 0); -} - -static enum power_state power_wait_s5_rtc_reset(void) -{ - static int s5_exit_tries; - - /* Wait for S5 exit and then attempt RTC reset */ - while ((power_get_signals() & IN_PCH_SLP_S4_DEASSERTED) == 0) { - /* Handle RSMRST passthru event while waiting */ - common_intel_x86_handle_rsmrst(POWER_S5); - if (task_wait_event(SECOND*4) == TASK_EVENT_TIMER) { - CPRINTS("timeout waiting for S5 exit"); - chipset_force_g3(); - - /* Assert RTCRST# and retry 5 times */ - intel_x86_rtc_reset(); - - if (++s5_exit_tries > 4) { - s5_exit_tries = 0; - return POWER_G3; /* Stay off */ - } - - udelay(10 * MSEC); - return POWER_G3S5; /* Power up again */ - } - } - - s5_exit_tries = 0; - return POWER_S5S3; /* Power up to next state */ -} -#endif - -#ifdef CONFIG_POWER_S0IX -/* - * Backup copies of SCI and SMI mask to preserve across S0ix suspend/resume - * cycle. If the host uses S0ix, BIOS is not involved during suspend and resume - * operations and hence SCI/SMI masks are programmed only once during boot-up. - * - * These backup variables are set whenever host expresses its interest to - * enter S0ix and then lpc_host_event_mask for SCI and SMI are cleared. When - * host resumes from S0ix, masks from backup variables are copied over to - * lpc_host_event_mask for SCI and SMI. - */ -static host_event_t backup_sci_mask; -static host_event_t backup_smi_mask; - -/* - * Clear host event masks for SMI and SCI when host is entering S0ix. This is - * done to prevent any SCI/SMI interrupts when the host is in suspend. Since - * BIOS is not involved in the suspend path, EC needs to take care of clearing - * these masks. - */ -static void lpc_s0ix_suspend_clear_masks(void) -{ - backup_sci_mask = lpc_get_host_event_mask(LPC_HOST_EVENT_SCI); - backup_smi_mask = lpc_get_host_event_mask(LPC_HOST_EVENT_SMI); - - lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, 0); - lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, 0); -} - -/* - * Restore host event masks for SMI and SCI when host exits S0ix. This is done - * because BIOS is not involved in the resume path and so EC needs to restore - * the masks from backup variables. - */ -static void lpc_s0ix_resume_restore_masks(void) -{ - /* - * No need to restore SCI/SMI masks if both backup_sci_mask and - * backup_smi_mask are zero. This indicates that there was a failure to - * enter S0ix(SLP_S0# assertion) and hence SCI/SMI masks were never - * backed up. - */ - if (!backup_sci_mask && !backup_smi_mask) - return; - - lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, backup_sci_mask); - lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, backup_smi_mask); - - backup_sci_mask = backup_smi_mask = 0; -} - -static void lpc_s0ix_hang_detected(void) -{ - /* - * Wake up the AP so they don't just chill in a non-suspended state and - * burn power. Overload a vaguely related event bit since event bits are - * at a premium. If the system never entered S0ix, then manually set the - * wake mask to pretend it did, so that the hang detect event wakes the - * system. - */ - if (power_get_state() == POWER_S0) { - host_event_t sleep_wake_mask; - - get_lazy_wake_mask(POWER_S0ix, &sleep_wake_mask); - lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, sleep_wake_mask); - } - - CPRINTS("Warning: Detected sleep hang! Waking host up!"); - host_set_single_event(EC_HOST_EVENT_HANG_DETECT); -} - -static void handle_chipset_suspend(void) -{ - /* Clear masks before any hooks are run for suspend. */ - lpc_s0ix_suspend_clear_masks(); -} -DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, handle_chipset_suspend, HOOK_PRIO_FIRST); - -static void handle_chipset_reset(void) -{ - if (chipset_in_state(CHIPSET_STATE_STANDBY)) { - CPRINTS("chipset reset: exit s0ix"); - power_reset_host_sleep_state(); - task_wake(TASK_ID_CHIPSET); - } -} -DECLARE_HOOK(HOOK_CHIPSET_RESET, handle_chipset_reset, HOOK_PRIO_FIRST); - -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); -} - -#endif /* CONFIG_POWER_S0IX */ - -void chipset_throttle_cpu(int throttle) -{ -#ifdef CONFIG_CPU_PROCHOT_ACTIVE_LOW - throttle = !throttle; -#endif /* CONFIG_CPU_PROCHOT_ACTIVE_LOW */ - if (chipset_in_state(CHIPSET_STATE_ON)) - gpio_set_level(GPIO_CPU_PROCHOT, throttle); -} - -enum power_state power_chipset_init(void) -{ - CPRINTS("%s: power_signal=0x%x", __func__, power_get_signals()); - - if (!system_jumped_to_this_image()) - return POWER_G3; - /* - * We are here as RW. We need to handle the following cases: - * - * 1. Late sysjump by software sync. AP is in S0. - * 2. Shutting down in recovery mode then sysjump by EFS2. AP is in S5 - * and expected to sequence down. - * 3. Rebooting from recovery mode then sysjump by EFS2. AP is in S5 - * and expected to sequence up. - * 4. RO jumps to RW from main() by EFS2. (a.k.a. power on reset, cold - * reset). AP is in G3. - */ - if ((power_get_signals() & IN_ALL_S0) == IN_ALL_S0) { - /* case #1. Disable idle task deep sleep when in S0. */ - disable_sleep(SLEEP_MASK_AP_RUN); - CPRINTS("already in S0"); - return POWER_S0; - } - if ((power_get_signals() & CHIPSET_G3S5_POWERUP_SIGNAL) - == CHIPSET_G3S5_POWERUP_SIGNAL) { - /* case #2 & #3 */ - CPRINTS("already in S5"); - return POWER_S5; - } - /* case #4 */ - chipset_force_g3(); - return POWER_G3; -} - -enum power_state common_intel_x86_power_handle_state(enum power_state state) -{ - switch (state) { - case POWER_G3: - break; - - case POWER_S5: -#ifdef CONFIG_BOARD_HAS_RTC_RESET - /* Wait for S5 exit and attempt RTC reset if supported */ - if (power_s5_up) - return power_wait_s5_rtc_reset(); -#endif - - if (chipset_get_sleep_signal(SYS_SLEEP_S4) == 1) - return POWER_S5S3; /* Power up to next state */ - break; - - case POWER_S3: - if (!power_has_signals(IN_PGOOD_ALL_CORE)) { - /* Required rail went away */ - chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL); - return POWER_S3S5; - } else if (chipset_get_sleep_signal(SYS_SLEEP_S3) == 1) { - /* Power up to next state */ - return POWER_S3S0; - } else if (chipset_get_sleep_signal(SYS_SLEEP_S4) == 0) { - /* Power down to next state */ - return POWER_S3S5; - } - break; - - case POWER_S0: - if (!power_has_signals(IN_PGOOD_ALL_CORE)) { - chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL); - return POWER_S0S3; - } else if (chipset_get_sleep_signal(SYS_SLEEP_S3) == 0) { - /* Power down to next state */ - return POWER_S0S3; -#ifdef CONFIG_POWER_S0IX - /* - * SLP_S0 may assert in system idle scenario without a kernel - * freeze call. This may cause interrupt storm since there is - * no freeze/unfreeze of threads/process in the idle scenario. - * Ignore the SLP_S0 assertions in idle scenario by checking - * the host sleep state. - */ - } else if (power_get_host_sleep_state() - == HOST_SLEEP_EVENT_S0IX_SUSPEND && - chipset_get_sleep_signal(SYS_SLEEP_S0IX) == 0) { - return POWER_S0S0ix; - } else { - sleep_notify_transition(SLEEP_NOTIFY_RESUME, - HOOK_CHIPSET_RESUME); -#endif - } - - break; - -#ifdef CONFIG_POWER_S0IX - case POWER_S0ix: - /* System in S0 only if SLP_S0 and SLP_S3 are de-asserted */ - if ((chipset_get_sleep_signal(SYS_SLEEP_S0IX) == 1) && - (chipset_get_sleep_signal(SYS_SLEEP_S3) == 1)) { - return POWER_S0ixS0; - } else if (!power_has_signals(IN_PGOOD_ALL_CORE)) { - return POWER_S0; - } - - break; -#endif - - case POWER_G3S5: - if (intel_x86_wait_power_up_ok() != EC_SUCCESS) { - chipset_force_shutdown( - CHIPSET_SHUTDOWN_BATTERY_INHIBIT); - return POWER_G3; - } -#ifdef CONFIG_CHIPSET_HAS_PRE_INIT_CALLBACK - /* - * Callback to do pre-initialization within the context of - * chipset task. - */ - chipset_pre_init_callback(); -#endif - - if (power_wait_signals(CHIPSET_G3S5_POWERUP_SIGNAL)) { - chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT); - return POWER_G3; - } - - power_s5_up = 1; - return POWER_S5; - - case POWER_S5S3: - if (!power_has_signals(IN_PGOOD_ALL_CORE)) { - /* Required rail went away */ - chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL); - return POWER_S5G3; - } - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_STARTUP); - -#ifdef CONFIG_POWER_S0IX - /* - * Clearing the S0ix flag on the path to S0 - * to handle any reset conditions. - */ - power_reset_host_sleep_state(); -#endif - return POWER_S3; - - case POWER_S3S0: - if (!power_has_signals(IN_PGOOD_ALL_CORE)) { - /* Required rail went away */ - chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL); - return POWER_S3S5; - } - - /* Enable wireless */ - wireless_set_state(WIRELESS_ON); - - lpc_s3_resume_clear_masks(); - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_RESUME); - - /* - * 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); - - /* - * Throttle CPU if necessary. This should only be asserted - * when +VCCP is powered (it is by now). - */ -#ifdef CONFIG_CPU_PROCHOT_ACTIVE_LOW - gpio_set_level(GPIO_CPU_PROCHOT, 1); -#else - gpio_set_level(GPIO_CPU_PROCHOT, 0); -#endif /* CONFIG_CPU_PROCHOT_ACTIVE_LOW */ - - return POWER_S0; - - case POWER_S0S3: - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SUSPEND); - - /* Suspend wireless */ - wireless_set_state(WIRELESS_SUSPEND); - - /* - * 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); - -#ifdef CONFIG_POWER_S0IX - /* re-init S0ix flag */ - power_reset_host_sleep_state(); -#endif - return POWER_S3; - -#ifdef CONFIG_POWER_S0IX - case POWER_S0S0ix: - /* - * Call hooks only if we haven't notified listeners of S0ix - * suspend. - */ - sleep_notify_transition(SLEEP_NOTIFY_SUSPEND, - HOOK_CHIPSET_SUSPEND); - sleep_suspend_transition(); - - /* - * Enable idle task deep sleep. Allow the low power idle task - * to go into deep sleep in S0ix. - */ - enable_sleep(SLEEP_MASK_AP_RUN); - return POWER_S0ix; - - case POWER_S0ixS0: - /* - * 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); - - sleep_resume_transition(); - return POWER_S0; -#endif - - case POWER_S3S5: - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - /* Disable wireless */ - wireless_set_state(WIRELESS_OFF); - - /* Call hooks after we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); - - /* Always enter into S5 state. The S5 state is required to - * correctly handle global resets which have a bit of delay - * while the SLP_Sx_L signals are asserted then deasserted. - */ - power_s5_up = 0; - return POWER_S5; - - case POWER_S5G3: - return chipset_force_g3(); - - default: - break; - } - - return state; -} - -void intel_x86_rsmrst_signal_interrupt(enum gpio_signal signal) -{ - int rsmrst_in = gpio_get_level(GPIO_RSMRST_L_PGOOD); - int rsmrst_out = gpio_get_level(GPIO_PCH_RSMRST_L); - - /* - * This function is called when rsmrst changes state. If rsmrst - * has been asserted (high -> low) then pass this new state to PCH. - */ - if (!rsmrst_in && (rsmrst_in != rsmrst_out)) - gpio_set_level(GPIO_PCH_RSMRST_L, rsmrst_in); - - /* - * Call the main power signal interrupt handler to wake up the chipset - * task which handles low->high rsmrst pass through. - */ - power_signal_interrupt(signal); -} - -__overridable void board_before_rsmrst(int rsmrst) -{ -} - -__overridable void board_after_rsmrst(int rsmrst) -{ -} - -void common_intel_x86_handle_rsmrst(enum power_state state) -{ - /* - * Pass through RSMRST asynchronously, as PCH may not react - * immediately to power changes. - */ - int rsmrst_in = gpio_get_level(GPIO_RSMRST_L_PGOOD); - int rsmrst_out = gpio_get_level(GPIO_PCH_RSMRST_L); - - /* Nothing to do. */ - if (rsmrst_in == rsmrst_out) - return; - - board_before_rsmrst(rsmrst_in); - -#ifdef CONFIG_CHIPSET_APL_GLK - /* Only passthrough RSMRST_L de-assertion on power up */ - if (rsmrst_in && !power_s5_up) - return; -#elif defined(CONFIG_CHIPSET_X86_RSMRST_DELAY) - /* - * Wait at least 10ms between power signals going high - * and deasserting RSMRST to PCH. - */ - if (rsmrst_in) - msleep(10); -#endif - - gpio_set_level(GPIO_PCH_RSMRST_L, rsmrst_in); - - CPRINTS("Pass through GPIO_RSMRST_L_PGOOD: %d", rsmrst_in); - - board_after_rsmrst(rsmrst_in); -} - -#ifdef CONFIG_POWER_TRACK_HOST_SLEEP_STATE - -__overridable void power_board_handle_host_sleep_event( - enum host_sleep_event state) -{ - /* Default weak implementation -- no action required. */ -} - -__override void power_chipset_handle_host_sleep_event( - enum host_sleep_event state, - struct host_sleep_event_context *ctx) -{ - power_board_handle_host_sleep_event(state); - -#ifdef CONFIG_POWER_S0IX - if (state == HOST_SLEEP_EVENT_S0IX_SUSPEND) { - /* - * Indicate to power state machine that a new host event for - * s0ix/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, lpc_s0ix_hang_detected); - power_signal_enable_interrupt(sleep_sig[SYS_SLEEP_S0IX]); - } else if (state == HOST_SLEEP_EVENT_S0IX_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); - lpc_s0ix_resume_restore_masks(); - power_signal_disable_interrupt(sleep_sig[SYS_SLEEP_S0IX]); - sleep_complete_resume(ctx); - /* - * If the sleep signal timed out and never transitioned, then - * the wake mask was modified to its suspend state (S0ix), so - * that the event wakes the system. Explicitly restore the wake - * mask to its S0 state now. - */ - power_update_wake_mask(); - } else if (state == HOST_SLEEP_EVENT_DEFAULT_RESET) { - power_signal_disable_interrupt(sleep_sig[SYS_SLEEP_S0IX]); - } -#endif - -} - -#endif - -__overridable void intel_x86_sys_reset_delay(void) -{ - /* - * Debounce time for SYS_RESET_L is 16 ms. Wait twice that period - * to be safe. - */ - udelay(32 * MSEC); -} - -void chipset_reset(enum chipset_reset_reason reason) -{ - /* - * Irrespective of cold_reset value, always toggle SYS_RESET_L to - * perform a chipset reset. RCIN# which was used earlier to trigger - * a warm reset is known to not work in certain cases where the CPU - * is in a bad state (crbug.com/721853). - * - * The EC cannot control warm vs cold reset of the chipset using - * SYS_RESET_L; it's more of a request. - */ - CPRINTS("%s: %d", __func__, reason); - - /* - * Toggling SYS_RESET_L will not have any impact when it's already - * low (i,e. Chipset is in reset state). - */ - if (gpio_get_level(GPIO_SYS_RESET_L) == 0) { - CPRINTS("Chipset is in reset state"); - return; - } - - report_ap_reset(reason); - - gpio_set_level(GPIO_SYS_RESET_L, 0); - intel_x86_sys_reset_delay(); - gpio_set_level(GPIO_SYS_RESET_L, 1); -} - -enum ec_error_list intel_x86_wait_power_up_ok(void) -{ -#ifdef CONFIG_CHARGER - int tries = 0; - - /* - * Allow charger to be initialized for up to defined tries, - * in case we're trying to boot the AP with no battery. - */ - while ((tries < CHARGER_INITIALIZED_TRIES) && - is_power_up_inhibited()) { - msleep(CHARGER_INITIALIZED_DELAY_MS); - tries++; - } - - /* - * Return to G3 if battery level is too low. Set - * power_up_inhibited in order to check the eligibility to boot - * AP up after battery SOC changes. - */ - if (tries == CHARGER_INITIALIZED_TRIES) { - CPRINTS("power-up inhibited"); - power_up_inhibited = 1; - return EC_ERROR_TIMEOUT; - } - - power_up_inhibited = 0; -#endif - -#if defined(CONFIG_VBOOT_EFS) || defined(CONFIG_VBOOT_EFS2) - /* - * We have to test power readiness here (instead of S5->S3) - * because when entering S5, EC enables EC_ROP_SLP_SUS pin - * which causes (short-powered) system to brown out. - */ - while (!system_can_boot_ap()) - msleep(200); -#endif - - return EC_SUCCESS; -} diff --git a/power/mt817x.c b/power/mt817x.c deleted file mode 100644 index e7e23605f2..0000000000 --- a/power/mt817x.c +++ /dev/null @@ -1,818 +0,0 @@ -/* Copyright 2015 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. - */ - -/* - * MT817x SoC power sequencing module for Chrome EC - * - * This implements the following features: - * - * - Cold reset powers on the AP - * - * When powered off: - * - Press pwron turns on the AP - * - Hold pwron turns on the AP, and then 8s later turns it off and leaves - * it off until pwron is released and pressed again - * - * When powered on: - * - The PMIC PWRON signal is released <= 1 second after the power button is - * released - * - Holding pwron for 8s powers off the AP - * - Pressing and releasing pwron within that 8s is ignored - * - If POWER_GOOD is dropped by the AP, then we power the AP off - * - If SUSPEND_L goes low, enter suspend mode. - * - */ - -#include "battery.h" -#include "chipset.h" /* ./common/chipset.c implements chipset functions too */ -#include "common.h" -#include "gpio.h" -#include "hooks.h" -#include "keyboard_scan.h" -#include "lid_switch.h" -#include "power.h" -#include "power_button.h" -#include "power_led.h" -#include "system.h" -#include "task.h" -#include "test_util.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -#define INT_BOTH_PULL_UP (GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH) - -/* masks for power signals */ -#define IN_POWER_GOOD POWER_SIGNAL_MASK(MTK_POWER_GOOD) -#define IN_SUSPEND POWER_SIGNAL_MASK(MTK_SUSPEND_ASSERTED) - -/* Long power key press to force shutdown */ -#define DELAY_FORCE_SHUTDOWN (8000 * MSEC) /* 8 seconds */ - -/* - * The power signal from SoC should be kept at least 50ms. - */ -#define POWER_DEBOUNCE_TIME (50 * MSEC) - -/* - * The suspend signal from SoC should be kept at least 50ms. - */ -#define SUSPEND_DEBOUNCE_TIME (50 * MSEC) - -/* - * The time to bootup the PMIC from power-off to power-on. - */ -#define PMIC_PWRON_PRESS_TIME (5000 * MSEC) - -/* - * The minimum time to assert the PMIC THERM pin is 32us. However, - * it needs to be extended to about 50ms to let the 5V rail - * dissipate fully. - */ -#define PMIC_THERM_HOLD_TIME (50 * MSEC) - -/* - * If the power key is pressed to turn on, then held for this long, we - * power off. - * - * Normal case: User releases power button and chipset_task() goes - * into the inner loop, waiting for next event to occur (power button - * press or POWER_GOOD == 0). - */ -#define DELAY_SHUTDOWN_ON_POWER_HOLD (8000 * MSEC) /* 8 seconds */ - -/* - * The hold time for pulling down the PMIC_WARM_RESET_H pin so that - * the AP can entery the recovery mode (flash SPI flash from USB). - */ -#define PMIC_WARM_RESET_H_HOLD_TIME (4 * MSEC) - -/* - * The hold time for pulling down the SYSTEM_POWER_H pin. - */ -#define PMIC_COLD_RESET_L_HOLD_TIME \ - (SUSPEND_DEBOUNCE_TIME + POWER_DEBOUNCE_TIME + (20 * MSEC)) - -/* - * The first time the PMIC sees power (AC or battery) it needs 200ms (+/-12% - * oscillator tolerance) for the RTC startup. In addition there is a startup - * time of approx. 0.5msec until V2_5 regulator starts up. */ -#define PMIC_RTC_STARTUP (225 * MSEC) - -/* Wait for 5V power source stable */ -#define PMIC_WAIT_FOR_5V_POWER_GOOD (1 * MSEC) - -/* - * If POWER_GOOD is lost, wait for PMIC to turn off its power completely - * before we turn off VBAT by set_system_power(0) - */ -#define PMIC_POWER_OFF_DELAY (50 * MSEC) - -/* TODO(crosbug.com/p/25047): move to HOOK_POWER_BUTTON_CHANGE */ -/* 1 if the power button was pressed last time we checked */ -static char power_button_was_pressed; - -/* 1 if lid-open event has been detected */ -static char lid_opened; - -/* time where we will power off, if power button still held down */ -static timestamp_t power_off_deadline; - -/* force AP power on (used for recovery keypress) */ -static int auto_power_on; - -enum power_request_t { - POWER_REQ_NONE, - POWER_REQ_OFF, - POWER_REQ_ON, - - POWER_REQ_COUNT, -}; - -static enum power_request_t power_request; - -/** - * Return values for check_for_power_off_event(). - */ -enum power_off_event_t { - POWER_OFF_CANCEL, - POWER_OFF_BY_POWER_BUTTON_PRESSED, - POWER_OFF_BY_LONG_PRESS, - POWER_OFF_BY_POWER_GOOD_LOST, - POWER_OFF_BY_POWER_REQ, - - POWER_OFF_EVENT_COUNT, -}; - -/** - * Return values for check_for_power_on_event(). - */ -enum power_on_event_t { - POWER_ON_CANCEL, - POWER_ON_BY_IN_POWER_GOOD, - POWER_ON_BY_AUTO_POWER_ON, - POWER_ON_BY_LID_OPEN, - POWER_ON_BY_POWER_BUTTON_PRESSED, - POWER_ON_BY_POWER_REQ_NONE, - - POWER_ON_EVENT_COUNT, -}; - -/** - * Parameters of mtk_backlight_override(). - */ -enum blacklight_override_t { - MTK_BACKLIGHT_FORCE_OFF, - MTK_BACKLIGHT_CONTROL_BY_SOC, - - MTK_BACKLIGHT_OVERRIDE_COUNT, -}; - -/* Forward declaration */ -static void chipset_turn_off_power_rails(void); - -/** - * Check the suspend signal is on after SUSPEND_DEBOUNCE_TIME to avoid transient - * state. - * - * @return non-zero if SUSPEND is asserted. - */ -static int is_suspend_asserted(void) -{ -#ifdef BOARD_OAK - if ((power_get_signals() & IN_SUSPEND) && - (system_get_board_version() < 4)) - usleep(SUSPEND_DEBOUNCE_TIME); -#endif - - return power_get_signals() & IN_SUSPEND; -} - -/** - * Check the suspend signal is off after SUSPEND_DEBOUNCE_TIME to avoid - * transient state. - * - * @return non-zero if SUSPEND is deasserted. - */ -static int is_suspend_deasserted(void) -{ -#ifdef BOARD_OAK - if (!(power_get_signals() & IN_SUSPEND) && - (system_get_board_version() < 4)) - usleep(SUSPEND_DEBOUNCE_TIME); -#endif - - return !(power_get_signals() & IN_SUSPEND); -} - -/** - * Check power good signal is on after POWER_DEBOUNCE_TIME to avoid transient - * state. - * - * @return non-zero if POWER_GOOD is asserted. - */ -static int is_power_good_asserted(void) -{ - if (!gpio_get_level(GPIO_SYSTEM_POWER_H)) - return 0; -#ifdef BOARD_OAK - else if ((power_get_signals() & IN_POWER_GOOD) && - (system_get_board_version() < 4)) - usleep(POWER_DEBOUNCE_TIME); -#endif - - return power_get_signals() & IN_POWER_GOOD; -} - -/** - * Check power good signal is off after POWER_DEBOUNCE_TIME to avoid transient - * state. - * - * @return non-zero if POWER_GOOD is deasserted. - */ -static int is_power_good_deasserted(void) -{ -#ifdef BOARD_OAK - /* - * Warm reset key from servo board lets the POWER_GOOD signal - * deasserted temporarily (about 1~2 seconds) on rev4. - * In order to detect this case, check the AP_RESET_L status, - * ignore the transient state if reset key is pressing. - */ - if (system_get_board_version() >= 4) { - if (0 == gpio_get_level(GPIO_AP_RESET_L)) - return 0; - } else { - if (!(power_get_signals() & IN_POWER_GOOD)) - usleep(POWER_DEBOUNCE_TIME); - } -#endif - if (0 == gpio_get_level(GPIO_AP_RESET_L)) - return 0; - - return !(power_get_signals() & IN_POWER_GOOD); -} - -/** - * Set the system power signal. - * - * @param asserted off (=0) or on (=1) - */ -static void set_system_power(int asserted) -{ - CPRINTS("set_system_power(%d)", asserted); - gpio_set_level(GPIO_SYSTEM_POWER_H, asserted); -} - -/** - * Set the PMIC PWRON signal. - * - * Note that asserting requires holding for PMIC_PWRON_PRESS_TIME. - * - * @param asserted Assert (=1) or deassert (=0) the signal. This is the - * logical level of the pin, not the physical level. - */ -static void set_pmic_pwron(int asserted) -{ - timestamp_t poll_deadline; - /* Signal is active-high */ - CPRINTS("set_pmic_pwron(%d)", asserted); - /* Oak rev1 power-on sequence: - * raise GPIO_SYSTEM_POWER_H - * wait for 5V power good, timeout 1 second - */ - if (asserted) { - set_system_power(asserted); - poll_deadline = get_time(); - poll_deadline.val += SECOND; - while (asserted && !gpio_get_level(GPIO_5V_POWER_GOOD) && - get_time().val < poll_deadline.val) - usleep(PMIC_WAIT_FOR_5V_POWER_GOOD); - if (!gpio_get_level(GPIO_5V_POWER_GOOD)) - CPRINTS("5V power not ready"); - } - - gpio_set_level(GPIO_PMIC_PWRON_H, asserted); -} - -/** - * Set the WARM RESET signal. - * - * @param asserted off (=0) or on (=1) - */ -static void set_warm_reset(int asserted) -{ - board_set_ap_reset(asserted); -} - -/** - * Check for some event triggering the shutdown. - * - * It can be either a long power button press or a shutdown triggered from the - * AP and detected by reading POWER_GOOD. - * - * @return non-zero if a shutdown should happen, 0 if not - */ -static int check_for_power_off_event(void) -{ - timestamp_t now; - int pressed = 0; - - /* - * Check for power button press. - */ - if (power_button_is_pressed()) { - pressed = POWER_OFF_BY_POWER_BUTTON_PRESSED; - } else if (power_request == POWER_REQ_OFF) { - power_request = POWER_REQ_NONE; - /* return non-zero for shudown down */ - return POWER_OFF_BY_POWER_REQ; - } - - now = get_time(); - if (pressed) { - if (!power_button_was_pressed) { - power_off_deadline.val = now.val + DELAY_FORCE_SHUTDOWN; - CPRINTS("power waiting for long press %u", - power_off_deadline.le.lo); - /* Ensure we will wake up to check the power key */ - timer_arm(power_off_deadline, TASK_ID_CHIPSET); - } else if (timestamp_expired(power_off_deadline, &now)) { - power_off_deadline.val = 0; - CPRINTS("power off after long press now=%u, %u", - now.le.lo, power_off_deadline.le.lo); - return POWER_OFF_BY_LONG_PRESS; - } - } else if (power_button_was_pressed) { - CPRINTS("power off cancel"); - timer_cancel(TASK_ID_CHIPSET); - } - - power_button_was_pressed = pressed; - - /* POWER_GOOD released by AP : shutdown immediate */ - if (is_power_good_deasserted()) { - /* - * Cancel long press timer if power is lost and the power button - * still press, otherwise EC will crash. - */ - if (power_button_was_pressed) - timer_cancel(TASK_ID_CHIPSET); - - CPRINTS("POWER_GOOD is lost"); - return POWER_OFF_BY_POWER_GOOD_LOST; - } - - return POWER_OFF_CANCEL; -} - -/** - * Set the LCD backlight enable pin and override the signal from SoC. - * - * @param asserted MTK_BACKLIGHT_FORCE_OFF, force off the panel backlight - * MTK_BACKLIGHT_CONTROL_BY_SOC, leave the control to SOC - */ -static void mtk_backlight_override(enum blacklight_override_t asserted) -{ - /* Signal is active-low */ - gpio_set_level(GPIO_EC_BL_OVERRIDE, !asserted); -} - -static void mtk_lid_event(void) -{ - enum blacklight_override_t bl_override; - - /* Override the panel backlight enable signal from SoC, - * force the backlight off on lid close. - */ - bl_override = lid_is_open() ? - MTK_BACKLIGHT_CONTROL_BY_SOC : - MTK_BACKLIGHT_FORCE_OFF; - mtk_backlight_override(bl_override); - - /* Power task only cares about lid-open events */ - if (!lid_is_open()) - return; - - lid_opened = 1; - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, mtk_lid_event, HOOK_PRIO_DEFAULT); - -enum power_state power_chipset_init(void) -{ - int init_power_state; - uint32_t reset_flags = system_get_reset_flags(); - - /* - * Force the AP shutdown unless we are doing SYSJUMP. Otherwise, - * the AP could stay in strange state. - */ - if (!(reset_flags & EC_RESET_FLAG_SYSJUMP)) { - CPRINTS("not sysjump; forcing AP shutdown"); - chipset_turn_off_power_rails(); - - /* - * The warm reset triggers AP into the recovery mode ( - * flash SPI from USB). - */ - chipset_reset(CHIPSET_RESET_UNKNOWN); - - init_power_state = POWER_G3; - } else { - /* In the SYSJUMP case, we check if the AP is on */ - if (is_power_good_asserted()) { - CPRINTS("SOC ON"); - /* - * Check and release PMIC power button signal, - * if it's deferred callback function is not triggered - * in RO before SYSJUMP. - */ - if (gpio_get_level(GPIO_PMIC_PWRON_H)) - set_pmic_pwron(0); - - init_power_state = POWER_S0; - if (is_suspend_asserted()) - enable_sleep(SLEEP_MASK_AP_RUN); - else - disable_sleep(SLEEP_MASK_AP_RUN); - } else { - CPRINTS("SOC OFF"); - init_power_state = POWER_G3; - enable_sleep(SLEEP_MASK_AP_RUN); - } - } - - /* Leave power off only if requested by reset flags */ - if (!(reset_flags & EC_RESET_FLAG_AP_OFF) && - !(reset_flags & EC_RESET_FLAG_SYSJUMP)) { - CPRINTS("reset_flag 0x%x", reset_flags); - auto_power_on = 1; - } - - /* - * Some batteries use clock stretching feature, which requires - * more time to be stable. See http://crosbug.com/p/28289 - */ - battery_wait_for_stable(); - - return init_power_state; -} - -/*****************************************************************************/ -/* Chipset interface */ - -static void chipset_turn_off_power_rails(void) -{ - /* Release the power on pin, if it was asserted */ - set_pmic_pwron(0); - - /* system power off */ - usleep(PMIC_POWER_OFF_DELAY); - set_system_power(0); -} - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s: %d", __func__, reason); - report_ap_reset(reason); - - chipset_turn_off_power_rails(); - - /* clean-up internal variable */ - power_request = POWER_REQ_NONE; -} - -/*****************************************************************************/ - -/** - * Power off the AP - */ -static void power_off(void) -{ - /* Check the power off status */ - if (!gpio_get_level(GPIO_SYSTEM_POWER_H)) - return; - - /* Call hooks before we drop power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - /* switch off all rails */ - chipset_turn_off_power_rails(); - - /* Change SUSPEND_L pin to high-Z to reduce power draw. */ - gpio_set_flags(power_signal_list[MTK_SUSPEND_ASSERTED].gpio, - GPIO_INPUT); - - /* Change EC_INT to low */ - gpio_set_level(GPIO_EC_INT_L, 0); - - lid_opened = 0; - enable_sleep(SLEEP_MASK_AP_RUN); -#ifdef HAS_TASK_POWERLED - powerled_set_state(POWERLED_STATE_OFF); -#endif - CPRINTS("power shutdown complete"); - - /* Call hooks after we drop power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); -} - -/** - * Check if there has been a power-on event - * - * This checks all power-on event signals and returns non-zero if any have been - * triggered (with debounce taken into account). - * - * @return non-zero if there has been a power-on event, 0 if not. - */ -static int check_for_power_on_event(void) -{ - int ap_off_flag; - - ap_off_flag = system_get_reset_flags() & EC_RESET_FLAG_AP_OFF; - system_clear_reset_flags(EC_RESET_FLAG_AP_OFF); - /* check if system is already ON */ - if (is_power_good_asserted()) { - if (ap_off_flag) { - CPRINTS("system is on, but EC_RESET_FLAG_AP_OFF is on"); - return POWER_ON_CANCEL; - } else { - CPRINTS("system is on, thus clear " "auto_power_on"); - /* no need to arrange another power on */ - auto_power_on = 0; - return POWER_ON_BY_IN_POWER_GOOD; - } - } else { - if (ap_off_flag) { - CPRINTS("EC_RESET_FLAG_AP_OFF is on"); - power_off(); - return POWER_ON_CANCEL; - } - - CPRINTS("POWER_GOOD is not asserted"); - } - - /* power on requested at EC startup for recovery */ - if (auto_power_on) { - auto_power_on = 0; - return POWER_ON_BY_AUTO_POWER_ON; - } - - /* Check lid open */ - if (lid_opened) { - lid_opened = 0; - return POWER_ON_BY_LID_OPEN; - } - - /* check for power button press */ - if (power_button_is_pressed()) - return POWER_ON_BY_POWER_BUTTON_PRESSED; - - if (power_request == POWER_REQ_ON) { - power_request = POWER_REQ_NONE; - return POWER_ON_BY_POWER_REQ_NONE; - } - - return POWER_OFF_CANCEL; -} - -void release_pmic_pwron_deferred(void) -{ - /* Release PMIC power button */ - set_pmic_pwron(0); -} -DECLARE_DEFERRED(release_pmic_pwron_deferred); - -/** - * Power on the AP - */ -static void power_on(void) -{ - uint64_t t; - - /* Set pull-up and enable interrupt */ - gpio_set_flags(power_signal_list[MTK_SUSPEND_ASSERTED].gpio, - GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH); - - /* Make sure we de-assert and GPIO_PMIC_WARM_RESET_H pin. */ - set_warm_reset(0); - - /* - * Before we push PMIC power button, wait for the PMI RTC ready, which - * takes PMIC_RTC_STARTUP from the AC/battery is plugged in. - */ - t = get_time().val; - if (t < PMIC_RTC_STARTUP) { - uint32_t wait = PMIC_RTC_STARTUP - t; - - CPRINTS("wait for %dms for PMIC RTC start-up", wait / MSEC); - usleep(wait); - } - - /* - * When power_on() is called, we are at S5S3. Initialize components - * to ready state before AP is up. - */ - hook_notify(HOOK_CHIPSET_PRE_INIT); - - /* Push the power button */ - set_pmic_pwron(1); - hook_call_deferred(&release_pmic_pwron_deferred_data, - PMIC_PWRON_PRESS_TIME); - - /* enable interrupt */ - gpio_set_flags(GPIO_SUSPEND_L, INT_BOTH_PULL_UP); - -#ifdef BOARD_OAK - if (system_get_board_version() <= 3) - gpio_set_flags(GPIO_EC_INT_L, GPIO_OUTPUT | GPIO_OUT_HIGH); - else - gpio_set_flags(GPIO_EC_INT_L, GPIO_ODR_HIGH); -#else - gpio_set_flags(GPIO_EC_INT_L, GPIO_ODR_HIGH); -#endif - - disable_sleep(SLEEP_MASK_AP_RUN); -#ifdef HAS_TASK_POWERLED - powerled_set_state(POWERLED_STATE_ON); -#endif - /* Call hooks now that AP is running */ - hook_notify(HOOK_CHIPSET_STARTUP); - - CPRINTS("AP running ..."); -} - -void chipset_reset(enum chipset_reset_reason reason) -{ - CPRINTS("%s: %d", __func__, reason); - report_ap_reset(reason); - - set_warm_reset(1); - usleep(PMIC_WARM_RESET_H_HOLD_TIME); - /* deassert the reset signals */ - set_warm_reset(0); -} - -enum power_state power_handle_state(enum power_state state) -{ - int value; - static int boot_from_g3; - - switch (state) { - case POWER_G3: - boot_from_g3 = check_for_power_on_event(); - if (boot_from_g3) - return POWER_G3S5; - break; - - case POWER_G3S5: - return POWER_S5; - - case POWER_S5: - if (boot_from_g3) { - value = boot_from_g3; - boot_from_g3 = 0; - } else { - value = check_for_power_on_event(); - } - - if (value) { - CPRINTS("power on %d", value); - return POWER_S5S3; - } - return state; - - case POWER_S5S3: - power_on(); - if (power_wait_signals(IN_POWER_GOOD) == EC_SUCCESS) { - CPRINTS("POWER_GOOD seen"); - power_button_was_pressed = 0; - return POWER_S3; - } else { - CPRINTS("POWER_GOOD not seen in time"); - } - set_pmic_pwron(0); - return POWER_S5; - - case POWER_S3: - if (is_power_good_deasserted()) { - power_off(); - return POWER_S3S5; - } else if (is_suspend_deasserted()) - return POWER_S3S0; - return state; - - case POWER_S3S0: - disable_sleep(SLEEP_MASK_AP_RUN); -#ifdef HAS_TASK_POWERLED - powerled_set_state(POWERLED_STATE_ON); -#endif - hook_notify(HOOK_CHIPSET_RESUME); - return POWER_S0; - - case POWER_S0: - value = check_for_power_off_event(); - if (value) { - CPRINTS("power off %d", value); - power_off(); - return POWER_S0S3; - } else if (is_suspend_asserted()) - return POWER_S0S3; - return state; - - case POWER_S0S3: -#ifdef HAS_TASK_POWERLED - if (lid_is_open()) - powerled_set_state(POWERLED_STATE_SUSPEND); - else - powerled_set_state(POWERLED_STATE_OFF); -#endif - /* - * if the power button is pressing, we need cancel the long - * press timer, otherwise EC will crash. - */ - if (power_button_was_pressed) - timer_cancel(TASK_ID_CHIPSET); - - /* Call hooks here since we don't know it prior to AP suspend */ - hook_notify(HOOK_CHIPSET_SUSPEND); - enable_sleep(SLEEP_MASK_AP_RUN); - return POWER_S3; - - case POWER_S3S5: - power_button_wait_for_release(-1); - power_button_was_pressed = 0; - return POWER_S5; - - case POWER_S5G3: - return POWER_G3; - } - - return state; -} - -static void powerbtn_mtk_changed(void) -{ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, powerbtn_mtk_changed, HOOK_PRIO_DEFAULT); - -/*****************************************************************************/ -/* Console debug command */ - -static const char *power_req_name[POWER_REQ_COUNT] = { - "none", - "off", - "on", -}; - -/* Power states that we can report */ -enum power_state_t { - PSTATE_UNKNOWN, - PSTATE_OFF, - PSTATE_SUSPEND, - PSTATE_ON, - - PSTATE_COUNT, -}; - -static const char * const state_name[] = { - "unknown", - "off", - "suspend", - "on", -}; - -static int command_power(int argc, char **argv) -{ - int v; - - if (argc < 2) { - enum power_state_t state; - - state = PSTATE_UNKNOWN; - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - state = PSTATE_OFF; - if (chipset_in_state(CHIPSET_STATE_SUSPEND)) - state = PSTATE_SUSPEND; - if (chipset_in_state(CHIPSET_STATE_ON)) - state = PSTATE_ON; - ccprintf("%s\n", state_name[state]); - - return EC_SUCCESS; - } - - if (!parse_bool(argv[1], &v)) - return EC_ERROR_PARAM1; - - power_request = v ? POWER_REQ_ON : POWER_REQ_OFF; - ccprintf("Requesting power %s\n", power_req_name[power_request]); - task_wake(TASK_ID_CHIPSET); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(power, command_power, - "on/off", - "Turn AP power on/off"); 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 diff --git a/power/mt8192.c b/power/mt8192.c deleted file mode 100644 index 323994faea..0000000000 --- a/power/mt8192.c +++ /dev/null @@ -1,543 +0,0 @@ -/* Copyright 2020 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. - */ - -/* - * MT8192 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 pwron within that 8s is ignored - */ - -#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_L) -#define IN_PGOOD_PMIC POWER_SIGNAL_MASK(PMIC_PWR_GOOD) -#define IN_AP_WDT_ASSERTED POWER_SIGNAL_MASK(AP_WDT_ASSERTED) - -/* 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 */ -#define FORCED_SHUTDOWN_DELAY (8 * SECOND) - -/* Long power key press to boot from S5/G3 state. */ -#ifndef POWERBTN_BOOT_DELAY -#define POWERBTN_BOOT_DELAY (10 * MSEC) -#endif -#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) - -/* Time delay in G3 to deassert EN_PP1800_S5_L */ -#define EN_PP1800_S5_L_DEASSERT_TIME (20 * MSEC) - -/* - * Time delay for AP on/off the AP_EC_WDT when received SYS_RST_ODL. - * Generally it can be done within 3 ms. - */ -#define AP_EC_WDT_TIMEOUT (100 * MSEC) - -/* 30 ms for hard reset, we hold it longer to prevent TPM false alarm. */ -#define SYS_RST_PULSE_LENGTH (50 * MSEC) - -/* power signal list. Must match order of enum power_signal. */ -const struct power_signal_info power_signal_list[] = { - {GPIO_PMIC_EC_PWRGD, POWER_SIGNAL_ACTIVE_HIGH, "PMIC_PWR_GOOD"}, - {GPIO_AP_IN_SLEEP_L, POWER_SIGNAL_ACTIVE_LOW, "AP_IN_S3_L"}, - {GPIO_AP_EC_WATCHDOG_L, POWER_SIGNAL_ACTIVE_LOW, "AP_WDT_ASSERTED"}, -}; -BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); - -static int forcing_shutdown; - -static void watchdog_interrupt_deferred(void) -{ - chipset_reset(CHIPSET_RESET_AP_WATCHDOG); -} -DECLARE_DEFERRED(watchdog_interrupt_deferred); - -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); -} - -/* - * 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_SYS_RST_ODL, 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) -{ - /* Pass AP_EC_WATCHDOG_L signal to PMIC */ - GPIO_SET_LEVEL(GPIO_EC_PMIC_WATCHDOG_L, gpio_get_level(signal)); - - /* Update power signals */ - power_signal_interrupt(signal); - - /* - * case 1: PMIC is good, WDT asserts, and EC is not asserting - * SYS_RST_ODL. This is AP initiated real WDT. - */ - if (gpio_get_level(GPIO_SYS_RST_ODL) && - power_get_signals() & IN_PGOOD_PMIC && - power_get_signals() & IN_AP_WDT_ASSERTED) - hook_call_deferred(&watchdog_interrupt_deferred_data, 0); - - /* - * case 2&3: Fall through. The chipset_reset should have been - * invoked. - */ -} - -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); - -void chipset_reset(enum chipset_reset_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 */ - -enum power_state power_chipset_init(void) -{ - int exit_hard_off = 1; - - /* Enable reboot / sleep control inputs from AP */ - gpio_enable_interrupt(GPIO_AP_EC_WARM_RST_REQ); - gpio_enable_interrupt(GPIO_AP_IN_SLEEP_L); - - if (system_get_reset_flags() & EC_RESET_FLAG_SYSJUMP) { - if ((power_get_signals() & IN_ALL_S0) == IN_ALL_S0) { - disable_sleep(SLEEP_MASK_AP_RUN); - power_signal_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) { - 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(); - - /* Start from S5 if the PMIC is already up. */ - if (power_get_signals() & IN_PGOOD_PMIC) { - /* Force shutdown from S5 if the PMIC is already up. */ - if (!exit_hard_off) - forcing_shutdown = 1; - return POWER_S5; - } - - return POWER_G3; -} - -enum power_state power_handle_state(enum power_state state) -{ - /* 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; - - switch (state) { - case POWER_G3: - - /* 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: - /* - * 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). - * - * Note: We might run twice at this line because we - * deasserts SYS_RST_ODL in S5->S3 and then WDT interrupt - * handler sets the wake event for chipset_task. This should be - * no harm, but to prevent misunderstanding in the console, we - * check EC_PMIC_EN_ODL before set. - */ - if (gpio_get_level(GPIO_EC_PMIC_EN_ODL)) { - CPRINTS("Forcing shutdown with long press."); - GPIO_SET_LEVEL(GPIO_EC_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; - - /* 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_EC_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_EC_PMIC_EN_ODL, 0); - msleep(PMIC_EN_PULSE_MS); - GPIO_SET_LEVEL(GPIO_EC_PMIC_EN_ODL, 1); - } - - /* - * 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; - } - - /* Release AP reset and waits for AP pulling WDT up. */ - power_signal_enable_interrupt(GPIO_AP_EC_WATCHDOG_L); - GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 1); - if (power_wait_mask_signals_timeout(0, IN_AP_WDT_ASSERTED, - AP_EC_WDT_TIMEOUT)) { - if (s5s3_retry) { - s5s3_retry = 0; - return POWER_S5S3; - } - /* Give up, go back to G3. */ - return POWER_S5G3; - } - - /* 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_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 */ - - /* - * 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); - - /* - * Assert SYS_RST_ODL, and waits for AP finishing epilogue and - * asserting WDT. - */ - GPIO_SET_LEVEL(GPIO_SYS_RST_ODL, 0); - if (EC_ERROR_TIMEOUT == - power_wait_signals_timeout(IN_AP_WDT_ASSERTED, - AP_EC_WDT_TIMEOUT)) { - CPRINTS("Timeout waitting AP watchdog, force if off"); - GPIO_SET_LEVEL(GPIO_EC_PMIC_WATCHDOG_L, 0); - } - power_signal_disable_interrupt(GPIO_AP_EC_WATCHDOG_L); - - /* 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_EC_PMIC_EN_ODL, 1); - - /* If PMIC is not off, go back to S5 and try again. */ - if (power_get_signals() & IN_PGOOD_PMIC) - return POWER_S5; - - return POWER_G3; - } - - 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/power/qcom.c b/power/qcom.c deleted file mode 100644 index ef9e329111..0000000000 --- a/power/qcom.c +++ /dev/null @@ -1,1169 +0,0 @@ -/* Copyright 2019 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. - */ - -/* - * SC7X80 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 pressed again - * - Lid open turns on the AP - * - * When powered on: - * - Holding power button for 8s powers off the AP - * - Pressing and releasing pwron within that 8s is ignored - * - If POWER_GOOD is dropped by the AP, then we power the AP off - */ - -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "gpio.h" -#include "hooks.h" -#include "lid_switch.h" -#include "power.h" -#include "power/qcom.h" -#include "power_button.h" -#include "system.h" -#include "task.h" -#include "util.h" - -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -/* Power signal list. Must match order of enum power_signal. */ -const struct power_signal_info power_signal_list[] = { - [SC7X80_AP_RST_ASSERTED] = { - GPIO_AP_RST_L, - POWER_SIGNAL_ACTIVE_LOW | POWER_SIGNAL_DISABLE_AT_BOOT, - "AP_RST_ASSERTED", - }, - [SC7X80_PS_HOLD] = { - GPIO_PS_HOLD, - POWER_SIGNAL_ACTIVE_HIGH, - "PS_HOLD", - }, - [SC7X80_POWER_GOOD] = { - GPIO_POWER_GOOD, - POWER_SIGNAL_ACTIVE_HIGH, - "POWER_GOOD", - }, - [SC7X80_AP_SUSPEND] = { - GPIO_AP_SUSPEND, - POWER_SIGNAL_ACTIVE_HIGH, - "AP_SUSPEND", - }, -#ifdef CONFIG_CHIPSET_SC7180 - [SC7X80_WARM_RESET] = { - GPIO_WARM_RESET_L, - POWER_SIGNAL_ACTIVE_HIGH, - "WARM_RESET_L", - }, - [SC7X80_DEPRECATED_AP_RST_REQ] = { - GPIO_DEPRECATED_AP_RST_REQ, - POWER_SIGNAL_ACTIVE_HIGH, - "DEPRECATED_AP_RST_REQ", - }, -#endif /* defined(CONFIG_CHIPSET_SC7180) */ -}; -BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); - -/* Masks for power signals */ -#define IN_POWER_GOOD POWER_SIGNAL_MASK(SC7X80_POWER_GOOD) -#define IN_AP_RST_ASSERTED POWER_SIGNAL_MASK(SC7X80_AP_RST_ASSERTED) -#define IN_SUSPEND POWER_SIGNAL_MASK(SC7X80_AP_SUSPEND) - - -/* Long power key press to force shutdown */ -#define DELAY_FORCE_SHUTDOWN (8 * SECOND) - -/* - * If the power button is pressed to turn on, then held for this long, we - * power off. - * - * Normal case: User releases power button and chipset_task() goes - * into the inner loop, waiting for next event to occur (power button - * press or POWER_GOOD == 0). - */ -#define DELAY_SHUTDOWN_ON_POWER_HOLD (8 * SECOND) - -/* - * After trigger PMIC power sequence, how long it triggers AP to turn on - * or off. Observed that the worst case is ~150ms. Pick a safe vale. - */ -#define PMIC_POWER_AP_RESPONSE_TIMEOUT (350 * MSEC) - -/* - * After force off the switch cap, how long the PMIC/AP totally off. - * Observed that the worst case is 2s. Pick a safe vale. - */ -#define FORCE_OFF_RESPONSE_TIMEOUT (4 * SECOND) - -/* Wait for polling the AP on signal */ -#define PMIC_POWER_AP_WAIT (1 * MSEC) - -/* The length of an issued low pulse to the PMIC_RESIN_L signal */ -#define PMIC_RESIN_PULSE_LENGTH (20 * MSEC) - -/* The timeout of the check if the system can boot AP */ -#define CAN_BOOT_AP_CHECK_TIMEOUT (1500 * MSEC) - -/* Wait for polling if the system can boot AP */ -#define CAN_BOOT_AP_CHECK_WAIT (200 * MSEC) - -/* The timeout of the check if the switchcap outputs good voltage */ -#define SWITCHCAP_PG_CHECK_TIMEOUT (100 * MSEC) - -/* Wait for polling if the switchcap outputs good voltage */ -#define SWITCHCAP_PG_CHECK_WAIT (6 * MSEC) - -/* - * Delay between power-on the system and power-on the PMIC. - * Some latest PMIC firmware needs this delay longer, for doing a cold - * reboot. - * - * Measured on Herobrine IOB + Trogdor MLB, the delay takes ~200ms. Set - * it with margin. - */ -#define SYSTEM_POWER_ON_DELAY (300 * MSEC) - -/* - * Delay between the PMIC power drop and power-off the system. - * Qualcomm measured the entire POFF duration is around 70ms. Setting - * this delay to the same value as the above power-on sequence, which - * has much safer margin. - */ -#define PMIC_POWER_OFF_DELAY (150 * MSEC) - -/* The AP_RST_L transition count of a normal AP warm reset */ -#define EXPECTED_AP_RST_TRANSITIONS 3 - -/* - * The timeout of waiting the next AP_RST_L transition. We measured - * the interval between AP_RST_L transitions is 130ms ~ 150ms. Pick - * a safer value. - */ -#define AP_RST_TRANSITION_TIMEOUT (450 * MSEC) - -/* TODO(crosbug.com/p/25047): move to HOOK_POWER_BUTTON_CHANGE */ -/* 1 if the power button was pressed last time we checked */ -static char power_button_was_pressed; - -/* 1 if lid-open event has been detected */ -static char lid_opened; - -/* Time where we will power off, if power button still held down */ -static timestamp_t power_off_deadline; - -/* Force AP power on (used for recovery keypress) */ -static int auto_power_on; - -enum power_request_t { - POWER_REQ_NONE, - POWER_REQ_OFF, - POWER_REQ_ON, - POWER_REQ_RESET, - - POWER_REQ_COUNT, -}; - -static enum power_request_t power_request; - -/** - * Return values for check_for_power_off_event(). - */ -enum power_off_event_t { - POWER_OFF_CANCEL, - POWER_OFF_BY_POWER_BUTTON_PRESSED, - POWER_OFF_BY_LONG_PRESS, - POWER_OFF_BY_POWER_GOOD_LOST, - POWER_OFF_BY_POWER_REQ_OFF, - POWER_OFF_BY_POWER_REQ_RESET, - - POWER_OFF_EVENT_COUNT, -}; - -/** - * Return values for check_for_power_on_event(). - */ -enum power_on_event_t { - POWER_ON_CANCEL, - POWER_ON_BY_AUTO_POWER_ON, - POWER_ON_BY_LID_OPEN, - POWER_ON_BY_POWER_BUTTON_PRESSED, - POWER_ON_BY_POWER_REQ_ON, - POWER_ON_BY_POWER_REQ_RESET, - - POWER_ON_EVENT_COUNT, -}; - -#ifdef CONFIG_CHIPSET_RESET_HOOK -static int ap_rst_transitions; - -static void notify_chipset_reset(void) -{ - if (ap_rst_transitions != EXPECTED_AP_RST_TRANSITIONS) - CPRINTS("AP_RST_L transitions not expected: %d", - ap_rst_transitions); - - ap_rst_transitions = 0; - hook_notify(HOOK_CHIPSET_RESET); -} -DECLARE_DEFERRED(notify_chipset_reset); -#endif - -void chipset_ap_rst_interrupt(enum gpio_signal signal) -{ -#ifdef CONFIG_CHIPSET_RESET_HOOK - int delay; - - /* - * Only care the raising edge and AP in S0/S3. The single raising edge - * of AP power-on during S5S3 is ignored. - */ - if (gpio_get_level(GPIO_AP_RST_L) && - chipset_in_state(CHIPSET_STATE_ON | CHIPSET_STATE_SUSPEND)) { - ap_rst_transitions++; - if (ap_rst_transitions >= EXPECTED_AP_RST_TRANSITIONS) { - /* - * Reach the expected transition count. AP is booting - * up. Notify HOOK_CHIPSET_RESET immediately. - */ - delay = 0; - } else { - /* - * Should have more transitions of the AP_RST_L signal. - * In case the AP_RST_L signal is not toggled, still - * notify HOOK_CHIPSET_RESET. - */ - delay = AP_RST_TRANSITION_TIMEOUT; - } - hook_call_deferred(¬ify_chipset_reset_data, delay); - } -#endif - power_signal_interrupt(signal); -} - -/* Issue a request to initiate a reset sequence */ -static void request_cold_reset(void) -{ - power_request = POWER_REQ_RESET; - task_wake(TASK_ID_CHIPSET); -} - -#ifdef CONFIG_CHIPSET_SC7180 - -/* 1 if AP_RST_L and PS_HOLD is overdriven by EC */ -static char ap_rst_overdriven; - -void chipset_warm_reset_interrupt(enum gpio_signal signal) -{ - /* - * The warm_reset signal is pulled-up by a rail from PMIC. If the - * warm_reset drops, it means: - * * Servo or Cr50 holds the signal, or - * * its pull-up rail POWER_GOOD drops. - */ - if (!gpio_get_level(GPIO_WARM_RESET_L)) { - if (gpio_get_level(GPIO_POWER_GOOD)) { - /* - * Servo or Cr50 holds the WARM_RESET_L signal. - * - * Overdrive AP_RST_L to hold AP. Overdrive PS_HOLD to - * emulate AP being up to trick the PMIC into thinking - * there’s nothing weird going on. - */ - ap_rst_overdriven = 1; - gpio_set_flags(GPIO_PS_HOLD, GPIO_INT_BOTH | - GPIO_SEL_1P8V | GPIO_OUT_HIGH); - gpio_set_flags(GPIO_AP_RST_L, GPIO_INT_BOTH | - GPIO_SEL_1P8V | GPIO_OUT_LOW); - } - /* Ignore the else clause, the pull-up rail drops. */ - } else { - if (ap_rst_overdriven) { - /* - * Servo or Cr50 releases the WARM_RESET_L signal. - * - * Cold reset the PMIC, doing S0->S5->S0 transition, - * by issuing a request to initiate a reset sequence, - * to recover the system. The transition to S5 makes - * POWER_GOOD drop that triggers an interrupt to - * high-Z both AP_RST_L and PS_HOLD. - */ - CPRINTS("Long warm reset ended, " - "cold resetting to restore confidence."); - request_cold_reset(); - } - /* If not overdriven, just a normal power-up, do nothing. */ - } - power_signal_interrupt(signal); -} - -void chipset_power_good_interrupt(enum gpio_signal signal) -{ - if (!gpio_get_level(GPIO_POWER_GOOD) && ap_rst_overdriven) { - /* - * POWER_GOOD is the pull-up rail of WARM_RESET_L. - * When POWER_GOOD drops, high-Z both AP_RST_L and PS_HOLD - * to restore their states. - */ - gpio_set_flags(GPIO_AP_RST_L, GPIO_INT_BOTH | - GPIO_SEL_1P8V); - gpio_set_flags(GPIO_PS_HOLD, GPIO_INT_BOTH | - GPIO_SEL_1P8V); - ap_rst_overdriven = 0; - } - power_signal_interrupt(signal); -} -#endif /* defined(CONFIG_CHIPSET_SC7180) */ - -static void sc7x80_lid_event(void) -{ - /* Power task only cares about lid-open events */ - if (!lid_is_open()) - return; - - lid_opened = 1; - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, sc7x80_lid_event, HOOK_PRIO_DEFAULT); - -static void sc7x80_powerbtn_changed(void) -{ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, sc7x80_powerbtn_changed, - HOOK_PRIO_DEFAULT); - -/** - * Wait the switchcap GPIO0 PVC_PG signal asserted. - * - * When the output voltage is over the threshold PVC_PG_ADJ, - * the PVC_PG is asserted. - * - * PVG_PG_ADJ is configured to 3.0V. - * GPIO0 is configured as PVC_PG. - * - * @param enable 1 to wait the PMIC/AP on. - * 0 to wait the PMIC/AP off. - * - * @return EC_SUCCESS or error - */ -static int wait_switchcap_power_good(int enable) -{ - timestamp_t poll_deadline; - - poll_deadline = get_time(); - poll_deadline.val += SWITCHCAP_PG_CHECK_TIMEOUT; - while (enable != board_is_switchcap_power_good() && - get_time().val < poll_deadline.val) { - usleep(SWITCHCAP_PG_CHECK_WAIT); - } - - /* - * Check the timeout case. Just show a message. More check later - * will switch the power state. - */ - if (enable != board_is_switchcap_power_good()) { - if (enable) - CPRINTS("SWITCHCAP NO POWER GOOD!"); - else - CPRINTS("SWITCHCAP STILL POWER GOOD!"); - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} - -/** - * Get the state of the system power signals. - * - * @return 1 if the system is powered, 0 if not - */ -static int is_system_powered(void) -{ - return board_is_switchcap_enabled(); -} - -/** - * Get the PMIC/AP power signal. - * - * We treat the PMIC chips and the AP as a whole here. Don't deal with - * the individual chip. - * - * @return 1 if the PMIC/AP is powered, 0 if not - */ -static int is_pmic_pwron(void) -{ - /* Use POWER_GOOD to indicate PMIC/AP is on/off */ - return gpio_get_level(GPIO_POWER_GOOD); -} - -/** - * Wait the PMIC/AP power-on state. - * - * @param enable 1 to wait the PMIC/AP on. - * 0 to wait the PMIC/AP off. - * @param timeout Number of microsecond of timeout. - * - * @return EC_SUCCESS or error - */ -static int wait_pmic_pwron(int enable, unsigned int timeout) -{ - timestamp_t poll_deadline; - - /* Check the AP power status */ - if (enable == is_pmic_pwron()) - return EC_SUCCESS; - - poll_deadline = get_time(); - poll_deadline.val += timeout; - while (enable != is_pmic_pwron() && - get_time().val < poll_deadline.val) { - usleep(PMIC_POWER_AP_WAIT); - } - - /* Check the timeout case */ - if (enable != is_pmic_pwron()) { - if (enable) - CPRINTS("AP POWER NOT READY!"); - else - CPRINTS("AP POWER STILL UP!"); - - return EC_ERROR_UNKNOWN; - } - return EC_SUCCESS; -} - -/** - * Set the state of the system power signals but without any check. - * - * The system power signals are the enable pins of SwitchCap. - * They control the power of the set of PMIC chips and the AP. - * - * @param enable 1 to enable or 0 to disable - */ -static void set_system_power_no_check(int enable) -{ - board_set_switchcap_power(enable); -} - -/** - * Set the state of the system power signals. - * - * The system power signals are the enable pins of SwitchCap. - * They control the power of the set of PMIC chips and the AP. - * - * @param enable 1 to enable or 0 to disable - * - * @return EC_SUCCESS or error - */ -static int set_system_power(int enable) -{ - int ret; - - CPRINTS("%s(%d)", __func__, enable); - set_system_power_no_check(enable); - - ret = wait_switchcap_power_good(enable); - - if (!enable) { - /* Ensure POWER_GOOD drop to low if it is a forced shutdown */ - ret |= wait_pmic_pwron(0, FORCE_OFF_RESPONSE_TIMEOUT); - } - usleep(SYSTEM_POWER_ON_DELAY); - - return ret; -} - -/** - * Set the PMIC/AP power-on state. - * - * It triggers the PMIC/AP power-on and power-off sequence. - * - * @param enable 1 to power the PMIC/AP on. - * 0 to power the PMIC/AP off. - * - * @return EC_SUCCESS or error - */ -static int set_pmic_pwron(int enable) -{ - int ret; - - CPRINTS("%s(%d)", __func__, enable); - - /* Check the PMIC/AP power state */ - if (enable == is_pmic_pwron()) - return EC_SUCCESS; - - if (!gpio_get_level(GPIO_PMIC_KPD_PWR_ODL)) { - CPRINTS("PMIC_KPD_PWR_ODL not pulled up by PMIC; cancel pwron"); - return EC_ERROR_UNKNOWN; - } - - /* - * Power-on sequence: - * 1. Hold down PMIC_KPD_PWR_ODL, which is a power-on trigger - * 2. PMIC supplies power to POWER_GOOD - * 3. Release PMIC_KPD_PWR_ODL - * - * Power-off sequence: - * 1. Hold down PMIC_KPD_PWR_ODL and PMIC_RESIN_L, which is a power-off - * trigger (requiring reprogramming PMIC registers to make - * PMIC_KPD_PWR_ODL + PMIC_RESIN_L as a shutdown trigger) - * 2. PMIC stops supplying power to POWER_GOOD (requiring - * reprogramming PMIC to set the stage-1 and stage-2 reset timers to - * 0 such that the pull down happens just after the deboucing time - * of the trigger, like 2ms) - * 3. Release PMIC_KPD_PWR_ODL and PMIC_RESIN_L - * - * If the above PMIC registers not programmed or programmed wrong, it - * falls back to the next functions, which cuts off the system power. - */ - - gpio_set_level(GPIO_PMIC_KPD_PWR_ODL, 0); - if (!enable) - gpio_set_level(GPIO_PMIC_RESIN_L, 0); - ret = wait_pmic_pwron(enable, PMIC_POWER_AP_RESPONSE_TIMEOUT); - gpio_set_level(GPIO_PMIC_KPD_PWR_ODL, 1); - if (!enable) - gpio_set_level(GPIO_PMIC_RESIN_L, 1); - - return ret; -} - -enum power_state power_chipset_init(void) -{ - int init_power_state; - uint32_t reset_flags = system_get_reset_flags(); - - /* Enable interrupts */ - if (IS_ENABLED(CONFIG_CHIPSET_SC7180)) { - gpio_enable_interrupt(GPIO_WARM_RESET_L); - gpio_enable_interrupt(GPIO_POWER_GOOD); - } - - /* - * Force the AP shutdown unless we are doing SYSJUMP. Otherwise, - * the AP could stay in strange state. - */ - if (!(reset_flags & EC_RESET_FLAG_SYSJUMP)) { - CPRINTS("not sysjump; forcing system shutdown"); - set_system_power_no_check(0); - init_power_state = POWER_G3; - } else { - /* In the SYSJUMP case, we check if the AP is on */ - if (power_get_signals() & IN_POWER_GOOD) { - CPRINTS("SOC ON"); - init_power_state = POWER_S0; - - /* - * Reenable the power signal AP_RST_L interrupt, which - * should be enabled during S5->S3 but sysjump makes - * it back to default, disabled. - */ - power_signal_enable_interrupt(GPIO_AP_RST_L); - - /* Disable idle task deep sleep when in S0 */ - disable_sleep(SLEEP_MASK_AP_RUN); - } else { - CPRINTS("SOC OFF"); - init_power_state = POWER_G3; - } - } - - /* Leave power off only if requested by reset flags */ - if (!(reset_flags & EC_RESET_FLAG_AP_OFF) && - !(reset_flags & EC_RESET_FLAG_SYSJUMP)) { - CPRINTS("auto_power_on set due to reset_flag 0x%x", - system_get_reset_flags()); - auto_power_on = 1; - } - - 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(); - } - - return init_power_state; -} - -/*****************************************************************************/ - -/** - * Power off the AP - */ -static void power_off(void) -{ - /* Check PMIC POWER_GOOD */ - if (is_pmic_pwron()) { - /* Do a graceful way to shutdown PMIC/AP first */ - set_pmic_pwron(0); - usleep(PMIC_POWER_OFF_DELAY); - - /* - * Disable signal interrupts, as they are floating when - * switchcap off. - */ - power_signal_disable_interrupt(GPIO_AP_RST_L); - } - - /* Check the switchcap status */ - if (is_system_powered()) { - /* Force to switch off all rails */ - set_system_power(0); - } - - lid_opened = 0; -} - -/** - * Check if the power is enough to boot the AP. - */ -static int power_is_enough(void) -{ - timestamp_t poll_deadline; - - /* If powered by adapter only, wait a while for PD negoiation. */ - poll_deadline = get_time(); - poll_deadline.val += CAN_BOOT_AP_CHECK_TIMEOUT; - - /* - * Wait for PD negotiation. If a system with drained battery, don't - * waste the time and exit the loop. - */ - while (!system_can_boot_ap() && !charge_want_shutdown() && - get_time().val < poll_deadline.val) { - usleep(CAN_BOOT_AP_CHECK_WAIT); - } - - return system_can_boot_ap() && !charge_want_shutdown(); -} - -/** - * Power on the AP - * - * @return EC_SUCCESS or error - */ -static int power_on(void) -{ - int ret; - - ret = set_system_power(1); - if (ret != EC_SUCCESS) - return ret; - - /* Enable signal interrupts */ - power_signal_enable_interrupt(GPIO_AP_RST_L); - - ret = set_pmic_pwron(1); - if (ret != EC_SUCCESS) { - CPRINTS("POWER_GOOD not seen in time"); - return ret; - } - - CPRINTS("POWER_GOOD seen"); - return EC_SUCCESS; -} - -/** - * Check if there has been a power-on event - * - * This checks all power-on event signals and returns non-zero if any have been - * triggered (with debounce taken into account). - * - * @return non-zero if there has been a power-on event, 0 if not. - */ -static uint8_t check_for_power_on_event(void) -{ - if (power_request == POWER_REQ_ON) { - power_request = POWER_REQ_NONE; - return POWER_ON_BY_POWER_REQ_ON; - } else if (power_request == POWER_REQ_RESET) { - power_request = POWER_REQ_NONE; - return POWER_ON_BY_POWER_REQ_RESET; - } - /* Clear invalid request */ - power_request = POWER_REQ_NONE; - - /* power on requested at EC startup for recovery */ - if (auto_power_on) { - auto_power_on = 0; - return POWER_ON_BY_AUTO_POWER_ON; - } - - /* Check lid open */ - if (lid_opened) { - lid_opened = 0; - return POWER_ON_BY_LID_OPEN; - } - - /* check for power button press */ - if (power_button_is_pressed()) - return POWER_ON_BY_POWER_BUTTON_PRESSED; - - return POWER_OFF_CANCEL; -} - -/** - * Check for some event triggering the shutdown. - * - * It can be either a long power button press or a shutdown triggered from the - * AP and detected by reading POWER_GOOD. - * - * @return non-zero if a shutdown should happen, 0 if not - */ -static uint8_t check_for_power_off_event(void) -{ - timestamp_t now; - int pressed = 0; - - if (power_request == POWER_REQ_OFF) { - power_request = POWER_REQ_NONE; - return POWER_OFF_BY_POWER_REQ_OFF; - } else if (power_request == POWER_REQ_RESET) { - /* - * The power_request flag will be cleared later - * in check_for_power_on_event() in S5. - */ - return POWER_OFF_BY_POWER_REQ_RESET; - } - /* Clear invalid request */ - power_request = POWER_REQ_NONE; - - /* - * Check for power button press. - */ - if (power_button_is_pressed()) - pressed = POWER_OFF_BY_POWER_BUTTON_PRESSED; - - now = get_time(); - if (pressed) { - if (!power_button_was_pressed) { - power_off_deadline.val = now.val + DELAY_FORCE_SHUTDOWN; - CPRINTS("power waiting for long press %u", - power_off_deadline.le.lo); - /* Ensure we will wake up to check the power key */ - timer_arm(power_off_deadline, TASK_ID_CHIPSET); - } else if (timestamp_expired(power_off_deadline, &now)) { - power_off_deadline.val = 0; - CPRINTS("power off after long press now=%u, %u", - now.le.lo, power_off_deadline.le.lo); - return POWER_OFF_BY_LONG_PRESS; - } - } else if (power_button_was_pressed) { - CPRINTS("power off cancel"); - timer_cancel(TASK_ID_CHIPSET); - } - - power_button_was_pressed = pressed; - - /* POWER_GOOD released by AP : shutdown immediately */ - if (!power_has_signals(IN_POWER_GOOD)) { - CPRINTS("POWER_GOOD is lost"); - return POWER_OFF_BY_POWER_GOOD_LOST; - } - - return POWER_OFF_CANCEL; -} - -/** - * Cancel the power button timer. - * - * The timer was previously created in the check_for_power_off_event(), - * which waited for the power button long press. Should cancel the timer - * during the power state transition; otherwise, EC will crash. - */ -static inline void cancel_power_button_timer(void) -{ - if (power_button_was_pressed) - timer_cancel(TASK_ID_CHIPSET); -} - -/*****************************************************************************/ -/* Chipset interface */ - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - - /* Issue a request to initiate a power-off sequence */ - power_request = POWER_REQ_OFF; - task_wake(TASK_ID_CHIPSET); -} - -void chipset_reset(enum chipset_reset_reason reason) -{ - int rv; - - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - - /* - * Warm reset sequence: - * 1. Issue a low pulse to PMIC_RESIN_L, which triggers PMIC - * to do a warm reset (requiring reprogramming PMIC registers - * to make PMIC_RESIN_L as a warm reset trigger). - * 2. PMIC then issues a low pulse to AP_RST_L to reset AP. - * EC monitors the signal to see any low pulse. - * 2.1. If a low pulse found, done. - * 2.2. If a low pulse not found (the above PMIC registers - * not programmed or programmed wrong), issue a request - * to initiate a cold reset power sequence. - */ - - gpio_set_level(GPIO_PMIC_RESIN_L, 0); - usleep(PMIC_RESIN_PULSE_LENGTH); - gpio_set_level(GPIO_PMIC_RESIN_L, 1); - - rv = power_wait_signals_timeout(IN_AP_RST_ASSERTED, - PMIC_POWER_AP_RESPONSE_TIMEOUT); - /* Exception case: PMIC not work as expected, request a cold reset */ - if (rv != EC_SUCCESS) { - CPRINTS("AP refuses to warm reset. Cold resetting."); - request_cold_reset(); - } -} - -/* - * Flag to fake the suspend signal to 1 or 0, or -1 means not fake it. - * - * TODO(waihong): Remove this flag and debug command when the AP_SUSPEND - * signal is working. - */ -static int fake_suspend = -1; - -static int command_fake_suspend(int argc, char **argv) -{ - int v; - - if (argc < 2) { - ccprintf("fake_suspend: %s\n", - fake_suspend == -1 ? "reset" - : (fake_suspend ? "on" : "off")); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "reset")) - fake_suspend = -1; - else if (parse_bool(argv[1], &v)) - fake_suspend = v; - else - return EC_ERROR_PARAM1; - - task_wake(TASK_ID_CHIPSET); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(fakesuspend, command_fake_suspend, - "on/off/reset", - "Fake the AP_SUSPEND signal"); - -/* Get system sleep state through GPIOs */ -static inline int chipset_get_sleep_signal(void) -{ - if (fake_suspend == -1) - return (power_get_signals() & IN_SUSPEND) == IN_SUSPEND; - else - return fake_suspend; -} - -static void suspend_hang_detected(void) -{ - CPRINTS("Warning: Detected sleep hang! Waking host up!"); - host_set_single_event(EC_HOST_EVENT_HANG_DETECT); -} - -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); - -__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); - power_signal_enable_interrupt(GPIO_AP_SUSPEND); - - } 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); - power_signal_disable_interrupt(GPIO_AP_SUSPEND); - sleep_complete_resume(ctx); - - } else if (state == HOST_SLEEP_EVENT_DEFAULT_RESET) { - power_signal_disable_interrupt(GPIO_AP_SUSPEND); - } -} - -/** - * Power handler for steady states - * - * @param state Current power state - * @return Updated power state - */ -enum power_state power_handle_state(enum power_state state) -{ - static uint8_t boot_from_off, shutdown_from_on; - - switch (state) { - case POWER_G3: - boot_from_off = check_for_power_on_event(); - if (boot_from_off) - return POWER_G3S5; - break; - - case POWER_G3S5: - return POWER_S5; - - case POWER_S5: - if (!boot_from_off) - boot_from_off = check_for_power_on_event(); - - if (boot_from_off) { - CPRINTS("power on %d", boot_from_off); - return POWER_S5S3; - } - break; - - case POWER_S5S3: - /* - * Wait for power button release before actually boot AP. - * It may be a long-hold power button with volume buttons - * to trigger the recovery button. We don't want AP up - * during the long-hold. - */ - power_button_wait_for_release(-1); - - /* If no enough power, return back to S5. */ - if (!power_is_enough()) { - boot_from_off = 0; - return POWER_S5; - } - - /* Initialize components to ready state before AP is up. */ - hook_notify(HOOK_CHIPSET_PRE_INIT); - - if (power_on() != EC_SUCCESS) { - power_off(); - boot_from_off = 0; - return POWER_S5; - } - CPRINTS("AP running ..."); - - /* Call hooks now that AP is running */ - hook_notify(HOOK_CHIPSET_STARTUP); - - /* - * Clearing the sleep failure detection tracking on the path - * to S0 to handle any reset conditions. - */ - power_reset_host_sleep_state(); - return POWER_S3; - - case POWER_S3: - if (!shutdown_from_on) - shutdown_from_on = check_for_power_off_event(); - - if (shutdown_from_on) { - CPRINTS("power off %d", shutdown_from_on); - return POWER_S3S5; - } - - /* - * AP has woken up and it deasserts the suspend signal; - * go to S0. - * - * In S0, it will wait for a host event and then trigger the - * RESUME hook. - */ - if (!chipset_get_sleep_signal()) - return POWER_S3S0; - break; - - case POWER_S3S0: - cancel_power_button_timer(); - -#ifdef CONFIG_CHIPSET_RESUME_INIT_HOOK - /* - * Notify the RESUME_INIT hooks, i.e. enabling SPI driver - * to receive host commands/events. - * - * If boot from an off state, notify the RESUME hooks too; - * otherwise (resume from S3), the normal RESUME hooks will - * be notified later, after receive a host resume event. - */ - hook_notify(HOOK_CHIPSET_RESUME_INIT); - if (boot_from_off) - hook_notify(HOOK_CHIPSET_RESUME); -#else - hook_notify(HOOK_CHIPSET_RESUME); -#endif - sleep_resume_transition(); - - boot_from_off = 0; - disable_sleep(SLEEP_MASK_AP_RUN); - return POWER_S0; - - case POWER_S0: - shutdown_from_on = check_for_power_off_event(); - if (shutdown_from_on) { - return POWER_S0S3; - } else if (power_get_host_sleep_state() - == HOST_SLEEP_EVENT_S3_SUSPEND && - chipset_get_sleep_signal()) { - return POWER_S0S3; - } - /* When receive the host event, trigger the RESUME hook. */ - sleep_notify_transition(SLEEP_NOTIFY_RESUME, - HOOK_CHIPSET_RESUME); - break; - - case POWER_S0S3: - cancel_power_button_timer(); - - /* - * Call SUSPEND hooks only if we haven't notified listeners of - * S3 suspend. - */ - sleep_notify_transition(SLEEP_NOTIFY_SUSPEND, - HOOK_CHIPSET_SUSPEND); -#ifdef CONFIG_CHIPSET_RESUME_INIT_HOOK - /* - * Pair with the HOOK_CHIPSET_RESUME_INIT, i.e. disabling SPI - * driver, by notifying the SUSPEND_COMPLETE hooks. - * - * If shutdown from an on state, notify the SUSPEND hooks too; - * otherwise (suspend from S0), the normal SUSPEND hooks have - * been notified in the above sleep_notify_transition() call. - */ - if (shutdown_from_on) - hook_notify(HOOK_CHIPSET_SUSPEND); - hook_notify(HOOK_CHIPSET_SUSPEND_COMPLETE); -#else - hook_notify(HOOK_CHIPSET_SUSPEND); -#endif - sleep_suspend_transition(); - - enable_sleep(SLEEP_MASK_AP_RUN); - return POWER_S3; - - case POWER_S3S5: - cancel_power_button_timer(); - - /* Call hooks before we drop power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - power_off(); - CPRINTS("power shutdown complete"); - - /* Call hooks after we drop power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); - - shutdown_from_on = 0; - - /* - * Wait forever for the release of the power button; otherwise, - * this power button press will then trigger a power-on in S5. - */ - power_button_wait_for_release(-1); - power_button_was_pressed = 0; - return POWER_S5; - - case POWER_S5G3: - return POWER_G3; - } - - return state; -} - -/*****************************************************************************/ -/* Console debug command */ - -static const char *power_req_name[POWER_REQ_COUNT] = { - "none", - "off", - "on", -}; - -/* Power states that we can report */ -enum power_state_t { - PSTATE_UNKNOWN, - PSTATE_OFF, - PSTATE_ON, - PSTATE_COUNT, -}; - -static const char * const state_name[] = { - "unknown", - "off", - "on", -}; - -static int command_power(int argc, char **argv) -{ - int v; - - if (argc < 2) { - enum power_state_t state; - - state = PSTATE_UNKNOWN; - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - state = PSTATE_OFF; - if (chipset_in_state(CHIPSET_STATE_ON)) - state = PSTATE_ON; - ccprintf("%s\n", state_name[state]); - - return EC_SUCCESS; - } - - if (!parse_bool(argv[1], &v)) - return EC_ERROR_PARAM1; - - power_request = v ? POWER_REQ_ON : POWER_REQ_OFF; - ccprintf("Requesting power %s\n", power_req_name[power_request]); - task_wake(TASK_ID_CHIPSET); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(power, command_power, - "on/off", - "Turn AP power on/off"); diff --git a/power/rk3288.c b/power/rk3288.c deleted file mode 100644 index c647ab97b2..0000000000 --- a/power/rk3288.c +++ /dev/null @@ -1,577 +0,0 @@ -/* Copyright 2013 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. - */ - -/* - * Rockchip SoC power sequencing module for Chrome EC - * - * This implements the following features: - * - * - Cold reset powers on the AP - * - * When powered off: - * - Press pwron turns on the AP - * - Hold pwron turns on the AP, and then 9s later turns it off and leaves - * it off until pwron is released and pressed again - * - * When powered on: - * - Holding pwron for 10.2s powers off the AP - * - Pressing and releasing pwron within that 10.2s is ignored - * - If POWER_GOOD is dropped by the pmic, then we cut off the pmic source - * - If SUSPEND_L goes low, enter suspend mode. - * - */ - -#include "battery.h" -#include "charge_state.h" -#include "chipset.h" /* This module implements chipset functions too */ -#include "clock.h" -#include "common.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "lid_switch.h" -#include "keyboard_scan.h" -#include "power.h" -#include "power_button.h" -#include "power_led.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -/* masks for power signals */ -#define IN_POWER_GOOD POWER_SIGNAL_MASK(RK_POWER_GOOD) -#define IN_SUSPEND POWER_SIGNAL_MASK(RK_SUSPEND_ASSERTED) - -/* Long power key press to force shutdown */ -#define DELAY_FORCE_SHUTDOWN (8 * SECOND) - -/* - * If the power key is pressed to turn on, then held for this long, we - * power off. - * - * Normal case: User releases power button and chipset_task() goes - * into the inner loop, waiting for next event to occur (power button - * press or power good == 0). - */ -#define DELAY_SHUTDOWN_ON_POWER_HOLD (8 * SECOND) - -/* - * The hold time for pulling down the PMIC_WARM_RESET_L pin so that - * the AP can entery the recovery mode (flash SPI flash from USB). - */ -#define PMIC_WARM_RESET_L_HOLD_TIME (4 * MSEC) - -/* - * Startup time for the PMIC source regulator. - */ -#define PMIC_SOURCE_STARTUP_TIME (50 * MSEC) - -/* - * Time before PMIC can be reset. - */ -#define PMIC_STARTUP_MS 300 - -/* TODO(crosbug.com/p/25047): move to HOOK_POWER_BUTTON_CHANGE */ -/* 1 if the power button was pressed last time we checked */ -static char power_button_was_pressed; - -/* 1 if lid-open event has been detected */ -static char lid_opened; - -/* time where we will power off, if power button still held down */ -static timestamp_t power_off_deadline; - -/* force AP power on (used for recovery keypress) */ -static int auto_power_on; - -enum power_request_t { - POWER_REQ_NONE, - POWER_REQ_OFF, - POWER_REQ_ON, - - POWER_REQ_COUNT, -}; - -static enum power_request_t power_request; - - -/* Forward declaration */ -static void chipset_turn_off_power_rails(void); - - -/** - * Set the PMIC WARM RESET signal. - * - * @param asserted Resetting (=0) or idle (=1) - */ -static void set_pmic_warm_reset(int asserted) -{ - /* Signal is active-low */ - gpio_set_level(GPIO_PMIC_WARM_RESET_L, asserted ? 0 : 1); -} - - -/** - * Set the PMIC PWRON signal. - * - * @param asserted Assert (=1) or deassert (=0) the signal. - */ -static void set_pmic_pwron(int asserted) -{ - /* Signal is active-high */ - gpio_set_level(GPIO_PMIC_PWRON, asserted ? 1 : 0); -} - -/** - * Set the PMIC source to force shutdown the AP. - * - * @param asserted Assert (=1) or deassert (=0) the signal. - */ -static void set_pmic_source(int asserted) -{ - /* Signal is active-high */ - gpio_set_level(GPIO_PMIC_SOURCE_PWREN, asserted ? 1 : 0); -} - -/** - * Check for some event triggering the shutdown. - * - * It can be either a long power button press or a shutdown triggered from the - * AP and detected by reading POWER_GOOD. - * - * @return non-zero if a shutdown should happen, 0 if not - */ -static int check_for_power_off_event(void) -{ - timestamp_t now; - int pressed = 0; - int ret = 0; - - /* - * Check for power button press. - */ - if (power_button_is_pressed()) { - pressed = 1; - } else if (power_request == POWER_REQ_OFF) { - power_request = POWER_REQ_NONE; - return 4; /* return non-zero for shudown down */ - } - - now = get_time(); - if (pressed) { - if (!power_button_was_pressed) { - power_off_deadline.val = now.val + DELAY_FORCE_SHUTDOWN; - CPRINTS("power waiting for long press %u", - power_off_deadline.le.lo); - /* Ensure we will wake up to check the power key */ - timer_arm(power_off_deadline, TASK_ID_CHIPSET); - } else if (timestamp_expired(power_off_deadline, &now)) { - power_off_deadline.val = 0; - CPRINTS("power off after long press now=%u, %u", - now.le.lo, power_off_deadline.le.lo); - return 2; - } - } else if (power_button_was_pressed) { - CPRINTS("power off cancel"); - timer_cancel(TASK_ID_CHIPSET); - } - - /* POWER_GOOD released by AP : shutdown immediately */ - if (!power_has_signals(IN_POWER_GOOD)) { - if (power_button_was_pressed) - timer_cancel(TASK_ID_CHIPSET); - ret = 3; - } - - power_button_was_pressed = pressed; - - return ret; -} - -static void rockchip_lid_event(void) -{ - /* Power task only cares about lid-open events */ - if (!lid_is_open()) - return; - - lid_opened = 1; - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, rockchip_lid_event, HOOK_PRIO_DEFAULT); - -enum power_state power_chipset_init(void) -{ - int init_power_state; - uint32_t reset_flags = system_get_reset_flags(); - - /* - * Force the AP shutdown unless we are doing SYSJUMP. Otherwise, - * the AP could stay in strange state. - */ - if (!(reset_flags & EC_RESET_FLAG_SYSJUMP)) { - CPRINTS("not sysjump; forcing AP shutdown"); - chipset_turn_off_power_rails(); - - /* - * The warm reset triggers AP into the RK recovery mode ( - * flash SPI from USB). - */ - chipset_reset(CHIPSET_RESET_INIT); - - init_power_state = POWER_G3; - } else { - /* In the SYSJUMP case, we check if the AP is on */ - if (power_get_signals() & IN_POWER_GOOD) - init_power_state = POWER_S0; - else - init_power_state = POWER_G3; - } - - /* Leave power off only if requested by reset flags */ - if (!(reset_flags & EC_RESET_FLAG_AP_OFF) && - !(reset_flags & EC_RESET_FLAG_SYSJUMP)) { - CPRINTS("auto_power_on set due to reset_flag 0x%x", - system_get_reset_flags()); - auto_power_on = 1; - } - - /* - * Some batteries use clock stretching feature, which requires - * more time to be stable. See http://crosbug.com/p/28289 - */ - battery_wait_for_stable(); - - return init_power_state; -} - -/*****************************************************************************/ -/* Chipset interface */ - -static void chipset_turn_off_power_rails(void) -{ - /* Release the power on pin, if it was asserted */ - set_pmic_pwron(0); - /* Close the pmic power source immediately */ - set_pmic_source(0); - - /* Keep AP and PMIC in reset the whole time */ - set_pmic_warm_reset(1); -} - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - chipset_turn_off_power_rails(); - - /* clean-up internal variable */ - power_request = POWER_REQ_NONE; -} - -/*****************************************************************************/ - -/** - * Check if there has been a power-on event - * - * This checks all power-on event signals and returns non-zero if any have been - * triggered (with debounce taken into account). - * - * @return non-zero if there has been a power-on event, 0 if not. - */ -static int check_for_power_on_event(void) -{ - int ap_off_flag; - - ap_off_flag = system_get_reset_flags() & EC_RESET_FLAG_AP_OFF; - system_clear_reset_flags(EC_RESET_FLAG_AP_OFF); - /* check if system is already ON */ - if (power_get_signals() & IN_POWER_GOOD) { - if (ap_off_flag) { - CPRINTS( - "system is on, but " - "EC_RESET_FLAG_AP_OFF is on"); - return 0; - } else { - CPRINTS( - "system is on, thus clear " - "auto_power_on"); - /* no need to arrange another power on */ - auto_power_on = 0; - return 1; - } - } - - /* power on requested at EC startup for recovery */ - if (auto_power_on) { - auto_power_on = 0; - return 2; - } - - /* Check lid open */ - if (lid_opened) { - lid_opened = 0; - return 3; - } - - /* check for power button press */ - if (power_button_is_pressed()) - return 4; - - if (power_request == POWER_REQ_ON) { - power_request = POWER_REQ_NONE; - return 5; - } - - return 0; -} - -/** - * Power on the AP - */ -static void power_on(void) -{ - int i; - - set_pmic_source(1); - usleep(PMIC_SOURCE_STARTUP_TIME); - - set_pmic_pwron(1); - /* - * BUG Workaround(crosbug.com/p/31635): usleep hangs in task when using - * big delays. - */ - for (i = 0; i < PMIC_STARTUP_MS; i++) - usleep(1 * MSEC); - - set_pmic_warm_reset(0); -} - -/** - * Power off the AP - */ -static void power_off(void) -{ - unsigned int power_off_timeout = 100; /* ms */ - - /* Call hooks before we drop power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - /* switch off all rails */ - chipset_turn_off_power_rails(); - /* Change SUSPEND_L and EC_INT pin to high-Z to reduce power draw. */ - gpio_set_flags(GPIO_SUSPEND_L, GPIO_INPUT); - gpio_set_flags(GPIO_EC_INT_L, GPIO_INPUT); - - /* Wait till we actually turn off to not mess up the state machine. */ - while (power_get_signals() & IN_POWER_GOOD) { - msleep(1); - power_off_timeout--; - ASSERT(power_off_timeout); - } - - lid_opened = 0; - enable_sleep(SLEEP_MASK_AP_RUN); - powerled_set_state(POWERLED_STATE_OFF); - - CPRINTS("power shutdown complete"); - - /* Call hooks after we drop power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); -} - -void chipset_reset(enum chipset_reset_reason reason) -{ - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - - CPRINTS("assert GPIO_PMIC_WARM_RESET_L for %d ms", - PMIC_WARM_RESET_L_HOLD_TIME / MSEC); - set_pmic_warm_reset(1); - usleep(PMIC_WARM_RESET_L_HOLD_TIME); - set_pmic_warm_reset(0); -} - -enum power_state power_handle_state(enum power_state state) -{ - int value; - static int boot_from_g3; - - switch (state) { - case POWER_G3: - boot_from_g3 = check_for_power_on_event(); - if (boot_from_g3) - return POWER_G3S5; - break; - - case POWER_G3S5: - return POWER_S5; - - case POWER_S5: - if (boot_from_g3) { - value = boot_from_g3; - boot_from_g3 = 0; - } else { - value = check_for_power_on_event(); - } - - if (value) { - CPRINTS("power on %d", value); - return POWER_S5S3; - } - return state; - - case POWER_S5S3: - hook_notify(HOOK_CHIPSET_PRE_INIT); - - power_on(); - - disable_sleep(SLEEP_MASK_AP_RUN); - powerled_set_state(POWERLED_STATE_ON); - - if (power_wait_signals(IN_POWER_GOOD) == EC_SUCCESS) { - CPRINTS("POWER_GOOD seen"); - if (power_button_wait_for_release( - DELAY_SHUTDOWN_ON_POWER_HOLD) == - EC_SUCCESS) { - power_button_was_pressed = 0; - set_pmic_pwron(0); - - /* setup misc gpio for S3/S0 functionality */ - gpio_set_flags(GPIO_SUSPEND_L, GPIO_INPUT - | GPIO_INT_BOTH | GPIO_PULL_DOWN); - gpio_set_flags(GPIO_EC_INT_L, GPIO_OUTPUT - | GPIO_OUT_HIGH); - - /* Call hooks now that AP is running */ - hook_notify(HOOK_CHIPSET_STARTUP); - - return POWER_S3; - } else { - CPRINTS("long-press button, shutdown"); - power_off(); - /* - * Since the AP may be up already, return S0S3 - * state to go through the suspend hook. - */ - return POWER_S0S3; - } - } else { - CPRINTS("POWER_GOOD not seen in time"); - } - - chipset_turn_off_power_rails(); - return POWER_S5; - - case POWER_S3: - if (!(power_get_signals() & IN_POWER_GOOD)) - return POWER_S3S5; - else if (!(power_get_signals() & IN_SUSPEND)) - return POWER_S3S0; - return state; - - case POWER_S3S0: - powerled_set_state(POWERLED_STATE_ON); - hook_notify(HOOK_CHIPSET_RESUME); - return POWER_S0; - - case POWER_S0: - value = check_for_power_off_event(); - if (value) { - CPRINTS("power off %d", value); - power_off(); - return POWER_S0S3; - } else if (power_get_signals() & IN_SUSPEND) - return POWER_S0S3; - return state; - - case POWER_S0S3: - if (lid_is_open()) - powerled_set_state(POWERLED_STATE_SUSPEND); - else - powerled_set_state(POWERLED_STATE_OFF); - /* Call hooks here since we don't know it prior to AP suspend */ - hook_notify(HOOK_CHIPSET_SUSPEND); - return POWER_S3; - - case POWER_S3S5: - power_button_wait_for_release(-1); - power_button_was_pressed = 0; - return POWER_S5; - - case POWER_S5G3: - return POWER_G3; - } - - return state; -} - -static void powerbtn_rockchip_changed(void) -{ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, powerbtn_rockchip_changed, - HOOK_PRIO_DEFAULT); - -/*****************************************************************************/ -/* Console debug command */ - -static const char *power_req_name[POWER_REQ_COUNT] = { - "none", - "off", - "on", -}; - -/* Power states that we can report */ -enum power_state_t { - PSTATE_UNKNOWN, - PSTATE_OFF, - PSTATE_SUSPEND, - PSTATE_ON, - - PSTATE_COUNT, -}; - -static const char * const state_name[] = { - "unknown", - "off", - "suspend", - "on", -}; - -static int command_power(int argc, char **argv) -{ - int v; - - if (argc < 2) { - enum power_state_t state; - - state = PSTATE_UNKNOWN; - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - state = PSTATE_OFF; - if (chipset_in_state(CHIPSET_STATE_SUSPEND)) - state = PSTATE_SUSPEND; - if (chipset_in_state(CHIPSET_STATE_ON)) - state = PSTATE_ON; - ccprintf("%s\n", state_name[state]); - - return EC_SUCCESS; - } - - if (!parse_bool(argv[1], &v)) - return EC_ERROR_PARAM1; - - power_request = v ? POWER_REQ_ON : POWER_REQ_OFF; - ccprintf("Requesting power %s\n", power_req_name[power_request]); - task_wake(TASK_ID_CHIPSET); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(power, command_power, - "on/off", - "Turn AP power on/off"); diff --git a/power/rk3399.c b/power/rk3399.c deleted file mode 100644 index 9db25f0b28..0000000000 --- a/power/rk3399.c +++ /dev/null @@ -1,610 +0,0 @@ -/* Copyright 2013 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. - */ - -/* rk3399 chipset power control module for Chrome EC */ - -/* - * The description of each CONFIG_CHIPSET_POWER_SEQ_VERSION: - * - * Version 0: Initial/default revision for clamshell / convertible. - * Version 1: Simplified power tree for tablet / detachable. - */ - -#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 "usb_charge.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -/* Input state flags */ -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 - #define IN_PGOOD_PP1250_S3 POWER_SIGNAL_MASK(PP1250_S3_PWR_GOOD) - #define IN_PGOOD_PP900_S0 POWER_SIGNAL_MASK(PP900_S0_PWR_GOOD) -#else - #define IN_PGOOD_PP5000 POWER_SIGNAL_MASK(PP5000_PWR_GOOD) - #define IN_PGOOD_SYS POWER_SIGNAL_MASK(SYS_PWR_GOOD) -#endif - -#define IN_PGOOD_AP POWER_SIGNAL_MASK(AP_PWR_GOOD) -#define IN_SUSPEND_DEASSERTED POWER_SIGNAL_MASK(SUSPEND_DEASSERTED) - -/* Rails requires for S3 and S0 */ -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 - #define IN_PGOOD_S3 (IN_PGOOD_PP1250_S3) - #define IN_PGOOD_S0 (IN_PGOOD_S3 | IN_PGOOD_PP900_S0 | IN_PGOOD_AP) - /* This board can optionally wake-on-USB in S3 */ - #define S3_USB_WAKE - /* This board has non-INT power signal pins */ - #define POWER_SIGNAL_POLLING - /* This board supports CR50 deep sleep mode */ - #define CR50_DEEP_SLEEP - /* - * If AP_PWR_GOOD assertion does not trigger an interrupt, poll the - * signal every 5ms, up to 200 times (~ 1 second timeout). - */ - #define PGOOD_S0_POLL_TIMEOUT (5 * MSEC) - #define PGOOD_S0_POLL_TRIES 200 -#else - #define IN_PGOOD_S3 (IN_PGOOD_PP5000) - #define IN_PGOOD_S0 (IN_PGOOD_S3 | IN_PGOOD_AP | IN_PGOOD_SYS) -#endif - -/* All inputs in the right state for S0 */ -#define IN_ALL_S0 (IN_PGOOD_S0 | IN_SUSPEND_DEASSERTED) - -/* Long power key press to force shutdown in S0 */ -#define FORCED_SHUTDOWN_DELAY (8 * SECOND) - -#define CHARGER_INITIALIZED_DELAY_MS 100 -#define CHARGER_INITIALIZED_TRIES 40 - -/* 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. - */ - -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 -static const struct power_seq_op s5s3_power_seq[] = { - { GPIO_PP900_S3_EN, 1, 2 }, - { GPIO_PP3300_S3_EN, 1, 2 }, - { GPIO_PP1800_S3_EN, 1, 2 }, - { GPIO_PP1250_S3_EN, 1, 2 }, -}; -#else -static const struct power_seq_op s5s3_power_seq[] = { - { GPIO_PPVAR_LOGIC_EN, 1, 0 }, - { GPIO_PP900_AP_EN, 1, 0 }, - { GPIO_PP900_PCIE_EN, 1, 2 }, - { GPIO_PP900_PMU_EN, 1, 0 }, - { GPIO_PP900_PLL_EN, 1, 0 }, - { GPIO_PP900_USB_EN, 1, 2 }, - { GPIO_SYS_RST_L, 0, 0 }, - { GPIO_PP1800_PMU_EN_L, 0, 2 }, - { GPIO_LPDDR_PWR_EN, 1, 2 }, - { GPIO_PP1800_USB_EN_L, 0, 2 }, - { GPIO_PP3300_USB_EN_L, 0, 0 }, - { GPIO_PP5000_EN, 1, 0 }, - { GPIO_PP3300_TRACKPAD_EN_L, 0, 1 }, - { GPIO_PP1800_LID_EN_L, 0, 0 }, - { GPIO_PP1800_SIXAXIS_EN_L, 0, 2 }, - { GPIO_PP1800_SENSOR_EN_L, 0, 0 }, -}; -#endif - -/* The power sequence for POWER_S3S0 */ -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 -static const struct power_seq_op s3s0_power_seq[] = { - { GPIO_AP_CORE_EN, 1, 2 }, - { GPIO_PP1800_S0_EN, 1, 0 }, -}; -#else -static const struct power_seq_op s3s0_power_seq[] = { - { GPIO_PPVAR_CLOGIC_EN, 1, 2 }, - { GPIO_PP900_DDRPLL_EN, 1, 2 }, - { GPIO_PP1800_AP_AVDD_EN_L, 0, 2 }, - { GPIO_AP_CORE_EN, 1, 2 }, - { GPIO_PP1800_S0_EN_L, 0, 2 }, - { GPIO_PP3300_S0_EN_L, 0, 0 }, -}; -#endif - -#ifdef S3_USB_WAKE -/* Sigs that may already be on in S3, if we need to wake-on-USB */ -static const struct power_seq_op s3s0_usb_wake_power_seq[] = { - { GPIO_PP900_S0_EN, 1, 2 }, - { GPIO_PP1800_USB_EN, 1, 2 }, - { GPIO_PP3300_S0_EN, 1, 2 }, -}; -#endif - -/* The power sequence for POWER_S0S3 */ -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 -static const struct power_seq_op s0s3_power_seq[] = { - { GPIO_AP_CORE_EN, 0, 20 }, -}; -#else -static const struct power_seq_op s0s3_power_seq[] = { - { GPIO_PP3300_S0_EN_L, 1, 20 }, - { GPIO_PP1800_S0_EN_L, 1, 1 }, - { GPIO_AP_CORE_EN, 0, 20 }, - { GPIO_PP1800_AP_AVDD_EN_L, 1, 1 }, - { GPIO_PP900_DDRPLL_EN, 0, 1 }, - { GPIO_PPVAR_CLOGIC_EN, 0, 0 }, -}; -#endif - -#ifdef S3_USB_WAKE -/* Sigs that need to be left on in S3, if we need to wake-on-USB */ -static const struct power_seq_op s0s3_usb_wake_power_seq[] = { - { GPIO_PP3300_S0_EN, 0, 20 }, - { GPIO_PP1800_S0_EN, 0, 1 }, - { GPIO_PP1800_USB_EN, 0, 1 }, - { GPIO_PP900_S0_EN, 0, 0 }, -}; -#endif - -/* The power sequence for POWER_S3S5 */ -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 -static const struct power_seq_op s3s5_power_seq[] = { - { GPIO_SYS_RST_L, 0, 0 }, - { GPIO_PP1250_S3_EN, 0, 2 }, - { GPIO_PP1800_S3_EN, 0, 2 }, - { GPIO_PP3300_S3_EN, 0, 2 }, - { GPIO_PP900_S3_EN, 0, 0 }, -}; -#else -static const struct power_seq_op s3s5_power_seq[] = { - { GPIO_PP1800_SENSOR_EN_L, 1, 0}, - { GPIO_PP1800_SIXAXIS_EN_L, 1, 0}, - { GPIO_PP1800_LID_EN_L, 1, 0 }, - { GPIO_PP3300_TRACKPAD_EN_L, 1, 0 }, - { GPIO_PP5000_EN, 0, 0 }, - { GPIO_PP3300_USB_EN_L, 1, 20 }, - { GPIO_PP1800_USB_EN_L, 1, 10 }, - { GPIO_LPDDR_PWR_EN, 0, 20 }, - { GPIO_PP1800_PMU_EN_L, 1, 2 }, - { GPIO_PP900_PLL_EN, 0, 0 }, - { GPIO_PP900_PMU_EN, 0, 0 }, - { GPIO_PP900_USB_EN, 0, 6 }, - { GPIO_PP900_PCIE_EN, 0, 0 }, - { GPIO_PP900_AP_EN, 0, 0 }, - { GPIO_PPVAR_LOGIC_EN, 0, 0 }, -}; -#endif - -static int forcing_shutdown; - -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); -} - -#define SYS_RST_HOLD_US (1 * MSEC) -void chipset_reset(enum chipset_reset_reason reason) -{ -#ifdef CONFIG_CMD_RTC - /* Print out the RTC to help correlate resets in logs. */ - print_system_rtc(CC_CHIPSET); -#endif - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - - /* Pulse SYS_RST */ - gpio_set_level(GPIO_SYS_RST_L, 0); - if (in_interrupt_context()) - udelay(SYS_RST_HOLD_US); - else - usleep(SYS_RST_HOLD_US); - gpio_set_level(GPIO_SYS_RST_L, 1); -} - -enum power_state power_chipset_init(void) -{ - if (system_jumped_to_this_image()) { - if ((power_get_signals() & IN_ALL_S0) == IN_ALL_S0) { - disable_sleep(SLEEP_MASK_AP_RUN); - CPRINTS("already in S0"); - return POWER_S0; - } - } else if (!(system_get_reset_flags() & EC_RESET_FLAG_AP_OFF)) - /* Auto-power on */ - chipset_exit_hard_off(); - - return POWER_G3; -} - -static void force_shutdown(void) -{ - forcing_shutdown = 1; - task_wake(TASK_ID_CHIPSET); -} -DECLARE_DEFERRED(force_shutdown); - -/* - * Debounce PGOOD_AP if we lose it suddenly during S0, since output voltage - * transitions may cause spurious pulses. - */ -#define PGOOD_AP_DEBOUNCE_TIMEOUT (100 * MSEC) - -/* - * The AP informs the EC of its S0 / S3 state through IN_SUSPEND_DEASSERTED / - * AP_EC_S3_S0_L. Latency between deassertion and power rails coming up must - * be minimized, so check for deassertion at various stages of our suspend - * power sequencing, and immediately transition out of suspend if necessary. - */ -#define SLEEP_INTERVAL_MS 5 -#define MSLEEP_CHECK_ABORTED_SUSPEND(msec) \ - do { \ - int sleep_remain = msec; \ - do { \ - msleep(MIN(sleep_remain, SLEEP_INTERVAL_MS)); \ - sleep_remain -= SLEEP_INTERVAL_MS; \ - if (!forcing_shutdown && \ - power_get_signals() & IN_SUSPEND_DEASSERTED) { \ - CPRINTS("suspend aborted"); \ - return POWER_S3S0; \ - } \ - } while (sleep_remain > 0); \ - } while (0) -BUILD_ASSERT(POWER_S3S0 != 0); - -/** - * 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. - * @return non-zero if suspend aborted during POWER_S0S3, 0 otherwise. - */ -static int 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; - if ((power_seq_ops == s0s3_power_seq) -#ifdef S3_USB_WAKE - || (power_seq_ops == s0s3_usb_wake_power_seq) -#endif - ) - MSLEEP_CHECK_ABORTED_SUSPEND(power_seq_ops[i].delay); - else - msleep(power_seq_ops[i].delay); - } - return 0; -} - -enum power_state power_handle_state(enum power_state state) -{ -#ifndef CR50_DEEP_SLEEP - static int sys_reset_asserted; -#endif -#ifdef S3_USB_WAKE - static int usb_wake_enabled; -#endif - int tries = 0; - - switch (state) { - case POWER_G3: - break; - - case POWER_S5: - if (forcing_shutdown) - return POWER_S5G3; - else - return POWER_S5S3; - break; - - case POWER_S3: - if (!power_has_signals(IN_PGOOD_S3) || forcing_shutdown) - return POWER_S3S5; - else if (power_get_signals() & IN_SUSPEND_DEASSERTED) - return POWER_S3S0; - break; - - case POWER_S0: - if (!power_has_signals(IN_PGOOD_S3) || - forcing_shutdown || - !(power_get_signals() & IN_SUSPEND_DEASSERTED)) - return POWER_S0S3; - -#if CONFIG_CHIPSET_POWER_SEQ_VERSION != 1 - /* - * Wait up to PGOOD_AP_DEBOUNCE_TIMEOUT for IN_PGOOD_AP to - * come back before transitioning back to S3. PGOOD_SYS can - * also glitch, with a glitch duration < 1ms, so debounce - * it here as well. - */ - if (power_wait_signals_timeout(IN_PGOOD_AP | IN_PGOOD_SYS, - PGOOD_AP_DEBOUNCE_TIMEOUT) - == EC_ERROR_TIMEOUT) - return POWER_S0S3; - - /* - * power_wait_signals_timeout() can block and consume task - * wake events, so re-verify the state of the world. - */ - if (!power_has_signals(IN_PGOOD_S3) || - forcing_shutdown || - !(power_get_signals() & IN_SUSPEND_DEASSERTED)) - return POWER_S0S3; -#endif - - break; - - case POWER_G3S5: - forcing_shutdown = 0; - - /* - * 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; - } - - /* Power up to next state */ - return POWER_S5; - - case POWER_S5S3: - power_seq_run(s5s3_power_seq, ARRAY_SIZE(s5s3_power_seq)); - -#ifndef CR50_DEEP_SLEEP - /* - * Assert SYS_RST now, to be released in S3S0, to avoid - * resetting the TPM soon after power-on. - */ - sys_reset_asserted = 1; -#endif - - if (power_wait_signals(IN_PGOOD_S3)) { - chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT); - return POWER_S3S5; - } - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_STARTUP); - - /* Power up to next state */ - return POWER_S3; - - case POWER_S3S0: -#ifdef S3_USB_WAKE - /* Bring up S3 USB wake rails, if they are down */ - if (!usb_wake_enabled) - power_seq_run(s3s0_usb_wake_power_seq, - ARRAY_SIZE(s3s0_usb_wake_power_seq)); - usb_wake_enabled = 0; -#endif - power_seq_run(s3s0_power_seq, ARRAY_SIZE(s3s0_power_seq)); - -#ifndef CR50_DEEP_SLEEP - /* Release SYS_RST if we came from S5 */ - if (sys_reset_asserted) { -#endif - msleep(10); - gpio_set_level(GPIO_SYS_RST_L, 1); - -#ifndef CR50_DEEP_SLEEP - sys_reset_asserted = 0; - } -#endif - -#ifdef POWER_SIGNAL_POLLING - /* - * Poll power signals every PGOOD_S0_POLL_TIMEOUT us, since - * AP_PWR_GOOD assertion doesn't trigger a power signal - * interrupt. - */ - while (power_wait_signals_timeout(IN_PGOOD_S0, - PGOOD_S0_POLL_TIMEOUT) == EC_ERROR_TIMEOUT && - ++tries < PGOOD_S0_POLL_TRIES) - ; - - if (tries >= PGOOD_S0_POLL_TRIES) { - CPRINTS("power timeout on input; " - "wanted 0x%04x, got 0x%04x", - IN_PGOOD_S0, power_get_signals() & IN_PGOOD_S0); -#else - if (power_wait_signals(IN_PGOOD_S0)) { -#endif /* POWER_SIGNAL_POLLING */ - chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT); - return POWER_S0S3; - } - - /* Call hooks now that rails are up */ - hook_notify(HOOK_CHIPSET_RESUME); - - /* - * 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); - MSLEEP_CHECK_ABORTED_SUSPEND(20); - - if (power_seq_run(s0s3_power_seq, ARRAY_SIZE(s0s3_power_seq))) - return POWER_S3S0; - -#ifdef S3_USB_WAKE - /* Leave up rails needed for S3 USB wake, if requested */ - usb_wake_enabled = (power_get_host_sleep_state() == - HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND); - if (!usb_wake_enabled && - power_seq_run(s0s3_usb_wake_power_seq, - ARRAY_SIZE(s0s3_usb_wake_power_seq))) - return POWER_S3S0; -#endif - - /* - * 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(&force_shutdown_data, -1); - } - - return POWER_S3; - - case POWER_S3S5: -#ifdef S3_USB_WAKE - /* Make sure all S3 rails are off */ - if (usb_wake_enabled) { - power_seq_run(s0s3_usb_wake_power_seq, - ARRAY_SIZE(s0s3_usb_wake_power_seq)); - usb_wake_enabled = 0; - } -#endif - - /* Call hooks before we remove power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - 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: - return POWER_G3; - } - - return state; -} - -static void power_button_changed(void) -{ - static uint8_t tablet_boot_on_button_release; - - if (power_button_is_pressed()) { - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { -#if CONFIG_CHIPSET_POWER_SEQ_VERSION != 1 - /* Power up from off */ - chipset_exit_hard_off(); -#else - tablet_boot_on_button_release = 1; -#endif - } - /* Delayed power down from S0/S3, cancel on PB release */ - hook_call_deferred(&force_shutdown_data, - FORCED_SHUTDOWN_DELAY); - } else { -#if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 - if (tablet_boot_on_button_release) { - /* Power up from off */ - chipset_exit_hard_off(); - tablet_boot_on_button_release = 0; - } -#endif - /* Power button released, cancel deferred shutdown */ - hook_call_deferred(&force_shutdown_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 - -#ifdef POWER_SIGNAL_POLLING -/* - * Polling for non-INT power signal pins. - * Call power_signal_interrupt() when the GPIO status of those pins changes. - */ -static void power_signal_changed(void) -{ - static uint8_t in_signals; /* Current power signal status */ - uint8_t inew = 0; - const struct power_signal_info *s = power_signal_list; - int i; - - BUILD_ASSERT(POWER_SIGNAL_COUNT <= 8); - - for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) { - /* Skip if this is an INT pin. */ - if (s->gpio < GPIO_IH_COUNT) - continue; - - if (power_signal_is_asserted(s)) - inew |= 1 << i; - } - - if (inew != in_signals) { - /* - * Pass a fake power gpio_signal to power_signal_interrupt(). - * Note that here we make power_signal_interrupt() reentrant. - */ - power_signal_interrupt(POWER_SIGNAL_COUNT); - in_signals = inew; - } -} -DECLARE_HOOK(HOOK_TICK, power_signal_changed, HOOK_PRIO_DEFAULT); -#endif diff --git a/power/sdm845.c b/power/sdm845.c deleted file mode 100644 index a8cc70b8ea..0000000000 --- a/power/sdm845.c +++ /dev/null @@ -1,882 +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. - */ - -/* - * SDM845 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 pressed again - * - Lid open turns on the AP - * - * When powered on: - * - Holding power button for 8s powers off the AP - * - Pressing and releasing pwron within that 8s is ignored - * - If POWER_GOOD is dropped by the AP, then we power the AP off - */ - -#include "charge_state.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 "util.h" - -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -/* Masks for power signals */ -#define IN_POWER_GOOD POWER_SIGNAL_MASK(SDM845_POWER_GOOD) -#define IN_AP_RST_ASSERTED POWER_SIGNAL_MASK(SDM845_AP_RST_ASSERTED) - - -/* Long power key press to force shutdown */ -#define DELAY_FORCE_SHUTDOWN (8 * SECOND) - -/* - * If the power button is pressed to turn on, then held for this long, we - * power off. - * - * Normal case: User releases power button and chipset_task() goes - * into the inner loop, waiting for next event to occur (power button - * press or POWER_GOOD == 0). - */ -#define DELAY_SHUTDOWN_ON_POWER_HOLD (8 * SECOND) - -/* - * After trigger PMIC power sequence, how long it triggers AP to turn on - * or off. Observed that the worst case is ~150ms. Pick a safe vale. - */ -#define PMIC_POWER_AP_RESPONSE_TIMEOUT (350 * MSEC) - -/* - * After force off the switch cap, how long the PMIC/AP totally off. - * Observed that the worst case is 2s. Pick a safe vale. - */ -#define FORCE_OFF_RESPONSE_TIMEOUT (4 * SECOND) - -/* Wait for polling the AP on signal */ -#define PMIC_POWER_AP_WAIT (1 * MSEC) - -/* The length of an issued low pulse to the PMIC_RESIN_L signal */ -#define PMIC_RESIN_PULSE_LENGTH (20 * MSEC) - -/* The timeout of the check if the system can boot AP */ -#define CAN_BOOT_AP_CHECK_TIMEOUT (500 * MSEC) - -/* Wait for polling if the system can boot AP */ -#define CAN_BOOT_AP_CHECK_WAIT (100 * MSEC) - -/* The timeout of the check if the switchcap outputs good voltage */ -#define SWITCHCAP_PG_CHECK_TIMEOUT (50 * MSEC) - -/* Wait for polling if the switchcap outputs good voltage */ -#define SWITCHCAP_PG_CHECK_WAIT (5 * MSEC) - -/* Delay between power-on the system and power-on the PMIC */ -#define SYSTEM_POWER_ON_DELAY (10 * MSEC) - -/* TODO(crosbug.com/p/25047): move to HOOK_POWER_BUTTON_CHANGE */ -/* 1 if the power button was pressed last time we checked */ -static char power_button_was_pressed; - -/* 1 if lid-open event has been detected */ -static char lid_opened; - -/* 1 if AP_RST_L and PS_HOLD is overdriven by EC */ -static char ap_rst_overdriven; - -/* Time where we will power off, if power button still held down */ -static timestamp_t power_off_deadline; - -/* Force AP power on (used for recovery keypress) */ -static int auto_power_on; - -enum power_request_t { - POWER_REQ_NONE, - POWER_REQ_OFF, - POWER_REQ_ON, - POWER_REQ_RESET, - - POWER_REQ_COUNT, -}; - -static enum power_request_t power_request; - -/** - * Return values for check_for_power_off_event(). - */ -enum power_off_event_t { - POWER_OFF_CANCEL, - POWER_OFF_BY_POWER_BUTTON_PRESSED, - POWER_OFF_BY_LONG_PRESS, - POWER_OFF_BY_POWER_GOOD_LOST, - POWER_OFF_BY_POWER_REQ_OFF, - POWER_OFF_BY_POWER_REQ_RESET, - - POWER_OFF_EVENT_COUNT, -}; - -/** - * Return values for check_for_power_on_event(). - */ -enum power_on_event_t { - POWER_ON_CANCEL, - POWER_ON_BY_IN_POWER_GOOD, - POWER_ON_BY_AUTO_POWER_ON, - POWER_ON_BY_LID_OPEN, - POWER_ON_BY_POWER_BUTTON_PRESSED, - POWER_ON_BY_POWER_REQ_ON, - POWER_ON_BY_POWER_REQ_RESET, - - POWER_ON_EVENT_COUNT, -}; - -/* Issue a request to initiate a reset sequence */ -static void request_cold_reset(void) -{ - power_request = POWER_REQ_RESET; - task_wake(TASK_ID_CHIPSET); -} - -/* AP-requested reset GPIO interrupt handlers */ -static void chipset_reset_request_handler(void) -{ - CPRINTS("AP wants reset"); - chipset_reset(CHIPSET_RESET_AP_REQ); -} -DECLARE_DEFERRED(chipset_reset_request_handler); - -void chipset_reset_request_interrupt(enum gpio_signal signal) -{ - hook_call_deferred(&chipset_reset_request_handler_data, 0); -} - -void chipset_warm_reset_interrupt(enum gpio_signal signal) -{ - /* - * The warm_reset signal is pulled-up by a rail from PMIC. If the - * warm_reset drops, it means: - * * Servo or Cr50 holds the signal, or - * * its pull-up rail POWER_GOOD drops. - */ - if (!gpio_get_level(GPIO_WARM_RESET_L)) { - if (gpio_get_level(GPIO_POWER_GOOD)) { - /* - * Servo or Cr50 holds the WARM_RESET_L signal. - * - * Overdrive AP_RST_L to hold AP. Overdrive PS_HOLD to - * emulate AP being up to trick the PMIC into thinking - * there’s nothing weird going on. - */ - ap_rst_overdriven = 1; - gpio_set_flags(GPIO_PS_HOLD, GPIO_INT_BOTH | - GPIO_SEL_1P8V | GPIO_OUT_HIGH); - gpio_set_flags(GPIO_AP_RST_L, GPIO_INT_BOTH | - GPIO_SEL_1P8V | GPIO_OUT_LOW); - } else { - /* - * The pull-up rail POWER_GOOD drops. - * - * High-Z both AP_RST_L and PS_HOLD to restore their - * states. - */ - gpio_set_flags(GPIO_AP_RST_L, GPIO_INT_BOTH | - GPIO_SEL_1P8V); - gpio_set_flags(GPIO_PS_HOLD, GPIO_INT_BOTH | - GPIO_SEL_1P8V); - ap_rst_overdriven = 0; - } - } else { - if (ap_rst_overdriven) { - /* - * Servo or Cr50 releases the WARM_RESET_L signal. - * - * Cold reset the PMIC, doing S0->S5->S0 transition, - * by issuing a request to initiate a reset sequence, - * to recover the system. The transition to S5 makes - * POWER_GOOD drop that triggers an interrupt to - * high-Z both AP_RST_L and PS_HOLD. - */ - request_cold_reset(); - } - /* If not overdriven, just a normal power-up, do nothing. */ - } - - power_signal_interrupt(signal); -} - -static void sdm845_lid_event(void) -{ - /* Power task only cares about lid-open events */ - if (!lid_is_open()) - return; - - lid_opened = 1; - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, sdm845_lid_event, HOOK_PRIO_DEFAULT); - -static void powerbtn_sdm845_changed(void) -{ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, powerbtn_sdm845_changed, - HOOK_PRIO_DEFAULT); - -/** - * Wait the switchcap GPIO0 PVC_PG signal asserted. - * - * When the output voltage is over the threshold PVC_PG_ADJ, - * the PVC_PG is asserted. - * - * PVG_PG_ADJ is configured to 3.0V. - * GPIO0 is configured as PVC_PG. - * - * @param enable 1 to wait the PMIC/AP on. - 0 to wait the PMIC/AP off. - */ -static void wait_switchcap_power_good(int enable) -{ - timestamp_t poll_deadline; - - poll_deadline = get_time(); - poll_deadline.val += SWITCHCAP_PG_CHECK_TIMEOUT; - while (enable != gpio_get_level(GPIO_DA9313_GPIO0) && - get_time().val < poll_deadline.val) { - usleep(SWITCHCAP_PG_CHECK_WAIT); - } - - /* - * Check the timeout case. Just show a message. More check later - * will switch the power state. - */ - if (enable != gpio_get_level(GPIO_DA9313_GPIO0)) { - if (enable) - CPRINTS("SWITCHCAP NO POWER GOOD!"); - else - CPRINTS("SWITCHCAP STILL POWER GOOD!"); - } - -} - -/** - * Get the state of the system power signals. - * - * @return 1 if the system is powered, 0 if not - */ -static int is_system_powered(void) -{ - return gpio_get_level(GPIO_SWITCHCAP_ON_L); -} - -/** - * Get the PMIC/AP power signal. - * - * We treat the PMIC chips and the AP as a whole here. Don't deal with - * the individual chip. - * - * @return 1 if the PMIC/AP is powered, 0 if not - */ -static int is_pmic_pwron(void) -{ - /* Use POWER_GOOD to indicate PMIC/AP is on/off */ - return gpio_get_level(GPIO_POWER_GOOD); -} - -/** - * Wait the PMIC/AP power-on state. - * - * @param enable 1 to wait the PMIC/AP on. - 0 to wait the PMIC/AP off. - * @param timeout Number of microsecond of timeout. - */ -static void wait_pmic_pwron(int enable, unsigned int timeout) -{ - timestamp_t poll_deadline; - - /* Check the AP power status */ - if (enable == is_pmic_pwron()) - return; - - poll_deadline = get_time(); - poll_deadline.val += timeout; - while (enable != is_pmic_pwron() && - get_time().val < poll_deadline.val) { - usleep(PMIC_POWER_AP_WAIT); - } - - /* Check the timeout case */ - if (enable != is_pmic_pwron()) { - if (enable) - CPRINTS("AP POWER NOT READY!"); - else - CPRINTS("AP POWER STILL UP!"); - } -} - -/** - * Set the state of the system power signals. - * - * The system power signals are the enable pins of SwitchCap and VBOB. - * They control the power of the set of PMIC chips and the AP. - * - * @param enable 1 to enable or 0 to disable - */ -static void set_system_power(int enable) -{ - CPRINTS("%s(%d)", __func__, enable); - gpio_set_level(GPIO_SWITCHCAP_ON_L, enable); - wait_switchcap_power_good(enable); - gpio_set_level(GPIO_VBOB_EN, enable); - if (enable) { - usleep(SYSTEM_POWER_ON_DELAY); - } else { - /* Ensure POWER_GOOD drop to low if it is a forced shutdown */ - wait_pmic_pwron(0, FORCE_OFF_RESPONSE_TIMEOUT); - } -} - -/** - * Set the PMIC/AP power-on state. - * - * It triggers the PMIC/AP power-on and power-off sequence. - * - * @param enable 1 to power the PMIC/AP on. - 0 to power the PMIC/AP off. - */ -static void set_pmic_pwron(int enable) -{ - CPRINTS("%s(%d)", __func__, enable); - - /* Check the PMIC/AP power state */ - if (enable == is_pmic_pwron()) - return; - - /* - * Power-on sequence: - * 1. Hold down PMIC_KPD_PWR_ODL, which is a power-on trigger - * 2. PM845 supplies power to POWER_GOOD - * 3. Release PMIC_KPD_PWR_ODL - * - * Power-off sequence: - * 1. Hold down PMIC_KPD_PWR_ODL and PMIC_RESIN_L, which is a power-off - * trigger (requiring reprogramming PMIC registers to make - * PMIC_KPD_PWR_ODL + PMIC_RESIN_L as a shutdown trigger) - * 2. PM845 stops supplying power to POWER_GOOD (requiring - * reprogramming PMIC to set the stage-1 and stage-2 reset timers to - * 0 such that the pull down happens just after the deboucing time - * of the trigger, like 2ms) - * 3. Release PMIC_KPD_PWR_ODL and PMIC_RESIN_L - * - * If the above PMIC registers not programmed or programmed wrong, it - * falls back to the next functions, which cuts off the system power. - */ - - gpio_set_level(GPIO_PMIC_KPD_PWR_ODL, 0); - if (!enable) - gpio_set_level(GPIO_PMIC_RESIN_L, 0); - wait_pmic_pwron(enable, PMIC_POWER_AP_RESPONSE_TIMEOUT); - gpio_set_level(GPIO_PMIC_KPD_PWR_ODL, 1); - if (!enable) - gpio_set_level(GPIO_PMIC_RESIN_L, 1); -} - -enum power_state power_chipset_init(void) -{ - int init_power_state; - uint32_t reset_flags = system_get_reset_flags(); - - /* Enable interrupts */ - gpio_enable_interrupt(GPIO_AP_RST_REQ); - gpio_enable_interrupt(GPIO_WARM_RESET_L); - gpio_enable_interrupt(GPIO_POWER_GOOD); - - /* - * Force the AP shutdown unless we are doing SYSJUMP. Otherwise, - * the AP could stay in strange state. - */ - if (!(reset_flags & EC_RESET_FLAG_SYSJUMP)) { - CPRINTS("not sysjump; forcing system shutdown"); - set_system_power(0); - init_power_state = POWER_G3; - } else { - /* In the SYSJUMP case, we check if the AP is on */ - if (power_get_signals() & IN_POWER_GOOD) { - CPRINTS("SOC ON"); - init_power_state = POWER_S0; - /* Disable idle task deep sleep when in S0 */ - disable_sleep(SLEEP_MASK_AP_RUN); - } else { - CPRINTS("SOC OFF"); - init_power_state = POWER_G3; - } - } - - /* Leave power off only if requested by reset flags */ - if (!(reset_flags & EC_RESET_FLAG_AP_OFF) && - !(reset_flags & EC_RESET_FLAG_SYSJUMP)) { - CPRINTS("auto_power_on set due to reset_flag 0x%x", - system_get_reset_flags()); - auto_power_on = 1; - } - - 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(); - } - - return init_power_state; -} - -/*****************************************************************************/ - -/** - * Power off the AP - */ -static void power_off(void) -{ - /* Check the power off status */ - if (!is_system_powered()) - return; - - /* Call hooks before we drop power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN); - - /* Do a graceful way to shutdown PMIC/AP first */ - set_pmic_pwron(0); - - /* Disable signal interrupts, as they are floating when switchcap off */ - power_signal_disable_interrupt(GPIO_AP_RST_L); - power_signal_disable_interrupt(GPIO_PMIC_FAULT_L); - - /* Force to switch off all rails */ - set_system_power(0); - - /* Turn off the 3.3V and 5V rails. */ - gpio_set_level(GPIO_EN_PP3300_A, 0); -#ifdef CONFIG_POWER_PP5000_CONTROL - power_5v_enable(task_get_current(), 0); -#else /* !defined(CONFIG_POWER_PP5000_CONTROL) */ - gpio_set_level(GPIO_EN_PP5000, 0); -#endif /* defined(CONFIG_POWER_PP5000_CONTROL) */ - - lid_opened = 0; - enable_sleep(SLEEP_MASK_AP_RUN); - CPRINTS("power shutdown complete"); - - /* Call hooks after we drop power rails */ - hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); -} - -/** - * Check if the power is enough to boot the AP. - */ -static int power_is_enough(void) -{ - timestamp_t poll_deadline; - - /* If powered by adapter only, wait a while for PD negoiation. */ - poll_deadline = get_time(); - poll_deadline.val += CAN_BOOT_AP_CHECK_TIMEOUT; - - /* - * Wait for PD negotiation. If a system with drained battery, don't - * waste the time and exit the loop. - */ - while (!system_can_boot_ap() && !charge_want_shutdown() && - get_time().val < poll_deadline.val) { - usleep(CAN_BOOT_AP_CHECK_WAIT); - } - - return system_can_boot_ap() && !charge_want_shutdown(); -} - -/** - * Power on the AP - */ -static void power_on(void) -{ - /* - * If no enough power, return and the state machine will transition - * back to S5. - */ - if (!power_is_enough()) - return; - - /* - * When power_on() is called, we are at S5S3. Initialize components - * to ready state before AP is up. - */ - hook_notify(HOOK_CHIPSET_PRE_INIT); - - /* Enable the 3.3V and 5V rail. */ - gpio_set_level(GPIO_EN_PP3300_A, 1); -#ifdef CONFIG_POWER_PP5000_CONTROL - power_5v_enable(task_get_current(), 1); -#else /* !defined(CONFIG_POWER_PP5000_CONTROL) */ - gpio_set_level(GPIO_EN_PP5000, 1); -#endif /* defined(CONFIG_POWER_PP5000_CONTROL) */ - - set_system_power(1); - - /* Enable signal interrupts */ - power_signal_enable_interrupt(GPIO_AP_RST_L); - power_signal_enable_interrupt(GPIO_PMIC_FAULT_L); - - set_pmic_pwron(1); - - disable_sleep(SLEEP_MASK_AP_RUN); - - CPRINTS("AP running ..."); -} - -/** - * Check if there has been a power-on event - * - * This checks all power-on event signals and returns non-zero if any have been - * triggered (with debounce taken into account). - * - * @return non-zero if there has been a power-on event, 0 if not. - */ -static uint8_t check_for_power_on_event(void) -{ - int ap_off_flag; - - ap_off_flag = system_get_reset_flags() & EC_RESET_FLAG_AP_OFF; - system_clear_reset_flags(EC_RESET_FLAG_AP_OFF); - - if (power_request == POWER_REQ_ON) { - power_request = POWER_REQ_NONE; - return POWER_ON_BY_POWER_REQ_ON; - } else if (power_request == POWER_REQ_RESET) { - power_request = POWER_REQ_NONE; - return POWER_ON_BY_POWER_REQ_RESET; - } - /* Clear invalid request */ - power_request = POWER_REQ_NONE; - - /* check if system is already ON */ - if (power_get_signals() & IN_POWER_GOOD) { - if (ap_off_flag) { - CPRINTS("system is on, but EC_RESET_FLAG_AP_OFF is on"); - return POWER_ON_CANCEL; - } - CPRINTS("system is on, thus clear auto_power_on"); - /* no need to arrange another power on */ - auto_power_on = 0; - return POWER_ON_BY_IN_POWER_GOOD; - } - if (ap_off_flag) { - CPRINTS("EC_RESET_FLAG_AP_OFF is on"); - power_off(); - return POWER_ON_CANCEL; - } - - CPRINTS("POWER_GOOD is not asserted"); - - /* power on requested at EC startup for recovery */ - if (auto_power_on) { - auto_power_on = 0; - return POWER_ON_BY_AUTO_POWER_ON; - } - - /* Check lid open */ - if (lid_opened) { - lid_opened = 0; - return POWER_ON_BY_LID_OPEN; - } - - /* check for power button press */ - if (power_button_is_pressed()) - return POWER_ON_BY_POWER_BUTTON_PRESSED; - - return POWER_OFF_CANCEL; -} - -/** - * Check for some event triggering the shutdown. - * - * It can be either a long power button press or a shutdown triggered from the - * AP and detected by reading POWER_GOOD. - * - * @return non-zero if a shutdown should happen, 0 if not - */ -static uint8_t check_for_power_off_event(void) -{ - timestamp_t now; - int pressed = 0; - - if (power_request == POWER_REQ_OFF) { - power_request = POWER_REQ_NONE; - return POWER_OFF_BY_POWER_REQ_OFF; - } else if (power_request == POWER_REQ_RESET) { - /* - * The power_request flag will be cleared later - * in check_for_power_on_event() in S5. - */ - return POWER_OFF_BY_POWER_REQ_RESET; - } - /* Clear invalid request */ - power_request = POWER_REQ_NONE; - - /* - * Check for power button press. - */ - if (power_button_is_pressed()) - pressed = POWER_OFF_BY_POWER_BUTTON_PRESSED; - - now = get_time(); - if (pressed) { - if (!power_button_was_pressed) { - power_off_deadline.val = now.val + DELAY_FORCE_SHUTDOWN; - CPRINTS("power waiting for long press %u", - power_off_deadline.le.lo); - /* Ensure we will wake up to check the power key */ - timer_arm(power_off_deadline, TASK_ID_CHIPSET); - } else if (timestamp_expired(power_off_deadline, &now)) { - power_off_deadline.val = 0; - CPRINTS("power off after long press now=%u, %u", - now.le.lo, power_off_deadline.le.lo); - return POWER_OFF_BY_LONG_PRESS; - } - } else if (power_button_was_pressed) { - CPRINTS("power off cancel"); - timer_cancel(TASK_ID_CHIPSET); - } - - power_button_was_pressed = pressed; - - /* POWER_GOOD released by AP : shutdown immediately */ - if (!power_has_signals(IN_POWER_GOOD)) { - if (power_button_was_pressed) - timer_cancel(TASK_ID_CHIPSET); - - CPRINTS("POWER_GOOD is lost"); - return POWER_OFF_BY_POWER_GOOD_LOST; - } - - return POWER_OFF_CANCEL; -} - -/*****************************************************************************/ -/* Chipset interface */ - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - - /* Issue a request to initiate a power-off sequence */ - power_request = POWER_REQ_OFF; - task_wake(TASK_ID_CHIPSET); -} - -void chipset_reset(enum chipset_reset_reason reason) -{ - int rv; - - CPRINTS("%s(%d)", __func__, reason); - report_ap_reset(reason); - - /* - * Warm reset sequence: - * 1. Issue a low pulse to PMIC_RESIN_L, which triggers PMIC - * to do a warm reset (requiring reprogramming PMIC registers - * to make PMIC_RESIN_L as a warm reset trigger). - * 2. PMIC then issues a low pulse to AP_RST_L to reset AP. - * EC monitors the signal to see any low pulse. - * 2.1. If a low pulse found, done. - * 2.2. If a low pulse not found (the above PMIC registers - * not programmed or programmed wrong), issue a request - * to initiate a cold reset power sequence. - */ - - gpio_set_level(GPIO_PMIC_RESIN_L, 0); - usleep(PMIC_RESIN_PULSE_LENGTH); - gpio_set_level(GPIO_PMIC_RESIN_L, 1); - - rv = power_wait_signals_timeout(IN_AP_RST_ASSERTED, - PMIC_POWER_AP_RESPONSE_TIMEOUT); - /* Exception case: PMIC not work as expected, request a cold reset */ - if (rv != EC_SUCCESS) - request_cold_reset(); -} - -/** - * Power handler for steady states - * - * @param state Current power state - * @return Updated power state - */ -enum power_state power_handle_state(enum power_state state) -{ - uint8_t value; - static uint8_t boot_from_g3, shutdown_from_s0; - - switch (state) { - case POWER_G3: - boot_from_g3 = check_for_power_on_event(); - if (boot_from_g3) - return POWER_G3S5; - break; - - case POWER_G3S5: - return POWER_S5; - - case POWER_S5: - if (boot_from_g3) { - value = boot_from_g3; - boot_from_g3 = 0; - } else { - value = check_for_power_on_event(); - } - - if (value) { - CPRINTS("power on %d", value); - return POWER_S5S3; - } - break; - - case POWER_S5S3: - /* - * Wait for power button release before actually boot AP. - * It may be a long-hold power button with volume buttons - * to trigger the recovery button. We don't want AP up - * during the long-hold. - */ - power_button_wait_for_release(-1); - - power_on(); - if (power_wait_signals(IN_POWER_GOOD) != EC_SUCCESS) { - CPRINTS("POWER_GOOD not seen in time"); - set_system_power(0); - return POWER_S5; - } - - CPRINTS("POWER_GOOD seen"); - /* Call hooks now that AP is running */ - hook_notify(HOOK_CHIPSET_STARTUP); - return POWER_S3; - - case POWER_S3: - if (shutdown_from_s0) { - value = shutdown_from_s0; - shutdown_from_s0 = 0; - } else { - value = check_for_power_off_event(); - } - - if (value) { - CPRINTS("power off %d", value); - return POWER_S3S5; - } - /* Go to S3S0 directly, as don't know if it is in suspend */ - return POWER_S3S0; - - case POWER_S3S0: - hook_notify(HOOK_CHIPSET_RESUME); - return POWER_S0; - - case POWER_S0: - shutdown_from_s0 = check_for_power_off_event(); - if (shutdown_from_s0) - return POWER_S0S3; - break; - - case POWER_S0S3: - /* - * If the power button is pressing, we need cancel the long - * press timer, otherwise EC will crash. - */ - if (power_button_was_pressed) - timer_cancel(TASK_ID_CHIPSET); - - /* Call hooks here since we don't know it prior to AP suspend */ - hook_notify(HOOK_CHIPSET_SUSPEND); - return POWER_S3; - - case POWER_S3S5: - power_off(); - /* - * Wait forever for the release of the power button; otherwise, - * this power button press will then trigger a power-on in S5. - */ - power_button_wait_for_release(-1); - power_button_was_pressed = 0; - return POWER_S5; - - case POWER_S5G3: - return POWER_G3; - } - - return state; -} - -/*****************************************************************************/ -/* Console debug command */ - -static const char *power_req_name[POWER_REQ_COUNT] = { - "none", - "off", - "on", -}; - -/* Power states that we can report */ -enum power_state_t { - PSTATE_UNKNOWN, - PSTATE_OFF, - PSTATE_ON, - PSTATE_COUNT, -}; - -static const char * const state_name[] = { - "unknown", - "off", - "on", -}; - -static int command_power(int argc, char **argv) -{ - int v; - - if (argc < 2) { - enum power_state_t state; - - state = PSTATE_UNKNOWN; - if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) - state = PSTATE_OFF; - if (chipset_in_state(CHIPSET_STATE_ON)) - state = PSTATE_ON; - ccprintf("%s\n", state_name[state]); - - return EC_SUCCESS; - } - - if (!parse_bool(argv[1], &v)) - return EC_ERROR_PARAM1; - - power_request = v ? POWER_REQ_ON : POWER_REQ_OFF; - ccprintf("Requesting power %s\n", power_req_name[power_request]); - task_wake(TASK_ID_CHIPSET); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(power, command_power, - "on/off", - "Turn AP power on/off"); diff --git a/power/skylake.c b/power/skylake.c deleted file mode 100644 index a4cb649fd5..0000000000 --- a/power/skylake.c +++ /dev/null @@ -1,194 +0,0 @@ -/* Copyright 2015 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. - */ - -/* Skylake IMVP8 / ROP PMIC chipset power control module for Chrome EC */ - -#include "chipset.h" -#include "console.h" -#include "gpio.h" -#include "hooks.h" -#include "lpc.h" -#include "panic.h" -#include "power/intel_x86.h" -#include "power_button.h" -#include "system.h" -#include "timer.h" - -/* Console output macros */ -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) - -static int forcing_shutdown; /* Forced shutdown in progress? */ - -/* Power signals list. Must match order of enum power_signal. */ -const struct power_signal_info power_signal_list[] = { -#ifdef CONFIG_POWER_S0IX - [X86_SLP_S0_DEASSERTED] = { - GPIO_PCH_SLP_S0_L, - POWER_SIGNAL_ACTIVE_HIGH | POWER_SIGNAL_DISABLE_AT_BOOT, - "SLP_S0_DEASSERTED", - }, -#endif - [X86_SLP_S3_DEASSERTED] = { - SLP_S3_SIGNAL_L, - POWER_SIGNAL_ACTIVE_HIGH, - "SLP_S3_DEASSERTED", - }, - [X86_SLP_S4_DEASSERTED] = { - SLP_S4_SIGNAL_L, - POWER_SIGNAL_ACTIVE_HIGH, - "SLP_S4_DEASSERTED", - }, - [X86_SLP_SUS_DEASSERTED] = { - GPIO_PCH_SLP_SUS_L, - POWER_SIGNAL_ACTIVE_HIGH, - "SLP_SUS_DEASSERTED", - }, - [X86_RSMRST_L_PWRGD] = { - GPIO_RSMRST_L_PGOOD, - POWER_SIGNAL_ACTIVE_HIGH, - "RSMRST_N_PWRGD", - }, - [X86_PMIC_DPWROK] = { - GPIO_PMIC_DPWROK, - POWER_SIGNAL_ACTIVE_HIGH, - "PMIC_DPWROK", - }, -}; -BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); - - -void chipset_force_shutdown(enum chipset_shutdown_reason reason) -{ - CPRINTS("%s()", __func__); - - /* - * Force off. Sending a reset command to the PMIC will power off - * the EC, so simulate a long power button press instead. This - * condition will reset once the state machine transitions to G3. - * Consider reducing the latency here by changing the power off - * hold time on the PMIC. - */ - if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) { - report_ap_reset(reason); - forcing_shutdown = 1; - power_button_pch_press(); - } -} - -__attribute__((weak)) void chipset_set_pmic_slp_sus_l(int level) -{ - gpio_set_level(GPIO_PMIC_SLP_SUS_L, level); -} - -enum power_state chipset_force_g3(void) -{ - CPRINTS("Forcing fake G3."); - - chipset_set_pmic_slp_sus_l(0); - - return POWER_G3; -} - -static void handle_slp_sus(enum power_state state) -{ - /* If we're down or going down don't do anythin with SLP_SUS_L. */ - if (state == POWER_G3 || state == POWER_S5G3) - return; - - /* Always mimic PCH SLP_SUS request for all other states. */ - chipset_set_pmic_slp_sus_l(gpio_get_level(GPIO_PCH_SLP_SUS_L)); -} - -void chipset_handle_espi_reset_assert(void) -{ - /* - * If eSPI_Reset# pin is asserted without SLP_SUS# being asserted, then - * it means that there is an unexpected power loss (global reset - * event). In this case, check if shutdown was being forced by pressing - * power button. If yes, release power button. - */ - if ((power_get_signals() & IN_PCH_SLP_SUS_DEASSERTED) && - forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } -} - -enum power_state power_handle_state(enum power_state state) -{ - enum power_state new_state; - - /* Process RSMRST_L state changes. */ - common_intel_x86_handle_rsmrst(state); - - if (state == POWER_S5 && forcing_shutdown) { - power_button_pch_release(); - forcing_shutdown = 0; - } - - new_state = common_intel_x86_power_handle_state(state); - - /* Process SLP_SUS_L state changes after a new state is decided. */ - handle_slp_sus(new_state); - - return new_state; -} - -/* Workaround for flags getting lost with power cycle */ -__attribute__((weak)) int board_has_working_reset_flags(void) -{ - return 1; -} - -#ifdef CONFIG_CHIPSET_HAS_PLATFORM_PMIC_RESET -void chipset_handle_reboot(void) -{ - int flags; - - if (system_jumped_to_this_image()) - return; - - /* Interrogate current reset flags from previous reboot. */ - flags = system_get_reset_flags(); - - /* - * Do not make PMIC re-sequence the power rails if the following reset - * conditions are not met. - */ - if (!(flags & - (EC_RESET_FLAG_WATCHDOG | EC_RESET_FLAG_SOFT | - EC_RESET_FLAG_HARD))) - return; - - /* Preserve AP off request. */ - if (flags & EC_RESET_FLAG_AP_OFF) { - /* Do not issue PMIC reset if board cannot save reset flags */ - if (!board_has_working_reset_flags()) { - ccprintf("Skip PMIC reset due to board issue.\n"); - cflush(); - return; - } - chip_save_reset_flags(EC_RESET_FLAG_AP_OFF); - } - -#ifdef CONFIG_CHIP_PANIC_BACKUP - /* Ensure panic data if any is backed up. */ - chip_panic_data_backup(); -#endif - - ccprintf("Restarting system with PMIC.\n"); - /* Flush console */ - cflush(); - - /* Bring down all rails but RTC rail (including EC power). */ - gpio_set_level(GPIO_EC_PLATFORM_RST, 1); - while (1) - ; /* wait here */ -} -#if !defined(CONFIG_VBOOT_EFS) || !defined(CONFIG_VBOOT_EFS2) -/* This is run in main for EFS1 & EFS2 */ -DECLARE_HOOK(HOOK_INIT, chipset_handle_reboot, HOOK_PRIO_FIRST); -#endif -#endif /* CONFIG_CHIPSET_HAS_PLATFORM_RESET */ |