/*
 * Sleep.c
 *
 * Suspend and hibernation functions
 *
 *  Created on: Jan 24, 2014
 *      Author: rob
 *  Modified Apr 2018, Thom Maughan
 */

#include "System.h"
#include "Sleep.h"
#include "user_IO.h"
//#include "uartstdio.h"	// User local version with larger RX buffer
#include "SDCard.h"
#include "Init.h"
#include "board_util.h"


extern uint32_t storeSysDataVariables(void);

extern int dbg_flag;  // main.c


void sleep_debug(void);


/*
 * sleep()
 * Puts controller in hibernate mode. Controller will wake by either keystroke or real-time clock
 * alarm match. Action after wake determined by state argument (IDLE or DEPLOYED). IDLE state returns
 * to main menu. DEPLOYED state returns to sample loop.
 * RCG 4/14
 */
void sleep(deploy_state state, uint32_t wake_time)
{
	uint32_t ulStatus;
	time_t current_time;

	sys_data.state = state;		// Save the current state for when we wakeup

	// THOM DEBUG
	//uprintf( "\ncurrent time: %u",ROM_HibernateRTCGet() );
	//uprintf("\nwakeup: %u, fptr: %u", sys_data.nextWakeUp, sys_data.next_gdata_fptr);
	//ROM_SysCtlDelay(MILLISECOND*100);

	if(state == DEPLOYED)
	{
	    if(wake_time > 0)   // THOM Polled Mode is wake_time=0
	    {
            // Check if wake time is in past to prevent lockup
            current_time = ROM_HibernateRTCGet();

            if(wake_time <=  current_time + 2)	// Wake time too soon
            {
                // THOM DEBUG
                //uprintf("\n\nWake time %u is <= current time %u\n", wake_time, current_time);
                //ROM_SysCtlDelay(MILLISECOND*25);
                wake_time = current_time + 2;	// Add a couple seconds
            }

            // Set wake time
            HibernateRTCMatchSet(0, wake_time);

            //Set wake condition on wake pin
            ROM_HibernateWakeSet(HIBERNATE_WAKE_PIN | HIBERNATE_WAKE_RTC);

            //uprintf("\n\nDebug in sleep.c line 56, Sleeping until ");
            //print_time_t_Time(wake_time);

            if(sys_data.test_mode == 1 || sys_data.output == VERBOSE)
            {
                uprintf("\n\nSleeping until ");
                print_time_t_Time(wake_time);
            }
	    } // if wake_time >0
	    else
	    {
	        // Polled mode (RTC is not used to wake)
	        ROM_HibernateWakeSet(HIBERNATE_WAKE_PIN);
	        uprintf("\n\nSleeping in polled mode..\n\n\n");
	    }
	}

	//else if(state == IDLE)
	else // state IDLE or COMMAND           THOM 8 May 2018  THOM Polled Mode
	{
        ROM_HibernateWakeSet(HIBERNATE_WAKE_PIN);
        if(state == IDLE)
            uprintf("\n\nSleeping...*");
        //else // COMMAND mode (polling), no print per Yui 17 Apr 2021
        //    uprintf("\n\nhahahaha\n");

		if(sys_data.output == VERBOSE)
		    uprintf("hit any key to wake\n\n\n");

        ROM_SysCtlDelay(MILLISECOND*25);
	}

//#ifdef  DEBUGSLEEP
	// Let message print out
	UARTFlushTx(0);
	//ROM_SysCtlDelay(MILLISECOND*250);           // THOM - take this out
	//ROM_SysCtlDelay(MILLISECON*25);

	//console_off();        //THOM - move this down for diagnostic printf 23 Sep 2019
//#endif

	// Save our sys_data variables before hibernating!  EEPROM has endurance of 500K Program/Erase cycles.
	// 500,000 seconds is 5.8 days => 8 second sampling interval is 46 days and the EEPROM is at it's limit
	// Copy the sys_data struct into EEPROM.  Last argument ensures # of bytes is multiple of 4
	if(storeSysDataVariables())     //if(EEPROMProgram((uint32_t*) &sys_data, 0x400, (sizeof(sys_data) + 3) & ~3))
	{
		uprintf("Error storing system data (sleep.c).\n");   // NOTE: this wont print due to console_off() above
		error_store("Error storing system data (sleep.c).");
		return;
	}

	console_off();  // THOM new location 24 Sep 2019

//	sleep_debug();      // THOM DEBUG - THIS SHOWS WR_COMPLETE interrupt is active

	// Clear any pending interrupts
	ulStatus = ROM_HibernateIntStatus(0);
	ROM_HibernateIntClear(ulStatus);

	ROM_HibernateRTCTrimSet (0x7FFF);	// Make sure trim is set to default to prevent lockup (see errata)

#if BOARD_SEAPHOX == 1       // if the board is the SEAPHOX then power is not removed so use VDD3ON, otherwise on the MFET, this control of VDD3ON will not allow a proper sleep
	// Disable CPU control of GPIO pins (sets VDD3ON bit)
	HibernateGPIORetentionEnable();
#endif

	// Tell hibernation module we want to sleep
	ROM_HibernateRequest();

    ROM_SysCtlDelay(100*MILLISECOND);    // Give time to hibernate.

    console_on();  // THOM new 5 Aug 2020 - debugging brds that dont sleep

    // Shouldn't get to this point, something went wrong - Typically it is due to running MFET compiled code on the SEAPHOX
    uprintf("\n\n\nHibernation error!  Possible cause: remove JP1 on MFET board \n\n\n");
    error_store("Hibernation error.  Possible cause: remove JP1 on MFET board.  \n");

    uprintf("\n\n\nCalling SysCtrlReset...\n\n\n");
    ROM_SysCtlDelay(100*MILLISECOND);

    // Code should never get here, but just in case, reset
    ROM_SysCtlReset();


}



