chen
2024-11-08 cc432b761c884a0bd8e9d83db0a4e26109fc08b1
keil/include/drivers/mk_power.c
对比新文件
@@ -0,0 +1,736 @@
/*
 * Copyright (c) 2019-2023 Beijing Hanwei Innovation Technology Ltd. Co. and
 * its subsidiaries and affiliates (collectly called MKSEMI).
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into an MKSEMI
 *    integrated circuit in a product or a software update for such product,
 *    must reproduce the above copyright notice, this list of conditions and
 *    the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of MKSEMI nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    MKSEMI integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be
 *    reverse engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY MKSEMI "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL MKSEMI OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "mk_power.h"
#include "mk_trace.h"
#include "mk_clock.h"
#include "mk_io.h"
#include "mk_flash.h"
#include "mk_uart.h"
#include "mk_gpio.h"
#include "mk_reset.h"
#include "mk_misc.h"
#ifdef WSF_EN
#include "wsf_timer.h"
#endif
#ifdef UWB_EN
#include "mk_uwb.h"
#include "mk_sleep_timer.h"
#endif
/* UCI SPI full-duplex handshake interface */
#ifndef UCI_INTF_SPI_FD_HS
#define UCI_INTF_SPI_FD_HS (0)
#endif
/* UCI SPI half-duplex handshake interface */
#ifndef UCI_INTF_SPI_HD_HS
#define UCI_INTF_SPI_HD_HS (0)
#endif
#if (UCI_INTF_SPI_FD_HS || UCI_INTF_SPI_HD_HS)
#include "mk_spi.h"
#endif
#ifndef I2C_CHECK_EN
#define I2C_CHECK_EN (0)
#endif
#if I2C_CHECK_EN
#include "mk_i2c.h"
#endif
#ifndef LOW_POWER_CLOCK_PPM
#define LOW_POWER_CLOCK_PPM (50)
#endif
#ifndef SYS_CLK_SOURCE
#define SYS_CLK_SOURCE CLOCK_62P4M_XTAL38P4M_TO_SYS_CLK
#endif
// Including 38.4MHz clock PLL ready time and application recovery time, unit: us
#define RECOVERY_TIME_FROM_LOW_POWER (1000)
// Considering 32KHz clock ppm and sleep time, x = sleep time (us), the result should < PHY_SLEEP_TIME_US_MIN
#define WAKEUP_IN_ADVANCE_TIME(x) (RECOVERY_TIME_FROM_LOW_POWER + (x)*LOW_POWER_CLOCK_PPM / 1000000)
#ifdef UWB_EN
extern uint32_t slp_cnt;
uint32_t slp_cnt = 0;
#endif
extern void board_prepare_for_power_down(void);
extern void board_restore_from_power_down(void);
extern void app_restore_from_power_down(void);
extern void power_fem_tx_ctrl(uint8_t on_off);
extern void power_fem_rx_ctrl(uint8_t on_off);
// on_off: 1 - ON, 0 - OFF
__WEAK void power_fem_tx_ctrl(uint8_t on_off)
{
}
// on_off: 1 - ON, 0 - OFF
__WEAK void power_fem_rx_ctrl(uint8_t on_off)
{
}
static struct POWER_REQUEST_T power_env = {
    .power_request[POWER_MODE_ACTIVE] = 0,
    .power_request[POWER_MODE_SLEEP] = 0,
    .power_request[POWER_MODE_POWER_DOWN] = 0,
    .power_request[POWER_MODE_DEEP_POWER_DOWN] = 0,
    .power_request[POWER_MODE_SHELF] = 0,
};
void power_init(void)
{
    if (SYSCON->SYS_CMU & SYSCON_SYS_CMU_32K_CLK_SEL_MSK)
    {
        // enable PMU, disable rco 32k
        SYSCON->PMU_CTRL0 |= (1U << 31) | (1 << 5);
    }
    else
    {
        // enable PMU, disable Xtal32k
        SYSCON->PMU_CTRL0 |= (1U << 31) | (1 << 7);
    }
    // DG REF from LP_BG
    SYSCON->REG_DIG &= ~(1U << 3);
    // fix high temperature wakeup fail issue
    SYSCON->IVREF_ULP = 0x20000;
#if DCDC_EN
    // DC-DC enable
    SYSCON->SYS_CTRL &= ~(3U << 18);
#endif
    bor_close();
}
void power_on_radio(uint8_t tx_en, uint8_t rx_en)
{
    // board_led_on(BOARD_LED_1);
    uint32_t val;
    // HW gate clock
#if 0
    // Clock on - TX | RX
    val = SYSCON->SYS_CMU;
    val = tx_en ? (val | (1U << CLOCK_TX)) : val;
    val = rx_en ? (val | (1U << CLOCK_RX)) : val;
    SYSCON->SYS_CMU = val;
#endif
    val = REG_READ(0x40000400);
    // Radio on
    REG_WRITE(0x40000400, val | 0x30000000);
    power_mode_request(POWER_UNIT_RF, POWER_MODE_SLEEP);
    if (tx_en)
    {
        power_fem_tx_ctrl(1);
    }
    if (rx_en)
    {
        power_fem_rx_ctrl(1);
    }
    // board_led_off(BOARD_LED_1);
    // LOG_INFO(TRACE_MODULE_DRIVER, "power on radio %x\r\n", SYSCON->SYS_CMU);
}
void power_off_radio(void)
{
    // board_led_on(BOARD_LED_2);
    // Radio off
    uint32_t val = REG_READ(0x40000400);
    val &= ~0x30000000U;
    REG_WRITE(0x40000400, val | 0x20000000);
#if 0
    // Clock off - TX | RX
    SYSCON->SYS_CMU &= ~((1U << CLOCK_RX) | (1U << CLOCK_TX));
#endif
    power_fem_tx_ctrl(0);
    power_fem_rx_ctrl(0);
    power_mode_clear(POWER_UNIT_RF);
    // board_led_off(BOARD_LED_2);
    // LOG_INFO(TRACE_MODULE_DRIVER, "power off radio\r\n");
}
void power_enter_sleep_mode(void)
{
    // board_led_off(BOARD_LED_1);
    //
    // WFI SLEEP
    //
    // LOG_INFO(TRACE_MODULE_DRIVER, "WFI SLEEP\r\n");
    // Ensure we SLEEP - SLEEPDEEP should be clear
    // SCR[2] = 0
    SCB->SCR &= ~(1UL << 2);
    // Wait For Interrupt
    __WFI();
    // board_led_on(BOARD_LED_1);
}
#ifdef SWD_WAKEUP_EN
static void power_swd_wakeup_en(void)
{
    // if ((REG_READ(0xE000EDF0) & 0x1) == 0)
    {
        /* Switch function of IO_16 from SWDIO to GPIO, the debugger can wakeup chip */
        gpio_pin_set_dir(IO_PIN_16, GPIO_DIR_IN, 0);
        io_pull_set(IO_PIN_16, IO_PULL_UP, IO_PULL_UP_LEVEL0);
        io_pin_mux_set(IO_PIN_16, IO_FUNC1);
        /* Read IO_16 and set the opposite level as wakeup level */
        power_wakeup_enable(POWER_WAKEUP_BY_GPIO_16, (GPIO->DATA & (1 << IO_PIN_16)) ? POWER_WAKEUP_LEVEL_LOW : POWER_WAKEUP_LEVEL_HIGH);
        /* disable swd */
    }
}
static void power_swd_restore(void)
{
    // if ((REG_READ(0xE000EDF0) & 0x1) == 0)
    {
        /* Disable IO_16 wakeup */
        power_wakeup_disable(POWER_WAKEUP_BY_GPIO_16);
        /* Restore IO_16 as SWDIO */
        io_pin_mux_set(IO_PIN_16, IO_FUNC0);
        /* re-enable swd */
    }
}
#endif
void RAM_FUNC enter_power_down_in_ram(void);
void enter_power_down_in_ram(void)
{
    uint32_t wakeup_en = 0;
    uint32_t wakeup_lvl = 0;
    SYSCON->PMU_CTRL1 &= ~(1U << 16);
    // (REFPLL | RXLDO | TXLDO | DREG)  -  OFF
    SYSCON->PMU_CTRL0 |= (1U << 10) | (1U << 11) | (1U << 12) | 0x1;
    /* Enable IO wakeup */
    NVIC_ClearPendingIRQ(WAKEUP_IRQn);
    NVIC_EnableIRQ(WAKEUP_IRQn);
    // flash power down  - by flash_close()
    flash_power_down(FLASH_ID0);
    uint8_t val = REG_READ_BYTE(EFUSE_SHADOW_BASE + 0x67);
    if ((val & 0x80) == 0)
    {
        // switch to external flash io
        REG_WRITE_BYTE(EFUSE_SHADOW_BASE + 0x67, val | 0x80);
    }
    wakeup_en = SYSCON->WAKEUP_EN & SYSCON_WAKEUP_EN_IO_MSK;
    wakeup_lvl = SYSCON->WAKEUP_POL;
#ifdef SWD_WAKEUP_EN
    power_swd_wakeup_en();
#endif
    // Latch IO
    SYSCON->IO_SLP_OUT = GPIO->DATAOUT;
    SYSCON->IO_SLP_OE = GPIO->OUTENSET;
    SYSCON->IO_SLP_EI = SYSCON->IO_EI;
    SYSCON->IO_SLP_PDN = SYSCON->IO_PDN;
    SYSCON->IO_SLP_PUP[0] = SYSCON->IO_PUP[0];
    SYSCON->IO_SLP_PUP[1] = SYSCON->IO_PUP[1];
    SYSCON->IO_SLP_PUP[2] = SYSCON->IO_PUP[2];
    // reduce DG current 60%
    SYSCON->CAP_DIV_CFG = 0x0;
    // Clock off - CALIB (retention)
    SYSCON->SYS_CMU &= ~(1U << CLOCK_CALIB);
    SYSCON->PMU_CTRL1 &= ~(1U << 17);
    // Wait For Interrupt
    __WFI();
    SYSCON->PMU_CTRL1 |= (1U << 17);
    // Clock on - CALIB
    SYSCON->SYS_CMU |= (1U << CLOCK_CALIB);
    if ((val & 0x80) == 0)
    {
        // switch to internal flash io
        REG_WRITE_BYTE(EFUSE_SHADOW_BASE + 0x67, val & 0x7f);
    }
    SYSCON->CAP_DIV_CFG = 0x7;
    // flash power up
    flash_power_up(FLASH_ID0);
#ifdef XIP_EN
    flash_open_for_xip(FLASH_ID0);
#endif
    NVIC_DisableIRQ(WAKEUP_IRQn);
    NVIC_ClearPendingIRQ(WAKEUP_IRQn);
    // Restore IO
    GPIO->DATAOUT = SYSCON->IO_SLP_OUT;
    GPIO->OUTENSET = SYSCON->IO_SLP_OE;
#ifdef SWD_WAKEUP_EN
    // power_swd_restore();
#endif
    if (wakeup_en)
    {
        GPIO->INTTYPECLR = wakeup_en;
        GPIO->INTPOLCLR = wakeup_en & wakeup_lvl;
        GPIO->INTPOLSET = wakeup_en & ~wakeup_lvl;
        GPIO->INTENSET = wakeup_en;
    }
#if DCDC_EN
    // BUCK ready   <50us
    while ((SYSCON->CLK_STATUS & 0x20) == 0)
    {
    }
    delay_us(10);
#endif
    // RXLDO - ON
    SYSCON->PMU_CTRL0 &= ~(1U << 11);
    delay_us(10);
    // TXLDO - ON
    SYSCON->PMU_CTRL0 &= ~(1U << 12);
    delay_us(10);
    // DREG - ON
    SYSCON->PMU_CTRL0 &= ~(0x1U);
    // DBB ready   <50us
    while ((SYSCON->SYSTEM_STATUS & (1U << 20)) == 0)
    {
    }
    SYSCON->PMU_CTRL1 |= (1U << 16);
}
void power_enter_power_down_mode(bool deep_en)
{
    //
    // WFI SLEEPDEEP
    //
    // LOG_INFO(TRACE_MODULE_DRIVER, "WFI SLEEPDEEP\r\n");
    board_prepare_for_power_down();
#ifdef UWB_EN
    // TODO: store uwb confiuguration
    struct KEY_T mac_ccm_key;
    mac_ccm_key.KEY_W0 = REG_READ(0x5000A080);
    mac_ccm_key.KEY_W1 = REG_READ(0x5000A084);
    mac_ccm_key.KEY_W2 = REG_READ(0x5000A088);
    mac_ccm_key.KEY_W3 = REG_READ(0x5000A08C);
    phy_timer_pause();
#endif
#if SYS_TICK_EN
    sys_tick_pause();
#endif
    // Ensure we SLEEPDEEP - SLEEPDEEP should be set
    // SCR[2] = 1
    SCB->SCR |= (1U << 2);
    if (deep_en)
    {
        // power off 32K in deep power down
        SYSCON->PMU_CTRL1 |= (1U << 7) | (1U << 5);
    }
    else
    {
        // keep 32k power on in power down mode
        SYSCON->PMU_CTRL1 &= ~((1U << 7) | (1U << 5));
    }
    // switch system clock from XTAL to RO
    clock_attach(CLOCK_48M_RO_TO_SYS_CLK);
    // for XIP
    enter_power_down_in_ram();
#ifdef UWB_EN
    // TODO: restore uwb configuration
    phy_restore(NULL);
    mac_restart();
    mac_update_ccm_key((uint32_t *)&mac_ccm_key);
#endif
    // crystal ready  >400us
    while ((SYSCON->CLK_STATUS & 0x1) == 0)
    {
    }
    // REFPLL - ON
    SYSCON->PMU_CTRL0 &= ~(1U << 10);
    // switch system clock from RO to XTAL
    clock_attach(SYS_CLK_SOURCE);
#ifdef UWB_EN
    slp_cnt = phy_timer_resume();
#endif
#if SYS_TICK_EN
    sys_tick_resume();
#endif
    board_restore_from_power_down();
}
void RAM_FUNC enter_shelf_mode_in_ram(uint8_t key, uint32_t cmd);
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100)
#pragma clang optimize off
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC optimize("O0")
#else
#pragma optimize = none
#endif
void enter_shelf_mode_in_ram(uint8_t key, uint32_t cmd)
{
    // flash power down  - by flash_close()
    flash_power_down(FLASH_ID0);
    SYSCON->SHELF_KEY = key;
    SYSCON->PMU_CTRL1 = cmd;
}
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100)
#pragma clang optimize on
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
void power_enter_shelf_mode(void)
{
    board_prepare_for_power_down();
    enter_shelf_mode_in_ram(0xD5, 0x80031FCB);
}
void power_mode_request(enum POWER_REQ_UNIT_T item, enum POWER_MODE_T mode)
{
    uint32_t lock = int_lock();
    power_env.power_request[POWER_MODE_ACTIVE] &= ~item;
    power_env.power_request[POWER_MODE_SLEEP] &= ~item;
    power_env.power_request[POWER_MODE_POWER_DOWN] &= ~item;
    power_env.power_request[POWER_MODE_DEEP_POWER_DOWN] &= ~item;
    power_env.power_request[POWER_MODE_SHELF] &= ~item;
    power_env.power_request[mode] |= item;
    int_unlock(lock);
}
void power_mode_clear(enum POWER_REQ_UNIT_T item)
{
    uint32_t lock = int_lock();
    power_env.power_request[POWER_MODE_ACTIVE] &= ~item;
    power_env.power_request[POWER_MODE_SLEEP] &= ~item;
    power_env.power_request[POWER_MODE_POWER_DOWN] &= ~item;
    power_env.power_request[POWER_MODE_DEEP_POWER_DOWN] &= ~item;
    power_env.power_request[POWER_MODE_SHELF] &= ~item;
    int_unlock(lock);
}
uint32_t power_mode_requester_get(enum POWER_MODE_T mode)
{
    return power_env.power_request[mode];
}
void power_wakeup_enable(enum POWER_WAKEUP_SOURCE_T src, enum POWER_WAKEUP_POLARITY_T pol)
{
    if (pol == POWER_WAKEUP_LEVEL_HIGH)
    {
        // high wakeup
        SYSCON->WAKEUP_POL &= ~(1 << src);
    }
    else if (pol == POWER_WAKEUP_LEVEL_LOW)
    {
        // low wakeup
        SYSCON->WAKEUP_POL |= (1 << src);
    }
    SYSCON->WAKEUP_EN |= (1 << src) | SYSCON_WAKEUP_EN_MSK;
}
void power_wakeup_disable(enum POWER_WAKEUP_SOURCE_T src)
{
    SYSCON->WAKEUP_EN &= ~(1 << src);
    if (SYSCON->WAKEUP_EN == SYSCON_WAKEUP_EN_MSK)
    {
        SYSCON->WAKEUP_EN = 0;
    }
}
static void power_check_io_power_mode(void)
{
    if (power_env.power_request[POWER_MODE_ACTIVE] & ~(uint32_t)(POWER_UNIT_GPIO))
    {
        // stay active
        return;
    }
    if (power_env.power_request[POWER_MODE_SLEEP])
    {
        // clear gpio active request
        power_mode_clear(POWER_UNIT_GPIO);
        return;
    }
    // check GPIO clock
    if ((SYSCON->SYS_CMU & (1 << CLOCK_GPIO)) == 0)
    {
        // clear gpio active request
        power_mode_clear(POWER_UNIT_GPIO);
        return;
    }
    uint32_t wake_en = SYSCON->WAKEUP_EN & SYSCON_WAKEUP_EN_IO_MSK;
    uint32_t wakeup_level = SYSCON->WAKEUP_POL;
    uint32_t real_level = GPIO->DATA;
    if ((wakeup_level ^ real_level) & wake_en)
    {
        // IO wakeup source exist - stay active
        power_mode_request(POWER_UNIT_GPIO, POWER_MODE_ACTIVE);
    }
    else
    {
        // clear gpio active request
        power_mode_clear(POWER_UNIT_GPIO);
    }
}
static void power_check_if_power_mode(void)
{
    // check UART power mode
    if ((uart_state_get(UART_ID0) & UART_STATE_BUSY_TX_RX) || (uart_state_get(UART_ID1) & UART_STATE_BUSY_TX_RX))
    {
        power_mode_request(POWER_UNIT_UART, POWER_MODE_SLEEP);
    }
    else if (uart_fifo_busy(UART_ID0) || uart_fifo_busy(UART_ID1))
    {
        power_mode_request(POWER_UNIT_UART, POWER_MODE_ACTIVE);
    }
#if I2C_CHECK_EN
    // check I2C power mode
    if (i2c_state_get(I2C_ID0) & I2C_STATE_BUSY_TX_RX)
    {
        power_mode_request(POWER_UNIT_I2C, POWER_MODE_SLEEP);
    }
#endif
}
static void power_clear_if_power_mode(void)
{
    power_mode_clear(POWER_UNIT_UART);
#if (UCI_INTF_SPI_FD_HS || UCI_INTF_SPI_HD_HS)
    power_mode_clear(POWER_UNIT_SPI);
#endif
#if I2C_CHECK_EN
    power_mode_clear(POWER_UNIT_I2C);
#endif
}
#ifdef UWB_EN
static void power_check_uwb_power_mode(void)
{
    enum POWER_MODE_T pm;
    // check os timer
    uint32_t os_time_ms = PHY_SLEEP_TIME_MS_MAX;
    uint32_t phy_time_us = PHY_SLEEP_TIME_MS_MAX * 1000;
#ifdef WSF_EN
    if (wsfOsReadyToSleep())
    {
        pm = WsfTimerSleepCheck(&os_time_ms);
        if (pm)
        {
#endif
            uint8_t busy = mac_is_busy();
            if (busy)
            {
                pm = POWER_MODE_SLEEP;
            }
            else
            {
                if (phy_timer_is_programmed())
                {
                    // check phy timer
                    uint32_t phy_time_tick = phy_timer_count_left();
                    phy_time_us = PHY_TIMER_COUNT_TO_US(phy_time_tick);
                }
                // sleep time
                uint32_t sleep_time_us = MIN((uint32_t)(os_time_ms * 1000), (uint32_t)phy_time_us);
                if (sleep_time_us > PHY_SLEEP_TIME_US_MIN)
                {
                    pm = POWER_MODE_POWER_DOWN;
                    // make sure wakeup in advance, need to correct by 32K ppm
                    sleep_time_us -= WAKEUP_IN_ADVANCE_TIME(sleep_time_us);
                    uint32_t sleep_time_tick = __US_TO_32K_CNT(sleep_time_us);
                    sleep_timer_start(sleep_time_tick);
                }
                else
                {
                    pm = POWER_MODE_SLEEP;
                }
            }
#ifdef WSF_EN
        }
    }
    else
    {
        pm = POWER_MODE_ACTIVE;
    }
#endif
    power_mode_request(POWER_UNIT_UWB, pm);
}
#endif
void power_manage(void)
{
    uint32_t lock = int_lock();
    uint8_t wakeup_from_power_down = 0;
    // check GPIO wakeup source
    power_check_io_power_mode();
    if (power_env.power_request[POWER_MODE_ACTIVE])
    {
        // stay in active mode
    }
    else
    {
#ifdef UWB_EN
        power_check_uwb_power_mode();
#endif
        power_check_if_power_mode();
        if (power_env.power_request[POWER_MODE_ACTIVE])
        {
            // stay in active mode
        }
        else if (power_env.power_request[POWER_MODE_SLEEP])
        {
            // enter sleep mode
            power_enter_sleep_mode();
        }
        else if (power_env.power_request[POWER_MODE_POWER_DOWN])
        {
            // enter power down mode
            // board_led_on(BOARD_LED_1);
            power_enter_power_down_mode(0);
            // board_led_off(BOARD_LED_1);
            wakeup_from_power_down = 1;
        }
        else if (power_env.power_request[POWER_MODE_DEEP_POWER_DOWN])
        {
            // enter deep power down mode
            // board_led_on(BOARD_LED_2);
            power_enter_power_down_mode(1);
            // board_led_off(BOARD_LED_2);
            wakeup_from_power_down = 1;
        }
        else
        {
            power_enter_shelf_mode();
        }
        power_clear_if_power_mode();
#ifdef UWB_EN
        power_mode_clear(POWER_UNIT_UWB);
#endif
    }
    // uint32_t int_pending = REG_READ(0xE000E200);
    int_unlock(lock);
    if (wakeup_from_power_down)
    {
#ifdef UWB_EN
        LOG_INFO(TRACE_MODULE_DRIVER, "Wakeup from power down %u\r\n", slp_cnt);
#else
        LOG_INFO(TRACE_MODULE_DRIVER, "Wakeup from power down\r\n");
#endif
        // TODO: restore app configuration if needed
        app_restore_from_power_down();
    }
}
__WEAK void board_prepare_for_power_down(void)
{
}
__WEAK void board_restore_from_power_down(void)
{
}
__WEAK void app_restore_from_power_down(void)
{
}