summaryrefslogtreecommitdiff
path: root/chip/stm32/clock-stm32f4.c
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2020-11-10 13:53:31 +0100
committerCommit Bot <commit-bot@chromium.org>2020-12-02 09:39:15 +0000
commit8d98b0e5dc06cd285ea4a65f8a93a76514d9dc59 (patch)
tree33d0f606f01d4baf952d15acd3a2895f0db69b23 /chip/stm32/clock-stm32f4.c
parentaba388b6562aa2d37d98f4325cb8d2b06a0d8b26 (diff)
downloadchrome-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>
Diffstat (limited to 'chip/stm32/clock-stm32f4.c')
-rw-r--r--chip/stm32/clock-stm32f4.c119
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 */