summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Berg <alecaberg@chromium.org>2013-10-14 17:55:42 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2013-10-21 23:59:38 +0000
commit10bd1db6d73e21619e789d73c8233dea2c493510 (patch)
treedfc03ce44ce9ef3697cb707d2f160861697628b4
parent1d6687429c1ea236bf09ac32d4936888a36730e6 (diff)
downloadchrome-ec-10bd1db6d73e21619e789d73c8233dea2c493510.tar.gz
lm4: Use low speed clock in deep sleep.
Changed the low power idle task to use the low speed clock in deep sleep. The low power idle task is currently only enabled for Peppy, Slippy, and Falco. This change decreases power consumption when the AP is not running. Note that the low speed clock is slow enough that the JTAG cannot be used and the EC console UART cannot be used. To work around that, this commit detects when the JTAG is in use and when the EC console is in use, and will not use the low speed clock if either is in use. The JTAG in use never clears after being set and the console in use clears after a fixed timeout period. BUG=None BRANCH=None TEST=Passes all unit tests. Tested that the EC console works when in deep sleep. Tested that it is possible to run flash_ec when in deep sleep and using the low speed clock. Change-Id: Ia65997eb8e607a5df9b2c7d68e4826bfb1e0194c Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/173326 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/falco/board.c9
-rw-r--r--board/falco/board.h2
-rw-r--r--board/peppy/board.c9
-rw-r--r--board/peppy/board.h2
-rw-r--r--board/slippy/board.c9
-rw-r--r--board/slippy/board.h2
-rw-r--r--chip/lm4/clock.c132
-rw-r--r--chip/lm4/gpio.c12
-rw-r--r--chip/lm4/jtag.c28
-rw-r--r--chip/lm4/uart.c73
-rw-r--r--chip/stm32/clock-stm32f.c8
-rw-r--r--common/console.c7
-rw-r--r--common/system_common.c24
-rw-r--r--common/uart_buffering.c5
-rw-r--r--include/clock.h5
-rw-r--r--include/gpio.h12
-rw-r--r--include/jtag.h12
-rw-r--r--include/system.h21
-rw-r--r--include/uart.h32
19 files changed, 383 insertions, 21 deletions
diff --git a/board/falco/board.c b/board/falco/board.c
index 41de41d920..487cc1ef39 100644
--- a/board/falco/board.c
+++ b/board/falco/board.c
@@ -17,6 +17,7 @@
#include "gpio.h"
#include "host_command.h"
#include "i2c.h"
+#include "jtag.h"
#include "keyboard_scan.h"
#include "lid_switch.h"
#include "lm4_adc.h"
@@ -30,6 +31,7 @@
#include "temp_sensor_g781.h"
#include "thermal.h"
#include "timer.h"
+#include "uart.h"
#include "util.h"
/* GPIO signal list. Must match order from enum gpio_signal. */
@@ -65,6 +67,11 @@ const struct gpio_info gpio_list[] = {
switch_interrupt},
{"WP_L", LM4_GPIO_A, (1<<4), GPIO_INT_BOTH,
switch_interrupt},
+ {"JTAG_TCK", LM4_GPIO_C, (1<<0), GPIO_DEFAULT,
+ jtag_interrupt},
+ {"UART0_RX", LM4_GPIO_A, (1<<0), GPIO_PULL_UP|
+ GPIO_INT_BOTH_DSLEEP,
+ uart_deepsleep_interrupt},
/* Other inputs */
{"FAN_ALERT_L", LM4_GPIO_B, (1<<0), GPIO_INPUT, NULL},
@@ -131,7 +138,7 @@ BUILD_ASSERT(ARRAY_SIZE(gpio_list) == GPIO_COUNT);
/* Pins with alternate functions */
const struct gpio_alt_func gpio_alt_funcs[] = {
- {GPIO_A, 0x03, 1, MODULE_UART}, /* UART0 */
+ {GPIO_A, 0x03, 1, MODULE_UART, GPIO_PULL_UP}, /* UART0 */
{GPIO_A, 0x40, 3, MODULE_I2C}, /* I2C1 SCL */
{GPIO_A, 0x80, 3, MODULE_I2C, GPIO_OPEN_DRAIN}, /* I2C1 SDA */
{GPIO_B, 0x04, 3, MODULE_I2C}, /* I2C0 SCL */
diff --git a/board/falco/board.h b/board/falco/board.h
index 5c8b0fe196..6cf4973213 100644
--- a/board/falco/board.h
+++ b/board/falco/board.h
@@ -86,6 +86,8 @@ enum gpio_signal {
GPIO_PCH_EDP_VDD_EN, /* PCH wants EDP enabled */
GPIO_RECOVERY_L, /* Recovery signal from servo */
GPIO_WP_L, /* Write protect input */
+ GPIO_JTAG_TCK, /* JTAG clock input */
+ GPIO_UART0_RX, /* UART0 RX input */
/* Other inputs */
GPIO_FAN_ALERT_L, /* From thermal sensor */
diff --git a/board/peppy/board.c b/board/peppy/board.c
index 0c53cb74e4..569d1ffa0a 100644
--- a/board/peppy/board.c
+++ b/board/peppy/board.c
@@ -15,6 +15,7 @@
#include "gpio.h"
#include "host_command.h"
#include "i2c.h"
+#include "jtag.h"
#include "keyboard_scan.h"
#include "lid_switch.h"
#include "lm4_adc.h"
@@ -28,6 +29,7 @@
#include "temp_sensor_g781.h"
#include "thermal.h"
#include "timer.h"
+#include "uart.h"
#include "util.h"
/* GPIO signal list. Must match order from enum gpio_signal. */
@@ -63,6 +65,11 @@ const struct gpio_info gpio_list[] = {
switch_interrupt},
{"WP_L", LM4_GPIO_A, (1<<4), GPIO_INT_BOTH,
switch_interrupt},
+ {"JTAG_TCK", LM4_GPIO_C, (1<<0), GPIO_DEFAULT,
+ jtag_interrupt},
+ {"UART0_RX", LM4_GPIO_A, (1<<0), GPIO_PULL_UP|
+ GPIO_INT_BOTH_DSLEEP,
+ uart_deepsleep_interrupt},
/* Other inputs */
{"FAN_ALERT_L", LM4_GPIO_B, (1<<0), GPIO_INPUT, NULL},
@@ -130,7 +137,7 @@ BUILD_ASSERT(ARRAY_SIZE(gpio_list) == GPIO_COUNT);
/* Pins with alternate functions */
const struct gpio_alt_func gpio_alt_funcs[] = {
- {GPIO_A, 0x03, 1, MODULE_UART}, /* UART0 */
+ {GPIO_A, 0x03, 1, MODULE_UART, GPIO_PULL_UP}, /* UART0 */
{GPIO_B, 0x04, 3, MODULE_I2C}, /* I2C0 SCL */
{GPIO_B, 0x08, 3, MODULE_I2C, GPIO_OPEN_DRAIN}, /* I2C0 SDA */
{GPIO_B, 0x40, 3, MODULE_I2C}, /* I2C5 SCL */
diff --git a/board/peppy/board.h b/board/peppy/board.h
index cd6f5a6e14..08acc14ffb 100644
--- a/board/peppy/board.h
+++ b/board/peppy/board.h
@@ -88,6 +88,8 @@ enum gpio_signal {
GPIO_PCH_EDP_VDD_EN, /* PCH wants EDP enabled */
GPIO_RECOVERY_L, /* Recovery signal from servo */
GPIO_WP_L, /* Write protect input */
+ GPIO_JTAG_TCK, /* JTAG clock input */
+ GPIO_UART0_RX, /* UART0 RX input */
/* Other inputs */
GPIO_FAN_ALERT_L, /* From thermal sensor */
diff --git a/board/slippy/board.c b/board/slippy/board.c
index ff1a71e06f..b10097142b 100644
--- a/board/slippy/board.c
+++ b/board/slippy/board.c
@@ -15,6 +15,7 @@
#include "gpio.h"
#include "host_command.h"
#include "i2c.h"
+#include "jtag.h"
#include "keyboard_scan.h"
#include "lid_switch.h"
#include "lm4_adc.h"
@@ -28,6 +29,7 @@
#include "temp_sensor_g781.h"
#include "thermal.h"
#include "timer.h"
+#include "uart.h"
#include "util.h"
/* GPIO signal list. Must match order from enum gpio_signal. */
@@ -63,6 +65,11 @@ const struct gpio_info gpio_list[] = {
switch_interrupt},
{"WP_L", LM4_GPIO_A, (1<<4), GPIO_INT_BOTH,
switch_interrupt},
+ {"JTAG_TCK", LM4_GPIO_C, (1<<0), GPIO_DEFAULT,
+ jtag_interrupt},
+ {"UART0_RX", LM4_GPIO_A, (1<<0), GPIO_PULL_UP|
+ GPIO_INT_BOTH_DSLEEP,
+ uart_deepsleep_interrupt},
/* Other inputs */
{"FAN_ALERT_L", LM4_GPIO_B, (1<<0), GPIO_INPUT, NULL},
@@ -128,7 +135,7 @@ BUILD_ASSERT(ARRAY_SIZE(gpio_list) == GPIO_COUNT);
/* Pins with alternate functions */
const struct gpio_alt_func gpio_alt_funcs[] = {
- {GPIO_A, 0x03, 1, MODULE_UART}, /* UART0 */
+ {GPIO_A, 0x03, 1, MODULE_UART, GPIO_PULL_UP}, /* UART0 */
{GPIO_B, 0x04, 3, MODULE_I2C}, /* I2C0 SCL */
{GPIO_B, 0x08, 3, MODULE_I2C, GPIO_OPEN_DRAIN}, /* I2C0 SDA */
{GPIO_B, 0x40, 3, MODULE_I2C}, /* I2C5 SCL */
diff --git a/board/slippy/board.h b/board/slippy/board.h
index 1356f997d1..93c3d9aa4e 100644
--- a/board/slippy/board.h
+++ b/board/slippy/board.h
@@ -86,6 +86,8 @@ enum gpio_signal {
GPIO_PCH_EDP_VDD_EN, /* PCH wants EDP enabled */
GPIO_RECOVERY_L, /* Recovery signal from servo */
GPIO_WP_L, /* Write protect input */
+ GPIO_JTAG_TCK, /* JTAG clock input */
+ GPIO_UART0_RX, /* UART0 RX input */
/* Other inputs */
GPIO_FAN_ALERT_L, /* From thermal sensor */
diff --git a/chip/lm4/clock.c b/chip/lm4/clock.c
index 478cb796eb..bbd2e503d1 100644
--- a/chip/lm4/clock.c
+++ b/chip/lm4/clock.c
@@ -16,6 +16,7 @@
#include "system.h"
#include "task.h"
#include "timer.h"
+#include "uart.h"
#include "util.h"
#include "watchdog.h"
@@ -27,9 +28,11 @@
/*
* Length of time for the processor to wake up from deep sleep. Actual
- * measurement gives anywhere from 75-200us, so this is conservative.
+ * measurement gives anywhere up to 780us, depending on the mode it is coming
+ * out of. The datasheet gives a maximum of 846us, for coming out of deep
+ * sleep in our worst case deep sleep mode.
*/
-#define DEEP_SLEEP_RECOVER_TIME_USEC 300
+#define DEEP_SLEEP_RECOVER_TIME_USEC 850
/* Low power idle statistics */
#ifdef CONFIG_LOW_POWER_IDLE
@@ -37,6 +40,15 @@ static int idle_sleep_cnt;
static int idle_dsleep_cnt;
static uint64_t idle_dsleep_time_us;
static int dsleep_recovery_margin_us = 1000000;
+
+/*
+ * Fixed amount of time to keep the console in use flag true after boot in
+ * order to give a permanent window in which the low speed clock is not used.
+ */
+#define CONSOLE_IN_USE_ON_BOOT_TIME (15*SECOND)
+
+static int console_in_use_timeout_sec = 60;
+static timestamp_t console_expire_time;
#endif
static int freq;
@@ -207,12 +219,23 @@ void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode)
#ifdef CONFIG_LOW_POWER_IDLE
+void clock_refresh_console_in_use(void)
+{
+ disable_sleep(SLEEP_MASK_CONSOLE);
+
+ /* Set console in use expire time. */
+ console_expire_time = get_time();
+ console_expire_time.val += console_in_use_timeout_sec * SECOND;
+
+}
+
/* Low power idle task. Executed when no tasks are ready to be scheduled. */
void __idle(void)
{
timestamp_t t0, t1, rtc_t0, rtc_t1;
int next_delay = 0;
int time_for_dsleep, margin_us;
+ int use_lfiosc;
/* Enable the hibernate IRQ used to wake up from deep sleep */
system_enable_hib_interrupt();
@@ -220,6 +243,17 @@ void __idle(void)
/* Set SRAM and flash power management to 'low power' in deep sleep. */
LM4_SYSTEM_DSLPPWRCFG = 0x23;
+ /* Enable JTAG interrupt which will notify us when JTAG is in use. */
+ gpio_enable_interrupt(GPIO_JTAG_TCK);
+
+ /*
+ * Initialize console in use to true and specify the console expire
+ * time in order to give a fixed window on boot in which the low speed
+ * clock will not be used in idle.
+ */
+ disable_sleep(SLEEP_MASK_CONSOLE);
+ console_expire_time.val = get_time().val + CONSOLE_IN_USE_ON_BOOT_TIME;
+
/*
* Print when the idle task starts. This is the lowest priority task,
* so this only starts once all other tasks have gotten a chance to do
@@ -243,10 +277,50 @@ void __idle(void)
time_for_dsleep = next_delay > (DEEP_SLEEP_RECOVER_TIME_USEC +
HIB_SET_RTC_MATCH_DELAY_USEC);
- if (!sleep_mask && time_for_dsleep) {
+ if (DEEP_SLEEP_ALLOWED && time_for_dsleep) {
/* Deep-sleep in STOP mode. */
idle_dsleep_cnt++;
+ /* Check if the console use has expired. */
+ if ((sleep_mask & SLEEP_MASK_CONSOLE) &&
+ t0.val > console_expire_time.val) {
+ /* Enable low speed deep sleep. */
+ enable_sleep(SLEEP_MASK_CONSOLE);
+
+ /*
+ * Wait one clock before checking if low speed
+ * deep sleep is allowed to give time for
+ * sleep mask to update.
+ */
+ clock_wait_cycles(1);
+
+ if (LOW_SPEED_DEEP_SLEEP_ALLOWED)
+ CPRINTF("[%T Disabling console in "
+ "deep sleep]\n");
+ }
+
+ /*
+ * Determine if we should use the LFIOSC (30kHz) or the
+ * PIOSC (16MHz) for the clock in deep sleep. Use the
+ * LFIOSC only if the sleep mask specifies that low
+ * speed sleep is allowed, the console UART TX is not
+ * busy, and the console UART buffer is empty.
+ */
+ use_lfiosc = LOW_SPEED_DEEP_SLEEP_ALLOWED &&
+ !uart_tx_in_progress() && uart_buffer_empty();
+
+ /* Set the deep sleep clock register. */
+ LM4_SYSTEM_DSLPCLKCFG = use_lfiosc ? 0x32 : 0x10;
+
+ /*
+ * If using low speed (LFIOSC) clock, disable console.
+ * This will also convert the console RX pin to a GPIO
+ * and set an edge interrupt to wake us from deep sleep
+ * if any action occurs on console.
+ */
+ if (use_lfiosc)
+ uart_enter_dsleep();
+
/* Set deep sleep bit. */
CPU_SCB_SYSCTRL |= 0x4;
@@ -274,6 +348,10 @@ void __idle(void)
t1.val = t0.val + (rtc_t1.val - rtc_t0.val);
force_time(t1);
+ /* If using low speed clock, re-enable the console. */
+ if (use_lfiosc)
+ uart_exit_dsleep();
+
/* Record time spent in deep sleep. */
idle_dsleep_time_us += (rtc_t1.val - rtc_t0.val);
@@ -615,5 +693,53 @@ DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats,
"",
"Print last idle stats",
NULL);
+
+/**
+ * Configure deep sleep clock settings.
+ */
+static int command_dsleepmask(int argc, char **argv)
+{
+ int v;
+
+ if (argc > 1) {
+ if (parse_bool(argv[1], &v)) {
+ /*
+ * Force deep sleep not to use low speed clock or
+ * allow it to use the low speed clock.
+ */
+ if (v)
+ disable_sleep(SLEEP_MASK_FORCE_NO_LOW_SPEED);
+ else
+ enable_sleep(SLEEP_MASK_FORCE_NO_LOW_SPEED);
+ } else {
+ /* Set console in use timeout. */
+ char *e;
+ v = strtoi(argv[1], &e, 10);
+ if (*e)
+ return EC_ERROR_PARAM1;
+
+ console_in_use_timeout_sec = v;
+
+ /* Refresh console in use to use new timeout. */
+ clock_refresh_console_in_use();
+ }
+ }
+
+ ccprintf("Sleep mask: %08x\n", sleep_mask);
+ ccprintf("Console in use timeout: %d sec\n",
+ console_in_use_timeout_sec);
+ ccprintf("DSLPCLKCFG register: 0x%08x\n", LM4_SYSTEM_DSLPCLKCFG);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(dsleepmask, command_dsleepmask,
+ "[ on | off | <timeout> sec]",
+ "Deep sleep clock settings:\nUse 'on' to force deep "
+ "sleep not to use low speed clock.\nUse 'off' to "
+ "allow deep sleep to auto-select using the low speed "
+ "clock.\n"
+ "Give a timeout value for the console in use timeout.\n"
+ "See also 'sleepmask'.",
+ NULL);
#endif /* CONFIG_LOW_POWER_IDLE */
diff --git a/chip/lm4/gpio.c b/chip/lm4/gpio.c
index 604cd91234..38b5e7ed46 100644
--- a/chip/lm4/gpio.c
+++ b/chip/lm4/gpio.c
@@ -156,6 +156,18 @@ int gpio_enable_interrupt(enum gpio_signal signal)
return EC_SUCCESS;
}
+int gpio_disable_interrupt(enum gpio_signal signal)
+{
+ const struct gpio_info *g = gpio_list + signal;
+
+ /* Fail if no interrupt handler */
+ if (!g->irq_handler)
+ return EC_ERROR_UNKNOWN;
+
+ LM4_GPIO_IM(g->port) &= ~g->mask;
+ return EC_SUCCESS;
+}
+
#ifdef CONFIG_LOW_POWER_IDLE
/**
* Convert GPIO port to a mask that can be used to set the
diff --git a/chip/lm4/jtag.c b/chip/lm4/jtag.c
index 6b59d7fead..a3867aa0f6 100644
--- a/chip/lm4/jtag.c
+++ b/chip/lm4/jtag.c
@@ -4,14 +4,15 @@
*/
#include "clock.h"
+#include "gpio.h"
#include "jtag.h"
#include "registers.h"
+#include "system.h"
void jtag_pre_init(void)
{
/* Enable clocks to GPIO block C in run and sleep modes. */
- clock_enable_peripheral(CGC_OFFSET_GPIO, 0x0004,
- CGC_MODE_RUN | CGC_MODE_SLEEP);
+ clock_enable_peripheral(CGC_OFFSET_GPIO, 0x0004, CGC_MODE_ALL);
/*
* Ensure PC0:3 are set to JTAG function. They should be set this way
@@ -35,7 +36,30 @@ void jtag_pre_init(void)
LM4_GPIO_DEN(LM4_GPIO_C) |= 0x0f;
LM4_GPIO_PUR(LM4_GPIO_C) |= 0x0f;
+ /* Set interrupt on either edge of the JTAG signals */
+ LM4_GPIO_IS(LM4_GPIO_C) &= ~0x0f;
+ LM4_GPIO_IBE(LM4_GPIO_C) |= 0x0f;
+
/* Re-lock commit register */
LM4_GPIO_CR(LM4_GPIO_C) &= ~0x0f;
LM4_GPIO_LOCK(LM4_GPIO_C) = 0;
}
+
+#ifdef CONFIG_LOW_POWER_IDLE
+void jtag_interrupt(enum gpio_signal signal)
+{
+ /*
+ * This interrupt is the first sign someone is trying to use
+ * the JTAG. Disable slow speed sleep so that the JTAG action
+ * can take place.
+ */
+ disable_sleep(SLEEP_MASK_JTAG);
+
+ /*
+ * Once we get this interrupt, disable it from occurring again
+ * to avoid repeated interrupts when debugging via JTAG.
+ */
+ gpio_disable_interrupt(GPIO_JTAG_TCK);
+}
+#endif /* CONFIG_LOW_POWER_IDLE */
+
diff --git a/chip/lm4/uart.c b/chip/lm4/uart.c
index 154579b56b..95c9bdba32 100644
--- a/chip/lm4/uart.c
+++ b/chip/lm4/uart.c
@@ -66,6 +66,12 @@ int uart_tx_ready(void)
return !(LM4_UART_FR(0) & 0x20);
}
+int uart_tx_in_progress(void)
+{
+ /* Transmit is in progress if the TX busy bit is set. */
+ return LM4_UART_FR(0) & 0x08;
+}
+
int uart_rx_available(void)
{
return !(LM4_UART_FR(0) & 0x10);
@@ -194,7 +200,7 @@ void uart_init(void)
* UART in run and sleep modes.
*/
mask |= 1;
- clock_enable_peripheral(CGC_OFFSET_UART, mask, CGC_MODE_DSLEEP);
+ clock_enable_peripheral(CGC_OFFSET_UART, mask, CGC_MODE_ALL);
#ifdef CONFIG_UART_HOST
mask |= (1 << CONFIG_UART_HOST);
@@ -222,6 +228,71 @@ void uart_init(void)
init_done = 1;
}
+#ifdef CONFIG_LOW_POWER_IDLE
+void uart_enter_dsleep(void)
+{
+ const struct gpio_info g = gpio_list[GPIO_UART0_RX];
+
+ /* Disable the UART0 module interrupt. */
+ task_disable_irq(LM4_IRQ_UART0);
+
+ /* Disable UART0 peripheral in deep sleep. */
+ clock_disable_peripheral(CGC_OFFSET_UART, 0x1, CGC_MODE_DSLEEP);
+
+ /*
+ * Set the UART0 RX pin to be a generic GPIO with the flags defined
+ * in the board.c file.
+ */
+ gpio_set_flags_by_mask(g.port, g.mask, g.flags);
+ gpio_set_alternate_function(g.port, g.mask, -1);
+
+ /* Clear any pending GPIO interrupts on the UART0 RX pin. */
+ LM4_GPIO_ICR(g.port) = g.mask;
+
+ /* Enable GPIO interrupts on the UART0 RX pin. */
+ gpio_enable_interrupt(GPIO_UART0_RX);
+}
+
+void uart_exit_dsleep(void)
+{
+ const struct gpio_info g = gpio_list[GPIO_UART0_RX];
+
+ /*
+ * If the UART0 RX GPIO interrupt has not fired, then no edge has been
+ * detected. Disable the GPIO interrupt so that switching the pin over
+ * to a UART pin doesn't inadvertently cause a GPIO edge interrupt.
+ * Note: we can't disable this interrupt if it has already fired
+ * because then the IRQ will not get called.
+ */
+ if (!(LM4_GPIO_MIS(g.port) & g.mask))
+ gpio_disable_interrupt(GPIO_UART0_RX);
+
+ /* Configure UART0 pins for use in UART peripheral. */
+ gpio_config_module(MODULE_UART, 1);
+
+ /* Clear pending interrupts on UART peripheral and enable interrupts. */
+ uart_clear_rx_fifo(0);
+ task_enable_irq(LM4_IRQ_UART0);
+
+ /* Enable UART0 peripheral in deep sleep */
+ clock_enable_peripheral(CGC_OFFSET_UART, 0x1, CGC_MODE_DSLEEP);
+}
+
+void uart_deepsleep_interrupt(enum gpio_signal signal)
+{
+ /*
+ * Activity seen on UART RX pin while UART was disabled for deep sleep.
+ * The console won't see that character because the UART is disabled,
+ * so we need to inform the clock module of UART activity ourselves.
+ */
+ clock_refresh_console_in_use();
+
+ /* Disable interrupts on UART0 RX pin to avoid repeated interrupts. */
+ gpio_disable_interrupt(GPIO_UART0_RX);
+}
+#endif /* CONFIG_LOW_POWER_IDLE */
+
+
/*****************************************************************************/
/* COMx functions */
diff --git a/chip/stm32/clock-stm32f.c b/chip/stm32/clock-stm32f.c
index 23db9f1f44..11b426a42f 100644
--- a/chip/stm32/clock-stm32f.c
+++ b/chip/stm32/clock-stm32f.c
@@ -177,6 +177,10 @@ void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
#ifdef CONFIG_LOW_POWER_IDLE
+void clock_refresh_console_in_use(void)
+{
+}
+
#ifdef CONFIG_FORCE_CONSOLE_RESUME
static void enable_serial_wakeup(int enable)
{
@@ -192,7 +196,7 @@ static void enable_serial_wakeup(int enable)
} else {
/* serial port wake up : don't go back to sleep */
if (STM32_EXTI_PR & (1 << 10))
- disable_sleep(SLEEP_MASK_FORCE);
+ disable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP);
/* restore keyboard external IT on PC10 */
STM32_AFIO_EXTICR(10 / 4) = save_exticr;
}
@@ -216,7 +220,7 @@ void __idle(void)
t0 = get_time();
next_delay = __hw_clock_event_get() - t0.le.lo;
- if (!sleep_mask && (next_delay > STOP_MODE_LATENCY)) {
+ if (DEEP_SLEEP_ALLOWED && (next_delay > STOP_MODE_LATENCY)) {
/* deep-sleep in STOP mode */
enable_serial_wakeup(1);
diff --git a/common/console.c b/common/console.c
index 78fd96a7c7..f310ef41a7 100644
--- a/common/console.c
+++ b/common/console.c
@@ -4,7 +4,7 @@
*/
/* Console module for Chrome EC */
-
+#include "clock.h"
#include "console.h"
#include "link_defs.h"
#include "system.h"
@@ -519,6 +519,11 @@ static void console_handle_char(int c)
void console_has_input(void)
{
+#ifdef CONFIG_LOW_POWER_IDLE
+ /* Notify the clock module that the console is in use. */
+ clock_refresh_console_in_use();
+#endif
+
/* Wake up the console task */
task_wake(TASK_ID_CONSOLE);
}
diff --git a/common/system_common.c b/common/system_common.c
index 81182ef987..4af045a641 100644
--- a/common/system_common.c
+++ b/common/system_common.c
@@ -779,15 +779,23 @@ DECLARE_CONSOLE_COMMAND(syslock, command_system_lock,
*/
static int command_sleepmask(int argc, char **argv)
{
- int off;
+ int v;
if (argc >= 2) {
- off = strtoi(argv[1], NULL, 10);
+ if (parse_bool(argv[1], &v)) {
+ if (v)
+ disable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP);
+ else
+ enable_sleep(SLEEP_MASK_FORCE_NO_DSLEEP);
+ } else {
+ char *e;
+ v = strtoi(argv[1], &e, 10);
+ if (*e)
+ return EC_ERROR_PARAM1;
- if (off)
- disable_sleep(SLEEP_MASK_FORCE);
- else
- enable_sleep(SLEEP_MASK_FORCE);
+ /* Set sleep mask directly. */
+ sleep_mask = v;
+ }
}
ccprintf("sleep mask: %08x\n", sleep_mask);
@@ -795,8 +803,8 @@ static int command_sleepmask(int argc, char **argv)
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(sleepmask, command_sleepmask,
- "[<sleep_mask>]",
- "Display/force sleep mask",
+ "[ on | off | <sleep_mask>]",
+ "Display/force sleep mask.\nSee also 'dsleepmask'.",
NULL);
#endif
diff --git a/common/uart_buffering.c b/common/uart_buffering.c
index 779835ae24..d49425229e 100644
--- a/common/uart_buffering.c
+++ b/common/uart_buffering.c
@@ -313,6 +313,11 @@ int uart_getc(void)
return -1;
}
+int uart_buffer_empty(void)
+{
+ return tx_buf_head == tx_buf_tail;
+}
+
#ifdef CONFIG_UART_RX_DMA
static void uart_rx_dma_init(void)
{
diff --git a/include/clock.h b/include/clock.h
index 875be91006..62c38bcd6a 100644
--- a/include/clock.h
+++ b/include/clock.h
@@ -81,4 +81,9 @@ void clock_enable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode);
*/
void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode);
+/**
+ * Notify the clock module that the UART for the console is in use.
+ */
+void clock_refresh_console_in_use(void);
+
#endif /* __CROS_EC_CLOCK_H */
diff --git a/include/gpio.h b/include/gpio.h
index bdedbf4bb7..fc46e995ce 100644
--- a/include/gpio.h
+++ b/include/gpio.h
@@ -169,6 +169,18 @@ void gpio_set_level(enum gpio_signal signal, int value);
int gpio_enable_interrupt(enum gpio_signal signal);
/**
+ * Disable interrupts for the signal.
+ *
+ * The signal must have been defined with
+ * an interrupt handler. Normally called by the module which handles the
+ * interrupt, if it doesn't want to process interrupts.
+ *
+ * @param signal Signal to disable interrupts for
+ * @return EC_SUCCESS, or non-zero if error.
+ */
+int gpio_disable_interrupt(enum gpio_signal signal);
+
+/**
* Set flags for GPIO(s) by port and mask.
*
* Use gpio_set_flags() to set flags for an individual GPIO by id.
diff --git a/include/jtag.h b/include/jtag.h
index 690158b4ec..9c7835dfd0 100644
--- a/include/jtag.h
+++ b/include/jtag.h
@@ -15,4 +15,16 @@
*/
void jtag_pre_init(void);
+#ifdef CONFIG_LOW_POWER_IDLE
+/**
+ * Interrupt handler for JTAG clock.
+ *
+ * @param signal Signal which triggered the interrupt.
+ */
+void jtag_interrupt(enum gpio_signal signal);
+#else
+#define jtag_interrupt NULL
+#endif
+
+
#endif /* __CROS_EC_JTAG_H */
diff --git a/include/system.h b/include/system.h
index 0c2936a33c..cf936d64bf 100644
--- a/include/system.h
+++ b/include/system.h
@@ -284,13 +284,25 @@ void system_enable_hib_interrupt(void);
/* Low power modes for idle API */
enum {
+ /*
+ * Sleep masks to prevent going in to deep sleep.
+ */
SLEEP_MASK_AP_RUN = (1 << 0), /* the main CPU is running */
SLEEP_MASK_UART = (1 << 1), /* UART communication on-going */
SLEEP_MASK_I2C = (1 << 2), /* I2C master communication on-going */
SLEEP_MASK_CHARGING = (1 << 3), /* Charging loop on-going */
SLEEP_MASK_USB_PWR = (1 << 4), /* USB power loop on-going */
- SLEEP_MASK_FORCE = (1 << 31), /* Force disabling low power modes */
+ SLEEP_MASK_FORCE_NO_DSLEEP = (1 << 15), /* Force disable. */
+
+
+ /*
+ * Sleep masks to prevent using slow speed clock in deep sleep.
+ */
+ SLEEP_MASK_JTAG = (1 << 16), /* JTAG is in use. */
+ SLEEP_MASK_CONSOLE = (1 << 17), /* Console is in use. */
+
+ SLEEP_MASK_FORCE_NO_LOW_SPEED = (1 << 31) /* Force disable. */
};
/*
@@ -299,6 +311,13 @@ enum {
*/
extern uint32_t sleep_mask;
+/*
+ * Macros to use to get whether deep sleep is allowed or whether
+ * low speed deep sleep is allowed.
+ */
+#define DEEP_SLEEP_ALLOWED (!(sleep_mask & 0x0000ffff))
+#define LOW_SPEED_DEEP_SLEEP_ALLOWED (!(sleep_mask & 0xffff0000))
+
/**
* Enable low power sleep mask. For low power sleep to take affect, all masks
* in the sleep mask enum above must be enabled.
diff --git a/include/uart.h b/include/uart.h
index 762d885f08..2f0bc8bd8c 100644
--- a/include/uart.h
+++ b/include/uart.h
@@ -101,6 +101,11 @@ void uart_tx_flush(void);
int uart_tx_ready(void);
/**
+ * Return non-zero if a transmit is in progress.
+ */
+int uart_tx_in_progress(void);
+
+/**
* Return non-zero if UART is ready to start a DMA transfer.
*/
int uart_tx_dma_ready(void);
@@ -195,6 +200,33 @@ void uart_process_input(void);
*/
void uart_process_output(void);
+/**
+ * Return boolean expressing whether UART buffer is empty or not.
+ */
+int uart_buffer_empty(void);
+
+/**
+ * Disable the EC console UART and convert the UART RX pin to a generic GPIO
+ * with an edge detect interrupt.
+ */
+void uart_enter_dsleep(void);
+
+/**
+ * Enable the EC console UART after a uart_enter_dsleep().
+ */
+void uart_exit_dsleep(void);
+
+#ifdef CONFIG_LOW_POWER_IDLE
+/**
+ * Interrupt handler for UART RX pin transition in deep sleep.
+ *
+ * @param signal Signal which triggered the interrupt.
+ */
+void uart_deepsleep_interrupt(enum gpio_signal signal);
+#else
+#define uart_deepsleep_interrupt NULL
+#endif
+
/*
* COMx functions
*/