#include "pwr_sleep.h"

extern void sleep_process_do(void);
extern void wake_process_do(void);


// *************************************
// * 配置sleep模式唤醒条件
// * port: 唤醒端口选择, 可选 WAKEUP_PORT_SEL 中任意值
// * edge: 边沿选择，可选 WAKEUP_EDGE_SEL 中任意值
// * return: None
// *************************************
static void wake_config(u8 port, u8 edge)
{
    WKUPEDG = 0;
    if (edge) {
        WKUPEDG |= (1 << port);      // 下降沿
    } else {
        WKUPEDG &= ~(1 << port);     // 上升沿
    }

    WKUPCON = 0;
    WKUPCON |= (1 << port);

    WKUPCPND = 0xff << 16;
}

// *************************************
// * 失能sleep模式的唤醒条件
// * return: None
// *************************************
static void wake_disable(void)
{
    WKUPEDG = 0;
    WKUPCON = 0;
    PORTINTEDG = 0;
    PORTINTEN = 0;
}

// *************************************
// * 检测WK Pin 状态
// * return: 按键状态
// *************************************
static FLAG_STATE is_wk_release(void)
{
    if (0 == (RTCCON & (1 << 19))) {
        return RESET;
    } else {
        return SET;
    }
}

// *************************************
// * 关闭PLL时钟
// * return: None
// *************************************
static void pll_disable(void)
{
    PLL0CON &= ~(1 << 18);
    PLL0CON &= ~((1 << 6) | (1 << 12));
}

// *************************************
// * 使能PLL时钟
// * return: None
// *************************************
static void pll_enable(void)
{
    PLL0CON |= (1 << 18);
    PLL0CON |= ((1 << 6) | (1 << 12));
}

// *************************************
// * 判读是否触发唤醒条件
// * return: SET-接收到唤醒条件，RESET-未接收到唤醒条件
// *************************************
extern u8 timer4wkflag;
static FLAG_STATE is_awakenable(void)
{
    if ((WKUPEDG >> 16) & 0xff) {
        return SET;
    } else {
        return RESET;
    }
}

// *************************************
// * 清除GPIO配置
// * return: None
// *************************************
static void gpio_deinit(void)
{
    GPIOADE = 0;
    GPIOBDE = (1 << 5);     //pwrkey
    GPIOEDE = 0;
    GPIOFDE = 0;
    GPIOGDE = 0x3f;         //MCP FLASH
}

// *************************************
// * 失能时钟，降低功耗
// * return: None
// *************************************
static void poweroff_clk_disable(void)
{
    RTCCON0 &= ~BIT(0);
    RTCCON0 &= ~BIT(8);
    PICCONCLR = BIT(0);
    CLKCON0 |= BIT(0);
    CLKCON0 = (CLKCON0 & ~(0x3 << 2));
    PLL0CON &= ~BIT(18);
    PLL0CON &= ~BIT(6);
    PLL0CON &= ~BIT(12);
    PLL1CON &= ~BIT(6);
    PLL1CON &= ~BIT(18);

    XO26MCON &= ~(0xf << 24);
    XO26MCON &= ~BIT(10);
    XO26MCON &= ~BIT(9);
}

// *************************************
// * 唤醒源配置
// * return: None
// *************************************
static void poweroff_wake_config(u8 waken_sel)
{
    RTCCON3 &= ~BIT(8);
    RTCCON3 &= ~BIT(10);
    RTCCON3 &= ~BIT(11);

    if (waken_sel & POWEROFF_WAKEUP_VUSB) {
        RTCCON3 |= BIT(11);
    } else if (waken_sel & POWEROFF_WAKEUP_ALARM) {
        RTCCON3 |= BIT(8);
    } else if (waken_sel & POWEROFF_WAKEUP_PB0) {
        RTCCON3 |= BIT(10);
        RTCCON13 = 0;
        RTCCON13 |= BIT(1);
        RTCCON13 |= BIT(13);
        if (POWEROFF_WK_LEVEL == POWEROFF_WK_LEVEL_L) {
            RTCCON13 |= BIT(5);
        } else {
            RTCCON13 |= BIT(9);
            RTCCON13 |= BIT(17);
        }
    } else if (waken_sel & POWEROFF_WAKEUP_PB1) {
        RTCCON3 |= BIT(10);
        RTCCON13 = 0;
        RTCCON13 |= BIT(2);
        RTCCON13 |= BIT(14);
        if (POWEROFF_WK_LEVEL == POWEROFF_WK_LEVEL_L) {
            RTCCON13 |= BIT(6);
        } else {
            RTCCON13 |= BIT(10);
            RTCCON13 |= BIT(18);
        }
    } else if (waken_sel & POWEROFF_WAKEUP_PB2) {
        RTCCON3 |= BIT(10);
        RTCCON13 = 0;
        RTCCON13 |= BIT(3);
        RTCCON13 |= BIT(15);
        if (POWEROFF_WK_LEVEL == POWEROFF_WK_LEVEL_L) {
            RTCCON13 |= BIT(7);
        } else {
            RTCCON13 |= BIT(11);
            RTCCON13 |= BIT(19);
        }
    } else if (waken_sel & POWEROFF_WAKEUP_PB5) {
        RTCCON3 |= BIT(10);
        RTCCON13 = 0;
        RTCCON13 |= BIT(0);
        RTCCON13 |= BIT(12);
        if (POWEROFF_WK_LEVEL == POWEROFF_WK_LEVEL_L) {
            RTCCON13 |= BIT(4);
        } else {
            RTCCON13 |= BIT(8);
            RTCCON13 |= BIT(16);
        }
    }
}

