diff options
Diffstat (limited to 'power/amd_x86.c')
-rw-r--r-- | power/amd_x86.c | 524 |
1 files changed, 0 insertions, 524 deletions
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; -} |