/*
#define HIBERNATE_INT_VDDFAIL   0x00000080
#define HIBERNATE_INT_RESET_WAKE                                              \
                                0x00000040
#define HIBERNATE_INT_GPIO_WAKE 0x00000020
#define HIBERNATE_INT_WR_COMPLETE                                             \
                                0x00000010
--#define HIBERNATE_INT_PIN_WAKE  0x00000008
#define HIBERNATE_INT_LOW_BAT   0x00000004
--#define HIBERNATE_INT_RTC_MATCH_0                                             \
                                0x00000001
*/

void sleep_debug(void)
{
    uint32_t uiStatus;

    uiStatus = ROM_HibernateIntStatus(false);   // Read the status to determine cause of wake.
    ROM_HibernateIntClear(uiStatus);            // Clear the hibernate interrupt

    // Wake pin
    if(uiStatus & HIBERNATE_INT_PIN_WAKE)           // 0x00000008
    {
        //uprintf("\nWake by pin");       // THOM DEBUG (comment this line out)

        if(sys_data.state == DEPLOYED)  // User hit key during sleep while deployed, enter command mode.
        {
            //uprintf(" - state = DEPLOYED ");       // THOM DEBUG (comment this line out)
            sys_data.state = COMMAND;
        }
    }
    else if(uiStatus & HIBERNATE_INT_RTC_MATCH_0)   // 0x00000001  // RTC match, Take a sample
    {
        //uprintf("\nWake by RTC match");     // THOM DEBUG (comment this line out)
        sys_data.state = DEPLOYED;
    }
    else if(uiStatus & HIBERNATE_INT_VDDFAIL)       // 0x00000080
    {
        //uprintf("\nWake by HIB INT VDDFAIL");     // THOM DEBUG (comment this line out)
        sys_data.state = IDLE;      // Drop through to main menu
    }

    else if(uiStatus & HIBERNATE_INT_RESET_WAKE)        // 0x00000040
    {
        //uprintf("\nWake by HIB INT RESET WAKE");     // THOM DEBUG (comment this line out)
        sys_data.state = IDLE;      // Drop through to main menu
    }
    else if(uiStatus & HIBERNATE_INT_GPIO_WAKE)         // 0x00000020
    {
        //uprintf("\nWake by HIB INT GPIO WAKE");     // THOM DEBUG (comment this line out)
        sys_data.state = IDLE;      // Drop through to main menu
    }
    else if(uiStatus & HIBERNATE_INT_WR_COMPLETE)       // 0x00000010
    {
        //uprintf("\nWake by HIB INT WR COMPLETE");     // THOM DEBUG (comment this line out)
        sys_data.state = IDLE;      // Drop through to main menu
    }
    else if(uiStatus & HIBERNATE_INT_PIN_WAKE)          // 0x00000008
    {
        //uprintf("\nWake by HIB INT PIN WAKE");     // THOM DEBUG (comment this line out)
        sys_data.state = IDLE;      // Drop through to main menu
    }
    else if(uiStatus & HIBERNATE_INT_LOW_BAT)           // 0x00000004
    {
        //uprintf("\nWake by HIB INT LOW BAT");     // THOM DEBUG (comment this line out)
        sys_data.state = IDLE;      // Drop through to main menu
    }
    // Reset button
    else
    {
        //uprintf("\nWake by Reset Button");      // THOM DEBUG (comment this line out)
        sys_data.state = IDLE;      // Drop through to main menu
    }
}

/*
 * sleep_handler()
 * Determines what woke the module (wake pin, RTC match, cold boot).
 * Drops through to Main Menu if woke by keystroke and status is IDLE.
 * Sets status as DEPLOYED if woke by RTC match (status should already be DEPLOYED).
 * Sets status to COMMAND if woke by keystroke and status is DEPLOYED.
 * Anything else is assumed to be a power-on-reset.
 * RCG 4/14
 * TM 16 Apr 2018 - MFET_BOARD sleep issues
 *
 */
