/****************************************************************************/
/* Copyright 2003-2016 MBARI                                                */
/****************************************************************************/
/* Summary  : Main Routine for OASIS Mooring Controller                     */
/* Filename : timer.c                                                       */
/* Author   : Robert Herlien (rah)                                          */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision : 1.0                                                           */
/* Created  : 01/13/2003                                                    */
/*                                                                          */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*                                                                          */
/****************************************************************************/
/* Modification History:                                                    */
/* 13jan2003 rah - created                                                  */
/* 20apr2016 rah - ported to OASIS5 from OASIS4 clock.c                     */
/****************************************************************************/
/* This module implements the RTC clock ticker and sleep handler            */ 
/* The system millisecond ticker is in FreeRTOS/PIC32/port.c                */
/* The system ticker also incrments clkMs, to maintain RTC time to          */
/* millisecond accuracy                                                     */
/****************************************************************************/

#include "GenericTypeDefs.h"
#include <xc.h>
#include <cp0defs.h>
#include <timer.h>
#include <rtc.h>
#include <picConfig.h>
#include <serial.h>
#include <dig_io.h>
#include <utils.h>

#include <time.h>


/************************************************************************/
/*  Declare Assembly language Interrupt Wrappers from port_asm.S        */
/************************************************************************/

extern void __attribute__( (interrupt(IPL5AUTO), vector(_EXTERNAL_4_VECTOR)) ) vPortRTCInterruptHandler(void);


/********************************/
/*      External Data			*/
/********************************/

Extern Int16	gmtOffset;


/********************************/
/*      Global Data             */
/********************************/

/* clkMs is global so system ms tick handler can increment it           */
volatile UINT16  clkMs;                 /* Milliseconds of time of day  */


/********************************/
/*      Module Local Data       */
/********************************/

static volatile time_t  clkTod;         /* Current time of day, seconds */
static volatile UINT32  slpSecs = 0;    /* Time left to sleep           */


/************************************************************************/
/* Function    : tmrBusyWaitUs                                          */
/* Purpose     : Delay a number of microseconds                         */
/* Input       : Microsecs to delay                                     */
/* Outputs     : None                                                   */
/************************************************************************/
void tmrBusyWaitUs(UINT32 us)
{
    UINT32 stCnt = _CP0_GET_COUNT();

    if (us > 1)
        while ((_CP0_GET_COUNT() - stCnt) < (((us-1)*(SYSCLK/1000000))/2))
            ;                               /* wait for usecs to expire     */

}

/************************************************************************/
/* Function    : clkInit                                                */
/* Purpose     : Initialize system time of day clock                    */
/* Input       : None                                                   */
/* Outputs     : 0 if OK, -1 if RTC time was bogus                      */
/************************************************************************/
int clkInit(void)
{
    struct tm   curTm;
    time_t      tmpTod;
    BYTE	clkRegs[4];

#ifdef PROTO
    INT4R = 0;                          /* INT4 = RPD3 = pin 78        */
#else
    INT4R = 7;                          /* INT4 = RPC13 = pin 73        */
#endif
    INTCONCLR = _INTCON_INT4EP_MASK;    /* Interrupt on negative edge   */
    IPC4bits.INT4IP = 2;                /* RTC interrupt priority       */
    IPC4bits.INT4IS = 0;

    // Set Alarm Regs to once/second
    rtcWriteReg(RTC_CTRL, 4);
    clkRegs[0] = clkRegs[1] = clkRegs[2] = clkRegs[3] = 0x80;
    rtcWriteMultRegs(RTC_ALM1_SEC, 4, clkRegs);
    rtcWriteReg(RTC_CTRL, 5);
    rtcWriteReg(RTC_CTRLSTAT, 0);

    do
    {
        IFS0CLR = _IFS0_INT4IF_MASK;
        clkMs = 0;                      /* Init ms to zero              */
		clkTod = rtcTime(&curTm);       /* Get real time of day         */
    } while (IFS0bits.INT4IF);          /* If got new second, try again */

    IEC0SET = _IEC0_INT4IE_MASK;        /* Enable one-second ints       */

    if ((curTm.tm_mon > 11) || (curTm.tm_mday < 1) ||
        (curTm.tm_mday > 31) || (curTm.tm_hour > 23) ||
        (curTm.tm_min >= 60) || (curTm.tm_sec > 60))
        return(-1);
    
    return(0);
}


/************************************************************************/
/* Function    : clkTime                                                */
/* Purpose     : Return time of day                                     */
/* Input       : None                                                   */
/* Outputs     : Current time of day                                    */
/************************************************************************/
time_t clkTime(void)
{
    return(clkTod);

} /* clkTime() */


