/*
 * 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 "IO.h"
#include "uartstdio.h"	// User local version with larger RX buffer
#include "SDCard.h"
#include "Init.h"

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

//	uprintf( "\ncurrent time: %u",ROM_HibernateRTCGet() );
//	uprintf("\nwakeup: %u", sys_data.nextWakeUp);

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

            if(wake_time <=  current_time + 2)	// Wake time too soon
            {
                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);

            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);
		uprintf("\n\nSleeping...hit any key to wake\n\n\n");
	}

//#ifdef  DEBUGSLEEP
	// Let message print out
	UARTFlushTx(0);
	ROM_SysCtlDelay(MILLISECOND*250);
	console_off();
//#endif

	// Save our sys_data variables before hibernating!
	// Copy the sys_data struct into EEPROM.  Last argument ensures # of bytes is multiple of 4
	if(EEPROMProgram((uint32_t*) &sys_data, 0x400, (sizeof(sys_data) + 3) & ~3))
	{
		uprintf("Error storing system data.\n");   // NOTE: this wont print due to console_off() above
		error_store("Error storing system data.");
		return;
	}

//	sleep_debug();      // THOM DEBUG - THIS SHOWS WR_COMPLETE interrupt is active and no errors from

	// 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_SEAFET == 1       // if the board is the SEAFET 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(MILLISECOND);	// Give time to hibernate.

	// Shouldn't get to this point, something went wrong
	uprintf("\n\n\nHybernation error!\n\n\n");
	error_store("Hybernation error.");

}

/*
 * 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
 *
 */

/*
#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
    }
}


void sleep_handler(void)
{
	uint32_t uiStatus;

	// 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
		{
			//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.
			{
				sys_data.state = COMMAND;
			}
		}

		// RTC match
		else if(uiStatus & HIBERNATE_INT_RTC_MATCH_0)	// 0x00000001  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
		}

		// 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() );

	// Otherwise, power on reset, finish configuring hibernation module
	if(!(uiStatus & (HIBERNATE_INT_PIN_WAKE | HIBERNATE_INT_RTC_MATCH_0)))
	{
		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
//		uprintf("\nPower On Reset");
	}

}

