/*
 * Kneron WDT test code driver
 *
 * Copyright (C) 2019 Kneron, Inc. All rights reserved.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include "kdrv_cmsis_core.h"
#include "io.h"
#include "kdrv_pwm.h"
#include "time.h"
#include "kdrv_wdt.h"
#include "kdrv_uart.h"
#include "kmdw_console.h"


volatile uint32_t wd_int_flag;

#define QC_MODE             0x80

#define EXIT_TUBE_FAIL      false
#define EXIT_TUBE_PASS      true

/**
 * @brief wdt_isr(), WDT ISR function
 */
void wdt_isr()
{
    wd_int_flag = 1;
    DSG("\n\rWDT Interrupt occured\n");
    kdrv_wdt_set_clear_status();
}



void wdt_start(void)
{
    kdrv_wdt_set_int_counter(0xff);  //signal asserting 256 clock cycles
    kdrv_wdt_set_auto_reload(APB_CLOCK);
    kdrv_wdt_reset();
    kdrv_wdt_enable();               //Enable WDT
}


void reset_board()
{
    kdrv_wdt_disable();
    kdrv_wdt_set_int_counter(1);
    kdrv_wdt_set_auto_reload(1);
    kdrv_wdt_reset();
    kdrv_wdt_enable();               //Enable WDT
    kdrv_wdt_sys_reset_enable();
}


// --------------------------------------------------------------------
//  check whether watch dog will generate interrupt (should within 1 sec) within 3 sec
// --------------------------------------------------------------------
int wdt_test(uint32_t mode)
{
    uint32_t j;
    int result = 0;
    unsigned long start_jiffies;
    char buf[40];

    DSG("\rWatchDog Test...\n");
    if (mode & QC_MODE) {
        j=(mode & 0xF);
    } else {
        //DSG("Please select clock source:(1)PCLK (2)32.768KHz(external)\r\n");
        DSG("Please select clock source:(1)PCLK \r\n");
        kdrv_uart_read(DRVUART_PORT0, (uint8_t *)buf, 1);
        j=atoi(buf);
    }

    if(j == 1) {
        //*(unsigned int *)(REG_WDT_CR)=*(unsigned int *)(REG_WDT_CR)&(~0x10);
        outw(REG_WDT_CR, inw(REG_WDT_CR) & ~WDT_CR_EXTCLK);
    //} else if(j == 2) {
    //    //*(unsigned int *)(REG_WDT_CR)=*(unsigned int *)(REG_WDT_CR)| (0x10);
    //    outw(REG_WDT_CR, inw(REG_WDT_CR) | WDT_CR_EXTCLK);
    } else {
        return 0;
    }
    /* set initial status */
    wd_int_flag = 0;
    result = false;

    kdrv_wdt_disable();
    kdrv_wdt_sys_int_enable();
    NVIC_EnableIRQ(WDT_FTWDT010_IRQ);

    wdt_start();
    
    start_jiffies = kdrv_current_t1_tick();
    DSG("\rstart!\n");
    while (wd_int_flag == 0) {
        if ((kdrv_current_t1_tick() - start_jiffies) >= (3*1000)) {
            /* msTicks* >= (3*1000) // JIFF_TO_SEC(jiffies - start_jiffies) >= 3*1000), 3sec */
            break;
        }
    }
    
    if (wd_int_flag == 1)
        result = true;

    kdrv_wdt_disable();
    NVIC_DisableIRQ(WDT_FTWDT010_IRQ);

    if (result) {
        DSG("\rPass!\n");
        result = 1;
    } else {
        DSG("\rFail!\n");
        result = 0;
    }

    DSG("End WatchDog Test!\n");

    return result;
}


#define LOOP_RESET_COUNTER          0x50000         /// program will continue to reset watch dog counter
/// when loop count is lower than this value
#define LOOP_END_TEST               0x2000000       /// if loop count reach this value and still not hw reset
/// then watch dog is fail
// --------------------------------------------------------------------
//  check whether watchDog restart is useful (can stop watch dog to reboot)
//  1. for loop to execute watchdog restart
//  2. after reach LOOP_RESET_COUNTER, wait for cpu reset
// --------------------------------------------------------------------
void wdt_reset_test()
{
    int i;

    kdrv_wdt_disable();
    kdrv_wdt_sys_reset_enable();
    wdt_start();

    DSG("Running\n");

    for (i=0; i<LOOP_END_TEST; ++i) {
        if (i==LOOP_RESET_COUNTER) {
            DSG("The test is PASS when CPU reset\n");
            DSG("Waiting CPU reset\n");
        } else if (i<LOOP_RESET_COUNTER) {
            if (i&0xfff) {
                kdrv_wdt_reset();
            }
        }

        if (!(i&0xfff)) {
            // There is some problem about
            DSG(".");
        }
    }
    DSG("\n\rIf you see this message \"The watchdog test is FAIL\", it presents WDT function fail\n");
}

int wdt_test_QC()
{
    return wdt_test(QC_MODE|0x1);
}


void wdt_test_main()
{
    int32_t item;
    char buf[80];
    uint32_t val;

    val = readl(SCU_FTSCU100_PA_BASE+0x00);     // Boot-up Status register in FTSCU100
    if ( (val & 0x200)==0x200 ) {     // reboot by the watchdog reset
        DSG("re-boot by the watchdog reset\n");
    } else if ( (val & 0x100)==0x100 ) {
        DSG("re-boot by the hardware reset\n");
    } else if ( (val & 0x10000)==0x10000 ) {
        DSG("boot up by the power button\n");
    }
    NVIC_SetVector((IRQn_Type)WDT_FTWDT010_IRQ, (uint32_t)wdt_isr);
    while (1) {
        DSG("Choose a test item:\n");
        DSG("1: Watchdog Test ");
        DSG("2: Watchdog Reset Test");
        DSG("3: Reset Board");
        kdrv_uart_read(DRVUART_PORT0, (uint8_t *)buf, 1);
        item = atoi(buf);

        switch (item) {
        case 1:
            wdt_test(0);
            break;
        case 2:
            wdt_reset_test();
            break;
        case 3:
            reset_board();
            break;          // for normal case, should never reach
        default:
            DSG("*** Non-existed item! ***\n");
            break;
        }  /* -----  end switch  ----- */
    }
}
