/*
 * @File name    : driver_lowpwr.c
 * @Author       : Bluetrum IOT Team
 * @Date         : 2023-02-15
 * @Version      : v2.0
 *
 * Copyright (c) by Bluetrum, All Rights Reserved.
 */

#include "driver_gpio.h"
#include "driver_saradc.h"
#include "driver_lowpwr.h"

extern void io_sleep(void);
extern void io_resume(void);


extern u32 __comm_vma;

static lowpwr_sfr_backup_t sfr_backup;
static uint32_t wkupedg_bak = 0;

AT(.com_sleep.lv2_get_wksrc)
uint32_t lowpwr_sleep_get_io_wksrc(void)
{
    return wkupedg_bak;
}

AT(.com_sleep.lv2_clr_wksrc)
void lowpwr_sleep_clr_io_wksrc(void)
{
    wkupedg_bak = 0;
}

u32 lowpwr_get_wakeup_source(void)
{
    u32 reason = RTCCON9;

    if(reason){
        if(reason & WK_LP_KEYSCAN){
            printf("KEY_SCAN_WK\n");
        }

        if(reason & WK_LP_GPIO){
            printf("GPIO_WK\n");
        }

        if(reason & WK_LP_TOUCH){
            printf("TOUCH_WK\n");
        }

        if(reason & WK_LP_INBOX){
            printf("INBOX_WK\n");
        }

        if(reason & WK_LP_VUSB){
            printf("VUSB_WK\n");
        }

        if(reason & WK_LP_WK0){
            printf("WKO_WK\n");
        }

        if(reason & WK_LP_RTC_1S){
            printf("RTC1S_WK\n");
        }

        if(reason & WK_LP_ALARM){
            printf("ALMWKP_WK\n");
        }

        RTCCON9 = 0x1fff;                                //Clr wakeup pending
        RTCCON10 = 0xffff;
    }

    return reason;

}

void lowpwr_wakeup_disable(void)
{
    RTCCON9 = 0x1fff;                           //Clr wakeup pending
    RTCCON10 = 0xffff;
    RTCCON3 &= 0xFF;

    WKUPCPND = 0xff << 16;                      //clear wakeup pending
    WKUPEDG = 0;
    WKUPCON = 0;
    PORTINTEDG = 0;
    PORTINTEN = 0;
}

void lowpwr_clr_wakeup_pending(void)
{
    WKUPCPND = 0xff << 16;                      //clear wakeup pending

    RTCCON9 = 0x1fff;                           //Clr wakeup pending
    RTCCON10 = 0xffff;
}

AT(.com_periph.lowpwr.sleep)
bool lowpwr_is_wakeup_pending(u32 ignore_src)
{
    u32 wk_pending = 0x1fff;

    if(ignore_src){
        wk_pending &= ~ignore_src;
    }

    return (RTCCON9 & wk_pending);
}

void lowpwr_wk0_wakeup_config(lowpwr_wakeup_typedef *config)
{
    if (config->lp_type == LP_TYPE_SLEEP) {
        wk0_edge_capture_config(config->edge);
        WKUPCON |= BIT(17);             //wakeup sleep mode enable
        RTCCON3 |= BIT(17);
    } else if(config->lp_type == LP_TYPE_POWOFF) {
        if (config->edge == WK_EDGE_FALLING) {
            wko_io_config(1, 0, 1);
            RTCCON1 &= ~BIT(6);         //wk pin low level wakeup
        } else {
            wko_io_config(0, 1, 1);
            RTCCON1 |= BIT(6);          //wk pin high level wakeup
        }
        RTCCON3 |= BIT(10);
    }
}

void lowpwr_gpio_wakeup_config(lowpwr_wakeup_typedef *config)
{
    if((config->gpiox == GPIOA_REG) || (config->gpiox == GPIOB_REG)){
        gpio_edge_cap_typedef gpio_cfg;

        gpio_cfg.edge = config->edge;
        gpio_cfg.gpio_pin = config->gpio_pin;
        gpio_cfg.gpiox = config->gpiox;
        gpio_cfg.gpio_pupd = config->gpio_pupd;
        gpio_edge_capture_config(&gpio_cfg);

        RTCCON3 |= BIT(17);
        WKUPCON |= BIT(17);                         //wakeup sleep mode enable
        WKUPCPND = 0xff << 16;                      //clear wakeup pending
    }
}

