/*******************************************************************************
 * (c) Copyright 2012-2013 Microsemi SoC Products Group.  All rights reserved.
 *
 * Flash Freeze system service demo.
 *
 *
 *
 */
#include "drivers/mss_sys_services/mss_sys_services.h"
#include "drivers/mss_uart/mss_uart.h"
#include "drivers/mss_rtc/mss_rtc.h"


//#include "drivers/mss_timer/mss_timer.h"
//#include "CMSIS/system_m2sxxx.h"

// SRAM and eNVM specific variables
#define SRAM_BASE_ADDR 0x30000000

//#define eNVM_BASE_ADDR 0x60000000
#define eNVM_BASE_ADDR 0x6007DF00


#define no_of_data_words 16
#define no_of_SRAMdata_words 64
#define SRAM_depth 64

/* RTC prescaler value for 1Mhz clock.
* In this demo the RTC clock source is set to be 1MHz
* for different clock source setting adjust the RTC_PRESCALER accordingly */
#define RTC_PRESCALER       (1000000u - 1u)

/* RTC time out value .*/
#define FLASH_FREEZE_TIMEOUT    10u



size_t UART_Polled_Rx
(
    mss_uart_instance_t * this_uart,
    uint8_t * rcv_buff,
    size_t buff_size
);

// eNVM access prototype function
void nvm_access (void);

// SRAM access prototype function
void SRAM_read (void);

void delay(int n);


/*==============================================================================
  Messages displayed over the UART.
 */
const uint8_t g_greeting_msg[] =
"\r\n\r\n\
**********************************************************************\r\n\
********* SmartFusion2 Flash*Freeze System Services Example **********\r\n\
**********************************************************************\r\n\
\r\n This example project exercises the Flash*Freeze system services.\r\n\
\r\n\
----------------------------------------------------------------------\r\n";

const uint8_t g_ff_request_msg[] =
"\r\n\r\nRequesting Flash*Freeze shutdown.\r\n";

const uint8_t g_ff_shutdown_msg[] =
"\r\n\r\n\
----------------------------------------------------------------------\r\n\
!!!!!!!!!!!!!!!!!!!!!!! Flash*Freeze shutdown. !!!!!!!!!!!!!!!!!!!!!!!\r\n\
----------------------------------------------------------------------";

const uint8_t g_ff_exit_msg[] =
"\r\n\r\n\
----------------------------------------------------------------------\r\n\
!!!!!!!!!!!!!!!!!!!!!!!! Flash*Freeze exited. !!!!!!!!!!!!!!!!!!!!!!!!\r\n\
----------------------------------------------------------------------\r\n";

const uint8_t g_rtc_wakeup_exit_msg[] =
"\r\n\r\n\
----------------------------------------------------------------------\r\n\
!!!!!!!!!!!!!!!!!!!!!!!!! RTC wake up event. !!!!!!!!!!!!!!!!!!!!!!!!!\r\n\
----------------------------------------------------------------------\r\n";

const uint8_t g_ff_success_msg[] =
"\r\n\r\nFlash*Freeze system service request success.\r\n\
---------------------\r\n\
\r\n";

const uint8_t g_ff_error_msg[] =
"\r\n\r\nFlash*Freeze system service request error.\r\n";



const uint8_t seperator1[] = "\r\n ------------------------------------------------------\r\n";
/*==============================================================================
  Private functions.
 */
static void display_menu(void);
static void RTC_Setup_Start(void);

static void display_greeting(void);
static void init_uart(void);
static void sys_services_event_handler(uint8_t opcode, uint8_t response);

/*==============================================================================
  UART selection.
  Replace the line below with this one if you want to use UART0 instead of
  UART1:
  mss_uart_instance_t * const gp_my_uart = &g_mss_uart1;
 */
mss_uart_instance_t * const gp_my_uart = &g_mss_uart1;



/*==============================================================================
  Main function.
 */


//Global variables.

// This variable indicate if device is in F*F or not in F*F mode
	uint8_t FF=0;

	uint8_t option = 0;
	size_t option_size;

