////////////////////////////////////////////////////////////////////////////////
/// @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_time;


////////////////////////////////////////////////////////////////////////////////
/// 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_time++;

    //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 freq)
{
    unsigned long ier;

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

    //Enable the timer interrupt in the interrupt controller.
    ier = XIo_In32(XPAR_XPS_INTC_0_BASEADDR + XIN_IER_OFFSET);
    XIntc_EnableIntr(XPAR_XPS_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, freq / CLOCK_SECOND);

    //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_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
    );
}


////////////////////////////////////////////////////////////////////////////////
/// Sets the timer for a time sometime in the future.
///
/// @param t        Pointer to the timer object.
/// @param interval Number of ticks after the timer will expire.
////////////////////////////////////////////////////////////////////////////////
void timer_set(ptimer t, unsigned long interval)
{
    t->interval = interval;
    t->start = clock_time;
}


////////////////////////////////////////////////////////////////////////////////
/// Resets the timer with the same interval.
///
/// @param t Pointer to the timer object.
////////////////////////////////////////////////////////////////////////////////
void timer_reset(ptimer t)
{
    t->start += t->interval;
}


////////////////////////////////////////////////////////////////////////////////
/// Restarts the timer from the current point in time.
///
/// @param t Pointer to the timer object.
////////////////////////////////////////////////////////////////////////////////
void timer_restart(ptimer t)
{
    t->start = clock_time;
}


////////////////////////////////////////////////////////////////////////////////
/// Tests whether the timer has expired.
///
/// @param t Pointer to the timer object.
///
/// @return Nonzero, if the timer has expired.
////////////////////////////////////////////////////////////////////////////////
unsigned char timer_expired(ptimer t)
{
    return ((unsigned long)(clock_time - t->start) >= t->interval);
}