void sleep_handler(void)
{
	uint32_t uiStatus;

	int dbgFlg = 0;

	// Need to enable the Hibernation peripheral after wake/reset, before using it.
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_HIBERNATE);

	// EnableExpClk should always be called, even if the module was already enabled,
	// because this function also initializes some timing parameters.
	ROM_HibernateEnableExpClk(ROM_SysCtlClockGet());

	// Check if we just came out of hibernate mode
	if(ROM_HibernateIsActive())
	{
		uiStatus = ROM_HibernateIntStatus(false);	// Read the status to determine cause of wake.
		ROM_HibernateIntClear(uiStatus);			// Clear the hibernate interrupt

		// Wake pin
		if(uiStatus & HIBERNATE_INT_PIN_WAKE)           // 0x00000008
		{
		    if(dbgFlg == 1)	uprintf("\nWake by pin\n");       // THOM DEBUG (comment this line out)

			if(sys_data.state == DEPLOYED)	// User hit key during sleep while deployed, enter command mode.
			{
				sys_data.state = COMMAND;
			}
		}

		// RTC match
		else if(uiStatus & HIBERNATE_INT_RTC_MATCH_0)	// 0x00000001  Take a sample
		{
		    if(dbgFlg == 1) uprintf("\nWake by RTC match\n");     // THOM DEBUG (comment this line out)
			sys_data.state = DEPLOYED;
		}
		else if(uiStatus & HIBERNATE_INT_VDDFAIL)       // 0x00000080
		{
		    if(dbgFlg == 1) uprintf("\nWake by HIB INT VDDFAIL\n");     // THOM DEBUG (comment this line out)
            sys_data.state = IDLE;      // Drop through to main menu
		}

		else if(uiStatus & HIBERNATE_INT_RESET_WAKE)        // 0x00000040
		{
		    if(dbgFlg == 1) uprintf("\nWake by HIB INT RESET WAKE\n");     // THOM DEBUG (comment this line out)
            sys_data.state = IDLE;      // Drop through to main menu
		}
		else if(uiStatus & HIBERNATE_INT_GPIO_WAKE)         // 0x00000020
		{
		    if(dbgFlg == 1) uprintf("\nWake by HIB INT GPIO WAKE\n");     // THOM DEBUG (comment this line out)
            sys_data.state = IDLE;      // Drop through to main menu
		}
		else if(uiStatus & HIBERNATE_INT_WR_COMPLETE)       // 0x00000010
		{
		    if(dbgFlg == 1) uprintf("\nWake by HIB INT WR COMPLETE\n");     // THOM DEBUG (comment this line out)
            sys_data.state = IDLE;      // Drop through to main menu
		}
		else if(uiStatus & HIBERNATE_INT_PIN_WAKE)          // 0x00000008
		{
		    if(dbgFlg == 1) uprintf("\nWake by HIB INT PIN WAKE\n");     // THOM DEBUG (comment this line out)
            sys_data.state = IDLE;      // Drop through to main menu
		}
		else if(uiStatus & HIBERNATE_INT_LOW_BAT)           // 0x00000004
		{
		    if(dbgFlg == 1) uprintf("\nWake by HIB INT LOW BAT\n");     // THOM DEBUG (comment this line out)
            sys_data.state = IDLE;      // Drop through to main menu
		}
		// Reset button
		else
		{
		    if(dbgFlg == 1) uprintf("\nWake by Reset Button\n");      // THOM DEBUG (comment this line out)
			sys_data.state = IDLE;		// Drop through to main menu
		}

		// THOM - need to check VDD3ON usage from MFET (TODO)
		HibernateGPIORetentionDisable();	// Return control of GPIO pins to CPU (clears VDD3ON bit)
	}

//	uprintf( "\n\nRTC Trim: %X", HibernateRTCTrimGet() );

	// THOM NOTE: this code does not execute at power on wakeup

	// Otherwise, power on reset, finish configuring hibernation module
	if(!(uiStatus & (HIBERNATE_INT_PIN_WAKE | HIBERNATE_INT_RTC_MATCH_0)))
	{
	    //uprintf("\nHey Man, 200msec delay in Sleep.c in sleep_handler\n\n");

	    if(dbgFlg == 1) uprintf("\nWake by power on reset\n");
		HibernateClockConfig(HIBERNATE_OSC_LOWDRIVE);

		// Next two lines prevent hibernate lockup (http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/275336.aspx)
		ROM_HibernateRTCTrimSet (0x7FFF);
		HibernateIntDisable (HIBERNATE_INT_PIN_WAKE | HIBERNATE_INT_LOW_BAT | HIBERNATE_INT_RTC_MATCH_0 | HIBERNATE_INT_WR_COMPLETE);

		// Let 32 kHz crystal stabilize
		ROM_SysCtlDelay(MILLISECOND*200);
		ROM_HibernateRTCEnable();	// Enable Real Time Clock

		sys_data.state = IDLE;		// Drop through to main menu
		if(dbgFlg == 1) uprintf("\nPower On Reset\n");
	}

}

