From b7200f19af0108c72ec6288221c9a58e7c114b41 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Fri, 7 Jun 2013 16:36:23 -0700 Subject: stm32: add hibernate support using stm32f100 standby mode Implement the EC hibernate mode by using the stm32f100 standby low power mode. Also add the special key combination Alt+VolUp+H to force hibernation. As we cannot de-activate the watchdog during long hibernation, the following workaround is implemented: we are woken-up once by the watchdog and go back to hibernate if we detect that condition. Signed-off-by: Vincent Palatin BRANCH=spring BUG=chrome-os-partner:19595 TEST=on Spring with rework on the EC wake-up pin, type "hibernate 10" and see the EC console going blank for 10s, then booting with reset cause equals to "hibernate". Press Alt+VolUp+H, then wake-up the system by pressing power key. Change-Id: If0fa8a38df3c75b380d1e5c0092d3396b27567c7 Reviewed-on: https://gerrit.chromium.org/gerrit/58004 Reviewed-by: Todd Broch Reviewed-by: Vic Yang Commit-Queue: Vincent Palatin Tested-by: Vincent Palatin --- chip/stm32/clock-stm32f100.c | 27 ++++++++++++++++++++++++--- chip/stm32/keyboard_scan.c | 43 +++++++++++++++++++++++++++++++------------ chip/stm32/system.c | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 85 insertions(+), 21 deletions(-) diff --git a/chip/stm32/clock-stm32f100.c b/chip/stm32/clock-stm32f100.c index 1e99e78248..6e56c4c317 100644 --- a/chip/stm32/clock-stm32f100.c +++ b/chip/stm32/clock-stm32f100.c @@ -73,12 +73,12 @@ static void finalize_rtc_write(void) ; } -uint32_t set_rtc_alarm(unsigned delay_us) +uint32_t set_rtc_alarm(unsigned delay_s, unsigned delay_us) { unsigned rtc_t0, rtc_t1; rtc_t0 = ((uint32_t)STM32_RTC_CNTH << 16) | STM32_RTC_CNTL; - rtc_t1 = rtc_t0 + delay_us / US_PER_RTC_TICK; + rtc_t1 = rtc_t0 + delay_us / US_PER_RTC_TICK + delay_s * RTC_FREQ; prepare_rtc_write(); /* set RTC alarm timestamp (using the 40kHz counter ) */ @@ -152,6 +152,26 @@ static void config_hispeed_clock(void) ; } +void __enter_hibernate(uint32_t seconds, uint32_t microseconds) +{ + if (seconds || microseconds) + set_rtc_alarm(seconds, microseconds); + + /* interrupts off now */ + asm volatile("cpsid i"); + + /* enable the wake up pin */ + STM32_PWR_CSR |= (1<<8); + STM32_PWR_CR |= 0xe; + CPU_SCB_SYSCTRL |= 0x4; + /* go to Standby mode */ + asm("wfi"); + + /* we should never reach that point */ + while (1) + ; +} + #ifdef CONFIG_LOW_POWER_IDLE #ifdef CONFIG_FORCE_CONSOLE_RESUME @@ -201,7 +221,8 @@ void __idle(void) /* set deep sleep bit */ CPU_SCB_SYSCTRL |= 0x4; - rtc_t0 = set_rtc_alarm(next_delay - STOP_MODE_LATENCY); + rtc_t0 = set_rtc_alarm(0, + next_delay - STOP_MODE_LATENCY); asm("wfi"); CPU_SCB_SYSCTRL &= ~0x4; diff --git a/chip/stm32/keyboard_scan.c b/chip/stm32/keyboard_scan.c index d719bfe43b..b11b7b2ccd 100644 --- a/chip/stm32/keyboard_scan.c +++ b/chip/stm32/keyboard_scan.c @@ -59,14 +59,16 @@ static uint8_t scan_edge_index[KB_OUTPUTS][KB_INPUTS]; #define MASK_VALUE_REFRESH 0x04 /* Key masks and values for warm reboot combination */ -#define MASK_INDEX_KEYR 3 -#define MASK_VALUE_KEYR 0x80 #define MASK_INDEX_VOL_UP 4 #define MASK_VALUE_VOL_UP 0x01 #define MASK_INDEX_RIGHT_ALT 10 #define MASK_VALUE_RIGHT_ALT 0x01 #define MASK_INDEX_LEFT_ALT 10 #define MASK_VALUE_LEFT_ALT 0x40 +#define MASK_INDEX_KEY_R 3 +#define MASK_VALUE_KEY_R 0x80 +#define MASK_INDEX_KEY_H 6 +#define MASK_VALUE_KEY_H 0x02 struct kbc_gpio { int num; /* logical row or column number */ @@ -249,25 +251,42 @@ void enter_polling_mode(void) */ static int check_runtime_keys(const uint8_t *state) { - int num_press; + int num_press = 0; int c; - /* Count number of key pressed */ - for (c = num_press = 0; c < KB_OUTPUTS; c++) { + /* + * All runtime key combos are (right or left ) alt + volume up + (some + * key NOT on the same col as alt or volume up ) + */ + if (state[MASK_INDEX_VOL_UP] != MASK_VALUE_VOL_UP) + return 0; + if (state[MASK_INDEX_RIGHT_ALT] != MASK_VALUE_RIGHT_ALT && + state[MASK_INDEX_LEFT_ALT] != MASK_VALUE_LEFT_ALT) + return 0; + + /* + * Count number of columns with keys pressed. We know two columns are + * pressed for volume up and alt, so if only one more key is pressed + * there will be exactly 3 non-zero columns. + */ + for (c = 0; c < KB_OUTPUTS; c++) { if (state[c]) - ++num_press; + num_press++; } - - if (num_press < 3) + if (num_press != 3) return 0; - if (state[MASK_INDEX_KEYR] == MASK_VALUE_KEYR && - state[MASK_INDEX_VOL_UP] == MASK_VALUE_VOL_UP && - (state[MASK_INDEX_RIGHT_ALT] == MASK_VALUE_RIGHT_ALT || - state[MASK_INDEX_LEFT_ALT] == MASK_VALUE_LEFT_ALT)) { + /* Check individual keys */ + if (state[MASK_INDEX_KEY_R] == MASK_VALUE_KEY_R) { + /* R = reboot */ + CPRINTF("[%T KB warm reboot]\n"); keyboard_clear_state(); system_warm_reboot(); return 1; + } else if (state[MASK_INDEX_KEY_H] == MASK_VALUE_KEY_H) { + /* H = hibernate */ + CPRINTF("[%T KB hibernate]\n"); + system_hibernate(0, 0); } return 0; diff --git a/chip/stm32/system.c b/chip/stm32/system.c index b973a9592f..7e0c0acb0c 100644 --- a/chip/stm32/system.c +++ b/chip/stm32/system.c @@ -5,6 +5,7 @@ /* System module for Chrome EC : hardware specific implementation */ +#include "console.h" #include "cpu.h" #include "registers.h" #include "system.h" @@ -55,6 +56,23 @@ static int bkpdata_write(enum bkpdata_index index, uint16_t value) return EC_SUCCESS; } +void __no_hibernate(uint32_t seconds, uint32_t microseconds) +{ + while (1) + /* NOT IMPLEMENTED */; +} + +void __enter_hibernate(uint32_t seconds, uint32_t microseconds) + __attribute__((weak, alias("__no_hibernate"))); + +void system_hibernate(uint32_t seconds, uint32_t microseconds) +{ + /* Flush console before hibernating */ + cflush(); + /* chip specific standby mode */ + __enter_hibernate(seconds, microseconds); +} + static void check_reset_cause(void) { uint32_t flags = bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS); @@ -96,13 +114,19 @@ static void check_reset_cause(void) if (!flags && (raw_cause & 0xfe000000)) flags |= RESET_FLAG_OTHER; - system_set_reset_flags(flags); -} + /* + * WORKAROUND: as we cannot de-activate the watchdog during + * long hibernation, we are woken-up once by the watchdog and + * go back to hibernate if we detect that condition, without + * watchdog initialized this time. + * The RTC deadline (if any) is already set. + */ + if ((flags & (RESET_FLAG_HIBERNATE | RESET_FLAG_WATCHDOG)) == + (RESET_FLAG_HIBERNATE | RESET_FLAG_WATCHDOG)) { + __enter_hibernate(0, 0); + } -void system_hibernate(uint32_t seconds, uint32_t microseconds) -{ - while (1) - /* NOT IMPLEMENTED */; + system_set_reset_flags(flags); } void system_pre_init(void) -- cgit v1.2.1