////////////////////////////////////////////////////////////////////////////////
/// @file timer.c
/// Timer functions.
///
/// @author  Tamas Raikovich
/// @version 1.0
/// @date    2012.01.21.
////////////////////////////////////////////////////////////////////////////////
#include <xparameters.h>
#include <xio.h>
#include <xintc_l.h>
#include <xtmrctr_l.h>
#include "timer.h"


///Timer interrupt counter.
volatile unsigned long clock_cnt;


////////////////////////////////////////////////////////////////////////////////
/// Timer interrupt handler routine.
///
/// @param instancePtr User specific data, 4th parameter of the XIntc_RegisterHandler.
////////////////////////////////////////////////////////////////////////////////
void timer_int_handler(void *instancePtr)
{
    unsigned long csr;

    //Increment the timer interrupt counter.
    clock_cnt++;

    //Clear the timer interrupt flag.
    csr = XTmrCtr_GetControlStatusReg(XPAR_XPS_TIMER_0_BASEADDR, 0);
    XTmrCtr_SetControlStatusReg(XPAR_XPS_TIMER_0_BASEADDR, 0, csr);
}


////////////////////////////////////////////////////////////////////////////////
/// Initializes the timer.
///
/// @param freq System clock frequency.
////////////////////////////////////////////////////////////////////////////////
void timer_init()
{
    unsigned long ier;

    //Clear the timer interrupt counter.
    clock_cnt = 0;

    //Register the timer interrupt handler routine.
    XIntc_RegisterHandler(
    	XPAR_INTC_0_BASEADDR,
    	XPAR_XPS_INTC_0_XPS_TIMER_0_INTERRUPT_INTR,
        (XInterruptHandler) timer_int_handler,
        0
    );

    //Enable the timer interrupt in the interrupt controller.
    ier = XIo_In32(XPAR_INTC_0_BASEADDR + XIN_IER_OFFSET);
    XIntc_EnableIntr(XPAR_INTC_0_BASEADDR, ier | XPAR_XPS_TIMER_0_INTERRUPT_MASK);

    //Set the value of the LOAD register.
    XTmrCtr_SetLoadReg(XPAR_XPS_TIMER_0_BASEADDR, 0, XPAR_PROC_BUS_0_FREQ_HZ / 100 - 1);

    //Reset the timer.
    XTmrCtr_SetControlStatusReg(
    	XPAR_XPS_TIMER_0_BASEADDR,
        0,
        XTC_CSR_INT_OCCURED_MASK | XTC_CSR_LOAD_MASK
    );

    //Start the timer.
    XTmrCtr_SetControlStatusReg(
    	XPAR_AXI_TIMER_0_BASEADDR,
        0,
        XTC_CSR_ENABLE_TMR_MASK | XTC_CSR_ENABLE_INT_MASK |
        XTC_CSR_AUTO_RELOAD_MASK | XTC_CSR_DOWN_COUNT_MASK
    );
}


////////////////////////////////////////////////////////////////////////////////
/// Resets the timer.
////////////////////////////////////////////////////////////////////////////////
void timer_clear()
{
	//Reset the timer.
	XTmrCtr_SetControlStatusReg(
		XPAR_XPS_TIMER_0_BASEADDR,
	    0,
	    XTC_CSR_INT_OCCURED_MASK | XTC_CSR_LOAD_MASK
	);

	//Clear the timer interrupt counter.
	clock_cnt = 0;

	//Start the timer.
	XTmrCtr_SetControlStatusReg(
	    XPAR_XPS_TIMER_0_BASEADDR,
	    0,
	    XTC_CSR_ENABLE_TMR_MASK | XTC_CSR_ENABLE_INT_MASK |
	    XTC_CSR_AUTO_RELOAD_MASK | XTC_CSR_DOWN_COUNT_MASK
	);
}


////////////////////////////////////////////////////////////////////////////////
/// Reads the elapsed time.
///
/// @param s  Pointer to the variable that stores the seconds.
/// @param ms Pointer to the variable that stores the milliseconds.
////////////////////////////////////////////////////////////////////////////////
void timer_get(unsigned long *s, unsigned long *ms)
{
    unsigned long cnt;

    //Calculate the elapsed milliseconds.
    cnt = clock_cnt;
    cnt = cnt * 10;

    *ms = cnt % 1000;
    *s = cnt / 1000;
}