void lowpwr_wakeup_config_do(lowpwr_wakeup_typedef *config)
{
    if(config){
        if(!(config->source & WK_LP_TOUCH)){
            RTCCON0 &= ~BIT(4);     //disable touch key
        }

        if(config->source & WK_LP_WK0){
            lowpwr_wk0_wakeup_config(config);
        }

        if(config->source & WK_LP_TOUCH){
            RTCCON0 |= BIT(4);
            RTCCON1 |= BIT(8);
            RTCCON1 |= BIT(9);          //VDDTK enable for touch key logic
            RTCCON3 |= BIT(14);         // Touch key long press wakeup enable
        }

        if(config->source & WK_LP_BT){
            RTCCON3 |= BIT(13);
        }

        if(config->source & WK_LP_ALARM){
            RTCCON0 |= BIT(19);
            RTCCON3 |= BIT(8);
        }

        if(config->source & WK_LP_RTC_1S){
            RTCCON3 |= BIT(9);
        }

        if(config->source & WK_LP_VUSB){
            RTCCON3 |= BIT(11);
        }

        if(config->source & WK_LP_KEYSCAN){
            RTCCON3 |= BIT(16);
        }

        if(config->source & WK_LP_INBOX){
           RTCCON3 |= BIT(12);

        }

        if(config->source & WK_LP_GPIO){
            lowpwr_gpio_wakeup_config(config);
        }
    }
}

void lowpwr_wakeup_config(lowpwr_wakeup_typedef *config)
{
    if(config){
        RTCCON3 &= 0xFF;
        lowpwr_wakeup_config_do(config);
    }
}

void lowpwr_wakeup_config_2(lowpwr_wakeup_typedef *config)
{
    if(config){
        lowpwr_wakeup_config_do(config);
    }
}

void lowpwr_wakeup_source_enable(WK_LP_SRC_SEL wk_src)
{
    if(wk_src & WK_LP_WK0){
        RTCCON3 |= BIT(17);
    }

    if(wk_src & WK_LP_TOUCH){
        RTCCON3 |= BIT(14);
    }

    if(wk_src & WK_LP_BT){
        RTCCON3 |= BIT(13);
    }

    if(wk_src & WK_LP_ALARM){
        RTCCON3 |= BIT(8);
    }

    if(wk_src & WK_LP_RTC_1S){
        RTCCON3 |= BIT(9);
    }

    if(wk_src & WK_LP_VUSB){
        RTCCON3 |= BIT(11);
    }

    if(wk_src & WK_LP_KEYSCAN){
        RTCCON3 |= BIT(16);
    }

    if(wk_src & WK_LP_INBOX){
       RTCCON3 |= BIT(12);

    }

    if(wk_src & WK_LP_GPIO){
        RTCCON3 |= BIT(17);
    }
}

void lowpwr_wakeup_source_disable(WK_LP_SRC_SEL wk_src)
{
    if (wk_src == WK_LP_SRC_ALL) {
        RTCCON3 &= 0xFF;
        return;
    }

    if(wk_src & WK_LP_WK0){
        RTCCON3 &= ~BIT(17);
    }

    if(wk_src & WK_LP_TOUCH){
        RTCCON3 &= ~BIT(14);         // Touch key long press wakeup enable
    }

    if(wk_src & WK_LP_BT){
        RTCCON3 &= ~BIT(13);
    }

    if(wk_src & WK_LP_ALARM){
        RTCCON3 &= ~BIT(8);
    }

    if(wk_src & WK_LP_RTC_1S){
        RTCCON3 &= ~BIT(9);
    }

    if(wk_src & WK_LP_VUSB){
        RTCCON3 &= ~BIT(11);
    }

    if(wk_src & WK_LP_KEYSCAN){
        RTCCON3 &= ~BIT(16);
    }

    if(wk_src & WK_LP_INBOX){
       RTCCON3 &= ~BIT(12);

    }

    if(wk_src & WK_LP_GPIO){
        RTCCON3 &= ~BIT(17);
    }
}