int main(void)
{

    /*
     * Initialize peripherals.
     */
    MSS_SYS_init(sys_services_event_handler);

    init_uart();

	/*
	 * Display greeting message.
	 */
	display_greeting();

	/* Print the menu options to the user */
	display_menu();


    while (1)
    {

    	if (option_size > 0)
    	{
    		switch(option)
    		{
				// status of the F*F service request
				uint8_t status;

				int8_t tx_complete;


    		case '1' :
    			// Check if the F*F variable was set to indicates that the device was put into F*F
    			if (FF == 0)
    			{

    				/*
					* Display a message indicating that Flash Freeze shutdown was
					* requested.
					* Please note that we must wait until the last character has been
					* transmitted by the UART. Entering flash freeze while the message
					* is sent by the UART would result in a corrupted message being
					* sent because of the switch to the standby clock taking place as
					* part of the Flash Freeze shutdown. The switch to the standby
					* clock would result in the UART baud rate changing while the
					* message is sent since the frequency clocking the UART would
					* change.
					*/
					MSS_UART_polled_tx_string(gp_my_uart, g_ff_request_msg);
					do {
							tx_complete = MSS_UART_tx_complete(gp_my_uart);
					} while(0 == tx_complete);

					/*
					 * Request Flash Freeze shutdown.
					 *  MSS_SYS_flash_freeze(MSS_SYS_FPGA_POWER_DOWN | MSS_SYS_ENVM0_POWER_DOWN | MSS_SYS_MPLL_POWER_DOWN);
					 *  If you want to boot from eNVM in release mode, you cann't power down the eNVM
					*/

					//status = MSS_SYS_flash_freeze(MSS_SYS_FPGA_POWER_DOWN);
					status = MSS_SYS_flash_freeze(MSS_SYS_FPGA_POWER_DOWN | MSS_SYS_MPLL_POWER_DOWN);


					if(MSS_SYS_SUCCESS == status)
					{
						FF =1;
						MSS_UART_polled_tx_string(gp_my_uart, g_ff_success_msg);
						display_menu();

					}
					else
					 {
						MSS_UART_polled_tx_string(gp_my_uart, g_ff_error_msg);
					 }
					}
    		        else
					 {
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n..... Device is already in Flash*Freeze......\n" );
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n\r\n" );
						display_menu();

					 }

    		break;

    		case '2' :

    			// when device was put into F*F, the FF variable is set to '1'.
    			// Check to see if FF is '1'.
    			if (FF == 1)
   				 {
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n..........Can not Write to SRAM because Device is in Flash*Freeze......\r\n" );
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n\r\n" );
						display_menu();

   				 }
    			else
					{
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"........Writing from eNVM to SRAM......\r\n" );
						// Call the nvm access function to take data from eNVM and write it to the SRAM
						nvm_access ();

						display_menu();

					}

    		break;

    		case '3' :

    			 if (FF == 1)
    				{
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n..........Can not Read SRAM because Device is in Flash*Freeze......\r\n" );
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n\r\n" );

						display_menu();

    				}
    			 else
    			   {
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"............ SRAM read......\r\n" );
						// Call the SRAM_read function to read the data in the SRAM
						SRAM_read ();

						display_menu();

    			  }
    		break;


    		case '4' :

    			 if (FF == 0)
					 {
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n........RTC wake up: Device is not in Flash*Freeze......\r\n" );
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n\r\n" );

						display_menu();

					 }
    			 else
					 {

						 MSS_UART_polled_tx_string(gp_my_uart, g_rtc_wakeup_exit_msg);
						 MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n.. RTC Flash*Freeze wake-up in progress......\r\n" );
						 RTC_Setup_Start();

						 option_size = 0;


					 }

    		break;

    		default :

						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n\r\n" );
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n ....Invalid..... choice\r\n" );
						MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n\r\n" );

    		break;

    		}
    	}

    }

}

/*==============================================================================
  System Services event handler.
 */
