/**
 * @file    wm_watchdog.c
 *
 * @brief   watchdog Driver Module
 *
 * @author  kevin
 *
 * Copyright (c) 2014 Winner Microelectronics Co., Ltd.
 */
#include "wm_debug.h"
#include "wm_regs.h"
#include "wm_irq.h"
#include "wm_cpu.h"
#include "wm_watchdog.h"

#define WDG_LOAD_VALUE_MAX  (0xFFFFFFFF / 40)
#define WDG_LOAD_VALUE_DEF  (20 * 1000 * 1000)

static volatile u8 wdg_reset = 0;
static volatile u8 wdg_enable = 0;
static volatile u32 wdg_value_us = WDG_LOAD_VALUE_DEF;
static volatile u32 wdg_jumpclear_flag = 0; /*0:donot jump clear, 1: jump clear, 2:close wdg*/
ATTRIBUTE_ISR void WDG_IRQHandler(void)
{
	csi_kernel_intrpt_enter();
    if (wdg_reset)
    {
    	csi_kernel_intrpt_exit();
        return;
    }
	csi_kernel_intrpt_exit();	
}

/**
 * @brief          This function is used to clear watchdog irq in case watchdog reset
 *
 * @param          None
 *
 * @return         None
 *
 * @note           None
 */
void tls_watchdog_clr(void)
{
	if (0 == wdg_jumpclear_flag)
	{
		tls_reg_write32(HR_WDG_INT_CLR, 0x01);
	}
}

static void __tls_watchdog_init(u32 usec)
{
    tls_sys_clk sysclk;

	tls_sys_clk_get(&sysclk);
	tls_irq_enable(WDG_IRQn);
	
	tls_reg_write32(HR_WDG_LOAD_VALUE, sysclk.apbclk * usec); 		/* 40M dominant frequency: 40 * 10^6 * (usec / 10^6) */
	tls_reg_write32(HR_WDG_CTRL, 0x3);             /* enable irq & reset */
}

static void __tls_watchdog_deinit(void)
{
	tls_irq_disable(WDG_IRQn);
	tls_reg_write32(HR_WDG_CTRL, 0);	
	tls_reg_write32(HR_WDG_INT_CLR, 0x01);
}

/**
 * @brief          This function is used to init watchdog
 *
 * @param[in]      usec    microseconds
 *
 * @return         None
 *
 * @note           None
 */
void tls_watchdog_init(u32 usec)
{
    __tls_watchdog_init(usec);

	wdg_value_us = usec;
	wdg_enable = 1;
}

/**
 * @brief          This function is used to deinit watchdog
 *
 * @param[in]     None
 *
 * @return         None
 *
 * @note           None
 */
void tls_watchdog_deinit(void)
{
	__tls_watchdog_deinit();

	wdg_value_us = WDG_LOAD_VALUE_DEF;
	wdg_enable = 0;
}

/**
 * @brief          This function is used to start calculating elapsed time. 
 *
 * @param[in]      None
 *
 * @return         elapsed time, unit:millisecond
 *
 * @note           None
 */
void tls_watchdog_start_cal_elapsed_time(void)
{
	if (wdg_enable)
	{
	    wdg_jumpclear_flag = 1;

	    __tls_watchdog_deinit();

    	__tls_watchdog_init(WDG_LOAD_VALUE_MAX);
	}
	else
	{
		wdg_jumpclear_flag = 2;
		__tls_watchdog_init(WDG_LOAD_VALUE_MAX);
	}
}

/**
 * @brief          This function is used to stop calculating & return elapsed time. 
 *
 * @param[in]     none
 *
 * @return         elapsed time, unit:millisecond
 *
 * @note           None
 */
u32 tls_watchdog_stop_cal_elapsed_time(void)
{
#define RT_TIME_BASE (40)
	u32 val = 0;

	switch (wdg_jumpclear_flag)
	{
		case 1:
		{
			val = (tls_reg_read32(HR_WDG_LOAD_VALUE) - tls_reg_read32(HR_WDG_CUR_VALUE))/RT_TIME_BASE;
			__tls_watchdog_deinit();
			__tls_watchdog_init(wdg_value_us);
			wdg_jumpclear_flag = 0;
		}
		break;
		
		case 2:
		{
			val = (tls_reg_read32(HR_WDG_LOAD_VALUE) - tls_reg_read32(HR_WDG_CUR_VALUE))/RT_TIME_BASE;
			__tls_watchdog_deinit();
			wdg_jumpclear_flag = 0;
		}
		break;

		default:
			wdg_jumpclear_flag = 0;
			break;
	}

	return val;	
}
/**
 * @brief          This function is used to reset system
 *
 * @param          None
 *
 * @return         None
 *
 * @note           None
 */
void tls_sys_reset(void)
{
    tls_os_set_critical();
	wdg_reset = 1;
	__tls_watchdog_deinit();
	tls_reg_write32(HR_WDG_LOCK, 0x1ACCE551);
	tls_reg_write32(HR_WDG_LOAD_VALUE, 0x100);
	tls_reg_write32(HR_WDG_CTRL, 0x3);
	tls_reg_write32(HR_WDG_LOCK, 1);
	while(1);
}

