summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVic (Chun-Ju) Yang <victoryang@chromium.org>2013-12-30 10:53:14 +0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-01-28 04:35:50 +0000
commit9785d09fcbc79b034e8b4f8b6a21fab418fafc3e (patch)
tree743ed349eb4debc23915f239a569e0de65f2feca
parentaa636df6ab83d8ca08dc87c4140dceb40dd65900 (diff)
downloadchrome-ec-stabilize-5339.B.tar.gz
mec1322: hibernation supportstabilize-5339.B
This adds hibernation support. The chip can be waken by either GPIO or a timer. The maximum delay allowed is ~2 hours. BUG=chrome-os-partner:24107 TEST=hibernate and wake by GPIO TEST=hibernate and wake by timer BRANCH=None Change-Id: I1e064638a5008894a002a06a738bf6104f18636d Signed-off-by: Vic (Chun-Ju) Yang <victoryang@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/181202 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/mec1322_evb/board.c1
-rw-r--r--board/mec1322_evb/board.h2
-rw-r--r--chip/mec1322/registers.h8
-rw-r--r--chip/mec1322/system.c156
4 files changed, 163 insertions, 4 deletions
diff --git a/board/mec1322_evb/board.c b/board/mec1322_evb/board.c
index cc9cc6c449..ae854af6ae 100644
--- a/board/mec1322_evb/board.c
+++ b/board/mec1322_evb/board.c
@@ -22,6 +22,7 @@ const struct gpio_info gpio_list[] = {
{"LED3", GPIO_PORT(15), (1 << 6), GPIO_ODR_LOW, NULL},
{"PCH_SMI_L", GPIO_PORT(4), (1 << 4), GPIO_ODR_HIGH, NULL},
{"PCH_WAKE_L", GPIO_PORT(20), (1 << 0), GPIO_ODR_HIGH, NULL},
+ {"S1", GPIO_PORT(6), (1 << 3), GPIO_INT_FALLING | GPIO_PULL_UP, NULL},
/* Unimplemented signals which we need to emulate for now */
GPIO_SIGNAL_NOT_IMPLEMENTED("RECOVERY_L"),
GPIO_SIGNAL_NOT_IMPLEMENTED("WP"),
diff --git a/board/mec1322_evb/board.h b/board/mec1322_evb/board.h
index 73a861fa47..57728ca529 100644
--- a/board/mec1322_evb/board.h
+++ b/board/mec1322_evb/board.h
@@ -13,6 +13,7 @@
#define CONFIG_WATCHDOG_HELP
#define CONFIG_FANS 1
#define CONFIG_ADC
+#define CONFIG_WAKE_PIN GPIO_S1
/* Modules we want to exclude */
#undef CONFIG_EEPROM
@@ -40,6 +41,7 @@ enum gpio_signal {
GPIO_LED3,
GPIO_PCH_SMI_L, /* SMI output */
GPIO_PCH_WAKE_L, /* PCH wake pin */
+ GPIO_S1, /* Switch S1 */
/*
* Signals which aren't implemented on MEC1322 eval board but we'll
* emulate anyway, to make it more convenient to debug other code.
diff --git a/chip/mec1322/registers.h b/chip/mec1322/registers.h
index edc276f0eb..396b9efbe7 100644
--- a/chip/mec1322/registers.h
+++ b/chip/mec1322/registers.h
@@ -44,6 +44,7 @@
#define MEC1322_EC_TRACE_EN REG32(MEC1322_EC_BASE + 0x1c)
#define MEC1322_EC_JTAG_EN REG32(MEC1322_EC_BASE + 0x20)
#define MEC1322_EC_WDT_CNT REG32(MEC1322_EC_BASE + 0x28)
+#define MEC1322_EC_ADC_VREF_PD REG32(MEC1322_EC_BASE + 0x38)
/* Interrupt aggregator */
@@ -285,6 +286,13 @@ static inline uintptr_t gpio_port_base(int port_id)
#define MEC1322_ADC_READ(x) REG32(MEC1322_ADC_BASE + 0x14 + (x) * 0x4)
+/* Hibernation timer */
+#define MEC1322_HTIMER_BASE 0x40009800
+#define MEC1322_HTIMER_PRELOAD REG16(MEC1322_HTIMER_BASE + 0x0)
+#define MEC1322_HTIMER_CONTROL REG16(MEC1322_HTIMER_BASE + 0x4)
+#define MEC1322_HTIMER_COUNT REG16(MEC1322_HTIMER_BASE + 0x8)
+
+
/* IRQ Numbers */
#define MEC1322_IRQ_I2C_0 0
#define MEC1322_IRQ_I2C_1 1
diff --git a/chip/mec1322/system.c b/chip/mec1322/system.c
index 7364029498..0b7565c641 100644
--- a/chip/mec1322/system.c
+++ b/chip/mec1322/system.c
@@ -9,6 +9,7 @@
#include "common.h"
#include "console.h"
#include "cpu.h"
+#include "gpio.h"
#include "host_command.h"
#include "registers.h"
#include "system.h"
@@ -50,7 +51,9 @@ static void check_reset_cause(void)
flags |= MEC1322_VBAT_RAM(HIBDATA_INDEX_SAVED_RESET_FLAGS);
MEC1322_VBAT_RAM(HIBDATA_INDEX_SAVED_RESET_FLAGS) = 0;
- if (status & (1 << 5) && !(flags & (RESET_FLAG_SOFT | RESET_FLAG_HARD)))
+ if (status & (1 << 5) && !(flags & (RESET_FLAG_SOFT |
+ RESET_FLAG_HARD |
+ RESET_FLAG_HIBERNATE)))
flags |= RESET_FLAG_WATCHDOG;
system_set_reset_flags(flags);
@@ -70,7 +73,7 @@ void system_pre_init(void)
check_reset_cause();
}
-void system_reset(int flags)
+void _system_reset(int flags, int wake_from_hibernate)
{
uint32_t save_flags = 0;
@@ -84,7 +87,9 @@ void system_reset(int flags)
if (flags & SYSTEM_RESET_LEAVE_AP_OFF)
save_flags |= RESET_FLAG_AP_OFF;
- if (flags & SYSTEM_RESET_HARD)
+ if (wake_from_hibernate)
+ save_flags |= RESET_FLAG_HIBERNATE;
+ else if (flags & SYSTEM_RESET_HARD)
save_flags |= RESET_FLAG_HARD;
else
save_flags |= RESET_FLAG_SOFT;
@@ -100,6 +105,11 @@ void system_reset(int flags)
;
}
+void system_reset(int flags)
+{
+ _system_reset(flags, 0);
+}
+
const char *system_get_chip_vendor(void)
{
return "smsc";
@@ -154,7 +164,145 @@ uint32_t system_get_scratchpad(void)
return MEC1322_VBAT_RAM(HIBDATA_INDEX_SCRATCHPAD);
}
+static void system_unpower_gpio(void)
+{
+ int i, j, k;
+ uint32_t val;
+ int want_skip;
+
+ const int pins[16][2] = {{0, 7}, {1, 7}, {2, 7}, {3, 6}, {4, 7}, {5, 7},
+ {6, 7}, {10, 7}, {11, 7}, {12, 7}, {13, 6},
+ {14, 7}, {15, 7}, {16, 5}, {20, 6}, {21, 1} };
+
+ const int skip[5][2] = {{13, 1}, /* VCC1_nRST */
+ {6, 3}, /* VCC_PWRGD */
+ {12, 1}, /* nRESET_OUT */
+ {14, 3}, /* RSMRST# */
+ {20, 5}, /* Not exist */
+ };
+
+ for (i = 0; i < 16; ++i) {
+ for (j = 0; j <= pins[i][1]; ++j) {
+ want_skip = 0;
+ for (k = 0; k < 5; ++k)
+ if (skip[k][0] == pins[i][0] &&
+ skip[k][1] == j)
+ want_skip = 1;
+ if (want_skip)
+ continue;
+
+ /*
+ * GPIO Input, pull-high, interrupt disabled
+ * TODO(crosbug.com/p/25302): Unpower GPIO instead of
+ * setting them to be input.
+ */
+ val = MEC1322_GPIO_CTL(pins[i][0], j);
+ val &= ~((1 << 12) | (1 << 13));
+ val &= ~(1 << 9);
+ val = (val & ~(0xf << 4)) | (0x4 << 4);
+ val = (val & ~0x3) | 0x1;
+ MEC1322_GPIO_CTL(pins[i][0], j) = val;
+ }
+ }
+}
+
void system_hibernate(uint32_t seconds, uint32_t microseconds)
{
- /* TODO(crosbug.com/p/24107): Implement this */
+ int i;
+
+ cflush();
+
+ /* Disable interrupts */
+ interrupt_disable();
+ for (i = 0; i <= 92; ++i) {
+ task_disable_irq(i);
+ task_clear_pending_irq(i);
+ }
+
+ for (i = 8; i <= 23; ++i)
+ MEC1322_INT_DISABLE(i) = 0xffffffff;
+ MEC1322_INT_BLK_DIS |= 0xffff00;
+
+ /* Set processor clock to lowest, 1MHz */
+ MEC1322_PCR_PROC_CLK_CTL = 48;
+
+ /* Power down ADC VREF */
+ MEC1322_EC_ADC_VREF_PD |= 1;
+
+ /* Assert nSIO_RESET */
+ MEC1322_PCR_PWR_RST_CTL |= 1;
+
+ /* Disable UART */
+ MEC1322_UART_ACT &= ~0x1;
+ MEC1322_LPC_ACT &= ~0x1;
+
+ /* Disable JTAG */
+ MEC1322_EC_JTAG_EN &= ~1;
+
+ /* Disable 32KHz clock */
+ MEC1322_VBAT_CE &= ~0x2;
+
+ /* Stop watchdog */
+ MEC1322_WDG_CTL &= ~1;
+
+ /* Stop timers */
+ MEC1322_TMR32_CTL(0) &= ~1;
+ MEC1322_TMR32_CTL(1) &= ~1;
+ MEC1322_TMR16_CTL(0) &= ~1;
+
+ /* Power down ADC */
+ MEC1322_ADC_CTRL &= ~1;
+
+ /* Disable blocks */
+ MEC1322_PCR_CHIP_SLP_EN |= 0x3;
+ MEC1322_PCR_EC_SLP_EN |= 0xe0700ff7;
+ MEC1322_PCR_HOST_SLP_EN |= 0x5f003;
+ MEC1322_PCR_EC_SLP_EN2 |= 0x1ffffff8;
+ MEC1322_PCR_SYS_SLP_CTL = (MEC1322_PCR_SYS_SLP_CTL & ~0x7) | 0x2;
+ MEC1322_PCR_SLOW_CLK_CTL &= 0xfffffc00;
+ CPU_SCB_SYSCTRL |= 0x4;
+
+ system_unpower_gpio();
+
+#ifdef CONFIG_WAKE_PIN
+ gpio_set_flags_by_mask(gpio_list[CONFIG_WAKE_PIN].port,
+ gpio_list[CONFIG_WAKE_PIN].mask,
+ gpio_list[CONFIG_WAKE_PIN].flags);
+ gpio_enable_interrupt(CONFIG_WAKE_PIN);
+ interrupt_enable();
+ task_enable_irq(MEC1322_IRQ_GIRQ8);
+ task_enable_irq(MEC1322_IRQ_GIRQ9);
+ task_enable_irq(MEC1322_IRQ_GIRQ10);
+ task_enable_irq(MEC1322_IRQ_GIRQ11);
+ task_enable_irq(MEC1322_IRQ_GIRQ20);
+#endif
+
+ if (seconds || microseconds) {
+ MEC1322_INT_BLK_EN |= 1 << 17;
+ MEC1322_INT_ENABLE(17) |= 1 << 20;
+ interrupt_enable();
+ task_enable_irq(MEC1322_IRQ_HTIMER);
+ if (seconds > 2) {
+ ASSERT(seconds <= 0xffff / 8);
+ MEC1322_HTIMER_CONTROL = 1;
+ MEC1322_HTIMER_PRELOAD =
+ (seconds * 8 + microseconds / 125000);
+ } else {
+ MEC1322_HTIMER_CONTROL = 0;
+ MEC1322_HTIMER_PRELOAD =
+ (seconds * 1000000 + microseconds) * 2 / 71;
+ }
+ }
+
+ asm("wfi");
+
+ /* We lost states of most modules, let's just reboot */
+ _system_reset(0, 1);
+}
+
+static void htimer_interrupt(void)
+{
+ /* Time to wake up */
+ _system_reset(0, 1);
}
+DECLARE_IRQ(MEC1322_IRQ_HTIMER, htimer_interrupt, 1);