summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2014-04-29 10:22:16 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-04-30 10:00:02 +0000
commitdd702e8447f278a322115c3ec1ce839db425c387 (patch)
tree1cb3aee685e6c561f4262569f416e02c22e6da9c
parent306e9a88b93908431ed6946c9c0ae6de309ceeec (diff)
downloadchrome-ec-dd702e8447f278a322115c3ec1ce839db425c387.tar.gz
baytrail: Workaround for stuck boot process
In some cases, the system will boot to S0 from the point of view of the EC, but PLTRST# will never deassert. Work around this by waiting 50 ms for PLTRST# to deassert. If it doesn't, force the chipset all the way down by deasserting RSMRST#, then pulse the power button to turn it back on. Also add a powerfail debug command to simulate this failure event, so that the recovery process can be tested. Add API to the LPC module to get the state of PLTRST#, and to the power button state machine to force it released when we shut down the chipset and and force another power button pulse as we reset the chipset. BUG=chrome-os-partner:28422 BRANCH=baytrail TEST=1. Boot system. Should boot normally. Shut system down. 2. powerfail 3. Boot system. On the EC console, should see the system come up, go back down through G3S5, then come back up. From the user's point of view, it just boots. 1. Boot system. Should boot normally. (That is, powerfail is not sticky) Change-Id: Ia57f196606f79b9f2fce7d9cd109ab932c3571aa Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/197523 Reviewed-by: Aaron Durbin <adurbin@chromium.org>
-rw-r--r--chip/lm4/lpc.c7
-rw-r--r--common/power_button_x86.c43
-rw-r--r--include/lpc.h7
-rw-r--r--include/power_button.h10
-rw-r--r--power/baytrail.c62
5 files changed, 116 insertions, 13 deletions
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c
index 3f1d878457..ef273be8b8 100644
--- a/chip/lm4/lpc.c
+++ b/chip/lm4/lpc.c
@@ -427,6 +427,11 @@ uint32_t lpc_get_host_event_mask(enum lpc_host_event_type type)
return event_mask[type];
}
+int lpc_get_pltrst_asserted(void)
+{
+ return (LM4_LPC_LPCSTS & (1<<10)) ? 1 : 0;
+}
+
/**
* Handle write to ACPI I/O port
*
@@ -618,7 +623,7 @@ void lpc_interrupt(void)
}
CPRINTF("[%T LPC RESET# %sasserted]\n",
- (LM4_LPC_LPCSTS & (1<<10)) ? "" : "de");
+ lpc_get_pltrst_asserted() ? "" : "de");
}
}
DECLARE_IRQ(LM4_IRQ_LPC, lpc_interrupt, 2);
diff --git a/common/power_button_x86.c b/common/power_button_x86.c
index c8854e99fe..954427aa5e 100644
--- a/common/power_button_x86.c
+++ b/common/power_button_x86.c
@@ -127,6 +127,34 @@ static void set_pwrbtn_to_pch(int high)
gpio_set_level(GPIO_PCH_PWRBTN_L, high);
}
+void power_button_pch_release(void)
+{
+ CPRINTF("[%T PB PCH force release]\n");
+
+ /* Deassert power button signal to PCH */
+ set_pwrbtn_to_pch(1);
+
+ /*
+ * If power button is actually pressed, eat the next release so we
+ * don't send an extra release.
+ */
+ if (power_button_is_pressed())
+ pwrbtn_state = PWRBTN_STATE_EAT_RELEASE;
+ else
+ pwrbtn_state = PWRBTN_STATE_IDLE;
+}
+
+void power_button_pch_pulse(void)
+{
+ CPRINTF("[%T PB PCH pulse]\n");
+
+ chipset_exit_hard_off();
+ set_pwrbtn_to_pch(0);
+ pwrbtn_state = PWRBTN_STATE_LID_OPEN;
+ tnext_state = get_time().val + PWRBTN_INITIAL_US;
+ task_wake(TASK_ID_POWERBTN);
+}
+
/**
* Handle debounced power button down.
*/
@@ -180,11 +208,7 @@ static void set_initial_pwrbtn_state(void)
* Otherwise, it might power on.
*/
CPRINTF("[%T PB init-off]\n");
- set_pwrbtn_to_pch(1);
- if (power_button_is_pressed())
- pwrbtn_state = PWRBTN_STATE_EAT_RELEASE;
- else
- pwrbtn_state = PWRBTN_STATE_IDLE;
+ power_button_pch_release();
} else {
/*
* All other EC reset conditions power on the main processor so
@@ -363,13 +387,8 @@ DECLARE_HOOK(HOOK_INIT, powerbtn_x86_init, HOOK_PRIO_DEFAULT);
static void powerbtn_x86_lid_change(void)
{
/* If chipset is off, pulse the power button on lid open to wake it. */
- if (lid_is_open() && chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
- chipset_exit_hard_off();
- set_pwrbtn_to_pch(0);
- pwrbtn_state = PWRBTN_STATE_LID_OPEN;
- tnext_state = get_time().val + PWRBTN_INITIAL_US;
- task_wake(TASK_ID_POWERBTN);
- }
+ if (lid_is_open() && chipset_in_state(CHIPSET_STATE_ANY_OFF))
+ power_button_pch_pulse();
}
DECLARE_HOOK(HOOK_LID_CHANGE, powerbtn_x86_lid_change, HOOK_PRIO_DEFAULT);
diff --git a/include/lpc.h b/include/lpc.h
index 0ecaa6b822..c16b1bc5d4 100644
--- a/include/lpc.h
+++ b/include/lpc.h
@@ -96,4 +96,11 @@ void lpc_set_host_event_mask(enum lpc_host_event_type type, uint32_t mask);
*/
uint32_t lpc_get_host_event_mask(enum lpc_host_event_type type);
+/**
+ * Return the state of platform reset.
+ *
+ * @return non-zero if PLTRST# is asserted (low); 0 if not asserted (high).
+ */
+int lpc_get_pltrst_asserted(void);
+
#endif /* __CROS_EC_LPC_H */
diff --git a/include/power_button.h b/include/power_button.h
index f3da24da9b..190073812e 100644
--- a/include/power_button.h
+++ b/include/power_button.h
@@ -24,4 +24,14 @@ int power_button_is_pressed(void);
*/
void power_button_interrupt(enum gpio_signal signal);
+/**
+ * For x86 systems, force-deassert the power button signal to the PCH.
+ */
+void power_button_pch_release(void);
+
+/**
+ * For x86 systems, force a pulse of the power button signal to the PCH.
+ */
+void power_button_pch_pulse(void);
+
#endif /* __CROS_EC_POWER_BUTTON_H */
diff --git a/power/baytrail.c b/power/baytrail.c
index d4c5fb1d1a..d0289aabc2 100644
--- a/power/baytrail.c
+++ b/power/baytrail.c
@@ -13,7 +13,9 @@
#include "hooks.h"
#include "host_command.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"
@@ -51,6 +53,8 @@
static int throttle_cpu; /* Throttle CPU? */
static int pause_in_s5 = 1; /* Pause in S5 when shutting down? */
+static int restart_from_s5; /* Force system back on from S5 */
+static int fake_pltrst_timeout; /* Fake PLTRST# timeout at next power-on */
void chipset_force_shutdown(void)
{
@@ -282,6 +286,31 @@ enum power_state power_handle_state(enum power_state state)
/* Set SYS and CORE PWROK */
gpio_set_level(GPIO_PCH_SYS_PWROK, 1);
gpio_set_level(GPIO_PCH_CORE_PWROK, 1);
+
+ /* Wait 50 ms for platform reset to deassert */
+ {
+ int i;
+
+ for (i = 0; i < 50; i++) {
+ usleep(MSEC);
+ if (!lpc_get_pltrst_asserted())
+ break;
+ }
+
+ if (i < 50 && !fake_pltrst_timeout) {
+ /* Deasserted in time */
+ CPRINTF("[%T power PLTRST# deasserted]\n");
+ } else {
+ /* Force a reset. See crosbug.com/p/28422 */
+ CPRINTF("[%T power PLTRST# timeout]\n");
+ power_button_pch_release();
+ chipset_force_shutdown();
+ restart_from_s5 = 1;
+
+ fake_pltrst_timeout = 0;
+ }
+ }
+
return POWER_S0;
case POWER_S0S3:
@@ -347,6 +376,29 @@ enum power_state power_handle_state(enum power_state state)
/* Turn off power to RAM */
gpio_set_level(GPIO_PP1350_EN, 0);
+ /*
+ * If restarting from S5, delay and fake power button press.
+ * See crosbug.com/p/28422.
+ */
+ if (restart_from_s5) {
+ CPRINTF("[%T power restart from S5]\n");
+
+ restart_from_s5 = 0;
+
+ /* Delay for system to shut down after rails dropped */
+ msleep(100);
+
+ /* Restart system via power button press */
+ power_button_pch_pulse();
+
+ /*
+ * Force system to start back up from scratch. This is
+ * needed to undo the effects of a previous call to
+ * chipset_force_shutdown().
+ */
+ return POWER_G3S5;
+ }
+
/* Start shutting down */
return pause_in_s5 ? POWER_S5 : POWER_S5G3;
@@ -392,3 +444,13 @@ DECLARE_CONSOLE_COMMAND(pause_in_s5, console_command_gsv,
"Should the AP pause in S5 during shutdown?",
NULL);
+static int console_command_powerfail(int argc, char **argv)
+{
+ ccprintf("Faking a failure of next power-on event\n");
+ fake_pltrst_timeout = 1;
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(powerfail, console_command_powerfail,
+ NULL,
+ "Fake PLTRST# failure during next power-on",
+ NULL);