From 4e27a42ff9d2b7bb566b6bc455404657a3250c91 Mon Sep 17 00:00:00 2001 From: Louis Yung-Chieh Lo Date: Thu, 2 Jan 2014 16:45:38 -0800 Subject: x86: generalize power state machine for all platforms (1/2) Renaming file names is the first step. Please see issue tracker for more details. BUG=chrome-os-partner:24832 BRANCH=link,falco,samus,rambi,peppy TEST=build all x86 boards. make clean BOARD=link && make -j32 BOARD=link && \ make clean BOARD=falco && make -j32 BOARD=falco && \ make clean BOARD=samus && make -j32 BOARD=samus && \ make clean BOARD=rambi && make -j32 BOARD=rambi && \ make clean BOARD=peppy && make -j32 BOARD=peppy Change-Id: I3a296a0c14f6bebefa858438b1320061ac71dd38 Signed-off-by: Louis Yung-Chieh Lo Reviewed-on: https://chromium-review.googlesource.com/181400 Reviewed-by: Randall Spangler --- board/falco/board.c | 2 +- board/link/board.c | 2 +- board/peppy/board.c | 2 +- board/rambi/board.c | 2 +- board/samus/board.c | 2 +- board/samus/power_sequence.c | 2 +- include/chipset_x86_common.h | 98 ---------- include/power.h | 98 ++++++++++ power/baytrail.c | 2 +- power/build.mk | 2 +- power/common.c | 422 +++++++++++++++++++++++++++++++++++++++++++ power/haswell.c | 2 +- power/ivybridge.c | 2 +- power/x86_common.c | 422 ------------------------------------------- test/adapter.c | 2 +- 15 files changed, 531 insertions(+), 531 deletions(-) delete mode 100644 include/chipset_x86_common.h create mode 100644 include/power.h create mode 100644 power/common.c delete mode 100644 power/x86_common.c diff --git a/board/falco/board.c b/board/falco/board.c index bfec566608..b343261ebc 100644 --- a/board/falco/board.c +++ b/board/falco/board.c @@ -8,7 +8,7 @@ #include "adc_chip.h" #include "backlight.h" #include "board.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "driver/charger/bq24738.h" #include "driver/temp_sensor/g781.h" diff --git a/board/link/board.c b/board/link/board.c index a080345764..95694b25ea 100644 --- a/board/link/board.c +++ b/board/link/board.c @@ -8,7 +8,7 @@ #include "adc_chip.h" #include "backlight.h" #include "chipset.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "driver/temp_sensor/tmp006.h" #include "extpower.h" diff --git a/board/peppy/board.c b/board/peppy/board.c index 35f7d5002b..5c85ead4d4 100644 --- a/board/peppy/board.c +++ b/board/peppy/board.c @@ -8,7 +8,7 @@ #include "adc_chip.h" #include "backlight.h" #include "chipset.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "driver/temp_sensor/g781.h" #include "extpower.h" diff --git a/board/rambi/board.c b/board/rambi/board.c index d71e74d063..5b1b295de7 100644 --- a/board/rambi/board.c +++ b/board/rambi/board.c @@ -7,7 +7,7 @@ #include "adc.h" #include "adc_chip.h" #include "backlight.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "driver/temp_sensor/tmp432.h" #include "extpower.h" diff --git a/board/samus/board.c b/board/samus/board.c index bca84b61c7..96139a5b0c 100644 --- a/board/samus/board.c +++ b/board/samus/board.c @@ -8,7 +8,7 @@ #include "adc.h" #include "adc_chip.h" #include "backlight.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "driver/temp_sensor/tmp006.h" #include "driver/als_isl29035.h" diff --git a/board/samus/power_sequence.c b/board/samus/power_sequence.c index f9ccd6de01..efd367ccb4 100644 --- a/board/samus/power_sequence.c +++ b/board/samus/power_sequence.c @@ -6,7 +6,7 @@ /* X86 chipset power control module for Chrome EC */ #include "chipset.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "console.h" #include "gpio.h" diff --git a/include/chipset_x86_common.h b/include/chipset_x86_common.h deleted file mode 100644 index 21576f1899..0000000000 --- a/include/chipset_x86_common.h +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright (c) 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 interface for x86 chipsets */ - -#ifndef __CROS_EC_CHIPSET_X86_COMMON_H -#define __CROS_EC_CHIPSET_X86_COMMON_H - -#include "common.h" -#include "gpio.h" - -enum x86_state { - /* Steady states */ - 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 */ -}; - -/* Information on an x86 signal */ -struct x86_signal_info { - enum gpio_signal gpio; /* GPIO for signal */ - int level; /* GPIO level which sets signal bit */ - const char *name; /* Name of signal */ -}; - -/* - * Each board must provide its signal list and a corresponding enum x86_signal. - */ -extern const struct x86_signal_info x86_signal_list[]; - -/* Convert enum x86_signal to a mask for signal functions */ -#define X86_SIGNAL_MASK(signal) (1 << (signal)) - -/** - * Return current input signal state (one or more X86_SIGNAL_MASK()s). - */ -uint32_t x86_get_signals(void); - -/** - * Check for required inputs - * - * @param want Mask of signals which must be present (one or more - * X86_SIGNAL_MASK()s). - * - * @return Non-zero if all present; zero if a required signal is missing. - */ -int x86_has_signals(uint32_t want); - -/** - * Wait for x86 input signals to be present - * - * @param want Mask of signals which must be present (one or more - * X86_SIGNAL_MASK()s). If want=0, stops waiting for - * signals. - * @return EC_SUCCESS when all inputs are present, or ERROR_TIMEOUT if timeout - * before reaching the desired state. - */ -int x86_wait_signals(uint32_t want); - -/** - * Chipset-specific initialization - * - * @return The state the chipset should start in. Usually X86_G3, but may - * be X86_G0 if the chipset was already on and we've jumped to this image. - */ -enum x86_state x86_chipset_init(void); - -/** - * Chipset-specific state handler - * - * @return The updated state for the x86 chipset. - */ -enum x86_state x86_handle_state(enum x86_state state); - -/** - * Interrupt handler for x86 chipset GPIOs. - */ -#ifdef CONFIG_CHIPSET_X86 -void x86_interrupt(enum gpio_signal signal); -#else -#define x86_interrupt NULL -#endif - -#endif /* __CROS_EC_CHIPSET_X86_COMMON_H */ diff --git a/include/power.h b/include/power.h new file mode 100644 index 0000000000..21576f1899 --- /dev/null +++ b/include/power.h @@ -0,0 +1,98 @@ +/* Copyright (c) 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 interface for x86 chipsets */ + +#ifndef __CROS_EC_CHIPSET_X86_COMMON_H +#define __CROS_EC_CHIPSET_X86_COMMON_H + +#include "common.h" +#include "gpio.h" + +enum x86_state { + /* Steady states */ + 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 */ +}; + +/* Information on an x86 signal */ +struct x86_signal_info { + enum gpio_signal gpio; /* GPIO for signal */ + int level; /* GPIO level which sets signal bit */ + const char *name; /* Name of signal */ +}; + +/* + * Each board must provide its signal list and a corresponding enum x86_signal. + */ +extern const struct x86_signal_info x86_signal_list[]; + +/* Convert enum x86_signal to a mask for signal functions */ +#define X86_SIGNAL_MASK(signal) (1 << (signal)) + +/** + * Return current input signal state (one or more X86_SIGNAL_MASK()s). + */ +uint32_t x86_get_signals(void); + +/** + * Check for required inputs + * + * @param want Mask of signals which must be present (one or more + * X86_SIGNAL_MASK()s). + * + * @return Non-zero if all present; zero if a required signal is missing. + */ +int x86_has_signals(uint32_t want); + +/** + * Wait for x86 input signals to be present + * + * @param want Mask of signals which must be present (one or more + * X86_SIGNAL_MASK()s). If want=0, stops waiting for + * signals. + * @return EC_SUCCESS when all inputs are present, or ERROR_TIMEOUT if timeout + * before reaching the desired state. + */ +int x86_wait_signals(uint32_t want); + +/** + * Chipset-specific initialization + * + * @return The state the chipset should start in. Usually X86_G3, but may + * be X86_G0 if the chipset was already on and we've jumped to this image. + */ +enum x86_state x86_chipset_init(void); + +/** + * Chipset-specific state handler + * + * @return The updated state for the x86 chipset. + */ +enum x86_state x86_handle_state(enum x86_state state); + +/** + * Interrupt handler for x86 chipset GPIOs. + */ +#ifdef CONFIG_CHIPSET_X86 +void x86_interrupt(enum gpio_signal signal); +#else +#define x86_interrupt NULL +#endif + +#endif /* __CROS_EC_CHIPSET_X86_COMMON_H */ diff --git a/power/baytrail.c b/power/baytrail.c index 203c63707b..fff9ae33ca 100644 --- a/power/baytrail.c +++ b/power/baytrail.c @@ -6,7 +6,7 @@ /* X86 chipset power control module for Chrome EC */ #include "chipset.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "console.h" #include "ec_commands.h" diff --git a/power/build.mk b/power/build.mk index b2cd14fe66..2042cdee34 100644 --- a/power/build.mk +++ b/power/build.mk @@ -11,4 +11,4 @@ power-$(CONFIG_CHIPSET_GAIA)+=gaia.o power-$(CONFIG_CHIPSET_HASWELL)+=haswell.o power-$(CONFIG_CHIPSET_IVYBRIDGE)+=ivybridge.o power-$(CONFIG_CHIPSET_TEGRA)+=tegra.o -power-$(CONFIG_CHIPSET_X86)+=x86_common.o +power-$(CONFIG_CHIPSET_X86)+=common.o diff --git a/power/common.c b/power/common.c new file mode 100644 index 0000000000..8ddabcf962 --- /dev/null +++ b/power/common.c @@ -0,0 +1,422 @@ +/* Copyright (c) 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 x86 chipsets */ + +#include "chipset.h" +#include "power.h" +#include "common.h" +#include "console.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "system.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +/* Console output macros */ +#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) + +static const char * const state_names[] = { + "G3", + "S5", + "S3", + "S0", + "G3->S5", + "S5->S3", + "S3->S0", + "S0->S3", + "S3->S5", + "S5->G3", +}; + +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 x86_state state = X86_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? */ + +/* Delay before hibernating, in seconds */ +static uint32_t hibernate_delay = 3600; + +/** + * Update input signals mask + */ +static void x86_update_signals(void) +{ + uint32_t inew = 0; + const struct x86_signal_info *s = x86_signal_list; + int i; + + for (i = 0; i < X86_SIGNAL_COUNT; i++, s++) { + if (gpio_get_level(s->gpio) == s->level) + inew |= 1 << i; + } + + if ((in_signals & in_debug) != (inew & in_debug)) + CPRINTF("[%T x86 in 0x%04x]\n", inew); + + in_signals = inew; +} + +uint32_t x86_get_signals(void) +{ + return in_signals; +} + +int x86_has_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; +} + +int x86_wait_signals(uint32_t want) +{ + in_want = want; + if (!want) + return EC_SUCCESS; + + while ((in_signals & in_want) != in_want) { + if (task_wait_event(DEFAULT_TIMEOUT) == TASK_EVENT_TIMER) { + x86_update_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(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; +} + +/** + * Set the low-level x86 chipset state. + * + * @param new_state New chipset state. + */ +void x86_set_state(enum x86_state new_state) +{ + /* Record the time we go into G3 */ + if (new_state == X86_G3) + last_shutdown_time = get_time().val; + + state = new_state; +} + +/** + * Common handler for x86 steady states + * + * @param state Current x86 state + * @return Updated x86 state + */ +static enum x86_state x86_common_state(enum x86_state state) +{ + switch (state) { + case X86_G3: + if (want_g3_exit) { + want_g3_exit = 0; + return X86_G3S5; + } + + in_want = 0; + if (extpower_is_present()) + task_wait_event(-1); + else { + uint64_t target_time = last_shutdown_time + + hibernate_delay * 1000000ull; + uint64_t 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: + /* Wait for inactivity timeout */ + x86_wait_signals(0); + if (task_wait_event(S5_INACTIVITY_TIMEOUT) == + TASK_EVENT_TIMER) { + /* Drop to G3; wake not requested yet */ + want_g3_exit = 0; + return X86_S5G3; + } + break; + + case X86_S3: + /* Wait for a message */ + x86_wait_signals(0); + task_wait_event(-1); + break; + + case X86_S0: + /* Wait for a message */ + x86_wait_signals(0); + task_wait_event(-1); + break; + + 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 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) +{ + /* 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); +} + +/*****************************************************************************/ +/* Task function */ + +void chipset_task(void) +{ + enum x86_state new_state; + + while (1) { + CPRINTF("[%T x86 power state %d = %s, in 0x%04x]\n", + state, state_names[state], in_signals); + + /* Always let the specific chipset handle the state first */ + new_state = x86_handle_state(state); + + /* + * If the state hasn't changed, run common steady-state + * handler. + */ + if (new_state == state) + new_state = x86_common_state(state); + + /* Handle state changes */ + if (new_state != state) + x86_set_state(new_state); + } +} + +/*****************************************************************************/ +/* Hooks */ + +static void x86_common_init(void) +{ + const struct x86_signal_info *s = x86_signal_list; + int i; + + /* Update input state */ + x86_update_signals(); + + /* Call chipset-specific init to set initial state */ + x86_set_state(x86_chipset_init()); + + /* Enable interrupts for input signals */ + for (i = 0; i < X86_SIGNAL_COUNT; i++, s++) + gpio_enable_interrupt(s->gpio); +} +DECLARE_HOOK(HOOK_INIT, x86_common_init, HOOK_PRIO_INIT_CHIPSET); + +static void x86_lid_change(void) +{ + /* Wake up the task to update power state */ + task_wake(TASK_ID_CHIPSET); +} +DECLARE_HOOK(HOOK_LID_CHANGE, x86_lid_change, HOOK_PRIO_DEFAULT); + +static void x86_ac_change(void) +{ + if (extpower_is_present()) { + 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_ac_change, HOOK_PRIO_DEFAULT); + +/*****************************************************************************/ +/* Interrupts */ + +void x86_interrupt(enum gpio_signal signal) +{ + /* Shadow signals and compare with our desired signal state. */ + x86_update_signals(); + + /* Wake up the task */ + task_wake(TASK_ID_CHIPSET); +} + +/*****************************************************************************/ +/* Console commands */ + +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); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo, + NULL, + "Show current x86 power state", + NULL); + +static int command_x86indebug(int argc, char **argv) +{ + const struct x86_signal_info *s = x86_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("x86 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 < X86_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(x86indebug, command_x86indebug, + "[mask]", + "Get/set x86 input debug mask", + NULL); + +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 == 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(hibdelay, command_hibernation_delay, + "[sec]", + "Set the delay before going into hibernation", + NULL); diff --git a/power/haswell.c b/power/haswell.c index cd00300f25..f9ee85d0c9 100644 --- a/power/haswell.c +++ b/power/haswell.c @@ -6,7 +6,7 @@ /* X86 chipset power control module for Chrome EC */ #include "chipset.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "console.h" #include "ec_commands.h" diff --git a/power/ivybridge.c b/power/ivybridge.c index 3a3c344223..5b61c04eef 100644 --- a/power/ivybridge.c +++ b/power/ivybridge.c @@ -6,7 +6,7 @@ /* X86 chipset power control module for Chrome EC */ #include "chipset.h" -#include "chipset_x86_common.h" +#include "power.h" #include "common.h" #include "console.h" #include "gpio.h" diff --git a/power/x86_common.c b/power/x86_common.c deleted file mode 100644 index d0b1088845..0000000000 --- a/power/x86_common.c +++ /dev/null @@ -1,422 +0,0 @@ -/* Copyright (c) 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 x86 chipsets */ - -#include "chipset.h" -#include "chipset_x86_common.h" -#include "common.h" -#include "console.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#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) - -static const char * const state_names[] = { - "G3", - "S5", - "S3", - "S0", - "G3->S5", - "S5->S3", - "S3->S0", - "S0->S3", - "S3->S5", - "S5->G3", -}; - -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 x86_state state = X86_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? */ - -/* Delay before hibernating, in seconds */ -static uint32_t hibernate_delay = 3600; - -/** - * Update input signals mask - */ -static void x86_update_signals(void) -{ - uint32_t inew = 0; - const struct x86_signal_info *s = x86_signal_list; - int i; - - for (i = 0; i < X86_SIGNAL_COUNT; i++, s++) { - if (gpio_get_level(s->gpio) == s->level) - inew |= 1 << i; - } - - if ((in_signals & in_debug) != (inew & in_debug)) - CPRINTF("[%T x86 in 0x%04x]\n", inew); - - in_signals = inew; -} - -uint32_t x86_get_signals(void) -{ - return in_signals; -} - -int x86_has_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; -} - -int x86_wait_signals(uint32_t want) -{ - in_want = want; - if (!want) - return EC_SUCCESS; - - while ((in_signals & in_want) != in_want) { - if (task_wait_event(DEFAULT_TIMEOUT) == TASK_EVENT_TIMER) { - x86_update_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(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; -} - -/** - * Set the low-level x86 chipset state. - * - * @param new_state New chipset state. - */ -void x86_set_state(enum x86_state new_state) -{ - /* Record the time we go into G3 */ - if (new_state == X86_G3) - last_shutdown_time = get_time().val; - - state = new_state; -} - -/** - * Common handler for x86 steady states - * - * @param state Current x86 state - * @return Updated x86 state - */ -static enum x86_state x86_common_state(enum x86_state state) -{ - switch (state) { - case X86_G3: - if (want_g3_exit) { - want_g3_exit = 0; - return X86_G3S5; - } - - in_want = 0; - if (extpower_is_present()) - task_wait_event(-1); - else { - uint64_t target_time = last_shutdown_time + - hibernate_delay * 1000000ull; - uint64_t 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: - /* Wait for inactivity timeout */ - x86_wait_signals(0); - if (task_wait_event(S5_INACTIVITY_TIMEOUT) == - TASK_EVENT_TIMER) { - /* Drop to G3; wake not requested yet */ - want_g3_exit = 0; - return X86_S5G3; - } - break; - - case X86_S3: - /* Wait for a message */ - x86_wait_signals(0); - task_wait_event(-1); - break; - - case X86_S0: - /* Wait for a message */ - x86_wait_signals(0); - task_wait_event(-1); - break; - - 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 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) -{ - /* 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); -} - -/*****************************************************************************/ -/* Task function */ - -void chipset_task(void) -{ - enum x86_state new_state; - - while (1) { - CPRINTF("[%T x86 power state %d = %s, in 0x%04x]\n", - state, state_names[state], in_signals); - - /* Always let the specific chipset handle the state first */ - new_state = x86_handle_state(state); - - /* - * If the state hasn't changed, run common steady-state - * handler. - */ - if (new_state == state) - new_state = x86_common_state(state); - - /* Handle state changes */ - if (new_state != state) - x86_set_state(new_state); - } -} - -/*****************************************************************************/ -/* Hooks */ - -static void x86_common_init(void) -{ - const struct x86_signal_info *s = x86_signal_list; - int i; - - /* Update input state */ - x86_update_signals(); - - /* Call chipset-specific init to set initial state */ - x86_set_state(x86_chipset_init()); - - /* Enable interrupts for input signals */ - for (i = 0; i < X86_SIGNAL_COUNT; i++, s++) - gpio_enable_interrupt(s->gpio); -} -DECLARE_HOOK(HOOK_INIT, x86_common_init, HOOK_PRIO_INIT_CHIPSET); - -static void x86_lid_change(void) -{ - /* Wake up the task to update power state */ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, x86_lid_change, HOOK_PRIO_DEFAULT); - -static void x86_ac_change(void) -{ - if (extpower_is_present()) { - 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_ac_change, HOOK_PRIO_DEFAULT); - -/*****************************************************************************/ -/* Interrupts */ - -void x86_interrupt(enum gpio_signal signal) -{ - /* Shadow signals and compare with our desired signal state. */ - x86_update_signals(); - - /* Wake up the task */ - task_wake(TASK_ID_CHIPSET); -} - -/*****************************************************************************/ -/* Console commands */ - -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); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo, - NULL, - "Show current x86 power state", - NULL); - -static int command_x86indebug(int argc, char **argv) -{ - const struct x86_signal_info *s = x86_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("x86 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 < X86_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(x86indebug, command_x86indebug, - "[mask]", - "Get/set x86 input debug mask", - NULL); - -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 == 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(hibdelay, command_hibernation_delay, - "[sec]", - "Set the delay before going into hibernation", - NULL); diff --git a/test/adapter.c b/test/adapter.c index 5ce9733b4b..f64197b387 100644 --- a/test/adapter.c +++ b/test/adapter.c @@ -16,7 +16,7 @@ #include "timer.h" #include "util.h" #include "chipset.h" -#include "chipset_x86_common.h" +#include "power.h" /* Normally private stuff from the modules we're going to test */ #include "adapter_externs.h" -- cgit v1.2.1