summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
Diffstat (limited to 'chip')
-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
5 files changed, 245 insertions, 8 deletions
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 8708ab637e..b8dc0f6570 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 7fd7101b70..8e593f7c65 100644
--- a/chip/lm4/uart.c
+++ b/chip/lm4/uart.c
@@ -69,6 +69,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);
@@ -192,7 +198,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);
@@ -217,6 +223,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);