static void stm32_idlepm(void) { static enum pm_state_e oldstate = PM_NORMAL; enum pm_state_e newstate; int ret; /* The following is logic that is done after the wake-up from PM_STANDBY * state. It decides whether to go back to the PM_NORMAL or to the deeper * power-saving mode PM_SLEEP: If the alarm expired with no "normal" * wake-up event, then PM_SLEEP is entered. * * Logically, this code belongs at the end of the PM_STANDBY case below, * does not work in the position for some unkown reason. */ if (oldstate == PM_STANDBY) { /* Were we awakened by the alarm? */ #ifdef CONFIG_RTC_ALARM if (g_alarmwakeup) { /* Yes.. Go to SLEEP mode */ newstate = PM_SLEEP; } else #endif { /* Resume normal operation */ newstate = PM_NORMAL; } } else { /* Let the PM system decide, which power saving level can be obtained */ newstate = pm_checkstate(); } /* Check for state changes */ if (newstate != oldstate) { llvdbg("newstate= %d oldstate=%d\n", newstate, oldstate); sched_lock(); /* Force the global state change */ ret = pm_changestate(newstate); if (ret < 0) { /* The new state change failed, revert to the preceding state */ (void)pm_changestate(oldstate); /* No state change... */ goto errout; } /* Then perform board-specific, state-dependent logic here */ switch (newstate) { case PM_NORMAL: { /* If we just awakened from PM_STANDBY mode, then reconfigure * clocking. */ if (oldstate == PM_STANDBY) { /* Re-enable clocking */ stm32_clockenable(); /* The system timer was disabled while in PM_STANDBY or * PM_SLEEP modes. But the RTC has still be running: Reset * the system time the current RTC time. */ #ifdef CONFIG_RTC clock_synchronize(); #endif } } break; case PM_IDLE: { } break; case PM_STANDBY: { /* Set the alarm as an EXTI Line */ #ifdef CONFIG_RTC_ALARM stm32_rtc_alarm(CONFIG_PM_ALARM_SEC, CONFIG_PM_ALARM_NSEC, true); #endif /* Wait 10ms */ up_mdelay(10); /* Enter the STM32 stop mode */ (void)stm32_pmstop(false); /* We have been re-awakened by some even: A button press? * An alarm? Cancel any pending alarm and resume the normal * operation. */ #ifdef CONFIG_RTC_ALARM stm32_exti_cancel(); ret = stm32_rtc_cancelalarm(); if (ret < 0) { lldbg("Warning: Cancel alarm failed\n"); } #endif /* Note: See the additional PM_STANDBY related logic at the * beginning of this function. That logic is executed after * this point. */ } break; case PM_SLEEP: { /* We should not return from standby mode. The only way out * of standby is via the reset path. */ /* Configure the RTC alarm to Auto Reset the system */ #ifdef CONFIG_PM_SLEEP_WAKEUP stm32_rtc_alarm(CONFIG_PM_SLEEP_WAKEUP_SEC, CONFIG_PM_SLEEP_WAKEUP_NSEC, false); #endif /* Wait 10ms */ up_mdelay(10); /* Enter the STM32 standby mode */ (void)stm32_pmstandby(); } break; default: break; } /* Save the new state */ oldstate = newstate; errout: sched_unlock(); } }
static void up_idlepm(void) { #ifdef CONFIG_RTC_ALARM struct timespec alarmtime; #endif static enum pm_state_e oldstate = PM_NORMAL; enum pm_state_e newstate; irqstate_t flags; int ret; /* Decide, which power saving level can be obtained */ newstate = pm_checkstate(); /* Check for state changes */ if (newstate != oldstate) { lldbg("newstate= %d oldstate=%d\n", newstate, oldstate); flags = irqsave(); /* Force the global state change */ ret = pm_changestate(newstate); if (ret < 0) { /* The new state change failed, revert to the preceding state */ (void)pm_changestate(oldstate); /* No state change... */ goto errout; } /* Then perform board-specific, state-dependent logic here */ switch (newstate) { case PM_NORMAL: { } break; case PM_IDLE: { } break; case PM_STANDBY: { #ifdef CONFIG_RTC_ALARM /* Disable RTC Alarm interrupt */ #warning "missing logic" /* Configure the RTC alarm to Auto Wake the system */ #warning "missing logic" /* The tv_nsec value must not exceed 1,000,000,000. That * would be an invalid time. */ #warning "missing logic" /* Set the alarm */ #warning "missing logic" #endif /* Call the STM32 stop mode */ stm32_pmstop(true); /* We have been re-awakened by some even: A button press? * An alarm? Cancel any pending alarm and resume the normal * operation. */ #ifdef CONFIG_RTC_ALARM #warning "missing logic" #endif /* Resume normal operation */ pm_changestate(PM_NORMAL); newstate = PM_NORMAL; } break; case PM_SLEEP: { /* We should not return from standby mode. The only way out * of standby is via the reset path. */ (void)stm32_pmstandby(); } break; default: break; } /* Save the new state */ oldstate = newstate; errout: irqrestore(flags); } }
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 }
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'); } }
static void up_idlepm(void) { static enum pm_state_e oldstate = PM_NORMAL; enum pm_state_e newstate; irqstate_t flags; int ret; /* Decide, which power saving level can be obtained */ newstate = pm_checkstate(); /* Check for state changes */ if (newstate != oldstate) { flags = irqsave(); /* Perform board-specific, state-dependent logic here */ llvdbg("newstate= %d oldstate=%d\n", newstate, oldstate); /* Then force the global state change */ ret = pm_changestate(newstate); if (ret < 0) { /* The new state change failed, revert to the preceding state */ (void)pm_changestate(oldstate); } else { /* Save the new state */ oldstate = newstate; } /* MCU-specific power management logic */ switch (newstate) { case PM_NORMAL: break; case PM_IDLE: break; case PM_STANDBY: stm32_pmstop(true); break; case PM_SLEEP: (void)stm32_pmstandby(); break; default: break; } irqrestore(flags); } }