From 9785d09fcbc79b034e8b4f8b6a21fab418fafc3e Mon Sep 17 00:00:00 2001 From: "Vic (Chun-Ju) Yang" Date: Mon, 30 Dec 2013 10:53:14 +0800 Subject: 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 Reviewed-on: https://chromium-review.googlesource.com/181202 Reviewed-by: Vincent Palatin --- board/mec1322_evb/board.c | 1 + board/mec1322_evb/board.h | 2 + chip/mec1322/registers.h | 8 +++ chip/mec1322/system.c | 156 ++++++++++++++++++++++++++++++++++++++++++++-- 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); -- cgit v1.2.1