void up_boot_standby_mode(void) { uint32_t gpio_off_mask; uint32_t regval; int i; DEBUGASSERT((getreg32(STM32_PWR_CR) & PWR_CR_DBP) != 0); /* * This function is called from up_boot.c, stm32_boardinitialize(). OS is not * running yet. Regulators and chip-selects have been initialized. IWDG not * enabled. If board has HWWDG, we must enable it from this function before * going to standby mode. */ regval = getreg32(CONFIG_STANDBYMODE_MAGIC_BKREG); if (regval != CONFIG_STANDBYMODE_MAGIC) { g_board_wakeup_from_standby = (regval == CONFIG_STANDBYMODE_MAGIC + 1); /* Standby mode was not requested, continue with regular boot. */ putreg32(0, CONFIG_STANDBYMODE_MAGIC_BKREG); for (i = 0; i < 4; i++) up_lowputc('r'); return; } #if defined(CONFIG_STM32_DFU) || defined (CONFIG_BOARD_HALTIAN_HWWDG) up_irqinitialize(); #endif /* Go into standby mode. */ putreg32(CONFIG_STANDBYMODE_MAGIC + 1, CONFIG_STANDBYMODE_MAGIC_BKREG); for (i = 0; i < 4; i++) up_lowputc('s'); /* Setup bootloader to jump directly to firmware. */ putreg32(BOARD_FIRMWARE_BASE_ADDR, CONFIG_BOOTLOADER_ADDR_BKREG); /* Now disable backup register access. If following interrupt handlers * access backup registers, they need to make sure to re-enable access. */ stm32_pwr_enablebkp(false); while (true) { /* Configure SDcard pins. */ gpio_initialize_sdcard_pins(); /* Configure display GPIOs. */ gpio_off_mask = ~(GPIO_PUPD_MASK | GPIO_MODE_MASK | GPIO_OUTPUT_SET); stm32_configgpio(GPIO_CHIP_SELECT_DISPLAY); stm32_configgpio(GPIO_LCD_SSD1309_CMDDATA); stm32_configgpio(GPIO_LCD_SSD1309_RESET); stm32_configgpio((GPIO_SPI2_MOSI & gpio_off_mask) | GPIO_OUTPUT_CLEAR | GPIO_OUTPUT); stm32_configgpio((GPIO_SPI2_SCK & gpio_off_mask) | GPIO_OUTPUT_CLEAR | GPIO_OUTPUT); /* Reconfigure GPIO's for stop mode (most pins are setup as analog input). */ up_reconfigure_gpios_for_pmstop(); /* We must kick HWWDG even from standby mode, else it resets device. */ board_wdginitialize_autokick(hwwdg_irq_in_standby); /* Setup 'power'-button as EXTI for wake-up. */ stm32_configgpio(GPIO_BTN_POWERKEY); stm32_gpiosetevent(GPIO_BTN_POWERKEY, true, false, true, button_pressed_down_handler); /* Our standby-mode is really ARM core's stop-mode. Enter stop-mode with MCU internal regulator in low-power mode. */ (void)stm32_pmstop(true); standby_do_trace('p'); } }
void board_deepsleep_with_stopmode(uint32_t secs) { #if defined(CONFIG_RTC) && defined(CONFIG_RTC_DATETIME) struct tm t; struct timespec ts; struct timespec rtc_start_ts; struct timespec rtc_end_ts; #endif #ifdef CONFIG_BOARD_DEEPSLEEP_RECONFIGURE_GPIOS uint32_t gpio_off_mask; bool sdcard_enabled; #endif uint32_t regval; int ret; #ifdef DEEPSLEEP_WAKETIME_DEBUG static struct timespec wake_ts; #endif #ifdef DEEPSLEEP_SLEEPTIME_DEBUG bool slept = false; #endif /* Do not allow other tasks to takeover (for example, by wake-up with semaphore * from EXTI interrupt) before we have restored MCU functionalities. */ sched_lock(); #ifdef DEEPSLEEP_SLEEPTIME_DEBUG lldbg("sleep for %d\n", secs); #endif #ifdef CONFIG_BOARD_DEEPSLEEP_RECONFIGURE_GPIOS /* Special support for SDcard. */ if (stm32_gpioread(GPIO_CHIP_SELECT_SDCARD) == false) { lldbg("ERROR! SDcard communication active while trying deep-sleep!\n"); goto skip_deepsleep; } /* Following devices do not have proper GPIO reconfigure recovery yet (and * some cannot work with deep-sleep at all). Abort deep-sleep instead of * introducing hard-to-diagnose bugs and general instability. */ if (stm32_gpioread(GPIO_REGULATOR_WLAN)) { lldbg("ERROR! Trying to deep-sleep when WLAN active!\n"); goto skip_deepsleep; } if (stm32_gpioread(GPIO_PWR_SWITCH_MODEM)) { lldbg("ERROR! Trying to deep-sleep when Modem active!\n"); goto skip_deepsleep; } if (stm32_gpioread(GPIO_REGULATOR_GPS)) { lldbg("ERROR! Trying to deep-sleep when GPS active!\n"); goto skip_deepsleep; } if (stm32_gpioread(GPIO_REGULATOR_BLUETOOTH)) { lldbg("ERROR! Trying to deep-sleep when Bluetooth active!\n"); goto skip_deepsleep; } if (stm32_gpioread(GPIO_REGULATOR_DISPLAY)) { lldbg("ERROR! Trying to deep-sleep when Display active!\n"); goto skip_deepsleep; } #endif /* CONFIG_BOARD_DEEPSLEEP_RECONFIGURE_GPIOS */ #if defined(CONFIG_RTC) && defined(CONFIG_RTC_DATETIME) /* Get RTC clock before STOP. */ stm32_rtc_getdatetime_with_subseconds(&t, &rtc_start_ts.tv_nsec); rtc_start_ts.tv_sec = mktime(&t); /* Get current time. */ ret = clock_gettime(CLOCK_REALTIME, &ts); DEBUGASSERT(ret != ERROR); /* Add nanoseconds to entropy pool. */ add_time_randomness(ts.tv_nsec); #endif #ifdef DEEPSLEEP_WAKETIME_DEBUG if (wake_ts.tv_nsec || wake_ts.tv_sec) { struct timespec curr_ts; int64_t msecs; clock_gettime(CLOCK_REALTIME, &curr_ts); msecs = ((int64_t)curr_ts.tv_sec - wake_ts.tv_sec) * 1000; msecs += ((int64_t)curr_ts.tv_nsec - wake_ts.tv_nsec) / (1000 * 1000); lldbg("sleep to sleep time: %d msecs\n", (int)msecs); } else { lldbg("first sleep (for %u secs)\n", secs); } #endif #ifdef CONFIG_RTC_PERIODIC_AUTORELOAD_WAKEUP if (secs > 0) { /* Prepare RTC for wake-up. */ ret = up_rtc_setperiodicwakeup(secs, NULL); DEBUGASSERT(ret == OK); } #else DEBUGASSERT(secs == 0); #endif /* Prevent USART interrupt activity, make sure that last transmission has * completed. */ stm32_serial_set_suspend(true); #ifdef CONFIG_BOARD_DEEPSLEEP_RECONFIGURE_GPIOS /* Read control state of SDCard regulator (TODO: add proper pwrctl api) */ sdcard_enabled = stm32_gpioread(GPIO_REGULATOR_SDCARD); if (sdcard_enabled) { /* Mark SDcard as disconnected for block driver. */ mmcsd_slot_pm_suspend(CONFIG_BOARD_MMCSDSLOTNO); } /* Force SDcard off. */ stm32_gpiowrite(GPIO_REGULATOR_SDCARD, false); /* SDcard is off, and does not react on GPIOs. Force GPIOs low to avoid leak * current. */ gpio_off_mask = ~(GPIO_PUPD_MASK | GPIO_MODE_MASK | GPIO_OUTPUT_SET); stm32_configgpio((GPIO_SPI3_MOSI & gpio_off_mask) | GPIO_OUTPUT_CLEAR | GPIO_OUTPUT); stm32_configgpio((GPIO_SPI3_MISO & gpio_off_mask) | GPIO_OUTPUT_CLEAR | GPIO_OUTPUT); stm32_configgpio((GPIO_SPI3_SCK & gpio_off_mask) | GPIO_OUTPUT_CLEAR | GPIO_OUTPUT); stm32_configgpio((GPIO_CHIP_SELECT_SDCARD & gpio_off_mask) | GPIO_OUTPUT_CLEAR | GPIO_OUTPUT); /* Reconfigure GPIO's for stop mode (most pins are setup as analog input). */ up_reconfigure_gpios_for_pmstop(); #endif #if defined(CONFIG_USBDEV) && defined (CONFIG_STM32_USB) /* Reset USB peripheral */ regval = getreg32(STM32_RCC_APB1RSTR); putreg32(regval | RCC_APB1RSTR_USBRST, STM32_RCC_APB1RSTR); putreg32(regval, STM32_RCC_APB1RSTR); #endif #if defined(CONFIG_STM32_PWR) /* Disable PVD during sleep because I_DD (PVD/BOR) consumes 2.6 uA, and because there is nothing better to do than die while sleeping if lose power in stop-mode. */ stm32_pwr_disablepvd(); #endif #ifndef CONFIG_BOARD_DEEPSLEEP_SKIP_PMSTOP /* Enter stop-mode with MCU internal regulator in low-power mode. */ ret = stm32_pmstop(true); #endif #ifdef DEEPSLEEP_SLEEPTIME_DEBUG slept = true; #endif #if defined(CONFIG_STM32_PWR) /* Re-enable PVD and check if voltage has dropped too much while we slept. We might have enough power for MCU, but not for SD card or other peripherals. */ stm32_pwr_enablepvd(); board_pwr_checkpvd(); #endif #ifdef CONFIG_RTC_PERIODIC_AUTORELOAD_WAKEUP if (secs > 0) { /* Prevent more wake-ups. */ (void)up_rtc_cancelperiodicwakeup(); } #endif #if defined(CONFIG_RTC) && defined(CONFIG_RTC_DATETIME) /* Get RTC clock after STOP. */ stm32_rtc_getdatetime_with_subseconds(&t, &rtc_end_ts.tv_nsec); rtc_end_ts.tv_sec = mktime(&t); /* Advance time by RTC. */ ts.tv_sec += rtc_end_ts.tv_sec - rtc_start_ts.tv_sec; ts.tv_nsec += rtc_end_ts.tv_nsec - rtc_start_ts.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_nsec += 1000 * 1000 * 1000; ts.tv_sec--; } if (ts.tv_nsec >= 1000 * 1000 * 1000) { ts.tv_nsec -= 1000 * 1000 * 1000; ts.tv_sec++; } /* Set current time / date */ ret = clock_settime(CLOCK_REALTIME, &ts); DEBUGASSERT(ret != ERROR); #ifdef DEEPSLEEP_WAKETIME_DEBUG wake_ts = ts; #endif #if defined(CONFIG_CLOCK_MONOTONIC) && defined(CONFIG_CLOCK_MONOTONIC_OFFSET_TIME) /* Get amount of time adjusted by RTC. */ ts.tv_sec = rtc_end_ts.tv_sec - rtc_start_ts.tv_sec; ts.tv_nsec = rtc_end_ts.tv_nsec - rtc_start_ts.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_nsec += 1000 * 1000 * 1000; ts.tv_sec--; } if (ts.tv_nsec >= 1000 * 1000 * 1000) { ts.tv_nsec -= 1000 * 1000 * 1000; ts.tv_sec++; } /* Adjust CLOCK_MONOTONIC offset. */ clock_increase_monotonic_offset(&ts); #endif #endif /* Stop mode disables HSE/HSI/PLL and wake happens with default system * clock (MSI, 2Mhz). So reconfigure clocks early on. RTC is however * handled before this for accurate time keeping. */ stm32_clockconfig(); #ifdef CONFIG_BOARD_DEEPSLEEP_RECONFIGURE_GPIOS /* Restore pin configuration */ up_restore_gpios_after_pmstop(); #endif #if defined(CONFIG_USBDEV) && defined (CONFIG_STM32_USB) /* Initialize USB */ /* TODO initialize USB properly, up_usbinitialize() call alone is not sufficient */ up_usbinitialize(); #endif /* Resume serial ports after setting up system clock, as USART baud rate is * configured relative to system clock. */ stm32_serial_set_suspend(false); /* Check that we have enough battery left, just in case PVD detection missed it. */ board_pwr_checkvbat(false); #ifdef CONFIG_BOARD_DEEPSLEEP_RECONFIGURE_GPIOS /* Special support for SD card. */ /* Restore SDcard GPIOs */ stm32_configgpio(GPIO_SPI3_MOSI); stm32_configgpio(GPIO_SPI3_MISO); stm32_configgpio(GPIO_SPI3_SCK); stm32_configgpio(GPIO_CHIP_SELECT_SDCARD); if (sdcard_enabled) { /* Re-enable power now that GPIOs are in correct state. */ stm32_gpiowrite(GPIO_REGULATOR_SDCARD, true); /* Mark SDcard as reconnected for block driver, will reinitialize/reconfigure * SDcard. Apparently, block driver works fine without additional delay * for regulator. Might be because plug-in nature of SDcards require * block driver to keep retrying initialization until card stabilizes. */ mmcsd_slot_pm_resume(CONFIG_BOARD_MMCSDSLOTNO); } skip_deepsleep: #endif /* Allow scheduler to switch tasks. */ sched_unlock(); #ifdef DEEPSLEEP_SLEEPTIME_DEBUG if (slept) { /* Get amount of time adjusted by RTC. */ ts.tv_sec = rtc_end_ts.tv_sec - rtc_start_ts.tv_sec; ts.tv_nsec = rtc_end_ts.tv_nsec - rtc_start_ts.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_nsec += 1000 * 1000 * 1000; ts.tv_sec--; } if (ts.tv_nsec >= 1000 * 1000 * 1000) { ts.tv_nsec -= 1000 * 1000 * 1000; ts.tv_sec++; } lldbg("slept for %u.%03u\n", ts.tv_sec, ts.tv_nsec / (1000 * 1000)); } #endif }