ALIGNED(512)
void lowpwr_pwroff_mode1_do(void)
{
    u32 rtccon3;

    saradc_deinit();         //disable saradc

    PICCONCLR = BIT(0);     //disable global interrupt
    NMICON = 0;

    RTCCON3 &= ~BIT(17);    //disable gpio wakeup

    RTCCON9 = 0x1fff;       //Clr wakeup pending

    if(RTCCON3 & BIT(14)){                          //Touch need rc2m clk
        RTCCON0 |= BIT(19);                         //RC2M_RTC enable
        asm("nop");asm("nop");asm("nop");
        RTCCON0 = (RTCCON0 & ~(0x03 << 12)) | (0x02 << 12); //CLK2M in Touch select SNF_RC_RTC
    }else{
        RTCCON0  &= ~BIT(19);                       //RC2M_RTC disable
    }

    RTCCON4 &= ~BIT(17);    //FGS
    RTCCON4 &= ~BIT(18);    //FGBUF
    RTCCON4 &= ~BIT(15);    //VIO_PD not work

    RTCCON4 = (RTCCON4 & ~(0x3f<<24)) | (5<<24) | (6<<27);  //no sfr access, bit26~bit24, VRTC11 is 1.025V; sfr access, bit29~27, VRTC11 is 1.09V

    CLKCON0 &= ~0xc;        //sys_clk switch to RC
    asm("nop");
    asm("nop");
    asm("nop");

    XOSCCON = 0;
    PLL0CON = 0;

    rtccon3 = RTCCON3 & ~(0xFF);
    rtccon3 |= BIT(5) | BIT(6);      //core power down enable

    WDT_DIS();

    RTCCON |= BIT(5);       //RTC wake up power down mode reset system enable
    RTCCON3 = rtccon3;

    LPMCON |= BIT(0);	    //sleep mode
    LPMCON |= BIT(1);       //idle mode

    asm("nop");asm("nop");asm("nop");

    while(1);
}

//in pwroff_mode2_final, can't use subfunction
AT(.com_text.pwroff.mode2)
static void pwroff_mode2_final(void)
{
    u32 rtccon3 = RTCCON3;
    u32 rtccon0 = RTCCON0;

    RTCCON12 = (RTCCON12 & ~0xf) | 0xa | (0x3<<6) | (0x3<<4); //disable WKO reset, RTC WDT, GPIO reset

    RTCCON4 &= ~(BIT(17) | BIT(18));    //FGS, FGBUF

    if(RTCCON3 & (BIT(8) | BIT(14) | BIT(16))){     //wakeup source need rc2m clk
        RTCCON0 |= BIT(19);                         //RC2M_RTC enable
    }else{
        RTCCON0  &= ~BIT(19);                       //RC2M_RTC disable
    }

    PWRCON1 = (PWRCON1 & ~(0XF<<14)) | (0X0<<14); //DI_SW DISABLE

    RTCCON8 &= ~BIT(29);
    RTCCON4 |= BIT(11);     //VDDIO_AON voltage
    RTCCON3 |= BIT(7);     //enable vddio aon ldo

    RTCCON4 = (RTCCON4 & ~(0x3f<<24)) | (5<<24) | (6<<27);  //no sfr access, bit26~bit24, VRTC11 is 1.025V; sfr access, bit29~27, VRTC11 is 1.09V

    rtccon0 = RTCCON0;

    if(RTCCON3 & BIT(14)){
        rtccon0 = (rtccon0 & ~(0x03 << 12)) | (0x02 << 12); //CLK2M in Touch select SNF_RC_RTC
    }

    if(RTCCON3 & BIT(16)){
        rtccon0 = (rtccon0 & ~(0x03 << 14)) | (0x02 << 14); //CLK2M in Keyscan select SNF_RC_RTC
    }

    RTCCON0 = rtccon0;

    RTCCON5 &= ~(1<<12);   //CLEAR LP FLAG
    WKUPCPND = 0xFF<<16;

    CLKCON0 &= ~0xc;        //sys_clk switch to RC
    asm("nop");
    asm("nop");
    asm("nop");

    XOSCCON = 0;
    PLL0CON = 0;

//  when switch power, reduce lp_top power, disable ram0/1 clock
    CLKGAT0 &= ~((3<<2) | BIT(15)); // sram0/1 & LP disable clock

    RTCCON5 &= ~(1<<11); // dis sram if

    RTCCON5 |= (BIT(15) | BIT(16));   //RAM0 RAM1 shutdown
    //RTCCON5 |= BIT(13); //RAM0/1 light sleep
    //RTCCON5 |= BIT(14); //RAM0/1 deep sleep

    // pmu power down
    LVDCON &= ~(BIT(3) | BIT(5));      //disable LVD reset

    rtccon3 = RTCCON3 & ~BIT(5);    //disable C2RL
    rtccon3 |= (BIT(4) | BIT(3));   //PD CORE2, disable all analog model except IO

    rtccon3 &= ~0x7;     //disable VDDCORE & VDDIO & VDDBT LDO

    //disable WDT
    WDT_DIS();

    RTCCON |= BIT(5);   //RTC wake up power down mode reset system enable

    RTCCON3 = rtccon3;
    LPMCON |= BIT(0);	//sleep mode
    LPMCON |= BIT(1);   //idle mode
    while(1);
}

