diff --git a/board/cr50/board.c b/board/cr50/board.c index 575d3511dd..c80c2f3930 100644 --- a/board/cr50/board.c +++ b/board/cr50/board.c @@ -123,7 +123,12 @@ void pmu_wakeup_interrupt(void) */ delay_sleep_by(3 * MINUTE); - if (!gpio_get_level(GPIO_SYS_RST_L_IN)) + /* + * If sys_rst_l is configured to wake on low and the signal is + * low then call sys_rst_asserted + */ + if (!gpio_get_level(GPIO_SYS_RST_L_IN) && + GREAD_FIELD(PINMUX, EXITINV0, DIOM0)) sys_rst_asserted(GPIO_SYS_RST_L_IN); } @@ -138,6 +143,32 @@ void pmu_wakeup_interrupt(void) } DECLARE_IRQ(GC_IRQNUM_PMU_INTR_WAKEUP_INT, pmu_wakeup_interrupt, 1); +void board_configure_deep_sleep_wakepins(void) +{ + /* + * Disable the i2c and spi slave wake sources since the TPM is + * not being used and reenable them in their init functions on + * resume. + */ + GWRITE_FIELD(PINMUX, EXITEN0, DIOA12, 0); /* SPS_CS_L */ + /* TODO remove i2cs wake event */ + + /* + * Whether it is a short pulse or long one waking on the rising edge is + * fine because the goal of sys_rst is to reset the TPM and after + * resuming from deep sleep the TPM will be reset. Cr50 doesn't need to + * read the low value and then reset. + * + * Configure cr50 to resume on the rising edge of sys_rst_l + */ + /* Disable sys_rst_l as a wake pin */ + GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 0); + /* Reconfigure and reenable it. */ + GWRITE_FIELD(PINMUX, EXITEDGE0, DIOM0, 1); /* edge sensitive */ + GWRITE_FIELD(PINMUX, EXITINV0, DIOM0, 0); /* wake on high */ + GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1); /* enable powerdown exit */ +} + static void init_interrupts(void) { int i; diff --git a/board/cr50/board.h b/board/cr50/board.h index de0ced7d36..c7814e5b36 100644 --- a/board/cr50/board.h +++ b/board/cr50/board.h @@ -123,6 +123,7 @@ enum usb_spi { USB_SPI_EC, }; +void board_configure_deep_sleep_wakepins(void); /* Interrupt handler */ void sys_rst_asserted(enum gpio_signal signal); void device_state_on(enum gpio_signal signal); diff --git a/chip/g/idle.c b/chip/g/idle.c index d1250115f2..5486cebaa9 100644 --- a/chip/g/idle.c +++ b/chip/g/idle.c @@ -5,6 +5,7 @@ #include "common.h" #include "console.h" +#include "hooks.h" #include "hwtimer.h" #include "rdd.h" #include "registers.h" @@ -101,13 +102,8 @@ static void prepare_to_sleep(void) /* Clear upcoming events. They don't matter in deep sleep */ __hw_clock_event_clear(); - /* - * Disable the i2c and spi slave wake sources since the TPM is - * not being used and reenable them in their init functions on - * resume. - */ - GWRITE_FIELD(PINMUX, EXITEN0, DIOA12, 0); /* SPS_CS_L */ - /* TODO remove i2cs wake event */ + /* Configure pins for deep sleep */ + board_configure_deep_sleep_wakepins(); /* * Preserve some state prior to deep sleep. Pretty much all we @@ -115,8 +111,6 @@ static void prepare_to_sleep(void) * reinitialized on resume. */ GREG32(PMU, PWRDN_SCRATCH18) = GR_USB_DCFG; - /* And the idle action */ - GREG32(PMU, PWRDN_SCRATCH17) = idle_action; /* Latch the pinmux values */ GREG32(PINMUX, HOLD) = 1; @@ -166,22 +160,33 @@ void clock_refresh_console_in_use(void) delay_sleep_by(10 * SECOND); } +void disable_deep_sleep(void) +{ + idle_action = IDLE_DEFAULT; +} +DECLARE_HOOK(HOOK_CHIPSET_RESUME, disable_deep_sleep, HOOK_PRIO_DEFAULT); + +void enable_deep_sleep(void) +{ + idle_action = IDLE_DEEP_SLEEP; +} +DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, enable_deep_sleep, HOOK_PRIO_DEFAULT); + /* Custom idle task, executed when no tasks are ready to be scheduled. */ void __idle(void) { int sleep_ok, sleep_delay_passed, next_evt_us; /* - * This register is preserved across soft reboots, but not hard. It - * defaults to zero, which is how we can tell whether this is the - * preserved value or not. We only need to remember it because we might - * change it with the console command. + * On init or resume from deep sleep set the idle action to default. If + * it should be something else it will be determined during runtime. + * + * Before changing idle_action check that it is not already set. It is + * possible that HOOK_CHIPSET_RESUME or SHUTDOWN were triggered before + * this and set the idle_action. */ - idle_action = GREG32(PMU, PWRDN_SCRATCH17); - if (idle_action == DONT_KNOW || idle_action >= NUM_CHOICES) { + if (!idle_action) idle_action = IDLE_DEFAULT; - GREG32(PMU, PWRDN_SCRATCH17) = idle_action; - } /* Disable sleep until 3 minutes after init */ delay_sleep_by(3 * MINUTE);