static void sys_services_event_handler(uint8_t opcode, uint8_t response)
{
    volatile uint32_t inc;

    if(FLASH_FREEZE_SHUTDOWN_OPCODE == opcode)
    {
        /* Delay */
    	delay(1000);


        /* Re-initialize the UART. */
        init_uart();
        MSS_UART_polled_tx_string(gp_my_uart, g_ff_shutdown_msg);

    }
    else if(FLASH_FREEZE_EXIT_OPCODE == opcode)
    {
    	FF = 0;
        /* Delay */
        delay(1000);

        /* Re-initialize the UART. */
        init_uart();

        MSS_UART_polled_tx_string(gp_my_uart, g_ff_exit_msg);
        display_menu();

    }

}
//============================================
/*==============================================================================
  Initialize the UART.
  This function needs to be called when the UART clock frequency changes as a
  result of switching between the main and standby clocks. Entering Flash Freeze
  results in the system becoming clocked by the standby clock. Exiting Flash*Freeze
  results in the system becoming clocked by the main system clock.
 */
static void init_uart(void)
{
    MSS_UART_init(gp_my_uart,
                  MSS_UART_57600_BAUD,
                     MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT);
}

/*==============================================================================
  Display menu options message when application is started.
 */
static void display_greeting(void)
{
    MSS_UART_polled_tx_string(gp_my_uart, g_greeting_msg);
}

/*==============================================================================
  Setup and start the RTC.
 */
static void RTC_Setup_Start(void)
{
	MSS_RTC_init(MSS_RTC_BINARY_MODE, RTC_PRESCALER);
	MSS_RTC_reset_counter();
	 /*
     * Set the RTC to generate a single shot alarm to exit Flash*Freeze.
     */
	MSS_RTC_set_binary_count_alarm(FLASH_FREEZE_TIMEOUT, MSS_RTC_SINGLE_SHOT_ALARM);

	MSS_RTC_enable_irq();
	MSS_RTC_start();
}

/*==============================================================================
  Display the menu options message .
 */
static void display_menu(void)
{
	/* Print the menu options to the user */
		MSS_UART_polled_tx_string( gp_my_uart,(const uint8_t*)"************MAIN MENU*************\r\n" );
		MSS_UART_polled_tx_string( gp_my_uart,(const uint8_t*)"\n\rSelect your choice and press Enter\n\r 1. Flash*Freeze \n\r 2. Write to SRAM \n\r 3. Read from SRAM \n\r 4. RTC Wake-Up\n\r");
		option_size = UART_Polled_Rx( gp_my_uart, &option, sizeof(option) );
}



/*==============================================================================
  RTC wake up interrupt service routine.
 */
void RTC_Wakeup_IRQHandler(void)
{
    int8_t tx_complete;

    // Set that F*F variable to zero as indication that it is out of F*F now.
    FF =0;

    do {
        tx_complete = MSS_UART_tx_complete(gp_my_uart);

    } while(0 == tx_complete);


    MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n" );
    MSS_RTC_clear_irq();

}


/*==================================================================================
* used to read the input received from the user
*/

  size_t UART_Polled_Rx ( mss_uart_instance_t * this_uart, uint8_t * rcv_buff, size_t buff_size)
{

	  size_t rcv_size = 0U;


    while( rcv_size < buff_size )
    {
       while ( ((this_uart->hw_reg->LSR) & 0x1) != 0U  )
       {
       rcv_buff[rcv_size] = this_uart->hw_reg->RBR;
           ++rcv_size;
       }
    }

    return rcv_size;
}


/*========================================================================*/

// Read from eNVM and write to SRAM

