diff options
author | Rob Barnes <robbarnes@google.com> | 2021-02-09 07:41:36 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-02-10 18:03:54 +0000 |
commit | d91a433c715099dc2bf7a2f5e9155fcfec69ef16 (patch) | |
tree | 3fed18cfa48f929a163638dd7569a72749ef829f /power/amd_x86.c | |
parent | 323a9788a25afe73ca646ef14824ca22711c506a (diff) | |
download | chrome-ec-d91a433c715099dc2bf7a2f5e9155fcfec69ef16.tar.gz |
power: Rename stoney.c to amd_x86.c
Rename stoney.c to amd_x86.c since it covers all currently supported
AMD chipsets. Add CHIPSET_CEZANNE to guard any differences between
STONEY and CEZANNE chipsets.
BUG=b:175234270
BRANCH=None
TEST=Build for zork and guybrush
Signed-off-by: Rob Barnes <robbarnes@google.com>
Change-Id: I96f82127729d64970b8d46fc8ef4ddba6489dd8a
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2683923
Reviewed-by: Edward Hill <ecgh@chromium.org>
Reviewed-by: Denis Brockus <dbrockus@chromium.org>
Reviewed-by: Diana Z <dzigterman@chromium.org>
Diffstat (limited to 'power/amd_x86.c')
-rw-r--r-- | power/amd_x86.c | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/power/amd_x86.c b/power/amd_x86.c new file mode 100644 index 0000000000..7924d6f361 --- /dev/null +++ b/power/amd_x86.c @@ -0,0 +1,300 @@ +/* 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 "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_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()); + + /* Pause in S5 when shutting down. */ + power_set_pause_in_s5(1); + + 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); + + gpio_set_level(pin_out, in_level); + + CPRINTS("Pass through %s: %d", gpio_get_name(pin_in), in_level); +} + +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); + + 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); + + /* 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; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { + /* Power down to next state */ + return POWER_S0S3; + } + 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); + + 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; + + default: + break; + } + return state; +} |