AT(.com_text.lp_port_init)
void lp_port_init(void)
{
    //config IO low power state
    //when RTCCON5 bit12=0, will select LPGPIO; otherwise, select GPIO
    //save IO

    if(pmu_vddio_to_flash()){
        GPIOGDE = BIT(2);
        GPIOGPU = BIT(2);
    }else{
        GPIOGDE = 0;
        GPIOGPU = 0;
    }

    CLKGAT0 |= BIT(15);         //lp_clk enable

    io_sleep();
}

AT(.com_text.pwroff_mode2)
void lowpwr_pwroff_mode2_do(void)
{
    u8 *src, *dst;
    u32 i;
    void (*p_func)(void);

    p_func = (void (*)(void))CACHE_ADDR;

    saradc_deinit();

    PICCONCLR = BIT(0);     //disable global interrupt
    NMICON = 0;

    RTCCON9 = 0x1fff;       //Clr wakeup pending
    RTCCON10 = 0xffff;

    PWRCON1 &= ~(0xf<<14);  //disable flash power gate
    PWRCON1 |= BIT(18);

    //copy pwroff_mode2_final to cache ram

    dst = (u8*) CACHE_ADDR;
    src = (u8*) (&pwroff_mode2_final);
    for(i = 0; i < 0x300; i++){
        *dst++ = *src++;
    }

    lp_port_init();

    //jmp to run lp_sniff_final()
    p_func();
}


AT(.com_sleep.sleep.lv1)
WEAK void lowpwr_sleep_lv1_proc(void)
{
    u32 pwrcon2 = PWRCON2;
    u32 pwrcon0 = PWRCON0;
    u32 lpmcon = LPMCON;

    PWRCON2 = (PWRCON2 & ~(0xf<<0)) | (0x0<<0);           // vddbt set 0.85v

    PLL0CON |= BIT(1);
    PLL0CON = (PLL0CON & ~(0x1));                         // disable vdd12 from AVDD

    XOSCCON &= ~(BIT(5) | 0xb<<24);                      // disable XOSC48M clkout

    // vddio AON
    RTCCON3 |= BIT(7);
    PWRCON0 = (PWRCON0 & ~(0XF<<5)) | 4<<5;
    delay_us(5);
    RTCCON3 &= ~BIT(1);

    //LPMCON = (LPMCON & ~(0X1F << 16)) | (0x0<<16);      // sniff vddcore set 0.7v

    WDT_DIS();

    LPMCON |= BIT(2);                                     // system sleep
    asm("nop");asm("nop");
    asm("nop");asm("nop");

    PWRCON1 |= BIT(7);                                    // enable vddbt climit
    PWRCON2 = pwrcon2;
    delay_us(10);                                         // disable vddbt climit
    PWRCON1 &= ~BIT(7);

    LPMCON = lpmcon;                                      // resume vddcore

    // vddio AON exit
    RTCCON3 |= BIT(1);
    PWRCON0 = pwrcon0;
    delay_us(1);
    RTCCON3 &= ~BIT(7);

    XOSCCON |= BIT(5) | 0xb<<24;                         // resume XOSC clkout

    WDT_EN();
}

