summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/zinger/runtime.c2
-rw-r--r--chip/stm32/clock-f.c89
-rw-r--r--chip/stm32/clock-f.h19
-rw-r--r--chip/stm32/clock-stm32f0.c53
4 files changed, 121 insertions, 42 deletions
diff --git a/board/zinger/runtime.c b/board/zinger/runtime.c
index 0caaa146fd..44a93b346a 100644
--- a/board/zinger/runtime.c
+++ b/board/zinger/runtime.c
@@ -180,7 +180,7 @@ uint32_t task_wait_event(int timeout_us)
CPU_SCB_SYSCTRL |= 0x4;
set_rtc_alarm(0, timeout_us - STOP_MODE_LATENCY,
- &rtc0);
+ &rtc0, 0);
asm volatile("wfi");
diff --git a/chip/stm32/clock-f.c b/chip/stm32/clock-f.c
index 1872b0ce3c..3333538326 100644
--- a/chip/stm32/clock-f.c
+++ b/chip/stm32/clock-f.c
@@ -71,8 +71,51 @@ static uint32_t sec_to_rtc_tr(uint32_t sec)
return rtc_tr;
}
+/* Register setup before RTC alarm is allowed for update */
+static void pre_work_set_rtc_alarm(void)
+{
+ rtc_unlock_regs();
+
+ /* Make sure alarm is disabled */
+ STM32_RTC_CR &= ~STM32_RTC_CR_ALRAE;
+ while (!(STM32_RTC_ISR & STM32_RTC_ISR_ALRAWF))
+ ;
+ STM32_RTC_ISR &= ~STM32_RTC_ISR_ALRAF;
+}
+
+/* Register setup after RTC alarm is updated */
+static void post_work_set_rtc_alarm(void)
+{
+ STM32_EXTI_PR = EXTI_RTC_ALR_EVENT;
+
+ /* Enable alarm and alarm interrupt */
+ STM32_EXTI_IMR |= EXTI_RTC_ALR_EVENT;
+ STM32_RTC_CR |= STM32_RTC_CR_ALRAE;
+
+ rtc_lock_regs();
+}
+
#ifdef CONFIG_HOSTCMD_RTC
-static uint8_t host_rtc_alarm_set;
+static struct wake_time host_wake_time;
+
+int is_host_wake_alarm_expired(timestamp_t ts)
+{
+ return host_wake_time.ts.val &&
+ timestamp_expired(host_wake_time.ts, &ts);
+}
+
+void restore_host_wake_alarm(void)
+{
+ if (!host_wake_time.ts.val)
+ return;
+
+ pre_work_set_rtc_alarm();
+
+ /* Set alarm time */
+ STM32_RTC_ALRMAR = host_wake_time.rtc_alrmar;
+
+ post_work_set_rtc_alarm();
+}
static uint32_t rtc_dr_to_sec(uint32_t rtc_dr)
{
@@ -163,7 +206,7 @@ void rtc_read(struct rtc_time_reg *rtc)
}
void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us,
- struct rtc_time_reg *rtc)
+ struct rtc_time_reg *rtc, uint8_t save_alarm)
{
uint32_t alarm_sec = 0;
uint32_t alarm_us = 0;
@@ -176,14 +219,7 @@ void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us,
/* Alarm timeout must be within 1 day (86400 seconds) */
ASSERT((delay_s + delay_us / SECOND) < SECS_PER_DAY);
- rtc_unlock_regs();
-
- /* Make sure alarm is disabled */
- STM32_RTC_CR &= ~STM32_RTC_CR_ALRAE;
- while (!(STM32_RTC_ISR & STM32_RTC_ISR_ALRAWF))
- ;
- STM32_RTC_ISR &= ~STM32_RTC_ISR_ALRAF;
-
+ pre_work_set_rtc_alarm();
rtc_read(rtc);
/* Calculate alarm time */
@@ -216,12 +252,17 @@ void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us,
STM32_RTC_ALRMASSR = delay_us ?
(us_to_rtcss(alarm_us) | 0x0f000000) : 0;
- /* Enable alarm and alarm interrupt */
- STM32_EXTI_PR = EXTI_RTC_ALR_EVENT;
- STM32_EXTI_IMR |= EXTI_RTC_ALR_EVENT;
- STM32_RTC_CR |= STM32_RTC_CR_ALRAE;
-
- rtc_lock_regs();
+#ifdef CONFIG_HOSTCMD_RTC
+ /*
+ * If alarm is set by the host, preserve the wake time timestamp
+ * and alarm registers.
+ */
+ if (save_alarm) {
+ host_wake_time.ts.val = delay_s * SECOND + get_time().val;
+ host_wake_time.rtc_alrmar = STM32_RTC_ALRMAR;
+ }
+#endif
+ post_work_set_rtc_alarm();
}
uint32_t get_rtc_alarm(void)
@@ -266,13 +307,13 @@ void reset_rtc_alarm(struct rtc_time_reg *rtc)
void __rtc_alarm_irq(void)
{
struct rtc_time_reg rtc;
-
reset_rtc_alarm(&rtc);
+
#ifdef CONFIG_HOSTCMD_RTC
- /* Do not wake up the host if the alarm was not set by the host */
- if (host_rtc_alarm_set) {
+ /* Wake up the host if there is a saved rtc wake alarm. */
+ if (host_wake_time.ts.val) {
+ host_wake_time.ts.val = 0;
host_set_single_event(EC_HOST_EVENT_RTC);
- host_rtc_alarm_set = 0;
}
#endif
}
@@ -331,6 +372,7 @@ void print_system_rtc(enum console_channel ch)
rtc_read(&rtc);
sec = rtc_to_sec(&rtc);
+
cprintf(ch, "RTC: 0x%08x (%d.00 s)\n", sec, sec);
}
@@ -376,7 +418,7 @@ static int command_rtc_alarm_test(int argc, char **argv)
return EC_ERROR_PARAM2;
}
- set_rtc_alarm(s, us, &rtc);
+ set_rtc_alarm(s, us, &rtc, 0);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(rtc_alarm, command_rtc_alarm_test,
@@ -424,10 +466,7 @@ static int system_rtc_set_alarm(struct host_cmd_handler_args *args)
if (p->time >= SECS_PER_DAY)
return EC_RES_INVALID_PARAM;
- if (p->time != EC_RTC_ALARM_CLEAR)
- host_rtc_alarm_set = 1;
-
- set_rtc_alarm(p->time, 0, &rtc);
+ set_rtc_alarm(p->time, 0, &rtc, 1);
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_RTC_SET_ALARM,
diff --git a/chip/stm32/clock-f.h b/chip/stm32/clock-f.h
index ffeca7518a..5e9acc748d 100644
--- a/chip/stm32/clock-f.h
+++ b/chip/stm32/clock-f.h
@@ -38,6 +38,12 @@ struct rtc_time_reg {
uint32_t rtc_dr; /* years, months, dates, week days */
};
+/* Save the RTC alarm wake time */
+struct wake_time {
+ timestamp_t ts;
+ uint32_t rtc_alrmar; /* the value of register STM32_RTC_ALRMAR */
+};
+
/* Convert between RTC regs in BCD and seconds */
uint32_t rtc_to_sec(const struct rtc_time_reg *rtc);
@@ -60,9 +66,9 @@ void rtc_read(struct rtc_time_reg *rtc);
/* Set RTC value */
void rtc_set(uint32_t sec);
-/* Set RTC wakeup */
+/* Set RTC wakeup, save alarm wakeup time when save_alarm != 0 */
void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us,
- struct rtc_time_reg *rtc);
+ struct rtc_time_reg *rtc, uint8_t save_alarm);
/* Clear RTC wakeup */
void reset_rtc_alarm(struct rtc_time_reg *rtc);
@@ -85,4 +91,13 @@ void config_hispeed_clock(void);
/* Get timer clock frequency (for STM32 only) */
int clock_get_timer_freq(void);
+/*
+ * Return 1 if host_wake_time is nonzero and the saved host_wake_time
+ * is expired at a given time, ts.
+ */
+int is_host_wake_alarm_expired(timestamp_t ts);
+
+/* Set RTC wakeup based on the value saved in host_wake_time */
+void restore_host_wake_alarm(void);
+
#endif /* __CROS_EC_CLOCK_F_H */
diff --git a/chip/stm32/clock-stm32f0.c b/chip/stm32/clock-stm32f0.c
index 30fcf8cca5..3509468056 100644
--- a/chip/stm32/clock-stm32f0.c
+++ b/chip/stm32/clock-stm32f0.c
@@ -39,12 +39,19 @@ static int dsleep_recovery_margin_us = 1000000;
/*
* minimum delay to enter stop mode
+ *
* STOP_MODE_LATENCY: max time to wake up from STOP mode with regulator in low
* power mode is 5 us + PLL locking time is 200us.
- * SET_RTC_MATCH_DELAY: max time to set RTC match alarm. if we set the alarm
+ *
+ * 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.
* For STM32F3, we are using HSE, which requires additional time to start up.
* Therefore, the latency for STM32F3 is set longer.
+ *
+ * RESTORE_HOST_ALARM_LATENCY: max latency between the deferred routine is
+ * called and the host alarm is actually restored. In practice, the max latency
+ * is measured as ~600us. 1000us should be conservative enough to guarantee
+ * we won't miss the host alarm.
*/
#ifdef CHIP_VARIANT_STM32F373
#define STOP_MODE_LATENCY 500 /* us */
@@ -57,6 +64,10 @@ static int dsleep_recovery_margin_us = 1000000;
#endif
#define SET_RTC_MATCH_DELAY 200 /* us */
+#ifdef CONFIG_HOSTCMD_RTC
+#define RESTORE_HOST_ALARM_LATENCY 1000 /* us */
+#endif
+
#endif /* CONFIG_LOW_POWER_IDLE */
/*
@@ -263,7 +274,7 @@ void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
struct rtc_time_reg rtc;
if (seconds || microseconds)
- set_rtc_alarm(seconds, microseconds, &rtc);
+ set_rtc_alarm(seconds, microseconds, &rtc, 0);
/* interrupts off now */
asm volatile("cpsid i");
@@ -283,6 +294,14 @@ void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
}
#endif
+#ifdef CONFIG_HOSTCMD_RTC
+static void restore_host_wake_alarm_deferred(void)
+{
+ restore_host_wake_alarm();
+}
+DECLARE_DEFERRED(restore_host_wake_alarm_deferred);
+#endif
+
#ifdef CONFIG_LOW_POWER_IDLE
void clock_refresh_console_in_use(void)
@@ -309,26 +328,28 @@ void __idle(void)
if (DEEP_SLEEP_ALLOWED &&
#ifdef CONFIG_HOSTCMD_RTC
/*
- * Don't go to deep sleep mode when the host is using
- * RTC alarm otherwise the wake point for the host
- * would be overwritten.
- *
- * TODO(chromium:769503): Find a smart way to enable deep
- * sleep mode even when the host is using stm32 rtc alarm.
+ * Don't go to deep sleep mode if we might miss the
+ * wake alarm that the host requested. Note that the
+ * host alarm always aligns to second. Considering the
+ * worst case, we have to ensure alarm won't go off
+ * within RESTORE_HOST_ALARM_LATENCY + 1 second after
+ * EC exits deep sleep mode.
*/
- !(STM32_RTC_CR & STM32_RTC_CR_ALRAE) &&
+ !is_host_wake_alarm_expired(
+ (timestamp_t)(next_delay + t0.val + SECOND +
+ RESTORE_HOST_ALARM_LATENCY)) &&
#endif
(next_delay > (STOP_MODE_LATENCY + SET_RTC_MATCH_DELAY))) {
- /* deep-sleep in STOP mode */
+ /* Deep-sleep in STOP mode */
idle_dsleep_cnt++;
uart_enable_wakeup(1);
- /* set deep sleep bit */
+ /* Set deep sleep bit */
CPU_SCB_SYSCTRL |= 0x4;
set_rtc_alarm(0, next_delay - STOP_MODE_LATENCY,
- &rtc0);
+ &rtc0, 0);
asm("wfi");
CPU_SCB_SYSCTRL &= ~0x4;
@@ -341,12 +362,16 @@ void __idle(void)
*/
config_hispeed_clock();
- /* fast forward timer according to RTC counter */
+ /* 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);
+#ifdef CONFIG_HOSTCMD_RTC
+ hook_call_deferred(
+ &restore_host_wake_alarm_deferred_data, 0);
+#endif
/* Record time spent in deep sleep. */
idle_dsleep_time_us += rtc_diff;
@@ -362,7 +387,7 @@ void __idle(void)
} else {
idle_sleep_cnt++;
- /* normal idle : only CPU clock stopped */
+ /* Normal idle : only CPU clock stopped */
asm("wfi");
}
#ifdef CONFIG_LOW_POWER_IDLE_LIMITED