summaryrefslogtreecommitdiff
path: root/chip/lm4/clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/lm4/clock.c')
-rw-r--r--chip/lm4/clock.c132
1 files changed, 129 insertions, 3 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 */