AT(.com_sleep.lv2)
WEAK void lowpwr_sleep_sfr_user_save(void)
{

}

AT(.com_sleep.lv2)
WEAK void lowpwr_sleep_sfr_user_resume(void)
{

}


AT(.com_sleep.lv2_do)
void lp_sleep_lv2_do(void)
{
    u32 rtccon3 = RTCCON3;

    // 1. disable clock
    CLKGAT0 &= ~(BIT(15) | BIT(3) | BIT(2)); //lp clk, sram0/1 disable clock
    //RTCCON4 &= ~(0x3<<22);                //VRTC24 to 1.7V

    // 2. disable interface
    //    disable if, then deep sleep memory, can not do in 1 cycle
    RTCCON5 &= ~(1<<11);    // dis sram if
    //RTCCON5 = (RTCCON5 & ~(0x7<<13)) | (1<<13);
    RTCCON5 |=  (2<<13);    // 1xx : shutdown
                            // 01x : deep sleep
                            // 001 : light sleep
                            // 000 : normal
    RTCCON5 |= (1<<17);     // set lp_flag

    RTCCON5 |= BIT(0);

    // pmu power down ...
    rtccon3 &= ~(1<<5);     // VDDCORE and VLCORE
    rtccon3 |= ((1<<3) | (1<<4));      // VLCORE LDO enable, RI_PD_CORE2 from rtc
    rtccon3 &= ~(0x7<<0);   // VDDBT/VDDIO/VDDCORE LDO disable

    RTCCON |= BIT(5);       //RTC wake up power down mode reset system enable
    RTCCON3 = rtccon3;
//    RTCCON3 |= (1<<6);          // PD CORE

    LPMCON |= BIT(0);
    LPMCON |= BIT(1);       //idle mode
    asm("nop");
    asm("nop");
    asm("nop");
    while(1);
}

AT(.com_sleep.lv2_proc)
void lp_sleep_lv2_proc(void)
{
    u32 rtccon0 = RTCCON0;
    u32 *dst = (u32 *)0x70000;
    u32 *src = (u32 *)&lp_sleep_lv2_do;

    for(uint i=0; i<512/4; i++) {
        *dst++ = *src++;
    }

    // 1, save IO
    CLKGAT0 |= (1<<15);         // LP_CLK Enable
    io_sleep();
    WKUPCPND = 0xFF<<16;
    wkupedg_bak = 0;

    sfr_backup.gpioa_fen = GPIOAFEN;
    sfr_backup.gpiob_fen = GPIOBFEN;
    sfr_backup.gpioa_fdiren = GPIOAFDIREN;
    sfr_backup.gpiob_fdiren = GPIOBFDIREN;
    sfr_backup.gpioa_drv = GPIOADRV;
    sfr_backup.gpiob_drv = GPIOBDRV;

    RTCCON9 = 0x1fff;           //Clr wakeup pending
    RTCCON10 = 0xffff;

    WDT_DIS();
    RTCCON12 = (RTCCON12 & ~0xf) | 0xa | (0x3<<6) | (0x3<<4);   //disable WKO reset, RTC WDT, GPIO reset

    // 2, analog disable
    RTCCON4 &= ~(BIT(17) | BIT(18));    //FGS,FGBUF

    RTCCON8 &= ~BIT(29);
    RTCCON3 |= BIT(7);      //enable vddio aon ldo

    RTCCON4 = (RTCCON4 & ~(0x3f<<24)) | (6<<24) | (6<<27);                  //no sfr access, bit26~bit24, VRTC11 is 0.83V; sfr access, bit29~27, VRTC11 is 1.1V

    // PG OFF
    PWRCON1 |= BIT(9) | BIT(18);            // SD/FS PG PD
    PWRCON1  = PWRCON1 & ~((0x3f << 10)) ;  // PG off
    // ... other analog pd

    if(RTCCON3 & BIT(14)){
        rtccon0 = (rtccon0 & ~(0x03 << 12)) | (0x02 << 12); //CLK2M in Touch select SNF_RC_RTC
    }

    if(RTCCON3 & BIT(16)){
        rtccon0 = (rtccon0 & ~(0x03 << 14)) | (0x02 << 14); //CLK2M in Keyscan select SNF_RC_RTC
    }

    rtccon0 = (rtccon0 & ~(0x7<<22)) | (1<<22);     //sys reset delay

    RTCCON0 = rtccon0;

    // 3, select sniff rc clock and disable pll,xosc
    CLKCON0  = (CLKCON0 & ~(3<<2)) | (0<<2); // sel rc2m
    asm("nop");
    asm("nop");
    asm("nop");

    // 4, PLL, XOSC, CLK2M_BT off
    PLL0CON  = 0;
    PLL0CON1 = 0;
    XOSCCON = 0;

    //RTCCON3 |= BIT(3) | BIT(5);             //enable RL2VLC & C2VLC
    //PWRCON0 = (PWRCON0 & ~0x1f) | 0x8;      //VDDCORE is 0.9V

    asm("j 0x70000");
};

