summaryrefslogtreecommitdiff
path: root/chip/stm32/clock-stm32l.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/stm32/clock-stm32l.c')
-rw-r--r--chip/stm32/clock-stm32l.c119
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)
{
/*