summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Yung-Chieh Lo <yjlou@chromium.org>2014-02-12 16:37:56 +0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-03-06 23:14:07 +0000
commit1484116cb0173bce860c4a26aaf5d9625824a401 (patch)
tree22a2d036e2586f7735a83dd7ddba8c15707115e8
parent6bdc69940188cd4f17ecde547afbbc3ad8335367 (diff)
downloadchrome-ec-1484116cb0173bce860c4a26aaf5d9625824a401.tar.gz
stm32l: supports fake-hibernate
We don't have available GPIO pin for power button as the hibernate (stand-by) wake-up source. Also, we don't want to do board change. So, put the EC in a decent infinite loop to pretend the hibernate mode and wait for particular wake-up event. This should be fine because the AP is already down before EC hibernates. BUG=chrome-os-partner:25435 BRANCH=Nyan TEST=see comment #6 of issue for detailed test steps. Change-Id: I2cae131789f9ca5808b60d5f2495222ca9016e7c Signed-off-by: Louis Yung-Chieh Lo <yjlou@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/186061 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--chip/stm32/clock-stm32l.c119
-rw-r--r--chip/stm32/config-stm32l100.h3
-rw-r--r--include/config.h3
-rw-r--r--include/power.h7
-rw-r--r--power/common.c5
-rw-r--r--power/tegra.c7
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);