AT(.com_sleep.lv2_wakeup)
void lp_wakeup_lv2_proc(void)
{
    bool gpio_pending = true;

    RTCCON4 &= ~BIT(6);                     //VDDBT to VCORE dis

    RTCCON4 |= BIT(5) ;                     //VCORE from VDDIO

    PICADR = (u32)&__comm_vma;

    if(!(sfr_backup.rtccon5 & BIT(0))){
        PWRCON1 = (PWRCON1 & ~(0X3<<2)) | (0X1<<2);       // DI_CLIMIT_S   109:85:366mA
        RTCCON5 &= ~BIT(0);
    }

    PWRCON0 |= BIT(20);                     //pmu normal

    wkupedg_bak = WKUPEDG;

    //pending is generated due to burrs after the gpio is wakeup from sleep. In this case, manually clear pending
    if(!(RTCCON9 & WK_LP_GPIO) && !(WKUPEDG & 0xff0000)){
        gpio_pending = false;
    }

    //resume IO
    io_resume();

    if (gpio_pending == false) {
        WKUPCPND = 0xFF<<16;
    }

    GPIOAFEN = sfr_backup.gpioa_fen;
    GPIOBFEN = sfr_backup.gpiob_fen;
    GPIOAFDIREN = sfr_backup.gpioa_fdiren;
    GPIOBFDIREN = sfr_backup.gpiob_fdiren;
    GPIOADRV = sfr_backup.gpioa_drv;
    GPIOBDRV = sfr_backup.gpiob_drv;


    // 2, analog init
    RTCCON3 |= BIT(0);                                          //VDDDBT
    PWRCON0 = (PWRCON0 & ~0x1f) | pmu_get_vddcore();            //VDDCORE is 1.15V
    asm("nop");

    PLL0CON |= BIT(12);                         //DI_EN_LDO
    delay(10);

    RSTCON0 = sfr_backup.rstcon0;               //pllsdm reset release
    delay(25);                                  //delay 100us, by rc


    // 3, PLL,XOSC,CLK2M_BT, on
    XOSCCON = sfr_backup.xosccon;
    PLL0CON  = sfr_backup.pll0con;
    PLL0CON1 = sfr_backup.pll0con1;

    delay(700);                                 //delay 1ms - 2ms for xosc to work

    CLKDIVCON0 = sfr_backup.clkdivcon0;

    // 4, resume CLK
    CLKCON1 = sfr_backup.clkcon1;               //xosc_mux_clk select xosc24m
    asm("nop");
    asm("nop");
    asm("nop");

    CLKCON0 = sfr_backup.clkcon0;               //sysclk select xosc_mux_clk
    asm("nop");
    asm("nop");

    LVDCON &= ~BIT(30);                         //Enable reset pending
}

