summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChromeOS Developer <dparker@chromium.org>2014-02-11 16:36:11 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-02-12 20:00:43 +0000
commit81abeb6135cabeb2325e4acc5d694f4c2942f899 (patch)
treea3ee72eb1f2abf506623d781e4ce4d337bdb6a9a
parentf11cdd71da3dae3fa1935a67202d436621db77ac (diff)
downloadchrome-ec-81abeb6135cabeb2325e4acc5d694f4c2942f899.tar.gz
Reapply "CHERRY-PICK:lm4: Add a low power idle task."
This reverts commit c087788a0579e7ba3a5a0f27361ead35d848743c. BUG=chrome-os-partner:25661 BRANCH=None TEST=make BOARD=wolf -j Signed-off-by: Dave Parker <dparker@chromium.org> Conflicts: board/falco/board.h board/peppy/board.h Change-Id: If79d491c4fc543353cea0a7fb2e8502d8cea328a Reviewed-on: https://chromium-review.googlesource.com/186012 Reviewed-by: Dave Parker <dparker@chromium.org> Commit-Queue: Dave Parker <dparker@chromium.org> Tested-by: Dave Parker <dparker@chromium.org>
-rw-r--r--board/falco/board.c10
-rw-r--r--board/falco/board.h1
-rw-r--r--board/peppy/board.c10
-rw-r--r--board/peppy/board.h1
-rw-r--r--board/slippy/board.c10
-rw-r--r--board/slippy/board.h1
-rw-r--r--chip/lm4/clock.c242
-rw-r--r--chip/lm4/config_chip.h8
-rw-r--r--chip/lm4/registers.h9
-rw-r--r--chip/lm4/system.c200
-rw-r--r--chip/lm4/uart.c21
-rw-r--r--chip/stm32/clock-stm32f.c36
-rw-r--r--chip/stm32/clock-stm32l.c10
-rw-r--r--chip/stm32/uart.c1
-rw-r--r--common/chipset_haswell.c12
-rw-r--r--common/console_output.c1
-rw-r--r--common/i2c_common.c6
-rw-r--r--common/system_common.c32
-rw-r--r--include/clock.h15
-rw-r--r--include/config.h1
-rw-r--r--include/console.h1
-rw-r--r--include/gpio.h2
-rw-r--r--include/system.h70
23 files changed, 558 insertions, 142 deletions
diff --git a/board/falco/board.c b/board/falco/board.c
index b2c53791af..e7494952d2 100644
--- a/board/falco/board.c
+++ b/board/falco/board.c
@@ -32,19 +32,19 @@
/* GPIO signal list. Must match order from enum gpio_signal. */
const struct gpio_info gpio_list[] = {
/* Inputs with interrupt handlers are first for efficiency */
- {"POWER_BUTTON_L", LM4_GPIO_A, (1<<2), GPIO_INT_BOTH,
+ {"POWER_BUTTON_L", LM4_GPIO_A, (1<<2), GPIO_INT_BOTH_DSLEEP,
power_button_interrupt},
- {"LID_OPEN", LM4_GPIO_A, (1<<3), GPIO_INT_BOTH,
+ {"LID_OPEN", LM4_GPIO_A, (1<<3), GPIO_INT_BOTH_DSLEEP,
lid_interrupt},
- {"AC_PRESENT", LM4_GPIO_H, (1<<3), GPIO_INT_BOTH,
+ {"AC_PRESENT", LM4_GPIO_H, (1<<3), GPIO_INT_BOTH_DSLEEP,
extpower_interrupt},
{"PCH_BKLTEN", LM4_GPIO_M, (1<<3), GPIO_INT_BOTH,
backlight_interrupt},
{"PCH_SLP_S0_L", LM4_GPIO_G, (1<<6), GPIO_INT_BOTH,
x86_interrupt},
- {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH,
+ {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH_DSLEEP,
x86_interrupt},
- {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH,
+ {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH_DSLEEP,
x86_interrupt},
{"PCH_SLP_SUS_L", LM4_GPIO_G, (1<<3), GPIO_INT_BOTH,
x86_interrupt},
diff --git a/board/falco/board.h b/board/falco/board.h
index b0791064bb..09ab9b5fb9 100644
--- a/board/falco/board.h
+++ b/board/falco/board.h
@@ -22,6 +22,7 @@
#define CONFIG_KEYBOARD_BOARD_CONFIG
#define CONFIG_KEYBOARD_PROTOCOL_8042
#define CONFIG_PECI_TJMAX 100
+#define CONFIG_LOW_POWER_IDLE
#define CONFIG_POWER_BUTTON
#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM_FAN
diff --git a/board/peppy/board.c b/board/peppy/board.c
index cc6e943721..25e3a7663f 100644
--- a/board/peppy/board.c
+++ b/board/peppy/board.c
@@ -30,19 +30,19 @@
/* GPIO signal list. Must match order from enum gpio_signal. */
const struct gpio_info gpio_list[] = {
/* Inputs with interrupt handlers are first for efficiency */
- {"POWER_BUTTON_L", LM4_GPIO_A, (1<<2), GPIO_INT_BOTH,
+ {"POWER_BUTTON_L", LM4_GPIO_A, (1<<2), GPIO_INT_BOTH_DSLEEP,
power_button_interrupt},
- {"LID_OPEN", LM4_GPIO_A, (1<<3), GPIO_INT_BOTH,
+ {"LID_OPEN", LM4_GPIO_A, (1<<3), GPIO_INT_BOTH_DSLEEP,
lid_interrupt},
- {"AC_PRESENT", LM4_GPIO_H, (1<<3), GPIO_INT_BOTH,
+ {"AC_PRESENT", LM4_GPIO_H, (1<<3), GPIO_INT_BOTH_DSLEEP,
extpower_interrupt},
{"PCH_BKLTEN", LM4_GPIO_M, (1<<3), GPIO_INT_BOTH,
backlight_interrupt},
{"PCH_SLP_S0_L", LM4_GPIO_G, (1<<6), GPIO_INT_BOTH,
x86_interrupt},
- {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH,
+ {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH_DSLEEP,
x86_interrupt},
- {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH,
+ {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH_DSLEEP,
x86_interrupt},
{"PCH_SLP_SUS_L", LM4_GPIO_G, (1<<3), GPIO_INT_BOTH,
x86_interrupt},
diff --git a/board/peppy/board.h b/board/peppy/board.h
index 73644086fa..7899299394 100644
--- a/board/peppy/board.h
+++ b/board/peppy/board.h
@@ -22,6 +22,7 @@
#define CONFIG_KEYBOARD_BOARD_CONFIG
#define CONFIG_KEYBOARD_PROTOCOL_8042
#define CONFIG_PECI_TJMAX 100
+#define CONFIG_LOW_POWER_IDLE
#define CONFIG_POWER_BUTTON
#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM_FAN
diff --git a/board/slippy/board.c b/board/slippy/board.c
index c05e5dc5be..fc170c76f9 100644
--- a/board/slippy/board.c
+++ b/board/slippy/board.c
@@ -30,19 +30,19 @@
/* GPIO signal list. Must match order from enum gpio_signal. */
const struct gpio_info gpio_list[] = {
/* Inputs with interrupt handlers are first for efficiency */
- {"POWER_BUTTON_L", LM4_GPIO_A, (1<<2), GPIO_INT_BOTH,
+ {"POWER_BUTTON_L", LM4_GPIO_A, (1<<2), GPIO_INT_BOTH_DSLEEP,
power_button_interrupt},
- {"LID_OPEN", LM4_GPIO_A, (1<<3), GPIO_INT_BOTH,
+ {"LID_OPEN", LM4_GPIO_A, (1<<3), GPIO_INT_BOTH_DSLEEP,
lid_interrupt},
- {"AC_PRESENT", LM4_GPIO_H, (1<<3), GPIO_INT_BOTH,
+ {"AC_PRESENT", LM4_GPIO_H, (1<<3), GPIO_INT_BOTH_DSLEEP,
extpower_interrupt},
{"PCH_BKLTEN", LM4_GPIO_M, (1<<3), GPIO_INT_BOTH,
backlight_interrupt},
{"PCH_SLP_S0_L", LM4_GPIO_G, (1<<6), GPIO_INT_BOTH,
x86_interrupt},
- {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH,
+ {"PCH_SLP_S3_L", LM4_GPIO_G, (1<<7), GPIO_INT_BOTH_DSLEEP,
x86_interrupt},
- {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH,
+ {"PCH_SLP_S5_L", LM4_GPIO_H, (1<<1), GPIO_INT_BOTH_DSLEEP,
x86_interrupt},
{"PCH_SLP_SUS_L", LM4_GPIO_G, (1<<3), GPIO_INT_BOTH,
x86_interrupt},
diff --git a/board/slippy/board.h b/board/slippy/board.h
index 19c741a0cc..4f45b4a850 100644
--- a/board/slippy/board.h
+++ b/board/slippy/board.h
@@ -22,6 +22,7 @@
#define CONFIG_KEYBOARD_BOARD_CONFIG
#define CONFIG_KEYBOARD_PROTOCOL_8042
#define CONFIG_LED_SLIPPY
+#define CONFIG_LOW_POWER_IDLE
#define CONFIG_POWER_BUTTON
#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM_FAN
diff --git a/chip/lm4/clock.c b/chip/lm4/clock.c
index 2d7365f9ab..478cb796eb 100644
--- a/chip/lm4/clock.c
+++ b/chip/lm4/clock.c
@@ -11,6 +11,7 @@
#include "cpu.h"
#include "gpio.h"
#include "hooks.h"
+#include "hwtimer.h"
#include "registers.h"
#include "system.h"
#include "task.h"
@@ -18,8 +19,26 @@
#include "util.h"
#include "watchdog.h"
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_CLOCK, outstr)
+#define CPRINTF(format, args...) cprintf(CC_CLOCK, format, ## args)
+
#define PLL_CLOCK 66666667 /* System clock = 200MHz PLL/3 = 66.667MHz */
+/*
+ * Length of time for the processor to wake up from deep sleep. Actual
+ * measurement gives anywhere from 75-200us, so this is conservative.
+ */
+#define DEEP_SLEEP_RECOVER_TIME_USEC 300
+
+/* Low power idle statistics */
+#ifdef CONFIG_LOW_POWER_IDLE
+static int idle_sleep_cnt;
+static int idle_dsleep_cnt;
+static uint64_t idle_dsleep_time_us;
+static int dsleep_recovery_margin_us = 1000000;
+#endif
+
static int freq;
/**
@@ -33,6 +52,17 @@ static void disable_pll(void)
LM4_SYSTEM_RCC_PWRDN |
LM4_SYSTEM_RCC_OSCSRC(1) |
LM4_SYSTEM_RCC_MOSCDIS;
+
+#ifdef CONFIG_LOW_POWER_IDLE
+ /*
+ * If using the low power idle, then set the ACG bit, which specifies
+ * that the sleep and deep sleep modes are using their own clock gating
+ * registers SCGC and DCGS respectively instead of using the run mode
+ * clock gating registers RCGC.
+ */
+ LM4_SYSTEM_RCC |= LM4_SYSTEM_RCC_ACG;
+#endif
+
LM4_SYSTEM_RCC2 &= ~LM4_SYSTEM_RCC2_USERCC2;
freq = INTERNAL_CLOCK;
@@ -56,6 +86,16 @@ static void enable_pll(void)
LM4_SYSTEM_RCC_OSCSRC(1) |
LM4_SYSTEM_RCC_MOSCDIS;
+#ifdef CONFIG_LOW_POWER_IDLE
+ /*
+ * If using the low power idle, then set the ACG bit, which specifies
+ * that the sleep and deep sleep modes are using their own clock gating
+ * registers SCGC and DCGS respectively instead of using the run mode
+ * clock gating registers RCGC.
+ */
+ LM4_SYSTEM_RCC |= LM4_SYSTEM_RCC_ACG;
+#endif
+
/* Wait for the PLL to lock */
clock_wait_cycles(1024);
while (!(LM4_SYSTEM_PLLSTAT & 1))
@@ -155,6 +195,105 @@ void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode)
*(LM4_SYSTEM_DCGC_BASE + offset) &= ~mask;
}
+/*
+ * The low power idle task does not support using the EEPROM,
+ * because it is dangerous to go to deep sleep while EEPROM
+ * transaction is in progress. To fix, LM4_EEPROM_EEDONE, should
+ * be checked before going in to deep sleep.
+ */
+#if defined(CONFIG_LOW_POWER_IDLE) && defined(CONFIG_EEPROM)
+#error "Low power idle mode does not support use of EEPROM"
+#endif
+
+#ifdef CONFIG_LOW_POWER_IDLE
+
+/* 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;
+
+ /* Enable the hibernate IRQ used to wake up from deep sleep */
+ system_enable_hib_interrupt();
+
+ /* Set SRAM and flash power management to 'low power' in deep sleep. */
+ LM4_SYSTEM_DSLPPWRCFG = 0x23;
+
+ /*
+ * 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
+ * their task inits and have gone to sleep.
+ */
+ CPRINTF("[%T low power idle task started]\n");
+
+ while (1) {
+ /*
+ * Disable interrupts before going to deep sleep in order to
+ * calculate the appropriate time to wake up. Note: the wfi
+ * instruction waits until an interrupt is pending, so it
+ * will still wake up even with interrupts disabled.
+ */
+ interrupt_disable();
+
+ t0 = get_time();
+ next_delay = __hw_clock_event_get() - t0.le.lo;
+
+ /* Do we have enough time before next event to deep sleep. */
+ time_for_dsleep = next_delay > (DEEP_SLEEP_RECOVER_TIME_USEC +
+ HIB_SET_RTC_MATCH_DELAY_USEC);
+
+ if (!sleep_mask && time_for_dsleep) {
+ /* Deep-sleep in STOP mode. */
+ idle_dsleep_cnt++;
+
+ /* Set deep sleep bit. */
+ CPU_SCB_SYSCTRL |= 0x4;
+
+ /* Record real time before sleeping. */
+ rtc_t0 = system_get_rtc();
+
+ /*
+ * Set RTC interrupt in time to wake up before
+ * next event.
+ */
+ system_set_rtc_alarm(0, next_delay -
+ DEEP_SLEEP_RECOVER_TIME_USEC);
+
+ /* Wait for interrupt: goes into deep sleep. */
+ asm("wfi");
+
+ /* Clear deep sleep bit. */
+ CPU_SCB_SYSCTRL &= ~0x4;
+
+ /* Disable and clear RTC interrupt. */
+ system_reset_rtc_alarm();
+
+ /* Fast forward timer according to RTC counter. */
+ rtc_t1 = system_get_rtc();
+ t1.val = t0.val + (rtc_t1.val - rtc_t0.val);
+ force_time(t1);
+
+ /* Record time spent in deep sleep. */
+ idle_dsleep_time_us += (rtc_t1.val - rtc_t0.val);
+
+ /* Calculate how close we were to missing deadline */
+ margin_us = next_delay - (int)(rtc_t1.val - rtc_t0.val);
+
+ /* Record the closest to missing a deadline. */
+ if (margin_us < dsleep_recovery_margin_us)
+ dsleep_recovery_margin_us = margin_us;
+ } else {
+ idle_sleep_cnt++;
+
+ /* Normal idle : only CPU clock stopped. */
+ asm("wfi");
+ }
+ interrupt_enable();
+ }
+}
+#endif /* CONFIG_LOW_POWER_IDLE */
+
/*****************************************************************************/
/* Console commands */
@@ -169,36 +308,56 @@ void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode)
* 3 : CPU in sleep mode and peripherals gated
* 4 : CPU in deep sleep mode
* 5 : CPU in deep sleep mode and peripherals gated
+ *
+ * Clocks :
+ * 0 : No change
+ * 1 : 16MHz
+ * 2 : 1 MHz
+ * 3 : 30kHz
+ *
+ * SRAM Power Management:
+ * 0 : Active
+ * 1 : Standby
+ * 3 : Low Power
+ *
+ * Flash Power Management:
+ * 0 : Active
+ * 2 : Low Power
*/
static int command_sleep(int argc, char **argv)
{
int level = 0;
int clock = 0;
+ int sram_pm = 0;
+ int flash_pm = 0;
uint32_t uartibrd = 0;
uint32_t uartfbrd = 0;
- if (argc >= 2) {
+ if (argc >= 2)
level = strtoi(argv[1], NULL, 10);
- }
- if (argc >= 3) {
+ if (argc >= 3)
clock = strtoi(argv[2], NULL, 10);
- }
+ if (argc >= 4)
+ sram_pm = strtoi(argv[3], NULL, 10);
+ if (argc >= 5)
+ flash_pm = strtoi(argv[4], NULL, 10);
#ifdef BOARD_bds
- /* remove LED current sink */
+ /* Remove LED current sink. */
gpio_set_level(GPIO_DEBUG_LED, 0);
#endif
- ccprintf("Going to sleep : level %d clock %d...\n", level, clock);
+ ccprintf("Sleep : level %d, clock %d, sram pm %d, flash_pm %d...\n",
+ level, clock, sram_pm, flash_pm);
cflush();
- /* clock setting */
+ /* Set clock speed. */
if (clock) {
/* Use ROM code function to set the clock */
void **func_table = (void **)*(uint32_t *)0x01000044;
void (*rom_clock_set)(uint32_t rcc) = func_table[23];
- /* disable interrupts */
+ /* Disable interrupts. */
asm volatile("cpsid i");
switch (clock) {
@@ -219,18 +378,20 @@ static int command_sleep(int argc, char **argv)
break;
}
- /* TODO: move this to the UART module; ugly to have
- UARTisms here. Also note this only fixes UART0,
- not UART1. */
+ /*
+ * TODO: move this to the UART module; ugly to have
+ * UARTisms here. Also note this only fixes UART0,
+ * not UART1.
+ */
if (uartfbrd) {
- /* Disable the port via UARTCTL and add HSE */
+ /* Disable the port via UARTCTL and add HSE. */
LM4_UART_CTL(0) = 0x0320;
- /* Set the baud rate divisor */
+ /* Set the baud rate divisor. */
LM4_UART_IBRD(0) = uartibrd;
LM4_UART_FBRD(0) = uartfbrd;
/* Poke UARTLCRH to make the new divisor take effect. */
LM4_UART_LCRH(0) = LM4_UART_LCRH(0);
- /* Enable the port */
+ /* Enable the port. */
LM4_UART_CTL(0) |= 0x0001;
}
asm volatile("cpsie i");
@@ -241,27 +402,50 @@ static int command_sleep(int argc, char **argv)
cflush();
}
+ /* Enable interrupts. */
asm volatile("cpsid i");
/* gate peripheral clocks */
if (level & 1) {
+ clock_disable_peripheral(CGC_OFFSET_WD, 0xffffffff,
+ CGC_MODE_ALL);
clock_disable_peripheral(CGC_OFFSET_TIMER, 0xffffffff,
CGC_MODE_ALL);
clock_disable_peripheral(CGC_OFFSET_GPIO, 0xffffffff,
CGC_MODE_ALL);
clock_disable_peripheral(CGC_OFFSET_DMA, 0xffffffff,
CGC_MODE_ALL);
+ clock_disable_peripheral(CGC_OFFSET_HIB, 0xffffffff,
+ CGC_MODE_ALL);
clock_disable_peripheral(CGC_OFFSET_UART, 0xffffffff,
CGC_MODE_ALL);
+ clock_disable_peripheral(CGC_OFFSET_SSI, 0xffffffff,
+ CGC_MODE_ALL);
+ clock_disable_peripheral(CGC_OFFSET_I2C, 0xffffffff,
+ CGC_MODE_ALL);
+ clock_disable_peripheral(CGC_OFFSET_ADC, 0xffffffff,
+ CGC_MODE_ALL);
clock_disable_peripheral(CGC_OFFSET_LPC, 0xffffffff,
CGC_MODE_ALL);
+ clock_disable_peripheral(CGC_OFFSET_PECI, 0xffffffff,
+ CGC_MODE_ALL);
+ clock_disable_peripheral(CGC_OFFSET_FAN, 0xffffffff,
+ CGC_MODE_ALL);
+ clock_disable_peripheral(CGC_OFFSET_EEPROM, 0xffffffff,
+ CGC_MODE_ALL);
clock_disable_peripheral(CGC_OFFSET_WTIMER, 0xffffffff,
CGC_MODE_ALL);
}
- /* set deep sleep bit */
+
+ /* Set deep sleep bit. */
if (level >= 4)
CPU_SCB_SYSCTRL |= 0x4;
- /* go to low power mode (forever ...) */
+
+ /* Set SRAM and flash PM for sleep and deep sleep. */
+ LM4_SYSTEM_SLPPWRCFG = (flash_pm << 4) | sram_pm;
+ LM4_SYSTEM_DSLPPWRCFG = (flash_pm << 4) | sram_pm;
+
+ /* Go to low power mode (forever ...) */
if (level > 1)
while (1) {
asm("wfi");
@@ -274,7 +458,7 @@ static int command_sleep(int argc, char **argv)
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(sleep, command_sleep,
- "[level [clock]]",
+ "[level [clock] [sram pm] [flash pm]]",
"Drop into sleep",
NULL);
#endif /* CONFIG_CMD_SLEEP */
@@ -409,3 +593,27 @@ DECLARE_CONSOLE_COMMAND(clockgates, command_clock_gating,
NULL);
#endif /* CONFIG_CMD_CLOCKGATES */
+#ifdef CONFIG_LOW_POWER_IDLE
+/**
+ * Print low power idle statistics
+ */
+static int command_idle_stats(int argc, char **argv)
+{
+ timestamp_t ts = get_time();
+
+ ccprintf("Num idle calls that sleep: %d\n", idle_sleep_cnt);
+ ccprintf("Num idle calls that deep-sleep: %d\n", idle_dsleep_cnt);
+ ccprintf("Time spent in deep-sleep: %.6lds\n",
+ idle_dsleep_time_us);
+ ccprintf("Total time on: %.6lds\n", ts.val);
+ ccprintf("Deep-sleep closest to wake deadline: %dus\n",
+ dsleep_recovery_margin_us);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats,
+ "",
+ "Print last idle stats",
+ NULL);
+#endif /* CONFIG_LOW_POWER_IDLE */
+
diff --git a/chip/lm4/config_chip.h b/chip/lm4/config_chip.h
index 9fbda8d970..ca785248b0 100644
--- a/chip/lm4/config_chip.h
+++ b/chip/lm4/config_chip.h
@@ -25,6 +25,12 @@
/* Number of I2C ports */
#define I2C_PORT_COUNT 6
+/*
+ * Time it takes to set the RTC match register. This value is conservatively
+ * set based on measurements around 200us.
+ */
+#define HIB_SET_RTC_MATCH_DELAY_USEC 300
+
/****************************************************************************/
/* Memory mapping */
@@ -35,7 +41,7 @@
#define CONFIG_STACK_SIZE 4096
/* non-standard task stack sizes */
-#define IDLE_TASK_STACK_SIZE 384
+#define IDLE_TASK_STACK_SIZE 512
#define LARGER_TASK_STACK_SIZE 640
/* Default task stack size */
diff --git a/chip/lm4/registers.h b/chip/lm4/registers.h
index 9450afea39..ac25724197 100644
--- a/chip/lm4/registers.h
+++ b/chip/lm4/registers.h
@@ -227,6 +227,7 @@ static inline int lm4_fan_addr(int ch, int offset)
#define LM4_SYSTEM_MISC REG32(0x400fe058)
#define LM4_SYSTEM_RESC REG32(0x400fe05c)
#define LM4_SYSTEM_RCC REG32(0x400fe060)
+#define LM4_SYSTEM_RCC_ACG (1 << 27)
#define LM4_SYSTEM_RCC_SYSDIV(x) (((x) & 0xf) << 23)
#define LM4_SYSTEM_RCC_USESYSDIV (1 << 22)
#define LM4_SYSTEM_RCC_PWRDN (1 << 13)
@@ -244,9 +245,17 @@ static inline int lm4_fan_addr(int ch, int offset)
#define LM4_SYSTEM_RCC2_BYPASS2 (1 << 11)
#define LM4_SYSTEM_RCC2_OSCSRC2(x) (((x) & 0x7) << 4)
#define LM4_SYSTEM_MOSCCTL REG32(0x400fe07c)
+#define LM4_SYSTEM_DSLPCLKCFG REG32(0x400fe144)
#define LM4_SYSTEM_PIOSCCAL REG32(0x400fe150)
#define LM4_SYSTEM_PIOSCSTAT REG32(0x400fe154)
#define LM4_SYSTEM_PLLSTAT REG32(0x400fe168)
+#define LM4_SYSTEM_SLPPWRCFG REG32(0x400fe188)
+#define LM4_SYSTEM_DSLPPWRCFG REG32(0x400fe18c)
+#define LM4_SYSTEM_LDOSPCTL REG32(0x400fe1b4)
+#define LM4_SYSTEM_LDOSPCAL REG32(0x400fe1b8)
+#define LM4_SYSTEM_LDODPCTL REG32(0x400fe1bc)
+#define LM4_SYSTEM_LDODPCAL REG32(0x400fe1c0)
+#define LM4_SYSTEM_SPDMST REG32(0x400fe1cc)
#define LM4_SYSTEM_BOOTCFG REG32(0x400fe1d0)
#define LM4_SYSTEM_BOOTCFG_MASK 0x7fff00ec /* Reserved bits of BOOTCFG reg */
/* Note: USER_REG3 is used to hold pre-programming process data and should not
diff --git a/chip/lm4/system.c b/chip/lm4/system.c
index 33bfd0227c..05f6def1bd 100644
--- a/chip/lm4/system.c
+++ b/chip/lm4/system.c
@@ -13,6 +13,7 @@
#include "registers.h"
#include "system.h"
#include "task.h"
+#include "timer.h"
#include "util.h"
/* Indices for hibernate data registers */
@@ -28,18 +29,20 @@ enum hibdata_index {
#define HIBDATA_WAKE_PIN (1 << 2) /* Wake pin */
/*
- * Time it takes wait_for_hibctl_wc() to return. Experimentally verified to
- * be ~200 us; the value below is somewhat conservative.
- */
-#define HIB_WAIT_USEC 1000
-
-/*
* Time to hibernate to trigger a power-on reset. 50 ms is sufficient for the
* EC itself, but we need a longer delay to ensure the rest of the components
* on the same power rail are reset and 5VALW has dropped.
*/
#define HIB_RESET_USEC 1000000
+/*
+ * Convert between microseconds and the hibernation module RTC subsecond
+ * register which has 15-bit resolution. Divide down both numerator and
+ * denominator to avoid integer overflow while keeping the math accurate.
+ */
+#define HIB_RTC_USEC_TO_SUBSEC(us) ((us) * (32768/64) / (1000000/64))
+#define HIB_RTC_SUBSEC_TO_USEC(ss) ((ss) * (1000000/64) / (32768/64))
+
/**
* Wait for a write to commit to a hibernate register.
*
@@ -187,7 +190,7 @@ void __attribute__((section(".iram.text"))) __enter_hibernate(int hibctl)
*
* @return the real-time clock seconds value.
*/
-uint32_t system_get_rtc(uint32_t *ss_ptr)
+static uint32_t system_get_rtc_sec_subsec(uint32_t *ss_ptr)
{
uint32_t rtc, rtc2;
uint32_t rtcss, rtcss2;
@@ -209,6 +212,17 @@ uint32_t system_get_rtc(uint32_t *ss_ptr)
return rtc;
}
+timestamp_t system_get_rtc(void)
+{
+ uint32_t rtc, rtc_ss;
+ timestamp_t time;
+
+ rtc = system_get_rtc_sec_subsec(&rtc_ss);
+
+ time.val = ((uint64_t)rtc) * SECOND + HIB_RTC_SUBSEC_TO_USEC(rtc_ss);
+ return time;
+}
+
/**
* Set the real-time clock.
*
@@ -222,6 +236,91 @@ void system_set_rtc(uint32_t seconds)
}
/**
+ * Set the hibernate RTC match time at a given time from now
+ *
+ * @param seconds Number of seconds from now for RTC match
+ * @param microseconds Number of microseconds from now for RTC match
+ */
+static void set_hibernate_rtc_match_time(uint32_t seconds,
+ uint32_t microseconds)
+{
+ uint32_t rtc, rtcss;
+
+ /*
+ * Make sure that the requested delay is not less then the
+ * amount of time it takes to set the RTC match registers,
+ * otherwise, the match event could be missed.
+ */
+ if (seconds == 0 && microseconds < HIB_SET_RTC_MATCH_DELAY_USEC)
+ microseconds = HIB_SET_RTC_MATCH_DELAY_USEC;
+
+ /* Calculate the wake match */
+ rtc = system_get_rtc_sec_subsec(&rtcss) + seconds;
+ rtcss += HIB_RTC_USEC_TO_SUBSEC(microseconds);
+ if (rtcss > 0x7fff) {
+ rtc += rtcss >> 15;
+ rtcss &= 0x7fff;
+ }
+
+ /* Set RTC alarm match */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBRTCM0 = rtc;
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBRTCSS = rtcss << 16;
+ wait_for_hibctl_wc();
+}
+
+/**
+ * Use hibernate module to set up an RTC interrupt at a given
+ * time from now
+ *
+ * @param seconds Number of seconds before RTC interrupt
+ * @param microseconds Number of microseconds before RTC interrupt
+ */
+void system_set_rtc_alarm(uint32_t seconds, uint32_t microseconds)
+{
+ /* Clear pending interrupt */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBIC = LM4_HIBERNATE_HIBRIS;
+
+ /* Set match time */
+ set_hibernate_rtc_match_time(seconds, microseconds);
+
+ /* Enable RTC interrupt on match */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBIM = 1;
+}
+
+/**
+ * Disable and clear the RTC interrupt.
+ */
+void system_reset_rtc_alarm(void)
+{
+ /* Disable hibernate interrupts */
+ LM4_HIBERNATE_HIBIM = 0;
+
+ /* Clear interrupts */
+ LM4_HIBERNATE_HIBIC = LM4_HIBERNATE_HIBRIS;
+}
+
+/**
+ * Hibernate module interrupt
+ */
+static void __hibernate_irq(void)
+{
+ system_reset_rtc_alarm();
+}
+DECLARE_IRQ(LM4_IRQ_HIBERNATE, __hibernate_irq, 1);
+
+/**
+ * Enable hibernate interrupt
+ */
+void system_enable_hib_interrupt(void)
+{
+ task_enable_irq(LM4_IRQ_HIBERNATE);
+}
+
+/**
* Internal hibernate function.
*
* @param seconds Number of seconds to sleep before RTC alarm
@@ -230,7 +329,6 @@ void system_set_rtc(uint32_t seconds)
*/
static void hibernate(uint32_t seconds, uint32_t microseconds, uint32_t flags)
{
- uint32_t rtc, rtcss;
uint32_t hibctl;
/* Set up wake reasons and hibernate flags */
@@ -244,46 +342,21 @@ static void hibernate(uint32_t seconds, uint32_t microseconds, uint32_t flags)
if (seconds || microseconds) {
hibctl |= LM4_HIBCTL_RTCWEN;
flags |= HIBDATA_WAKE_RTC;
+
+ set_hibernate_rtc_match_time(seconds, microseconds);
} else {
hibctl &= ~LM4_HIBCTL_RTCWEN;
}
wait_for_hibctl_wc();
LM4_HIBERNATE_HIBCTL = hibctl;
- /* Store hibernate flags */
- hibdata_write(HIBDATA_INDEX_WAKE, flags);
-
/* Clear pending interrupt */
wait_for_hibctl_wc();
LM4_HIBERNATE_HIBIC = LM4_HIBERNATE_HIBRIS;
- /* Add expected overhead for hibernate register writes */
- microseconds += HIB_WAIT_USEC * 4;
-
- /*
- * The code below must run uninterrupted to make sure we accurately
- * calculate the RTC match value.
- */
- interrupt_disable();
-
- /*
- * Calculate the wake match, compensating for additional delays caused
- * by writing to the hibernate register.
- */
- rtc = system_get_rtc(&rtcss) + seconds;
- rtcss += microseconds * (32768/64) / (1000000/64);
- if (rtcss > 0x7fff) {
- rtc += rtcss >> 15;
- rtcss &= 0x7fff;
- }
-
- /* Set RTC alarm match */
- wait_for_hibctl_wc();
- LM4_HIBERNATE_HIBRTCM0 = rtc;
- wait_for_hibctl_wc();
- LM4_HIBERNATE_HIBRTCSS = rtcss << 16;
+ /* Store hibernate flags */
+ hibdata_write(HIBDATA_INDEX_WAKE, flags);
- wait_for_hibctl_wc();
__enter_hibernate(hibctl | LM4_HIBCTL_HIBREQ);
}
@@ -296,6 +369,8 @@ void system_hibernate(uint32_t seconds, uint32_t microseconds)
void system_pre_init(void)
{
+ uint32_t hibctl;
+
/*
* Enable clocks to the hibernation module in run, sleep,
* and deep sleep modes.
@@ -331,6 +406,16 @@ void system_pre_init(void)
}
/*
+ * Set wake reasons to RTC match and WAKE pin by default.
+ * Before going in to hibernate, these may change.
+ */
+ hibctl = LM4_HIBERNATE_HIBCTL;
+ hibctl |= LM4_HIBCTL_RTCWEN;
+ hibctl |= LM4_HIBCTL_PINWEN;
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBCTL = hibctl;
+
+ /*
* Initialize registers after reset to work around LM4 chip errata
* (still present in A3 chip stepping).
*/
@@ -504,9 +589,9 @@ static int command_system_rtc(int argc, char **argv)
return EC_ERROR_INVAL;
}
- rtc = system_get_rtc(&rtcss);
+ rtc = system_get_rtc_sec_subsec(&rtcss);
ccprintf("RTC: 0x%08x.%04x (%d.%06d s)\n",
- rtc, rtcss, rtc, (rtcss * (1000000/64)) / (32768/64));
+ rtc, rtcss, rtc, HIB_RTC_SUBSEC_TO_USEC(rtcss));
return EC_SUCCESS;
}
@@ -515,6 +600,41 @@ DECLARE_CONSOLE_COMMAND(rtc, command_system_rtc,
"Get/set real-time clock",
NULL);
+#ifdef CONFIG_CMD_RTC_ALARM
+/**
+ * Test the RTC alarm by setting an interrupt on RTC match.
+ */
+static int command_rtc_alarm_test(int argc, char **argv)
+{
+ int s = 1, us = 0;
+ char *e;
+
+ ccprintf("Setting RTC alarm\n");
+ system_enable_hib_interrupt();
+
+ if (argc > 1) {
+ s = strtoi(argv[1], &e, 10);
+ if (*e)
+ return EC_ERROR_PARAM1;
+
+ }
+ if (argc > 2) {
+ us = strtoi(argv[2], &e, 10);
+ if (*e)
+ return EC_ERROR_PARAM2;
+
+ }
+
+ system_set_rtc_alarm(s, us);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(rtc_alarm, command_rtc_alarm_test,
+ "[seconds [microseconds]]",
+ "Test alarm",
+ NULL);
+#endif /* CONFIG_CMD_RTC_ALARM */
+
/*****************************************************************************/
/* Host commands */
@@ -522,7 +642,7 @@ static int system_rtc_get_value(struct host_cmd_handler_args *args)
{
struct ec_response_rtc *r = args->response;
- r->time = system_get_rtc(NULL);
+ r->time = system_get_rtc_sec_subsec(NULL);
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
diff --git a/chip/lm4/uart.c b/chip/lm4/uart.c
index 04ccd7ba8e..bb08aa1477 100644
--- a/chip/lm4/uart.c
+++ b/chip/lm4/uart.c
@@ -11,6 +11,7 @@
#include "gpio.h"
#include "lpc.h"
#include "registers.h"
+#include "system.h"
#include "task.h"
#include "uart.h"
#include "util.h"
@@ -26,6 +27,12 @@ int uart_init_done(void)
void uart_tx_start(void)
{
+ /* If interrupt is already enabled, nothing to do */
+ if (LM4_UART_IM(0) & 0x20)
+ return;
+
+ /* Do not allow deep sleep while transmit in progress */
+ disable_sleep(SLEEP_MASK_UART);
/*
* Re-enable the transmit interrupt, then forcibly trigger the
* interrupt. This works around a hardware problem with the
@@ -39,6 +46,9 @@ void uart_tx_start(void)
void uart_tx_stop(void)
{
LM4_UART_IM(0) &= ~0x20;
+
+ /* Re-allow deep sleep */
+ enable_sleep(SLEEP_MASK_UART);
}
int uart_tx_stopped(void)
@@ -176,11 +186,16 @@ void uart_init(void)
{
uint32_t mask = 0;
- /* Enable UART0 and Host UART in run, sleep, and deep sleep modes. */
+ /*
+ * Enable UART0 in run, sleep, and deep sleep modes. Enable the Host
+ * UART in run and sleep modes.
+ */
mask |= 1;
- mask |= (1 << CONFIG_UART_HOST);
+ clock_enable_peripheral(CGC_OFFSET_UART, mask, CGC_MODE_DSLEEP);
- clock_enable_peripheral(CGC_OFFSET_UART, mask, CGC_MODE_ALL);
+ mask |= (1 << CONFIG_UART_HOST);
+ clock_enable_peripheral(CGC_OFFSET_UART, mask,
+ CGC_MODE_RUN | CGC_MODE_SLEEP);
gpio_config_module(MODULE_UART, 1);
diff --git a/chip/stm32/clock-stm32f.c b/chip/stm32/clock-stm32f.c
index e878d7856d..23db9f1f44 100644
--- a/chip/stm32/clock-stm32f.c
+++ b/chip/stm32/clock-stm32f.c
@@ -35,19 +35,6 @@
#define RTC_FREQ 40000 /* Hz */
#define US_PER_RTC_TICK (1000000 / RTC_FREQ)
-/* On-going actions preventing to go into deep-sleep mode */
-static uint32_t sleep_mask;
-
-void enable_sleep(uint32_t mask)
-{
- atomic_clear(&sleep_mask, mask);
-}
-
-void disable_sleep(uint32_t mask)
-{
- atomic_or(&sleep_mask, mask);
-}
-
static void wait_rtc_ready(void)
{
/* wait for Registers Synchronized Flag */
@@ -298,27 +285,4 @@ void clock_init(void)
task_enable_irq(STM32_IRQ_RTC_ALARM);
}
-/*****************************************************************************/
-/* Console commands */
-static int command_sleepmask(int argc, char **argv)
-{
- int off;
-
- if (argc >= 2) {
- off = strtoi(argv[1], NULL, 10);
-
- if (off)
- disable_sleep(SLEEP_MASK_FORCE);
- else
- enable_sleep(SLEEP_MASK_FORCE);
- }
-
- ccprintf("sleep mask: %08x\n", sleep_mask);
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(sleepmask, command_sleepmask,
- "[0|1]",
- "Display/force sleep mack",
- NULL);
diff --git a/chip/stm32/clock-stm32l.c b/chip/stm32/clock-stm32l.c
index 4d5b1babeb..63805eedda 100644
--- a/chip/stm32/clock-stm32l.c
+++ b/chip/stm32/clock-stm32l.c
@@ -32,16 +32,6 @@ enum clock_osc {
static int freq;
static int current_osc;
-void enable_sleep(uint32_t mask)
-{
- /* low power mode not implemented */
-}
-
-void disable_sleep(uint32_t mask)
-{
- /* low power mode not implemented */
-}
-
int clock_get_freq(void)
{
return freq;
diff --git a/chip/stm32/uart.c b/chip/stm32/uart.c
index dab5231845..ec6a14c4d5 100644
--- a/chip/stm32/uart.c
+++ b/chip/stm32/uart.c
@@ -10,6 +10,7 @@
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
+#include "system.h"
#include "task.h"
#include "uart.h"
#include "util.h"
diff --git a/common/chipset_haswell.c b/common/chipset_haswell.c
index f4417ce2a1..ba7564a41a 100644
--- a/common/chipset_haswell.c
+++ b/common/chipset_haswell.c
@@ -261,6 +261,12 @@ enum x86_state x86_handle_state(enum x86_state state)
/* Call hooks now that rails are up */
hook_notify(HOOK_CHIPSET_RESUME);
+ /*
+ * Disable idle task deep sleep. This means that the low
+ * power idle task will not go into deep sleep while in S0.
+ */
+ disable_sleep(SLEEP_MASK_AP_RUN);
+
/* Wait 99ms after all voltages good */
msleep(99);
@@ -293,6 +299,12 @@ enum x86_state x86_handle_state(enum x86_state state)
wireless_enable(0);
/*
+ * Enable idle task deep sleep. Allow the low power idle task
+ * to go into deep sleep in S3 or lower.
+ */
+ enable_sleep(SLEEP_MASK_AP_RUN);
+
+ /*
* Deassert prochot since CPU is off and we're about to drop
* +VCCP.
*/
diff --git a/common/console_output.c b/common/console_output.c
index 385c48d0af..d2fb8e2ab1 100644
--- a/common/console_output.c
+++ b/common/console_output.c
@@ -22,6 +22,7 @@ static const char *channel_names[CC_CHANNEL_COUNT] = {
"command",
"charger",
"chipset",
+ "clock",
"dma",
"events",
"gpio",
diff --git a/common/i2c_common.c b/common/i2c_common.c
index f18c8004f8..aa749912f6 100644
--- a/common/i2c_common.c
+++ b/common/i2c_common.c
@@ -22,17 +22,15 @@ static struct mutex port_mutex[I2C_PORT_COUNT];
void i2c_lock(int port, int lock)
{
if (lock) {
-#ifdef CHIP_stm32
/* Don't allow deep sleep when I2C port is locked */
disable_sleep(SLEEP_MASK_I2C);
-#endif
+
mutex_lock(port_mutex + port);
} else {
mutex_unlock(port_mutex + port);
-#ifdef CHIP_stm32
+
/* Allow deep sleep again after I2C port is unlocked */
enable_sleep(SLEEP_MASK_I2C);
-#endif
}
}
diff --git a/common/system_common.c b/common/system_common.c
index ecf72c0782..778a7ef672 100644
--- a/common/system_common.c
+++ b/common/system_common.c
@@ -4,7 +4,6 @@
*/
/* System module for Chrome EC : common functions */
-
#include "clock.h"
#include "common.h"
#include "console.h"
@@ -79,6 +78,9 @@ static int disable_jump; /* Disable ALL jumps if system is locked */
static int force_locked; /* Force system locked even if WP isn't enabled */
static enum ec_reboot_cmd reboot_at_shutdown;
+/* On-going actions preventing going into deep-sleep mode */
+uint32_t sleep_mask;
+
int system_is_locked(void)
{
if (force_locked)
@@ -749,6 +751,34 @@ DECLARE_CONSOLE_COMMAND(syslock, command_system_lock,
"Lock the system, even if WP is disabled",
NULL);
+#ifdef CONFIG_LOW_POWER_IDLE
+/**
+ * Modify and print the sleep mask which controls access to deep sleep
+ * mode in the idle task.
+ */
+static int command_sleepmask(int argc, char **argv)
+{
+ int off;
+
+ if (argc >= 2) {
+ off = strtoi(argv[1], NULL, 10);
+
+ if (off)
+ disable_sleep(SLEEP_MASK_FORCE);
+ else
+ enable_sleep(SLEEP_MASK_FORCE);
+ }
+
+ ccprintf("sleep mask: %08x\n", sleep_mask);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(sleepmask, command_sleepmask,
+ "[<sleep_mask>]",
+ "Display/force sleep mask",
+ NULL);
+#endif
+
/*****************************************************************************/
/* Host commands */
diff --git a/include/clock.h b/include/clock.h
index 8c6d3dd098..2f14ffc62b 100644
--- a/include/clock.h
+++ b/include/clock.h
@@ -39,18 +39,6 @@ void clock_enable_pll(int enable, int notify);
*/
void clock_wait_cycles(uint32_t cycles);
-/* Low power modes for idle API */
-
-enum {
- 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 */
-};
-
/* Clock gate control modes for clock_enable_peripheral() */
#define CGC_MODE_RUN (1 << 0)
#define CGC_MODE_SLEEP (1 << 1)
@@ -77,7 +65,4 @@ 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);
-void enable_sleep(uint32_t mask);
-void disable_sleep(uint32_t mask);
-
#endif /* __CROS_EC_CLOCK_H */
diff --git a/include/config.h b/include/config.h
index 1808f89c75..9e0ff87107 100644
--- a/include/config.h
+++ b/include/config.h
@@ -164,6 +164,7 @@
#undef CONFIG_CMD_POWERLED
#undef CONFIG_CMD_SCRATCHPAD
#undef CONFIG_CMD_SLEEP
+#undef CONFIG_CMD_RTC_ALARM
/*****************************************************************************/
diff --git a/include/console.h b/include/console.h
index 827517eee8..e53f4b23d3 100644
--- a/include/console.h
+++ b/include/console.h
@@ -30,6 +30,7 @@ enum console_channel {
* inside a console command routine. */
CC_CHARGER,
CC_CHIPSET,
+ CC_CLOCK,
CC_DMA,
CC_EVENTS,
CC_GPIO,
diff --git a/include/gpio.h b/include/gpio.h
index eee7711ee3..b3861ede08 100644
--- a/include/gpio.h
+++ b/include/gpio.h
@@ -28,6 +28,7 @@
#define GPIO_INT_LOW (1 << 10) /* Interrupt on low level */
#define GPIO_INT_HIGH (1 << 11) /* Interrupt on high level */
#define GPIO_DEFAULT (1 << 12) /* Don't set up on boot */
+#define GPIO_INT_DSLEEP (1 << 13) /* Interrupt in deep sleep */
/* Common flag combinations */
#define GPIO_OUT_LOW (GPIO_OUTPUT | GPIO_LOW)
@@ -37,6 +38,7 @@
#define GPIO_INT_EDGE (GPIO_INT_RISING | GPIO_INT_FALLING | GPIO_INT_BOTH)
#define GPIO_INT_LEVEL (GPIO_INT_LOW | GPIO_INT_HIGH)
#define GPIO_INT_ANY (GPIO_INT_EDGE | GPIO_INT_LEVEL)
+#define GPIO_INT_BOTH_DSLEEP (GPIO_INT_BOTH | GPIO_INT_DSLEEP)
/* Note that if no flags are present, the signal is a high-Z input */
/* GPIO signal definition structure, for use by board.c */
diff --git a/include/system.h b/include/system.h
index 26e61932c0..0c2936a33c 100644
--- a/include/system.h
+++ b/include/system.h
@@ -8,7 +8,9 @@
#ifndef __CROS_EC_SYSTEM_H
#define __CROS_EC_SYSTEM_H
+#include "atomic.h"
#include "common.h"
+#include "timer.h"
/* Reset causes */
#define RESET_FLAG_OTHER (1 << 0) /* Other known reason */
@@ -268,4 +270,72 @@ void system_hibernate(uint32_t seconds, uint32_t microseconds);
int system_get_console_force_enabled(void);
int system_set_console_force_enabled(int enabled);
+/**
+ * Read the real-time clock.
+ *
+ * @return The real-time clock value as a timestamp.
+ */
+timestamp_t system_get_rtc(void);
+
+/**
+ * Enable hibernate interrupt
+ */
+void system_enable_hib_interrupt(void);
+
+/* Low power modes for idle API */
+enum {
+ 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 */
+};
+
+/*
+ * Current sleep mask. You may read from this variable, but must NOT
+ * modify it; use enable_sleep() or disable_sleep() to do that.
+ */
+extern uint32_t sleep_mask;
+
+/**
+ * Enable low power sleep mask. For low power sleep to take affect, all masks
+ * in the sleep mask enum above must be enabled.
+ *
+ * @param Sleep mask to enable.
+ */
+static inline void enable_sleep(uint32_t mask)
+{
+ atomic_clear(&sleep_mask, mask);
+}
+
+/**
+ * Disable low power sleep mask. For low power sleep to take affect, all masks
+ * in the sleep mask enum above must be enabled.
+ *
+ * @param Sleep mask to enable.
+ */
+static inline void disable_sleep(uint32_t mask)
+{
+ atomic_or(&sleep_mask, mask);
+}
+
+/**
+ * Use hibernate module to set up an RTC interrupt at a given
+ * time from now
+ *
+ * Note: If time given is less than HIB_SET_RTC_MATCH_DELAY_USEC, then it will
+ * set the interrupt at exactly HIB_SET_RTC_MATCH_DELAY_USEC.
+ *
+ * @param seconds Number of seconds before RTC interrupt
+ * @param microseconds Number of microseconds before RTC interrupt
+ */
+void system_set_rtc_alarm(uint32_t seconds, uint32_t microseconds);
+
+/**
+ * Disable and clear the RTC interrupt.
+ */
+void system_reset_rtc_alarm(void);
+
#endif /* __CROS_EC_SYSTEM_H */