diff --git a/core/embed/sys/power_manager/stm32u5/power_manager.c b/core/embed/sys/power_manager/stm32u5/power_manager.c index 40bf21135d..a38afe618c 100644 --- a/core/embed/sys/power_manager/stm32u5/power_manager.c +++ b/core/embed/sys/power_manager/stm32u5/power_manager.c @@ -209,6 +209,22 @@ pm_status_t pm_get_state(pm_state_t* state) { return PM_OK; } +// This callback is called from inside the system_suspend() function +// when the rtc wake-up timer expires. The callback can perform +// measurements and update the fuel gauge state. +// - The callback can schedule the next wake-up by calling +// rtc_wakeup_timer_start(). +// - If the callback return with wakeup_flags set, system_suspend() returns. +void pm_rtc_wakeup_callback(void* context) { + // TODO: update fuel gauge state + + if (true) { + rtc_wakeup_timer_start(0, pm_rtc_wakeup_callback, NULL); + } else { + wakeup_flags_set(WAKEUP_FLAG_RTC); + } +} + pm_status_t pm_suspend(wakeup_flags_t* wakeup_reason) { pm_driver_t* drv = &g_pm; @@ -234,13 +250,14 @@ pm_status_t pm_suspend(wakeup_flags_t* wakeup_reason) { irq_unlock(irq_key); #ifdef USE_RTC - // TODO: Uncomment to wake up by RTC timer - // Automatically wakes up after 10 seconds with PM_WAKEUP_FLAG_RTC set - // rtc_wakeup_timer_start(10); + // Automatically wakes up after specified time and call pm_rtc_wakeup_callback + rtc_wakeup_timer_start(10, pm_rtc_wakeup_callback, NULL); #endif wakeup_flags_t wakeup_flags = system_suspend(); + rtc_wakeup_timer_stop(); + // TODO: Handle wake-up flags // UNUSED(wakeup_flags); diff --git a/core/embed/sys/time/inc/sys/rtc.h b/core/embed/sys/time/inc/sys/rtc.h index 799de0ad82..4febcc4785 100644 --- a/core/embed/sys/time/inc/sys/rtc.h +++ b/core/embed/sys/time/inc/sys/rtc.h @@ -29,13 +29,29 @@ */ bool rtc_init(void); +/** + * @brief Callback invoked when the RTC wakeup event occurs + * + * @param context Context pointer passed to rtc_wakeup_timer_start + */ +typedef void (*rtc_wakeup_callback_t)(void* context); + /** * @brief Schedule a wakeup event after a specified number of seconds * * Configures the RTC to wake up the system from STOP mode after the specified - * number of seconds. After waking up, the PM_WAKEUP_FLAG_RTC flag is set. + * number of seconds. After waking up, callback is called if not NULL otherwise + * the WAKEUP_FLAG_RTC flag is set. * * @param seconds Number of seconds (1 to 65536) to wait before waking up. + * @param callback Callback function to be called when the wakeup event occurs. + * @param context Context pointer to be passed to the callback function. * @return true if the wakeup was successfully scheduled, false otherwise */ -bool rtc_wakeup_timer_start(uint32_t seconds); +bool rtc_wakeup_timer_start(uint32_t seconds, rtc_wakeup_callback_t callback, + void* context); + +/** + * @brief Stop the RTC wakeup timer + */ +void rtc_wakeup_timer_stop(void); diff --git a/core/embed/sys/time/stm32u5/rtc.c b/core/embed/sys/time/stm32u5/rtc.c index 57fecf618f..a9040d67f3 100644 --- a/core/embed/sys/time/stm32u5/rtc.c +++ b/core/embed/sys/time/stm32u5/rtc.c @@ -31,6 +31,8 @@ typedef struct { bool initialized; RTC_HandleTypeDef hrtc; + rtc_wakeup_callback_t callback; + void* callback_context; } rtc_driver_t; // RTC driver instance @@ -70,7 +72,8 @@ bool rtc_init(void) { return true; } -bool rtc_wakeup_timer_start(uint32_t seconds) { +bool rtc_wakeup_timer_start(uint32_t seconds, rtc_wakeup_callback_t callback, + void* context) { rtc_driver_t* drv = &g_rtc_driver; if (!drv->initialized) { @@ -81,6 +84,11 @@ bool rtc_wakeup_timer_start(uint32_t seconds) { return false; } + irq_key_t irq_key = irq_lock(); + drv->callback = callback; + drv->callback_context = context; + irq_unlock(irq_key); + HAL_StatusTypeDef status; status = HAL_RTCEx_SetWakeUpTimer_IT(&drv->hrtc, seconds - 1, @@ -92,17 +100,39 @@ bool rtc_wakeup_timer_start(uint32_t seconds) { return true; } +void rtc_wakeup_timer_stop(void) { + rtc_driver_t* drv = &g_rtc_driver; + + if (!drv->initialized) { + return; + } + + HAL_RTCEx_DeactivateWakeUpTimer(&drv->hrtc); + drv->callback = NULL; + drv->callback_context = NULL; +} + void RTC_IRQHandler(void) { + rtc_driver_t* drv = &g_rtc_driver; + IRQ_LOG_ENTER(); mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT); if (READ_BIT(RTC->MISR, RTC_MISR_WUTMF) != 0U) { // Clear the wakeup timer interrupt flag WRITE_REG(RTC->SCR, RTC_SCR_CWUTF); + + rtc_wakeup_callback_t callback = drv->callback; + void* callback_context = drv->callback_context; + // Deactivate the wakeup timer to prevent re-triggering - HAL_RTCEx_DeactivateWakeUpTimer(&g_rtc_driver.hrtc); - // Signal the wakeup event to the power manager - wakeup_flags_set(WAKEUP_FLAG_RTC); + rtc_wakeup_timer_stop(); + + if (callback != NULL) { + callback(callback_context); + } else { + wakeup_flags_set(WAKEUP_FLAG_RTC); + } } mpu_restore(mpu_mode);