summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-05-09 15:34:50 -0700
committerRandall Spangler <rspangler@chromium.org>2012-05-09 16:54:17 -0700
commit30a33e6b0412c132c375cc569f9629da6eb168f3 (patch)
tree9aa61aafc6209ab62ea36553af238052f07dfae0
parent1655c8727a82d8a344400f1708d315decb57c73c (diff)
downloadchrome-ec-release-R20-2268.B.tar.gz
Drop DPWROK when system is off for more than 10 secrelease-R20-2268.B
This saves ~70mw of power. To make this work, I also had to stretch the power button signal to give the system a chance to come back up when the user taps the power button. Signed-off-by: Randall Spangler <rspangler@chromium.org> BUG=chrome-os-partner:9574 TEST=manual For each of the following tests, wait ~15 sec after the system is powered off to give it a chance to drop DPWROK. 1) tap power button -> system turns on 2) hold power button 1 sec -> system turns on 3) open lid -> system turns on 4) silego reset (power+refresh, or power+esc on proto1) -> system stays off 5) silego recovery (power+esc+refresh) -> system turns on 6) hold down power button and type 'reboot' on EC console -> system turns on 7) type 'powerbtn' on EC console -> system turns on Change-Id: I781cf3e665104192521b7fb9ff75a3c3e7f43464
-rw-r--r--board/bds/board.c7
-rw-r--r--chip/lm4/power_button.c60
-rw-r--r--common/gaia_power.c30
-rw-r--r--common/usb_charge.c2
-rw-r--r--common/x86_power.c173
-rw-r--r--include/chipset.h23
6 files changed, 201 insertions, 94 deletions
diff --git a/board/bds/board.c b/board/bds/board.c
index 777e84c3e5..89bd337af3 100644
--- a/board/bds/board.c
+++ b/board/bds/board.c
@@ -86,12 +86,17 @@ const struct gpio_info gpio_list[GPIO_COUNT] = {
/* BDS system is only half-wired to an x86 chipset, so it can't tell what state
* the chipset is in. Rather than scatter ifdef's everywhere, put a mock
* chipset interface here. */
-int chipset_in_state(enum chipset_state in_state)
+int chipset_in_state(int state_mask)
{
return 1; /* Sure, I'm in whatever state you want. */
}
+void chipset_exit_hard_off(void)
+{
+}
+
+
void configure_board(void)
{
}
diff --git a/chip/lm4/power_button.c b/chip/lm4/power_button.c
index b2841c77b1..836a663403 100644
--- a/chip/lm4/power_button.c
+++ b/chip/lm4/power_button.c
@@ -42,13 +42,14 @@
#define PWRBTN_DEBOUNCE_US 30000 /* Debounce time for power button */
#define PWRBTN_DELAY_T0 32000 /* 32ms (PCH requires >16ms) */
#define PWRBTN_DELAY_T1 (4000000 - PWRBTN_DELAY_T0) /* 4 secs - t0 */
-#define PWRBTN_RECOVERY_US 200000 /* Length of time to simulate power button
- * press when booting into
- * keyboard-controlled recovery mode */
+#define PWRBTN_INITIAL_US 200000 /* Length of time to stretch initial power
+ * button press to give chipset a chance to
+ * wake up (~100ms) and react to the press
+ * (~16ms). Also used as pulse length for
+ * simulated power button presses when the
+ * system is off. */
#define LID_DEBOUNCE_US 30000 /* Debounce time for lid switch */
-#define LID_PWRBTN_US PWRBTN_DELAY_T0 /* Length of time to simulate power
- * button press on lid open */
enum power_button_state {
PWRBTN_STATE_STOPPED = 0,
@@ -59,6 +60,7 @@ enum power_button_state {
PWRBTN_STATE_STOPPING,
PWRBTN_STATE_BOOT_RESET,
PWRBTN_STATE_BOOT_RECOVERY,
+ PWRBTN_STATE_WAS_OFF,
};
static enum power_button_state pwrbtn_state = PWRBTN_STATE_STOPPED;
@@ -146,10 +148,16 @@ static void state_machine(uint64_t tnow)
switch (pwrbtn_state) {
case PWRBTN_STATE_START:
- if (chipset_in_state(CHIPSET_STATE_SOFT_OFF)) {
- /* Chipset is off, so just pass the true power button
- * state to the chipset. */
- pwrbtn_state = PWRBTN_STATE_HELD_DOWN;
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
+ /* Chipset is off, so wake the chipset and send it a
+ * long enough pulse to wake up. After that we'll
+ * reflect the true power button state. If we don't
+ * stretch the pulse here, the user may release the
+ * power button before the chipset finishes waking from
+ * hard off state. */
+ chipset_exit_hard_off();
+ tnext_state = tnow + PWRBTN_INITIAL_US;
+ pwrbtn_state = PWRBTN_STATE_WAS_OFF;
} else {
/* Chipset is on, so send the chipset a pulse */
tnext_state = tnow + PWRBTN_DELAY_T0;
@@ -166,10 +174,10 @@ static void state_machine(uint64_t tnow)
/* If the chipset is already off, don't tell it the power
* button is down; it'll just cause the chipset to turn on
* again. */
- if (!chipset_in_state(CHIPSET_STATE_SOFT_OFF))
- set_pwrbtn_to_pch(0);
- else
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF))
CPRINTF("[%T PB chipset already off]\n");
+ else
+ set_pwrbtn_to_pch(0);
pwrbtn_state = PWRBTN_STATE_HELD_DOWN;
break;
case PWRBTN_STATE_STOPPING:
@@ -180,6 +188,17 @@ static void state_machine(uint64_t tnow)
set_pwrbtn_to_pch(1);
pwrbtn_state = PWRBTN_STATE_BOOT_RESET;
break;
+ case PWRBTN_STATE_WAS_OFF:
+ if (get_power_button_pressed()) {
+ /* User is still holding the power button */
+ pwrbtn_state = PWRBTN_STATE_HELD_DOWN;
+ } else {
+ /* Stop stretching the power button press */
+ *memmap_switches &= ~EC_LPC_SWITCH_POWER_BUTTON_PRESSED;
+ keyboard_set_power_button(0);
+ pwrbtn_state = PWRBTN_STATE_STOPPING;
+ }
+ break;
case PWRBTN_STATE_STOPPED:
case PWRBTN_STATE_HELD_DOWN:
case PWRBTN_STATE_BOOT_RESET:
@@ -195,6 +214,9 @@ static void power_button_changed(uint64_t tnow)
if (pwrbtn_state == PWRBTN_STATE_BOOT_RECOVERY) {
/* Ignore all power button changes during the recovery pulse */
CPRINTF("[%T PB changed during recovery pulse]\n");
+ } else if (pwrbtn_state == PWRBTN_STATE_WAS_OFF) {
+ /* Ignore all power button changes during an initial pulse */
+ CPRINTF("[%T PB changed during initial pulse]\n");
} else if (get_power_button_pressed()) {
/* Power button pressed */
CPRINTF("[%T PB pressed]\n");
@@ -233,12 +255,13 @@ static void lid_switch_open(uint64_t tnow)
lpc_set_host_events(EC_LPC_HOST_EVENT_MASK(
EC_LPC_HOST_EVENT_LID_OPEN));
- /* If the chipset is is soft-off, send a power button pulse to
- * wake up the chipset. */
- if (chipset_in_state(CHIPSET_STATE_SOFT_OFF)) {
+ /* If the chipset is off, send a power button pulse to wake up the
+ * chipset. */
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
+ chipset_exit_hard_off();
set_pwrbtn_to_pch(0);
pwrbtn_state = PWRBTN_STATE_STOPPING;
- tnext_state = tnow + LID_PWRBTN_US;
+ tnext_state = tnow + PWRBTN_INITIAL_US;
task_wake(TASK_ID_POWERBTN);
}
}
@@ -317,7 +340,7 @@ static int power_button_init(void)
* the PCH so it powers on. */
set_pwrbtn_to_pch(0);
pwrbtn_state = PWRBTN_STATE_BOOT_RECOVERY;
- tnext_state = get_time().val + PWRBTN_RECOVERY_US;
+ tnext_state = get_time().val + PWRBTN_INITIAL_US;
} else {
/* Keyboard-controlled reset, so don't let the PCH see
* that the power button was pressed. Otherwise, it
@@ -395,7 +418,7 @@ void power_button_task(void)
static int command_powerbtn(int argc, char **argv)
{
- int ms = 100; /* Press duration in ms */
+ int ms = PWRBTN_INITIAL_US / 1000; /* Press duration in ms */
char *e;
if (argc > 1) {
@@ -411,6 +434,7 @@ static int command_powerbtn(int argc, char **argv)
* PCH. It does not simulate the full state machine which sends SMIs
* and other events to other parts of the EC and chipset. */
ccprintf("Simulating %d ms power button press.\n", ms);
+ chipset_exit_hard_off();
set_pwrbtn_to_pch(0);
usleep(ms * 1000);
set_pwrbtn_to_pch(1);
diff --git a/common/gaia_power.c b/common/gaia_power.c
index 1683ecb006..58fa0e68cf 100644
--- a/common/gaia_power.c
+++ b/common/gaia_power.c
@@ -136,24 +136,28 @@ int gaia_power_init(void)
/*****************************************************************************/
/* Chipset interface */
-/* Returns non-zero if the chipset is in the specified state. */
-int chipset_in_state(enum chipset_state in_state)
+int chipset_in_state(int state_mask)
{
- switch (in_state) {
- case CHIPSET_STATE_SOFT_OFF:
- return ap_on == 0;
- case CHIPSET_STATE_SUSPEND:
- /* TODO: implement */
- return 0;
- case CHIPSET_STATE_ON:
- return ap_on;
- }
+ /* If AP is off, match any off state for now */
+ if ((state_mask & CHIPSET_STATE_ANY_OFF) && !ap_on)
+ return 1;
+
+ /* If AP is on, match on state */
+ if ((state_mask & CHIPSET_STATE_ON) && ap_on)
+ return 1;
- /* Should never get here since we list all states above, but compiler
- * doesn't seem to understand that. */
+ /* TODO: detect suspend state */
+
+ /* In any other case, we don't have a match */
return 0;
}
+
+void chipset_exit_hard_off(void)
+{
+ /* TODO: implement, if/when we take the AP down to a hard-off state */
+}
+
/*****************************************************************************/
void gaia_power_task(void)
diff --git a/common/usb_charge.c b/common/usb_charge.c
index eea5d3ec72..282c8ddbcb 100644
--- a/common/usb_charge.c
+++ b/common/usb_charge.c
@@ -139,7 +139,7 @@ DECLARE_CONSOLE_COMMAND(usbchargemode, command_set_mode);
static int usb_charge_init(void)
{
- if (chipset_in_state(CHIPSET_STATE_SOFT_OFF))
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF))
usb_charge_all_ports_off();
else
usb_charge_all_ports_on();
diff --git a/common/x86_power.c b/common/x86_power.c
index 50478c8bd0..135009cb71 100644
--- a/common/x86_power.c
+++ b/common/x86_power.c
@@ -24,6 +24,9 @@
* transition, just jump to the next state. */
#define DEFAULT_TIMEOUT 1000000
+/* Timeout for dropping back from S5 to G3 */
+#define S5_INACTIVITY_TIMEOUT 10000000
+
enum x86_state {
X86_G3 = 0, /* System is off (not technically all the
* way into G3, which means totally
@@ -38,6 +41,7 @@ enum x86_state {
X86_S3S0, /* S3 -> S0 */
X86_S0S3, /* S0 -> S3 */
X86_S3S5, /* S3 -> S5 */
+ X86_S5G3, /* S5 -> G3 */
};
static const char * const state_names[] = {
@@ -50,6 +54,7 @@ static const char * const state_names[] = {
"S3->S0",
"S0->S3",
"S3->S5",
+ "S5->G3",
};
/* Input state flags */
@@ -87,7 +92,7 @@ static const char * const state_names[] = {
static enum x86_state state; /* 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 int want_g3_exit; /* Should we exit the G3 state? */
/* Update input signal state */
static void update_in_signals(void)
@@ -210,22 +215,56 @@ void x86_power_reset(int cold_reset)
/*****************************************************************************/
/* Chipset interface */
-/* Returns non-zero if the chipset is in the specified state. */
-/* TODO: change in_state to bitmask so multiple states can be checked */
-int chipset_in_state(enum chipset_state in_state)
+int chipset_in_state(int state_mask)
{
- switch (in_state) {
- case CHIPSET_STATE_SOFT_OFF:
- return (state == X86_S5);
- case CHIPSET_STATE_SUSPEND:
- return (state == X86_S3);
- case CHIPSET_STATE_ON:
- return (state == X86_S0);
+ 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;
}
- /* Should never get here since we list all states above, but compiler
- * doesn't seem to understand that. */
- return 0;
+ /* 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;
+ task_wake(TASK_ID_X86POWER);
}
/*****************************************************************************/
@@ -245,8 +284,10 @@ void x86_power_interrupt(enum gpio_signal signal)
static int x86_power_init(void)
{
- /* Default to G3 state unless proven otherwise */
- state = X86_G3;
+ /* Default to moving towards S5 state unless proven otherwise. This
+ * supports booting the main processor during the boot process. We'll
+ * drop back to G3 if we stay inactive in S5.*/
+ state = X86_G3S5;
/* Update input state */
update_in_signals();
@@ -309,8 +350,60 @@ void x86_power_task(void)
switch (state) {
case X86_G3:
- /* Move to S5 state on boot */
- state = X86_G3S5;
+ if (want_g3_exit) {
+ want_g3_exit = 0;
+ state = X86_G3S5;
+ break;
+ }
+
+ /* Steady state; wait for a message */
+ in_want = 0;
+ task_wait_event(-1);
+ break;
+
+ case X86_S5:
+ if (gpio_get_level(GPIO_PCH_SLP_S5n) == 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:
+ if (gpio_get_level(GPIO_PCH_SLP_S3n) == 1) {
+ /* Power up to next state */
+ state = X86_S3S0;
+ break;
+ } else if (gpio_get_level(GPIO_PCH_SLP_S5n) == 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 (gpio_get_level(GPIO_PCH_SLP_S3n) == 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:
@@ -419,50 +512,22 @@ void x86_power_task(void)
state = X86_S5;
break;
- case X86_S5:
- if (gpio_get_level(GPIO_PCH_SLP_S5n) == 1) {
- /* Power up to next state */
- state = X86_S5S3;
- break;
- }
-
- /* Otherwise, steady state; wait for a message */
- in_want = 0;
- task_wait_event(-1);
- break;
+ case X86_S5G3:
+ /* Deassert DPWROK, assert RSMRST# */
+ gpio_set_level(GPIO_PCH_DPWROK, 0);
+ gpio_set_level(GPIO_PCH_RSMRSTn, 0);
- case X86_S3:
- if (gpio_get_level(GPIO_PCH_SLP_S3n) == 1) {
- /* Power up to next state */
- state = X86_S3S0;
- break;
- } else if (gpio_get_level(GPIO_PCH_SLP_S5n) == 0) {
- /* Power down to next state */
- state = X86_S3S5;
- break;
- }
+ /* Switch off +5V always-on */
+ gpio_set_level(GPIO_ENABLE_5VALW, 0);
- /* Otherwise, steady state; wait for a message */
- in_want = 0;
- task_wait_event(-1);
+ state = X86_G3;
break;
-
- case X86_S0:
- if (gpio_get_level(GPIO_PCH_SLP_S3n) == 0) {
- /* Power down to next state */
- state = X86_S0S3;
- break;
- }
-
- /* Otherwise, steady state; wait for a message */
- in_want = 0;
- task_wait_event(-1);
}
}
}
/*****************************************************************************/
-/* Console commnands */
+/* Console commands */
static int command_x86power(int argc, char **argv)
{
diff --git a/include/chipset.h b/include/chipset.h
index 8e126ccddc..ac192f29c9 100644
--- a/include/chipset.h
+++ b/include/chipset.h
@@ -13,20 +13,29 @@
#include "common.h"
-/* Chipset state.
+/* Chipset state mask
*
* Note that this is a non-exhaustive list of states which the main chipset can
* be in, and is potentially one-to-many for real, underlying chipset states.
* That's why chipset_in_state() asks "Is the chipset in something
* approximating this state?" and not "Tell me what state the chipset is in and
* I'll compare it myself with the state(s) I want." */
-enum chipset_state {
- CHIPSET_STATE_SOFT_OFF, /* Soft off (S5) */
- CHIPSET_STATE_SUSPEND, /* Suspend (S3) */
- CHIPSET_STATE_ON, /* On (S0) */
+enum chipset_state_mask {
+ CHIPSET_STATE_HARD_OFF = 0x01, /* Hard off (G3) */
+ CHIPSET_STATE_SOFT_OFF = 0x02, /* Soft off (S5) */
+ CHIPSET_STATE_SUSPEND = 0x04, /* Suspend (S3) */
+ CHIPSET_STATE_ON = 0x08, /* On (S0) */
+ /* Common combinations */
+ CHIPSET_STATE_ANY_OFF = (CHIPSET_STATE_HARD_OFF |
+ CHIPSET_STATE_SOFT_OFF), /* Any off state */
};
-/* Returns non-zero if the chipset is in the specified state. */
-int chipset_in_state(enum chipset_state in_state);
+/* Return non-zero if the chipset is in one of the states specified in the
+ * mask. */
+int chipset_in_state(int state_mask);
+
+/* Ask the chipset to exit the hard off state. Does nothing if the chipset has
+ * already left the state, or was not in the state to begin with. */
+void chipset_exit_hard_off(void);
#endif /* __CROS_EC_CHIPSET_H */