void nvm_access (void)
{
  uint32_t *nvm_word_ptr, read_data;
  uint32_t *fab_sram_word_ptr;
  volatile int i, k;

  uint8_t fab_sram_word[4] = "0000";
  const uint8_t greating6[] = "\r\n Reading from eNVM...\r\n";
  const uint8_t greating7[] = "\r\n Writing to SRAM...\r\n";
  const uint8_t greating8[] = "\r\n Reading SRAM...\r\n";

  uint8_t tim_val[] = "00000000";
  int j;

  		  nvm_word_ptr = (uint32_t *)eNVM_BASE_ADDR;
  		  fab_sram_word_ptr = (uint32_t *)SRAM_BASE_ADDR;

		  for( i = 0; i< (no_of_data_words)-1; i++)
		  {

			//Read the eNVM content
			read_data = *nvm_word_ptr;

			MSS_UART_polled_tx( &g_mss_uart1, greating6, sizeof(greating6) );
			j = sprintf(tim_val, "%8X", nvm_word_ptr);
			MSS_UART_polled_tx( &g_mss_uart1, tim_val, sizeof(tim_val) );
		    j = sprintf(tim_val, "%8X", read_data);
		    MSS_UART_polled_tx( &g_mss_uart1, tim_val, sizeof(tim_val) );

		    //convert 32 bit to 8 bit
		    fab_sram_word[0] = read_data;
		    fab_sram_word[1] = read_data >> 8;
		    fab_sram_word[2] = read_data >> 16;
		    fab_sram_word[3] = read_data >> 24;

				for	( k = 0; k< 4; k++)
					  {

						//Write each 8 bit to SRAM

						*fab_sram_word_ptr = fab_sram_word[k];

					    MSS_UART_polled_tx( &g_mss_uart1, greating7, sizeof(greating7) );

					   j = sprintf(tim_val, "%8X", fab_sram_word_ptr);
					    MSS_UART_polled_tx( &g_mss_uart1, tim_val, sizeof(tim_val) );

					    j = sprintf(tim_val, "%8X", fab_sram_word[k]);
					    MSS_UART_polled_tx( &g_mss_uart1, tim_val, sizeof(tim_val) );

						// Reading from SRAM as a sanity check to ensure that the value was
					    // written into the SRAM correctly.

					    MSS_UART_polled_tx( &g_mss_uart1, greating8, sizeof(greating8) );
					    read_data = *fab_sram_word_ptr;
					    j = sprintf(tim_val, "%8X", fab_sram_word_ptr);
					   	MSS_UART_polled_tx( &g_mss_uart1, tim_val, sizeof(tim_val) );
					   	j = sprintf(tim_val, "%8X", read_data);
					   	MSS_UART_polled_tx( &g_mss_uart1, tim_val, sizeof(tim_val) );

					    fab_sram_word_ptr = fab_sram_word_ptr + 1;

		  		}
				MSS_UART_polled_tx_string(gp_my_uart,(const uint8_t*)"\r\n" );
		    nvm_word_ptr = nvm_word_ptr + 1;
		  }
}



/*========================================================================*/
// Read from SRAM and print out the content

void SRAM_read (void)
{
  uint32_t *nvm_word_ptr, read_data;
  uint32_t *fab_sram_word_ptr;
  volatile int i, k;

  const uint8_t greating8[] = "\r\n Reading SRAM...\r\n";

  uint8_t tim_val[] = "00000000";
  int j;

  	  fab_sram_word_ptr = (uint32_t *)SRAM_BASE_ADDR;

		  for( i = 0; i<(no_of_SRAMdata_words)-4; i++)
		  {

				 read_data = *fab_sram_word_ptr;

				 MSS_UART_polled_tx( &g_mss_uart1, greating8, sizeof(greating8) );
				 j = sprintf(tim_val, "%8X", fab_sram_word_ptr);
				 MSS_UART_polled_tx( &g_mss_uart1, tim_val, sizeof(tim_val) );
				 j = sprintf(tim_val, "%8X", read_data);
				 MSS_UART_polled_tx( &g_mss_uart1, tim_val, sizeof(tim_val) );
				 MSS_UART_polled_tx( &g_mss_uart1, seperator1, sizeof(seperator1) );


				fab_sram_word_ptr = fab_sram_word_ptr + 1;

		  }

}

/*============================================================
* Delay function
*/
void delay(int n)
{
  while(n>0) {
    n--;
  }
}
