diff options
author | Duncan Laurie <dlaurie@chromium.org> | 2013-05-13 17:58:28 -0700 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-05-14 08:35:16 -0700 |
commit | 199252ea21d3090f057c333073fbeb8b80b5b141 (patch) | |
tree | 3d908f733645696d93543806b6b6ce75f18133dc | |
parent | 3615ac4c0b6141f9d5a3fb008d09f6792155815c (diff) | |
download | chrome-ec-199252ea21d3090f057c333073fbeb8b80b5b141.tar.gz |
slippy: Basic power sequencing
Still some work to do here but this now works.
NOTE: This makes the system behave like a normal
cros device where the power is applied automatically.
For some (other, unknown) reason the "reboot ap-off"
is not passing flags correctly to keep it off.
BUG=chrome-os-partner:19398
BRANCH=none
TEST=successful state transition from G3 to S0
Change-Id: I694136b9611e18ac8fb7b1e960bd10caa258ce28
Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/51077
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r-- | board/slippy/board.c | 13 | ||||
-rw-r--r-- | board/slippy/board.h | 4 | ||||
-rw-r--r-- | common/x86_power_haswell.c | 600 |
3 files changed, 564 insertions, 53 deletions
diff --git a/board/slippy/board.c b/board/slippy/board.c index c1cbdb79a6..8f775670aa 100644 --- a/board/slippy/board.c +++ b/board/slippy/board.c @@ -14,6 +14,7 @@ #include "lid_switch.h" #include "lm4_adc.h" #include "peci.h" +#include "power_button.h" #include "registers.h" #include "switch.h" #include "temp_sensor.h" @@ -26,7 +27,7 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { /* Inputs with interrupt handlers are first for efficiency */ {"POWER_BUTTON_L", LM4_GPIO_A, (1<<2), GPIO_INT_BOTH, - switch_interrupt}, + power_button_interrupt}, {"LID_OPEN", LM4_GPIO_A, (1<<3), GPIO_INT_BOTH, lid_interrupt}, {"AC_PRESENT", LM4_GPIO_H, (1<<3), GPIO_INT_BOTH, @@ -51,8 +52,6 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { x86_power_interrupt}, {"VCORE_PGOOD", LM4_GPIO_C, (1<<6), GPIO_INT_BOTH, x86_power_interrupt}, - {"CPU_PGOOD", LM4_GPIO_C, (1<<4), GPIO_INT_BOTH, - x86_power_interrupt}, {"PCH_EDP_VDD_EN", LM4_GPIO_J, (1<<1), GPIO_INT_BOTH, x86_power_interrupt}, {"RECOVERY_L", LM4_GPIO_A, (1<<5), GPIO_PULL_UP|GPIO_INT_BOTH, @@ -81,6 +80,7 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"SYS_PWROK", LM4_GPIO_H, (1<<2), GPIO_OUT_LOW, NULL}, {"WLAN_OFF_L", LM4_GPIO_J, (1<<4), GPIO_OUT_LOW, NULL}, {"CHARGE_L", LM4_GPIO_E, (1<<6), GPIO_OUT_LOW, NULL}, + {"CPU_PGOOD", LM4_GPIO_C, (1<<4), GPIO_OUT_LOW, NULL}, {"ENABLE_BACKLIGHT", LM4_GPIO_M, (1<<7), GPIO_OUT_LOW, NULL}, {"ENABLE_TOUCHPAD", LM4_GPIO_N, (1<<1), GPIO_OUT_LOW, NULL}, @@ -105,10 +105,9 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"USB1_ENABLE", LM4_GPIO_E, (1<<4), GPIO_OUT_LOW, NULL}, {"USB2_ENABLE", LM4_GPIO_D, (1<<5), GPIO_OUT_LOW, NULL}, - /* {"PCH_SUSACK_L", LM4_GPIO_F, (1<<3), GPIO_INPUT, NULL}, */ - {"PCH_CATERR_L", LM4_GPIO_F, (1<<3), GPIO_INPUT, NULL}, - {"PCH_RTCRST_L", LM4_GPIO_F, (1<<6), GPIO_INPUT, NULL}, - {"PCH_SRTCRST_L", LM4_GPIO_F, (1<<7), GPIO_INPUT, NULL}, + {"PCH_SUSACK_L", LM4_GPIO_F, (1<<3), GPIO_OUT_HIGH, NULL}, + {"PCH_RTCRST_L", LM4_GPIO_F, (1<<6), GPIO_HI_Z, NULL}, + {"PCH_SRTCRST_L", LM4_GPIO_F, (1<<7), GPIO_HI_Z, NULL}, }; /* ADC channels. Must be in the exactly same order as in enum adc_channel. */ diff --git a/board/slippy/board.h b/board/slippy/board.h index c96f2ca4bb..2d9680c2e6 100644 --- a/board/slippy/board.h +++ b/board/slippy/board.h @@ -78,7 +78,6 @@ enum gpio_signal { GPIO_PP1350_PGOOD, /* Power good on 1.35V (DRAM) */ GPIO_PP5000_PGOOD, /* Power good on 5V */ GPIO_VCORE_PGOOD, /* Power good on core VR */ - GPIO_CPU_PGOOD, /* Power good on CPU */ GPIO_PCH_EDP_VDD_EN, /* PCH wants EDP enabled */ GPIO_RECOVERY_L, /* Recovery signal from servo */ GPIO_WRITE_PROTECT, /* Write protect input */ @@ -104,6 +103,7 @@ enum gpio_signal { GPIO_SYS_PWROK, /* EC thinks everything is up and ready */ GPIO_WLAN_OFF_L, /* Disable WiFi chip? Or just the radio? */ GPIO_CHARGE_L, /* Allow battery to charge when on AC */ + GPIO_CPU_PGOOD, /* Power good to the CPU */ GPIO_ENABLE_BACKLIGHT, /* Enable backlight power */ GPIO_ENABLE_TOUCHPAD, /* Enable touchpad power */ @@ -126,7 +126,7 @@ enum gpio_signal { GPIO_USB1_ENABLE, /* USB port 1 output power enable */ GPIO_USB2_ENABLE, /* USB port 2 output power enable */ - GPIO_PCH_CATERR_L, /* Wanted CATERR# from PCH, probably NC */ + GPIO_PCH_SUSACK_L, /* Acknowledge PCH SUSWARN# signal */ GPIO_PCH_RTCRST_L, /* Not supposed to be here */ GPIO_PCH_SRTCRST_L, /* Not supposed to be here */ diff --git a/common/x86_power_haswell.c b/common/x86_power_haswell.c index 19f46a9b5e..4d3709fc22 100644 --- a/common/x86_power_haswell.c +++ b/common/x86_power_haswell.c @@ -14,6 +14,7 @@ #include "host_command.h" #include "lid_switch.h" #include "power_button.h" +#include "registers.h" #include "switch.h" #include "system.h" #include "task.h" @@ -25,6 +26,173 @@ #define CPUTS(outstr) cputs(CC_CHIPSET, outstr) #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 */ +#define S5_INACTIVITY_TIMEOUT (10 * SECOND) + +enum x86_state { + X86_G3 = 0, /* + * System is off (not technically all the + * way into G3, which means totally + * unpowered...) + */ + X86_S5, /* System is soft-off */ + X86_S3, /* Suspend; RAM on, processor is asleep */ + X86_S0, /* System is on */ + + /* Transitions */ + X86_G3S5, /* G3 -> S5 (at system init time) */ + X86_S5S3, /* S5 -> S3 */ + X86_S3S0, /* S3 -> S0 */ + X86_S0S3, /* S0 -> S3 */ + X86_S3S5, /* S3 -> S5 */ + X86_S5G3, /* S5 -> G3 */ +}; + +static const char * const state_names[] = { + "G3", + "S5", + "S3", + "S0", + "G3->S5", + "S5->S3", + "S3->S0", + "S0->S3", + "S3->S5", + "S5->G3", +}; + +/* Input state flags */ +#define IN_PGOOD_PP5000 0x0001 +#define IN_PGOOD_PP1350 0x0002 +#define IN_PGOOD_PP1050 0x0004 +#define IN_PGOOD_VCORE 0x0008 +#define IN_PCH_SLP_S0n_DEASSERTED 0x0010 +#define IN_PCH_SLP_S3n_DEASSERTED 0x0020 +#define IN_PCH_SLP_S5n_DEASSERTED 0x0040 +#define IN_PCH_SLP_SUSn_DEASSERTED 0x0080 +#define IN_PCH_SUSWARNn_DEASSERTED 0x0100 + +/* All always-on supplies */ +#define IN_PGOOD_ALWAYS_ON (IN_PGOOD_PP5000) +/* All non-core power rails */ +#define IN_PGOOD_ALL_NONCORE (IN_PGOOD_PP1350 | IN_PGOOD_PP1050) +/* All core power rails */ +#define IN_PGOOD_ALL_CORE (IN_PGOOD_VCORE) +/* Rails required for S3 */ +#define IN_PGOOD_S3 (IN_PGOOD_ALWAYS_ON | IN_PGOOD_PP1350) +/* 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_PCH_SLP_S3n_DEASSERTED | \ + IN_PCH_SLP_S5n_DEASSERTED) +/* All inputs in the right state for S0 */ +#define IN_ALL_S0 (IN_PGOOD_ALWAYS_ON | IN_PGOOD_ALL_NONCORE | \ + IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED) + +static enum x86_state state = X86_G3; /* Current state */ +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 int want_g3_exit; /* Should we exit the G3 state? */ +static int throttle_cpu; /* Throttle CPU? */ + +/* When did we enter G3? */ +static uint64_t last_shutdown_time; +/* Delay before go into hibernation in seconds*/ +static uint32_t hibernate_delay = 3600; /* 1 Hour */ + +/** + * Update input signal state. + */ +static void update_in_signals(void) +{ + uint32_t inew = 0; + int v; + + if (gpio_get_level(GPIO_PP5000_PGOOD)) + inew |= IN_PGOOD_PP5000; + if (gpio_get_level(GPIO_PP1350_PGOOD)) + inew |= IN_PGOOD_PP1350; + if (gpio_get_level(GPIO_PP1050_PGOOD)) + inew |= IN_PGOOD_PP1050; + if (gpio_get_level(GPIO_VCORE_PGOOD)) + inew |= IN_PGOOD_VCORE; + + if (gpio_get_level(GPIO_PCH_SLP_S0_L)) + inew |= IN_PCH_SLP_S0n_DEASSERTED; + if (gpio_get_level(GPIO_PCH_SLP_S3_L)) + inew |= IN_PCH_SLP_S3n_DEASSERTED; + if (gpio_get_level(GPIO_PCH_SLP_S5_L)) + inew |= IN_PCH_SLP_S5n_DEASSERTED; + if (gpio_get_level(GPIO_PCH_SLP_SUS_L)) + inew |= IN_PCH_SLP_SUSn_DEASSERTED; + + v = gpio_get_level(GPIO_PCH_SUSWARN_L); + if (v) + inew |= IN_PCH_SUSWARNn_DEASSERTED; + /* Copy SUSWARN# signal from PCH to SUSACK# */ + gpio_set_level(GPIO_PCH_SUSACK_L, v); + + if ((in_signals & in_debug) != (inew & in_debug)) + CPRINTF("[%T x86 in 0x%04x]\n", inew); + + in_signals = inew; +} + +/** + * Check for required inputs + * + * @param want Input flags which must be present (IN_*) + * + * @return Non-zero if all present; zero if a required signal is missing. + */ +static int have_all_in_signals(uint32_t want) +{ + if ((in_signals & want) == want) + return 1; + + CPRINTF("[%T x86 power lost input; wanted 0x%04x, got 0x%04x]\n", + want, in_signals & want); + + return 0; +} + +/** + * Wait for inputs to be present + * + * @param want Input flags which must be present (IN_*) + * + * @return EC_SUCCESS when all inputs are present, or ERROR_TIMEOUT if timeout + * before reaching the desired state. + */ +static int wait_in_signals(uint32_t want) +{ + in_want = want; + + while ((in_signals & in_want) != in_want) { + if (task_wait_event(DEFAULT_TIMEOUT) == TASK_EVENT_TIMER) { + update_in_signals(); + CPRINTF("[%T x86 power timeout on input; " + "wanted 0x%04x, got 0x%04x]\n", + in_want, in_signals & in_want); + return EC_ERROR_TIMEOUT; + } + /* + * TODO: 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; +} /*****************************************************************************/ /* Chipset interface */ @@ -78,14 +246,59 @@ void chipset_reset(int cold_reset) int chipset_in_state(int state_mask) { - /* HEY - Hard code to off for now. Perhaps use a console command to - * manually specify certain states for the other tasks to look at. */ - return CHIPSET_STATE_HARD_OFF; + int need_mask = 0; + + /* + * TODO: 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 X86_G3: + need_mask = CHIPSET_STATE_HARD_OFF; + break; + case X86_G3S5: + case X86_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 X86_S5: + need_mask = CHIPSET_STATE_SOFT_OFF; + break; + case X86_S5S3: + case X86_S3S5: + need_mask = CHIPSET_STATE_SOFT_OFF | CHIPSET_STATE_SUSPEND; + break; + case X86_S3: + need_mask = CHIPSET_STATE_SUSPEND; + break; + case X86_S3S0: + case X86_S0S3: + need_mask = CHIPSET_STATE_SUSPEND | CHIPSET_STATE_ON; + break; + case X86_S0: + need_mask = CHIPSET_STATE_ON; + break; + } + + /* Return non-zero if all needed bits are present */ + return (state_mask & need_mask) == need_mask; } void chipset_exit_hard_off(void) { - CPRINTF("[%T %s()]\n", __func__); + /* If not in the hard-off state nor headed there, nothing to do */ + if (state != X86_G3 && state != X86_S5G3) + return; + + /* Set a flag to leave G3, then wake the task */ + want_g3_exit = 1; + + if (task_start_called()) + task_wake(TASK_ID_CHIPSET); } void chipset_throttle_cpu(int throttle) @@ -98,7 +311,8 @@ void chipset_throttle_cpu(int throttle) static void x86_lid_change(void) { - CPRINTF("[%T %s()]\n", __func__); + /* Wake up the task to update power state */ + task_wake(TASK_ID_CHIPSET); } DECLARE_HOOK(HOOK_LID_CHANGE, x86_lid_change, HOOK_PRIO_DEFAULT); @@ -108,16 +322,48 @@ static void x86_power_ac_change(void) CPRINTF("[%T x86 AC on]\n"); } else { CPRINTF("[%T x86 AC off]\n"); + + if (state == X86_G3) { + last_shutdown_time = get_time().val; + task_wake(TASK_ID_CHIPSET); + } } } DECLARE_HOOK(HOOK_AC_CHANGE, x86_power_ac_change, HOOK_PRIO_DEFAULT); static void x86_power_init(void) { - CPRINTF("[%T %s()]\n", __func__); + /* Update input state */ + update_in_signals(); + in_want = 0; + + /* The initial state is G3. Set shut down timestamp to now. */ + last_shutdown_time = get_time().val; + + /* + * 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 ((in_signals & IN_ALL_S0) == IN_ALL_S0) { + CPRINTF("[%T x86 already in S0]\n"); + state = X86_S0; + } else { + /* Force all signals to their G3 states */ + CPRINTF("[%T x86 forcing G3]\n"); + gpio_set_level(GPIO_PCH_PWROK, 0); + gpio_set_level(GPIO_VCORE_EN, 0); + gpio_set_level(GPIO_SUSP_VR_EN, 0); + gpio_set_level(GPIO_PP1350_EN, 0); + gpio_set_level(GPIO_PP3300_DX_EN, 0); + gpio_set_level(GPIO_PP5000_EN, 0); + gpio_set_level(GPIO_PCH_RSMRST_L, 0); + gpio_set_level(GPIO_PCH_DPWROK, 0); + } + } /* Enable interrupts for our GPIOs */ - gpio_enable_interrupt(GPIO_POWER_BUTTON_L); gpio_enable_interrupt(GPIO_LID_OPEN); gpio_enable_interrupt(GPIO_AC_PRESENT); gpio_enable_interrupt(GPIO_PCH_BKLTEN); @@ -130,7 +376,6 @@ static void x86_power_init(void) gpio_enable_interrupt(GPIO_PP1350_PGOOD); gpio_enable_interrupt(GPIO_PP5000_PGOOD); gpio_enable_interrupt(GPIO_VCORE_PGOOD); - gpio_enable_interrupt(GPIO_CPU_PGOOD); gpio_enable_interrupt(GPIO_PCH_EDP_VDD_EN); gpio_enable_interrupt(GPIO_RECOVERY_L); gpio_enable_interrupt(GPIO_WRITE_PROTECT); @@ -142,7 +387,11 @@ DECLARE_HOOK(HOOK_INIT, x86_power_init, HOOK_PRIO_INIT_CHIPSET); void x86_power_interrupt(enum gpio_signal signal) { - CPRINTF("[%T %s(%d)]\n", __func__, signal); + /* Shadow signals and compare with our desired signal state. */ + update_in_signals(); + + /* Wake up the task */ + task_wake(TASK_ID_CHIPSET); } /*****************************************************************************/ @@ -150,59 +399,322 @@ void x86_power_interrupt(enum gpio_signal signal) void chipset_task(void) { + uint64_t time_now; + while (1) { - CPRINTF("[%T %s()]\n", __func__); + CPRINTF("[%T x86 power state %d = %s, in 0x%04x]\n", + state, state_names[state], in_signals); + + switch (state) { + case X86_G3: + if (want_g3_exit) { + want_g3_exit = 0; + state = X86_G3S5; + break; + } + + in_want = 0; + if (extpower_is_present()) + task_wait_event(-1); + else { + uint64_t target_time = last_shutdown_time + + hibernate_delay * 1000000ull; + time_now = get_time().val; + if (time_now > target_time) { + /* + * Time's up. Hibernate until wake pin + * asserted. + */ + CPRINTF("[%T x86 hibernating]\n"); + system_hibernate(0, 0); + } else { + uint64_t wait = target_time - time_now; + if (wait > TASK_MAX_WAIT_US) + wait = TASK_MAX_WAIT_US; + + /* Wait for a message */ + task_wait_event(wait); + } + } + break; + + case X86_S5: + if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 1) { + /* Power up to next state */ + state = X86_S5S3; + break; + } + + /* Wait for inactivity timeout */ + in_want = 0; + if (task_wait_event(S5_INACTIVITY_TIMEOUT) == + TASK_EVENT_TIMER) { + /* Drop to G3; wake not requested yet */ + want_g3_exit = 0; + state = X86_S5G3; + } + break; + + case X86_S3: + /* Check for state transitions */ + if (!have_all_in_signals(IN_PGOOD_S3)) { + /* Required rail went away */ + chipset_force_shutdown(); + state = X86_S3S5; + break; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { + /* Power up to next state */ + state = X86_S3S0; + break; + } else if (gpio_get_level(GPIO_PCH_SLP_S5_L) == 0) { + /* Power down to next state */ + state = X86_S3S5; + break; + } + + /* Otherwise, steady state; wait for a message */ + in_want = 0; + task_wait_event(-1); + break; + + case X86_S0: + if (!have_all_in_signals(IN_PGOOD_S0)) { + /* Required rail went away */ + chipset_force_shutdown(); + state = X86_S0S3; + break; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { + /* Power down to next state */ + state = X86_S0S3; + break; + } + + /* Otherwise, steady state; wait for a message */ + in_want = 0; + task_wait_event(-1); + break; + + case X86_G3S5: + gpio_set_level(GPIO_PP5000_EN, 1); + if (wait_in_signals(IN_PGOOD_PP5000)) { + chipset_force_shutdown(); + state = X86_G3; + break; + } + + gpio_set_level(GPIO_SUSP_VR_EN, 1); + if (wait_in_signals(IN_PGOOD_PP1050)) { + chipset_force_shutdown(); + state = X86_G3; + break; + } + + /* + * Wait 10ms after +3VALW good, since that powers + * VccDSW and VccSUS. + */ + msleep(10); + + /* Assert DPWROK, deassert RSMRST# */ + gpio_set_level(GPIO_PCH_DPWROK, 1); + gpio_set_level(GPIO_PCH_RSMRST_L, 1); + + /* Wait 5ms for SUSCLK to stabilize */ + msleep(5); + + state = X86_S5; + break; + + case X86_S5S3: + /* Wait for the always-on rails to be good */ + if (wait_in_signals(IN_PGOOD_ALWAYS_ON)) { + chipset_force_shutdown(); + state = X86_S5; + } + + /* Turn on power to RAM */ + gpio_set_level(GPIO_PP1350_EN, 1); + if (wait_in_signals(IN_PGOOD_S3)) { + chipset_force_shutdown(); + state = X86_S5; + } + + /* + * Enable touchpad power so it can wake the system from + * suspend. + */ + gpio_set_level(GPIO_ENABLE_TOUCHPAD, 1); + + /* Call hooks now that rails are up */ + hook_notify(HOOK_CHIPSET_STARTUP); + + state = X86_S3; + break; + + case X86_S3S0: + /* Turn on power rails */ + gpio_set_level(GPIO_CPU_PGOOD, 1); + gpio_set_level(GPIO_PP3300_DX_EN, 1); + + /* Enable WLAN */ + gpio_set_level(GPIO_WLAN_OFF_L, 1); + + /* Wait for non-core power rails good */ + if (wait_in_signals(IN_PGOOD_S0)) { + chipset_force_shutdown(); + gpio_set_level(GPIO_WLAN_OFF_L, 0); + state = X86_S3; + } + + /* + * Enable +CPU_CORE. The CPU itself will request + * the supplies when it's ready. + */ + gpio_set_level(GPIO_VCORE_EN, 1); + + /* Call hooks now that rails are up */ + hook_notify(HOOK_CHIPSET_RESUME); + + /* Wait 99ms after all voltages good */ + msleep(99); + + /* + * Throttle CPU if necessary. This should only be + * asserted when +VCCP is powered (it is by now). + */ + gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu); + + /* Set PCH_PWROK */ + gpio_set_level(GPIO_PCH_PWROK, 1); + gpio_set_level(GPIO_SYS_PWROK, 1); + + state = X86_S0; + break; + + case X86_S0S3: + /* Call hooks before we remove power rails */ + hook_notify(HOOK_CHIPSET_SUSPEND); + + /* Clear PCH_PWROK */ + gpio_set_level(GPIO_SYS_PWROK, 0); + gpio_set_level(GPIO_PCH_PWROK, 0); + + /* Wait 40ns */ + udelay(1); + + /* Disable +CPU_CORE */ + gpio_set_level(GPIO_VCORE_EN, 0); + + /* Disable WLAN */ + gpio_set_level(GPIO_WLAN_OFF_L, 0); + + /* + * Deassert prochot since CPU is off and we're about + * to drop +VCCP. + */ + gpio_set_level(GPIO_CPU_PROCHOT, 0); - /* for wait power button */ - while (gpio_get_level(GPIO_POWER_BUTTON_L) == 1) - usleep(200000); + /* Turn off power rails */ + gpio_set_level(GPIO_PP3300_DX_EN, 0); - CPRINTF("[%T %s() PWRBTN is pressed.]\n", __func__); + state = X86_S3; + break; + + case X86_S3S5: + /* Call hooks before we remove power rails */ + hook_notify(HOOK_CHIPSET_SHUTDOWN); + + /* Disable touchpad power */ + gpio_set_level(GPIO_ENABLE_TOUCHPAD, 0); + + /* Turn off power to RAM */ + gpio_set_level(GPIO_PP1350_EN, 0); - gpio_set_level(GPIO_PP5000_EN, 1); - usleep(200000); + state = X86_S5; + break; - gpio_set_level(GPIO_PCH_DPWROK, 1); - usleep(200000); + case X86_S5G3: + /* Deassert DPWROK, assert RSMRST# */ + gpio_set_level(GPIO_PCH_DPWROK, 0); + gpio_set_level(GPIO_PCH_RSMRST_L, 0); - gpio_set_level(GPIO_SUSP_VR_EN, 1); - usleep(200000); + gpio_set_level(GPIO_PP5000_EN, 0); + gpio_set_level(GPIO_SUSP_VR_EN, 0); - gpio_set_level(GPIO_PCH_RSMRST_L, 1); - usleep(200000); + /* Record the time we go into G3 */ + last_shutdown_time = get_time().val; - gpio_set_level(GPIO_PCH_PWRBTN_L, 0); - usleep(200000); + state = X86_G3; + break; + } + } +} - gpio_set_level(GPIO_PCH_PWRBTN_L, 1); - usleep(200000); +/*****************************************************************************/ +/* Console commands */ - gpio_set_level(GPIO_PP1350_EN, 1); - usleep(200000); +static int command_powerinfo(int argc, char **argv) +{ + /* + * Print x86 power state in same format as state machine. This is + * used by FAFT tests, so must match exactly. + */ + ccprintf("[%T x86 power state %d = %s, in 0x%04x]\n", + state, state_names[state], in_signals); - gpio_set_level(GPIO_PCH_PWROK, 1); - usleep(200000); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo, + NULL, + "Show current x86 power state", + NULL); - gpio_set_level(GPIO_SYS_PWROK, 1); - usleep(200000); +static int command_x86indebug(int argc, char **argv) +{ + char *e; - CPRINTF("[%T %s() boot seq done.]\n", __func__); + /* If one arg, set the mask */ + if (argc == 2) { + int m = strtoi(argv[1], &e, 0); + if (*e) + return EC_ERROR_PARAM1; - while (1) - task_wait_event(-1); + in_debug = m; } -} -/*****************************************************************************/ -/* Console commands */ + /* Print the mask */ + ccprintf("x86 in: 0x%04x\n", in_signals); + ccprintf("debug mask: 0x%04x\n", in_debug); + return EC_SUCCESS; +}; +DECLARE_CONSOLE_COMMAND(x86indebug, command_x86indebug, + "[mask]", + "Get/set x86 input debug mask", + NULL); -static int command_power(int argc, char **argv) +static int command_hibernation_delay(int argc, char **argv) { - ccprintf("No commands defined yet. Add some for bringup.\n"); + 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 == X86_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(power, command_power, - NULL, - "Manually drive power states for bringup", +DECLARE_CONSOLE_COMMAND(hibdelay, command_hibernation_delay, + "[sec]", + "Set the delay before going into hibernation", NULL); - |