diff options
-rw-r--r-- | board/zinger/runtime.c | 2 | ||||
-rw-r--r-- | chip/stm32/clock-f.c | 89 | ||||
-rw-r--r-- | chip/stm32/clock-f.h | 19 | ||||
-rw-r--r-- | chip/stm32/clock-stm32f0.c | 53 |
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 |