mec1322: hibernation support

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>
This commit is contained in:
Vic (Chun-Ju) Yang
2013-12-30 10:53:14 +08:00
committed by chrome-internal-fetch
parent aa636df6ab
commit 9785d09fcb
4 changed files with 163 additions and 4 deletions

View File

@@ -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"),

View File

@@ -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.

View File

@@ -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

View File

@@ -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);