// *************************************
// * 电源、RTC始终配置
// * return: None
// *************************************
static void poweroff_config(u8 mode)
{
    if( mode==POWEROFF_WAKEUP_ALARM )
    {
        RTCALM = RTCCNT +5;//wake after 5 seconds
        RTCCON0 |= BIT(0);// ring_2M
        RTCCON3 |= BIT(8);// wakeup source
    }
    RTCCON |= BIT(5);           // reset system when wake up
    RTCCON0 &= ~BIT(4);         // disable touch key between core interface
	RTCCON0 &= ~BIT(13);        // disable RCOSC
    RTCCON5 |= BIT(6);          // VDDBT low power mode enable
    RTCCON8 &= ~BIT(15);        // vusb voltage divide enable bit
    RTCCON9 = 0x3ff;            // clear all pending
    RTCCON11 &= ~(0x03L);       // clear WK pin filter select
    RTCCON11 |= BIT(2);         // config Wk pin filter select

    PWRCON1 &= ~(0x1f << 14);   // disable Flash Power Gate
    PWRCON1 |= BIT(18);         // pdown flash power gate

    RTCCON3 |= BIT(6);          // Core power down enable, VDDCORE short disable
    RTCCON3 |= BIT(7);          // RI_EN_VDDIO_AON   RTC 2.9V LDO enable
    if( mode==POWEROFF_WAKEUP_ALARM )
        RTCCON3 &= ~(0x07L);        // Disable VDDCORE VDDIO VDDBUCK, VDDXOEN
    else
        RTCCON3 &= ~(0x17L);        // Disable VDDCORE VDDIO VDDBUCK, VDDXOEN
}

// *************************************
// * 启动cpu休眠，执行后cpu停止工作
// * return: None
// *************************************
static void sleep(void)
{
    LPMCON |= (1 << 0);
}

// *************************************
// * 唤醒条件触发时的上电检测
// * return: None
// *************************************
void pwr_on_check(void)
{
    uint32_t reg_tmp;
    u16 down_time = 0;
    u8 gpio_tmp, reg_tmp_offset, wk_level;

    reg_tmp = RTCCON9;
    RTCCON9 = 0x03ff;

    switch (POWEROFF_WK_IO) {
        case POWEROFF_WAKEUP_PB0:
            gpio_tmp = GPIO_PIN_0;
            reg_tmp_offset = 7;
            break;
        case POWEROFF_WAKEUP_PB1:
            gpio_tmp = GPIO_PIN_1;
            reg_tmp_offset = 8;
            break;
        case POWEROFF_WAKEUP_PB2:
            gpio_tmp = GPIO_PIN_2;
            reg_tmp_offset = 9;
            break;
        case POWEROFF_WAKEUP_PB5:
            gpio_tmp = GPIO_PIN_5;
            reg_tmp_offset = 2;
            break;
    }

    if (POWEROFF_WK_LEVEL == POWEROFF_WK_LEVEL_L) {
        wk_level = RESET;
    } else {
        wk_level = SET;
    }

    if ((reg_tmp & (1 << reg_tmp_offset)) != RESET) {
        while (1) {
            if (gpio_read_bit(GPIOB_REG, gpio_tmp) == wk_level) {
                down_time++;
                if (down_time > 300) {
                    return;
                }
            } else {
                pwrdwn_enter();
            }

            delay_ms(10);
        }
    } else {
        return;
    }
}

AT(.sleep_text.sleep)
void sys_sleep_cb(u8 lpclk_type)
{
    u32 gpiogde = GPIOGDE;
    GPIOGDE = BIT(2) | BIT(4);
    sys_enter_sleep(lpclk_type);
    GPIOGDE = gpiogde;
}

