diff options
-rw-r--r-- | chip/stm32/clock-stm32l.c | 119 | ||||
-rw-r--r-- | chip/stm32/config-stm32l100.h | 3 | ||||
-rw-r--r-- | include/config.h | 3 | ||||
-rw-r--r-- | include/power.h | 7 | ||||
-rw-r--r-- | power/common.c | 5 | ||||
-rw-r--r-- | power/tegra.c | 7 |
6 files changed, 139 insertions, 5 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) { /* diff --git a/chip/stm32/config-stm32l100.h b/chip/stm32/config-stm32l100.h index a79bd2282e..1d18b6c5e8 100644 --- a/chip/stm32/config-stm32l100.h +++ b/chip/stm32/config-stm32l100.h @@ -51,3 +51,6 @@ /* Use DMA for UART receive */ #define CONFIG_UART_RX_DMA + +/* Fake hibernate mode */ +#define CONFIG_STM32L_FAKE_HIBERNATE diff --git a/include/config.h b/include/config.h index a8e52f5c7b..76deccc7e4 100644 --- a/include/config.h +++ b/include/config.h @@ -665,6 +665,9 @@ /* Default stack size to use for tasks, in bytes */ #undef CONFIG_STACK_SIZE +/* Fake hibernate mode */ +#undef CONFIG_STM32L_FAKE_HIBERNATE + /* * Compile common code to handle simple switch inputs such as the recovery * button input from the servo debug interface. diff --git a/include/power.h b/include/power.h index 5469ffad3f..36054e8bd1 100644 --- a/include/power.h +++ b/include/power.h @@ -73,6 +73,13 @@ int power_has_signals(uint32_t want); int power_wait_signals(uint32_t want); /** + * Set the low-level power chipset state. + * + * @param new_state New chipset state. + */ +void power_set_state(enum power_state new_state); + +/** * Chipset-specific initialization * * @return The state the chipset should start in. Usually POWER_G3, but may diff --git a/power/common.c b/power/common.c index 3a92780bf4..816ab50716 100644 --- a/power/common.c +++ b/power/common.c @@ -114,11 +114,6 @@ int power_wait_signals(uint32_t want) return EC_SUCCESS; } -/** - * Set the low-level power chipset state. - * - * @param new_state New chipset state. - */ void power_set_state(enum power_state new_state) { /* Record the time we go into G3 */ diff --git a/power/tegra.c b/power/tegra.c index 021626ee93..4ca3a172f3 100644 --- a/power/tegra.c +++ b/power/tegra.c @@ -318,6 +318,10 @@ static void power_on(void) { uint64_t t; + /* Set pull-up and enable interrupt */ + gpio_set_flags(GPIO_SUSPEND_L, GPIO_INPUT | GPIO_PULL_UP | + GPIO_INT_BOTH); + /* Make sure we de-assert the PMI_THERM_L and AP_RESET_L pin. */ set_pmic_therm(0); set_ap_reset(0); @@ -390,6 +394,9 @@ static void power_off(void) /* switch off all rails */ chipset_force_shutdown(); + /* Change SUSPEND_L pin to high-Z to reduce power draw. */ + gpio_set_flags(GPIO_SUSPEND_L, GPIO_INPUT); + lid_opened = 0; enable_sleep(SLEEP_MASK_AP_RUN); powerled_set_state(POWERLED_STATE_OFF); |