diff options
-rw-r--r-- | chip/lm4/lpc.c | 7 | ||||
-rw-r--r-- | common/power_button_x86.c | 43 | ||||
-rw-r--r-- | include/lpc.h | 7 | ||||
-rw-r--r-- | include/power_button.h | 10 | ||||
-rw-r--r-- | power/baytrail.c | 62 |
5 files changed, 116 insertions, 13 deletions
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c index 0593570b61..99f212af19 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 * @@ -619,7 +624,7 @@ static 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); |