diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2020-11-10 13:53:31 +0100 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-12-02 09:39:15 +0000 |
commit | 8d98b0e5dc06cd285ea4a65f8a93a76514d9dc59 (patch) | |
tree | 33d0f606f01d4baf952d15acd3a2895f0db69b23 | |
parent | aba388b6562aa2d37d98f4325cb8d2b06a0d8b26 (diff) | |
download | chrome-ec-8d98b0e5dc06cd285ea4a65f8a93a76514d9dc59.tar.gz |
stm32: add STOP mode on STM32F4
Implement a low power idle mode using the STM32F4 STOP mode.
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
BUG=b:130561737
TEST=manual, on bloonchipper, check we can still capture fingerprint.
read the MCU power consumption.
BRANCH=fpmcu-bloonchipper
Change-Id: I11249e9b68c989033263e34e1cde3f19ffe7c54c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2537631
Tested-by: Vincent Palatin <vpalatin@chromium.org>
Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r-- | chip/stm32/clock-stm32f4.c | 119 |
1 files changed, 117 insertions, 2 deletions
diff --git a/chip/stm32/clock-stm32f4.c b/chip/stm32/clock-stm32f4.c index 3580b7a02a..87e4fe061b 100644 --- a/chip/stm32/clock-stm32f4.c +++ b/chip/stm32/clock-stm32f4.c @@ -260,6 +260,8 @@ static void clock_pll_configure(void) PLLCFGR_PLLR(i2sdiv); } +void low_power_init(void); + void config_hispeed_clock(void) { #ifdef CONFIG_STM32_CLOCK_HSE_HZ @@ -273,8 +275,10 @@ void config_hispeed_clock(void) /* Switch SYSCLK to PLL, setup bus prescalers. */ clock_switch_osc(OSC_PLL); - /* we cannot go to low power mode as we are running on the PLL */ - disable_sleep(SLEEP_MASK_PLL); + +#ifdef CONFIG_LOW_POWER_IDLE + low_power_init(); +#endif } void clock_wait_bus_cycles(enum bus_type bus, uint32_t cycles) @@ -421,3 +425,114 @@ void rtc_set(uint32_t sec) rtc_lock_regs(); } #endif + +#ifdef CONFIG_LOW_POWER_IDLE +/* Low power idle statistics */ +static int idle_sleep_cnt; +static int idle_dsleep_cnt; +static uint64_t idle_dsleep_time_us; +static int dsleep_recovery_margin_us = 1000000; + +/* STOP_MODE_LATENCY: delay to wake up from STOP mode with main regulator off */ +#define STOP_MODE_LATENCY 50 /* us */ +/* + * SET_RTC_MATCH_DELAY: max time to set RTC match alarm. If we set the alarm + * in the past, it will never wake up and cause a watchdog. + */ +#define SET_RTC_MATCH_DELAY 120 /* us */ + + +void low_power_init(void) +{ + /* Turn off the main regulator during stop mode */ + STM32_PWR_CR |= STM32_PWR_CR_LPSDSR /* aka LPDS */; +} + +void clock_refresh_console_in_use(void) +{ +} + +void __idle(void) +{ + timestamp_t t0; + uint32_t rtc_diff; + int next_delay, margin_us; + struct rtc_time_reg rtc0, rtc1; + + while (1) { + asm volatile("cpsid i"); + + t0 = get_time(); + next_delay = __hw_clock_event_get() - t0.le.lo; + + if (DEEP_SLEEP_ALLOWED && + (next_delay > (STOP_MODE_LATENCY + SET_RTC_MATCH_DELAY))) { + /* Deep-sleep in STOP mode */ + idle_dsleep_cnt++; + + /* + * TODO(b/174337385) no support for wake-up on USART + * uart_enable_wakeup(1); + */ + + /* Set deep sleep bit */ + CPU_SCB_SYSCTRL |= 0x4; + + set_rtc_alarm(0, next_delay - STOP_MODE_LATENCY, + &rtc0, 0); + /* ensure outstanding memory transactions complete */ + asm volatile("dsb"); + + asm("wfi"); + + CPU_SCB_SYSCTRL &= ~0x4; + + /*uart_enable_wakeup(0);*/ + + /* Fast forward timer according to RTC counter */ + reset_rtc_alarm(&rtc1); + rtc_diff = get_rtc_diff(&rtc0, &rtc1); + t0.val = t0.val + rtc_diff; + force_time(t0); + + /* Record time spent in deep sleep. */ + idle_dsleep_time_us += rtc_diff; + + /* Calculate how close we were to missing deadline */ + margin_us = next_delay - rtc_diff; + if (margin_us < 0) + /* Use CPUTS to save stack space */ + CPUTS("Idle overslept!\n"); + + /* 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"); + } + asm volatile("cpsie i"); + } +} + +/* 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: %.6llds\n", + idle_dsleep_time_us); + ccprintf("Total time on: %.6llds\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"); +#endif /* CONFIG_LOW_POWER_IDLE */ |