diff options
Diffstat (limited to 'chip/stm32/clock-stm32l.c')
-rw-r--r-- | chip/stm32/clock-stm32l.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/chip/stm32/clock-stm32l.c b/chip/stm32/clock-stm32l.c index b2de4e8073..2f1b0f655a 100644 --- a/chip/stm32/clock-stm32l.c +++ b/chip/stm32/clock-stm32l.c @@ -14,6 +14,18 @@ #include "registers.h" #include "util.h" +#ifdef CONFIG_STM32L_FAKE_HIBERNATE +#include "extpower.h" +#include "keyboard_config.h" +#include "lid_switch.h" +#include "power.h" +#include "power_button.h" +#include "system.h" +#include "task.h" + +static int fake_hibernate; +#endif + /* High-speed oscillator is 16 MHz */ #define HSI_CLOCK 16000000 /* @@ -159,6 +171,113 @@ void clock_enable_module(enum module_id module, int enable) clock_mask = new_mask; } +#ifdef CONFIG_STM32L_FAKE_HIBERNATE +/* + * This is for NOT having enough hibernate (more precisely, the stand-by mode) + * wake-up source pin. STM32L100 supports 3 wake-up source pins: + * + * WKUP1 (PA0) -- used for ACOK_PMU + * WKUP2 (PC13) -- used for LID_OPEN + * WKUP3 (PE6) -- cannot be used due to IC package. + * + * However, we need the power button as a wake-up source as well and there is + * no available pin for us (we don't want to move the ACOK_PMU pin). + * + * Fortunately, the STM32L is low-power enough so that we don't need the + * super-low-power mode. So, we fake this hibernate mode and accept the + * following wake-up source. + * + * RTC alarm (faked as well). + * Power button + * Lid open + * AC detected + * + * The original issue is here: crosbug.com/p/25435. + */ +void __enter_hibernate(uint32_t seconds, uint32_t microseconds) +{ + int i; + fake_hibernate = 1; + +#ifdef CONFIG_POWER_COMMON + /* + * A quick hack to stop annoying messages from charger task. + * + * When the battery is under 3%, the power task would call + * power_off() to shutdown AP. However, the power_off() would + * notify the HOOK_CHIPSET_SHUTDOWN, where the last hook is + * charge_shutdown() and it hibernates the power task (infinite + * loop -- not real CPU hibernate mode). Unfortunately, the + * charger task is still running. It keeps generating annoying + * log message. + * + * Thus, the hack is to set the power state machine (before we + * enter infinite loop) so that the charger task thinks the AP + * is off and stops generating messages. + */ + power_set_state(POWER_G3); +#endif + + /* + * Change keyboard outputs to high-Z to reduce power draw. + * We don't need corresponding code to change them back, + * because fake hibernate is always exited with a reboot. + * + * A little hacky to do this here. + */ + for (i = GPIO_KB_OUT00; i < GPIO_KB_OUT00 + KEYBOARD_COLS; i++) + gpio_set_flags(i, GPIO_INPUT); + + ccprintf("[%T fake hibernate. waits for power button/lid/RTC/AC]\n"); + cflush(); + + if (seconds || microseconds) { + if (seconds) + sleep(seconds); + if (microseconds) + usleep(microseconds); + } else { + while (1) + task_wait_event(-1); + } + + ccprintf("[%T fake RTC alarm fires. resets EC]\n"); + cflush(); + system_reset(SYSTEM_RESET_HARD); +} + +static void fake_hibernate_power_button_hook(void) +{ + if (fake_hibernate && lid_is_open() && !power_button_is_pressed()) { + ccprintf("[%T %s() resets EC]\n", __func__); + cflush(); + system_reset(SYSTEM_RESET_HARD); + } +} +DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, fake_hibernate_power_button_hook, + HOOK_PRIO_DEFAULT); + +static void fake_hibernate_lid_hook(void) +{ + if (fake_hibernate && lid_is_open()) { + ccprintf("[%T %s() resets EC]\n", __func__); + cflush(); + system_reset(SYSTEM_RESET_HARD); + } +} +DECLARE_HOOK(HOOK_LID_CHANGE, fake_hibernate_lid_hook, HOOK_PRIO_DEFAULT); + +static void fake_hibernate_ac_hook(void) +{ + if (fake_hibernate && extpower_is_present()) { + ccprintf("[%T %s() resets EC]\n", __func__); + cflush(); + system_reset(SYSTEM_RESET_HARD); + } +} +DECLARE_HOOK(HOOK_AC_CHANGE, fake_hibernate_ac_hook, HOOK_PRIO_DEFAULT); +#endif + void clock_init(void) { /* |