AT(.com_text.sleep)
bool sys_sleep_check(u32 *sleep_time)
{
    if(*sleep_time > BT_WAKEUP_INTERVAL) {
        *sleep_time = BT_WAKEUP_INTERVAL;
        return true;
    }
    return false;
}


// 睡眠模式入口
AT(.com_text.sleep)
void sleep_enter(void)
{
    u32 sysclk;

#if TIMED_WK_EN
    u32 wakeup_count;
    u32 wakeup_time = 0;
#endif

    gpio_typedef gpio_struct[5];

    printf("%s\n", __func__);

    bt_enter_sleep();

    sys_set_tmr_enable(0, 0);

    // 等待按键松开
    while (!is_wk_release());

    // 挂起自定义外设
    sleep_process_do();

    // 保存时钟配置
    sysclk = get_cur_sysclk();
    set_sys_clk(SYS_24M);

    // 关闭PLL0
    pll_disable();

    // 记录IO状态
    memcpy(&gpio_struct[0], GPIOA_REG, sizeof(gpio_typedef));
    memcpy(&gpio_struct[1], GPIOB_REG, sizeof(gpio_typedef));
    memcpy(&gpio_struct[2], GPIOE_REG, sizeof(gpio_typedef));
    memcpy(&gpio_struct[3], GPIOF_REG, sizeof(gpio_typedef));
    memcpy(&gpio_struct[4], GPIOG_REG, sizeof(gpio_typedef));
    memset(GPIOA_REG, 0, sizeof(gpio_typedef));
    memset(GPIOE_REG, 0, sizeof(gpio_typedef));
    memset(GPIOF_REG, 0, sizeof(gpio_typedef));
    GPIOGDE = 0x3f;         // MCP FLASH

    // 配置唤醒条件
    wake_config(SLEEP_WK_IO, SLEEP_WK_EDGE);
//rtc_calendar_init();
	// 监听唤醒操作
    while(1) {
        WDT_CLR();
        bt_is_sleep();//低功耗断开SPP时，功耗会降低。不加此语句，功耗为3mA+;加了变500uA.
#if TIMED_WK_EN
        wakeup_count = bt_sleep_proc();
        if (wakeup_count == 1) {
            printf("wakeup_time1 = %d\r\n",wakeup_time);
            wakeup_time++;
        if (wakeup_time > 100)  // 每 50s 唤醒一次
                break;
        }
#else
        bt_sleep_proc();
#endif

        if (is_awakenable() == SET) {
                printf("wakeup_time = %d\r\n",wakeup_time);
            break;
        }
    }

    printf("wakeup\n");

    // 恢复IO配置
    memcpy(GPIOA_REG, &gpio_struct[0], sizeof(gpio_typedef));
    memcpy(GPIOB_REG, &gpio_struct[1], sizeof(gpio_typedef));
    memcpy(GPIOE_REG, &gpio_struct[2], sizeof(gpio_typedef));
    memcpy(GPIOF_REG, &gpio_struct[3], sizeof(gpio_typedef));
    memcpy(GPIOG_REG, &gpio_struct[4], sizeof(gpio_typedef));

    // 失能唤醒条件
    wake_disable();

    //开启PLL0
    pll_enable();

    // 重新配置系统时钟
    set_sys_clk(sysclk);

    // 重新唤醒自定义外设
    wake_process_do();

    sys_set_tmr_enable(1, 1);

    bt_exit_sleep();
    printf("sleep_exit\n");
}

extern rtc_clk_typedef clk_struct_test;
// 休眠模式入口
AT(.com_text.sleep)
void pwrdwn_enter(void)
{

    printf("power off.\n");

    set_buck_mode(0);

    // 清除IO配置
    gpio_deinit();

    // 配置时钟
    poweroff_clk_disable();


    // 唤醒条件配置
    poweroff_wake_config(POWEROFF_WAKEUP_ALARM);
    poweroff_wake_config(POWEROFF_WK_IO);
//poweroff_wake_config(POWEROFF_WAKEUP_VUSB);

    // 配置电源、RTC时钟
    poweroff_config(POWEROFF_WAKEUP_ALARM);
    poweroff_config(POWEROFF_WK_IO);
//poweroff_config(POWEROFF_WAKEUP_VUSB);
    printf("entet:clk_struct_test.clk_source_sel  = %d\r\n",RTCCON0);
    // cpu休眠
    WDT_DIS();
    sleep();
    asm("nop");asm("nop");asm("nop");
    while (1);
}
