/**
  *****************************************************************************
  * @file    sys_low_power.c
  * @author  Solution Team
  * @version V1.00
  * @date    28-February-2023
  * @brief   System low power implementation
  *****************************************************************************
  * @attention 
  *  Copyright (c) 2017 - 2023 Unicmicro Co., Ltd. 
  *  All Rights Reserved. 
  *****************************************************************************
  */

#include "arch.h"



#include "libc.h"
#include "drivers.h"
#include "sys_low_power.h"
#include "sys_config.h"
#include "sys_defs.h"

#if LOW_POWER_ENABLED

// Sleep auto wake up tick count, clock is 4MHz/250ns, configured value need to left shift 18 bits.

static volatile sys_tick_t s_sys_check_active_start = 0;
static volatile u32_t s_sys_idle_hold_time = SAL_SECOND_US * SYS_CONFIG_IDLE_HOLD_TIME;

static bool_t (*s_on_wakeup_handler)(void) = NULL;
static bool_t (*s_on_sleep_handler)(void) = NULL;

void sys_lpm_init(void)
{
    if ((drv_chip_wkup_flg()) && (s_on_wakeup_handler))
    {
        s_on_wakeup_handler();
    }
    s_sys_idle_hold_time = sys_cfg_idle_hold_time_get() * SAL_SECOND_US;
    s_sys_check_active_start = now();
}

void sys_lpm_activate(void)
{
    sync_enter_critical();
    s_sys_check_active_start = now();
    sync_exit_critical();
}

sys_tick_t sys_lpm_get(void)
{
    return s_sys_check_active_start ;
}

void sys_lpm_on_wakeup_handler_set(bool_t (*on_wakeup_handler)(void))
{
    sync_enter_critical();
    s_on_wakeup_handler = on_wakeup_handler;
    sync_exit_critical();
}

void sys_lpm_on_sleep_handler_set(bool_t (*on_sleep_handler)(void))
{
    sync_enter_critical();
    s_on_sleep_handler = on_sleep_handler;
    sync_exit_critical();
}

void sys_lpm_idle_hold_time_set(u32_t time)
{
    sync_enter_critical();
    s_sys_idle_hold_time = time * SAL_SECOND_US;
    sync_exit_critical();
}

void sys_state_check_and_sleep(void)
{
    sync_enter_critical();
    if (sys_cfg_sleep_mode_get())
    {
        if ((u32_t)(now() - s_sys_check_active_start) >= s_sys_idle_hold_time)
        {
            if (s_on_sleep_handler)
            {
                // if on sleep handler executes failure, exit sleep
                if (!s_on_sleep_handler())
                {
                    goto exit_sleep;
                }
            }

            if (sys_cfg_auto_wakeup_get())
            {
                drv_chip_auto_wkup_set(sys_cfg_auto_wakeup_get());
            }
#if CLI_ENABLED
            if (sys_cfg_dbg_get()) drv_uart_putstr("sleep ... ");
#endif /* CLI_ENABLED */
            drv_chip_pd_ctrl_set((PD_CTRL_REG_IGAIN_FLDSET|PD_CTRL_REG_PDISEN_FLDSET|PD_CTRL_REG_PD431_FLDSET|PD_CTRL_REG_PD10U_FLDSET|PD_CTRL_REG_PDSAR_FLDSET));

#ifdef SYS_WDT_ENABLED /* added by James Gan - 2019-08-14 */
#if SYS_WDT_ENABLED
            drv_wdt_init(0, FALSE);
#endif /* SYS_WDT_ENABLED */
#endif /* SYS_WDT_ENABLED */

            delay(200);
#if BOOT_LOADER_ENABLED
            DRV_REG_SET(PRG_ADDR_OFFSET_REG, APP_ADDR_OFFSET);
#endif
            drv_chip_sleep();
        }
    }
exit_sleep:
    sync_exit_critical();
}
#endif /* LOW_POWER_ENABLED */

