summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Berg <alecaberg@chromium.org>2013-10-07 12:23:35 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2013-10-15 00:27:14 +0000
commitc5b90d7e77511e45f2c0021c1a0bc18b09310933 (patch)
treeaf3ab9f0b74cb239808cca51c681be840d32a107
parent193f2298bd1a078f1ac07aacf9dc50132cbb39d3 (diff)
downloadchrome-ec-stabilize-4825.B.tar.gz
lm4: Add a low power idle task.stabilize-4825.B
First implementation of a low power idle task for the LM4 chip. The low power mode is selected by defining CONFIG_LOW_POWER_IDLE in a board.h file. This commit turns it on for Peppy, Slippy, and Falco only because those are the only boards tested. When using the low power idle task, the chip goes in to deep sleep when it can. Deep sleep disables clocks to most peripherals and puts the onboard flash and RAM into a low power mode. The chip is woken out of deep sleep using the RTC in the hibernate module. Increased the idle task stack size to handle more involved idle task. In board.c, the array of GPIO info can be used to select which GPIO points can wake up the EC from deep sleep. Currenlty selected are the power button, lid open, AC present, PCH_SLP_S3, and PCH_SLP_S5. Additionally the port with the KB scan row GPIO point is also enabled to wake up the EC from deep sleep. Signed-off-by: Alec Berg <alecaberg@chromium.org> BUG=None BRANCH=none TEST=Passes all unit tests. Runs on slippy, peppy, and falco with no noticeable side affects. Verified that the power consumed by the EC is lower when in S3, S5 and G3 by scoping the sense resistor powering the chip. Change-Id: I83fa9a159a4b79201b99f2c32678dc4fc8921726 Reviewed-on: https://chromium-review.googlesource.com/172183 Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org> Commit-Queue: Alec Berg <alecaberg@chromium.org> Tested-by: Alec Berg <alecaberg@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.c16
-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/system.h70
22 files changed, 552 insertions, 141 deletions
diff --git a/board/falco/board.c b/board/falco/board.c
index b7f434d056..c037441214 100644
--- a/board/falco/board.c
+++ b/board/falco/board.c
@@ -36,19 +36,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 5f9ca32e22..bfb692a577 100644
--- a/board/falco/board.h
+++ b/board/falco/board.h
@@ -28,6 +28,7 @@
#define CONFIG_FAN_POWER_GOOD GPIO_PP5000_PGOOD
#define CONFIG_KEYBOARD_BOARD_CONFIG
#define CONFIG_KEYBOARD_PROTOCOL_8042
+#define CONFIG_LOW_POWER_IDLE
#define CONFIG_POWER_BUTTON
#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM
diff --git a/board/peppy/board.c b/board/peppy/board.c
index f1d55dd738..738ea662b0 100644
--- a/board/peppy/board.c
+++ b/board/peppy/board.c
@@ -33,19 +33,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 160e6f1d2b..7f00f1bcd4 100644
--- a/board/peppy/board.h
+++ b/board/peppy/board.h
@@ -31,6 +31,7 @@
#define CONFIG_KEYBOARD_BOARD_CONFIG
#define CONFIG_KEYBOARD_PROTOCOL_8042
#define CONFIG_LED_COMMON
+#define CONFIG_LOW_POWER_IDLE
#define CONFIG_POWER_BUTTON
#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM
diff --git a/board/slippy/board.c b/board/slippy/board.c
index cf937ead4f..df731f3610 100644
--- a/board/slippy/board.c
+++ b/board/slippy/board.c
@@ -33,19 +33,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 7dc2cb1bd9..2443005ebc 100644
--- a/board/slippy/board.h
+++ b/board/slippy/board.h
@@ -29,6 +29,7 @@
#define CONFIG_FAN_POWER_GOOD GPIO_PP5000_PGOOD
#define CONFIG_KEYBOARD_BOARD_CONFIG
#define CONFIG_KEYBOARD_PROTOCOL_8042
+#define CONFIG_LOW_POWER_IDLE
#define CONFIG_POWER_BUTTON
#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM
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 1ade3365f4..a534bb7a57 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 13fad41be6..ad9c634947 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"
@@ -32,6 +33,9 @@ void uart_tx_start(void)
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
@@ -45,6 +49,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);
}
void uart_tx_flush(void)
@@ -182,14 +189,19 @@ 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;
+ clock_enable_peripheral(CGC_OFFSET_UART, mask, CGC_MODE_DSLEEP);
#ifdef CONFIG_UART_HOST
mask |= (1 << CONFIG_UART_HOST);
#endif
- clock_enable_peripheral(CGC_OFFSET_UART, mask, CGC_MODE_ALL);
+ 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 09b8f00704..3cb09f69bf 100644
--- a/chip/stm32/clock-stm32l.c
+++ b/chip/stm32/clock-stm32l.c
@@ -33,16 +33,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 78bbcc3e58..f2650591ed 100644
--- a/chip/stm32/uart.c
+++ b/chip/stm32/uart.c
@@ -11,6 +11,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 b92bbbf5ce..6bc4b3283d 100644
--- a/common/chipset_haswell.c
+++ b/common/chipset_haswell.c
@@ -282,6 +282,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);
@@ -314,6 +320,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 7f70651f04..bad734d67a 100644
--- a/common/console_output.c
+++ b/common/console_output.c
@@ -22,6 +22,7 @@ static const char * const channel_names[] = {
"command",
"charger",
"chipset",
+ "clock",
"dma",
"events",
"gpio",
diff --git a/common/i2c_common.c b/common/i2c_common.c
index a291d5caa4..dcbc4fb73d 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 55ea35d20d..81182ef987 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"
@@ -82,6 +81,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)
@@ -770,6 +772,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 343b43ecee..875be91006 100644
--- a/include/clock.h
+++ b/include/clock.h
@@ -55,18 +55,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)
@@ -93,7 +81,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 72dffe49c2..e44a97e96c 100644
--- a/include/config.h
+++ b/include/config.h
@@ -214,6 +214,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 623d5a5551..1dd37e42c9 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/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 */