/************************************************************************/
/* Function    : clkGetTime                                             */
/* Purpose     : Get time of day with milliseconds                      */
/* Input       : Ptrs to time_t and ms                                  */
/* Outputs     : Current time of day                                    */
/************************************************************************/
time_t clkGetTime(time_t *todp, UINT16 *ms)
{
    if (todp)
        *todp = clkTod;

    if (ms)
        *ms = clkMs;
    
    return(clkTod);

} /* clkTime() */


/************************************************************************/
/* Function    : clkSetTime                                             */
/* Purpose     : Set Date and Time to system and RTC                    */
/* Input       : time_t for new time									*/
/* Outputs     : None                                                   */
/************************************************************************/
int clkSetTime(time_t newTod)
{
    rtcSetTime(newTod);
    clkTod = newTod;
    clkMs = 0;

} /* clk_write() */


/************************************************************************/
/* Function    : clkPrintTime                                           */
/* Purpose     : Print date & time to serial port                       */
/* Input       : Ptr to time to print.  NULL means now					*/
/* Outputs     : None                                                   */
/************************************************************************/
void clkPrintTime(time_t *tod)
{
	struct tm     *tmptr;
	char          buf[32];

	if (tod != NULL)
		tmptr = localtime(tod);
	else
		tmptr = localtime((time_t *)&clkTod);

	xprintf("%04d/%02d/%02d %02d:%02d:%02d", tmptr->tm_year + 1900,
			tmptr->tm_mon+1, tmptr->tm_mday, tmptr-> tm_hour,
			tmptr->tm_min, tmptr->tm_sec);

} /* clkPrintTime() */


/************************************************************************/
/* Function	   : clkLocalHour											*/
/* Purpose	   : Get hour in local time zone							*/
/* Input	   : None													*/
/* Outputs	   : Hour (0 - 23)											*/
/************************************************************************/
Nat16 clkLocalHour(void)
{
    struct tm   curTm;

	rtcTime(&curTm);					/* Get real time of day         */
	return((curTm.tm_hour + gmtOffset + 24) % 24);

} /* clkLocalHour() */


/************************************************************************/
/* Function    : ioSleep                                                */
/* Purpose     : Sleep for given number of seconds                      */
/* Input       : Time to sleep                                          */
/* Outputs     : None                                                   */
/************************************************************************/
void ioSleep(UINT32 secs)
{
    IEC0CLR = _IEC0_T1IE_MASK;          /* Turn off ms interrupt        */
    digTx0Dis();                        /* Turn off Tx                  */
    digTx1Dis();
    digTx2Dis();
    digTx3Dis();
    digTx4Dis();
	digBlinkyOff();						/* Turn off Blinky				*/
	digTest3Off();

	picUnlock();					
	OSCCONSET = _OSCCON_SLPEN_MASK;		/* Set sleep bit                */
	picLock();

	setupConsoleWakeFromSleep();		/* Set up hardware for console wakeup*/

    for (slpSecs = secs; slpSecs && (consoleWakeupReceived() == 0); )
    {
		_wait();
    }

    clkMs = 0;                          /* Init ms to zero              */
    clkTod = rtcTime(NULL);             /* Resync time of day           */

	clearConsoleWakeFromSleep();		/* Restore console				*/
    IEC0SET = _IEC0_T1IE_MASK;          /* Turn on ms interrupt         */
    digTx0En();                         /* Turn on Tx                   */

} /* ioSleep() */


/************************************************************************/
/**********               ISRs                                  *********/
/************************************************************************/

/************************************************************************/
/* Function    : RTCack                                                 */
/* Purpose     : Interrupt Completion routine for RTC                   */
/* Input       : None                                                   */
/************************************************************************/
static void RTCack(void)
{
	rtcTaskPinOn();						/* For debugging				*/
	rtcWriteReg(RTC_CTRLSTAT, 0x00);    /* Clear the int in the RTC     */
	if (slpSecs)                        /* Decrement sleep counter      */
		slpSecs--;
#if (configUSE_PREEMPTION == 0)			/* If no preemption, we can get	*/
	else								/*  locked out and lose time	*/
		clkTod = rtcTime(NULL);	
#endif
	rtcTaskPinOff();					/* For debugging				*/
}


/************************************************************************/
/* Function    : _RTCInterrupt                                          */
/* Purpose     : External Interrupt 4, servicing the RTC 1/sec interrupt*/
/* Input       : None                                                   */
/* Comment     : Called from assembly language wrapper in port_asm.S    */
/************************************************************************/
void _RTCInterrupt(void)
{
	BaseType_t	xHigherPriorityTaskWoken;

    rtcPinOn();

    clkTod++;                           /* increment time of day        */
    clkMs = 0;                          /* clear ms count               */
	xHigherPriorityTaskWoken = pdFALSE;

	xTimerPendFunctionCallFromISR(RTCack, NULL, 0, &xHigherPriorityTaskWoken);

	portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);

	IFS0CLR = _IFS0_INT4IF_MASK;        /* clear the interrupt flag     */
    rtcPinOff();
}