AT(.com_sleep.lv2_proc)
WEAK void lowpwr_sleep_lv2_proc(void)
{
    sfr_backup.piccon  = PICCON;
    sfr_backup.picen   = PICEN;
    sfr_backup.picpr   = PICPR;
    sfr_backup.nmicon  = NMICON;

    sfr_backup.clkgat0 = CLKGAT0;
    sfr_backup.clkgat1 = CLKGAT1;
    sfr_backup.clkgat2 = CLKGAT2;

    sfr_backup.pwrcon0 = PWRCON0 & ~BIT(20);
    sfr_backup.pwrcon1 = PWRCON1;
    sfr_backup.pwrcon2 = PWRCON2;

    sfr_backup.clkcon0 = CLKCON0;
    sfr_backup.clkcon1 = CLKCON1;
    sfr_backup.clkdivcon0 = CLKDIVCON0;

    sfr_backup.xosccon = XOSCCON;
    sfr_backup.pll0con = PLL0CON;
    sfr_backup.pll0con1 = PLL0CON1;

    sfr_backup.rstcon0 = RSTCON0;
    sfr_backup.rtccon = RTCCON;
    sfr_backup.rtccon5 = RTCCON5;

    lowpwr_sleep_sfr_save();
    lowpwr_sleep_sfr_user_save();

    u32 gpiogde = GPIOGDE;
    u32 gpiogpu = GPIOGPU;
    if(pmu_vddio_to_flash()){
        GPIOGDE = BIT(2);                      //SPICS
        GPIOGPU = BIT(2);
    }else{
        GPIOGDE = 0;
        GPIOGPU = 0;
        PWRCON1 &= ~(0XF<<14);                          //DI_SW DISABLE
    }

    sfr_backup.rtccon0 = RTCCON0;
    sfr_backup.rtccon12 = RTCCON12;

    if(RTCCON3 & (BIT(8) | BIT(9) | BIT(13) | BIT(14) | BIT(16))){      //wakeup source need rc2m clk
        RTCCON0 |= BIT(19);                         //RC2M_RTC enable
    }else{
        RTCCON0  &= ~BIT(19);                       //RC2M_RTC disable
    }

    lp_sleep(lp_sleep_lv2_proc, lp_wakeup_lv2_proc);

    RTCCON4 |= BIT(5) | BIT(6);                     //VCORE from VDDBT & VDDIO

    RTCCON12 = sfr_backup.rtccon12;
    RTCCON0 = sfr_backup.rtccon0;
    asm("nop");
    asm("nop");

    PWRCON1 = sfr_backup.pwrcon1 | BIT(7);
    PWRCON2 = sfr_backup.pwrcon2;
    PWRCON0 = sfr_backup.pwrcon0 | BIT(20);

    CLKGAT0 = sfr_backup.clkgat0;
    CLKGAT1 = sfr_backup.clkgat1;
    CLKGAT2 = sfr_backup.clkgat2;

	lowpwr_sleep_sfr_resume();
    lowpwr_sleep_sfr_user_resume();

    GPIOGDE = gpiogde;
    GPIOGPU = gpiogpu;

    RTCCON = sfr_backup.rtccon;

	PICCON  = sfr_backup.piccon;
	PICPR   = sfr_backup.picpr;
	PICEN   = sfr_backup.picen;
	NMICON  = sfr_backup.nmicon;

    if(sfr_backup.rtccon5 & BIT(0)){
        RTCCON4 &= ~BIT(6);                         //LDO Mode,VCORE disable VDDBT, from VDDIO
    }else{
        RTCCON4 &= ~BIT(5);                         //Buck Mode,VCORE disable VDDIO, from VDDBT
    }

    PWRCON1 &= ~BIT(7);


}

AT(.com_sleep.sleep)
void lowpwr_sleep_proc(u8 lpclk_type)
{
    if (lpclk_type == CLK_RCDIV) {
        lowpwr_sleep_lv2_proc();
    } else {
        lowpwr_sleep_lv1_proc();
    }
}

