/******************************************************************************
*
* Freescale Semiconductor Inc.
* (c) Copyright 2011-2012 Freescale Semiconductor, Inc.
* ALL RIGHTS RESERVED.
*
***************************************************************************//*!
*
* @file flash.c
*
* @author a13984
*
* @version 0.0.1
*
* @date Sep 24, 2011
*
* @brief application entry point which performs application specific tasks. 
*
*******************************************************************************
*
* provide a demo for how to initialize the PT60, output messages via SCI, 
* flash operations, etc.
* NOTE:
*	printf call may occupy a lot of memory (around 1924 bytes), so please
*	consider your code size before using printf.
******************************************************************************/
#include "flash.h"
/******************************************************************************
* Global variables
******************************************************************************/
extern char __SEG_START_FLASH_ROUTINES[];
extern char __SEG_SIZE_FLASH_ROUTINES[];




/******************************************************************************
* Constants and macros
******************************************************************************/


/******************************************************************************
* Local types
******************************************************************************/

/******************************************************************************
* Local function prototypes
******************************************************************************/

/******************************************************************************
* Local variables
******************************************************************************/

/******************************************************************************
* Local functions
******************************************************************************/

/******************************************************************************
* Global functions
******************************************************************************/
/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: Flash_CopyInRAM
*
* @brief This section of the code is the one that copies the routine into RAM. 
* It is following the steps  documented in Technical Note 228
*        
* @param  
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/

/*
Start_Copy_In_RAM refers to the begining of the segment ToCopyToRAM. 
This segment contains the functions after they have been copied to RAM.
*/
#define Start_Copy_In_RAM __SEG_START_FLASH_ROUTINES
#define Size_Copy_In_RAM __SEG_SIZE_FLASH_ROUTINES

void	Flash_CopyInRAM(void) 
{
  char *srcPtr, *dstPtr;
  uint16_t count;
  uint16_t sizeBytes = (uint16_t)Size_Copy_In_RAM;
  srcPtr = (char*)Start_Copy_In_RAM;
  dstPtr = (char*)&Flash_Program1LongWord;	// must be the start address of RAM_CODE
  for (count = 0; count < sizeBytes;  count++) 
  {
    *dstPtr++ = *srcPtr++;
  }
}


void	Flash_CopyRouinte2RAM(char *func, uint16_t sizeFunc) 
{
  char *srcPtr, *dstPtr;
  uint16_t count;
  srcPtr = func;
  dstPtr = (char*)&Flash_Program1LongWord;	// must be the start address of RAM_CODE
  for (count = 0; count < sizeFunc;  count++) 
  {
    *dstPtr++ = *srcPtr++;
  }
}

/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: Flash_Init
*
* @brief initialize flash driver
*        
* @param  
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/

uint16_t Flash_Init(void)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	uint8_t clkDIV = BUS_CLK_HZ/1000000L - 1;
	
	if(!NVM_FSTAT_CCIF)
	{
		err |= FLASH_ERR_INIT_CCIF;
		return err;
	}
	/* initialize the flash clock to be within spec 1MHz 
	 * 
	 */
	if(!NVM_FCLKDIV_FDIVLCK)
	{
		/* FCLKDIV register is not locked */
		if(NVM_FCLKDIV_FDIVLD && (NVM_FCLKDIV_FDIV != clkDIV))
		{
			/* flash clock prescaler is loaded but with wrong value */
			err |= FLASH_ERR_INIT_FDIV;
			return (err);
		}
		NVM_FCLKDIV_FDIV =  clkDIV;
#if 0
		NVM_FCLKDIV_FDIVLCK = 1; /* lock the prescaler */
#endif
	}
	else
	{
		/* FCLKDIV register is locked */
		if((NVM_FCLKDIV_FDIV != clkDIV))
		{
			/* flash clock prescaler is wrong */
			err |= FLASH_ERR_INIT_FDIV;
		}
	}
	return (err);	
}

/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: FlashProgram
*
* @brief program flash routine, each program operation supports up to 2 longwords
* 		 programming
*        
* @param  
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/

uint16_t Flash_Program(uint16_t wNVMTargetAddress, uint8_t *pData, uint16_t sizeBytes)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	uint16_t w2LongWordCount = sizeBytes>>3;
	uint8_t  wLeftBytes = (sizeBytes & 0x07);
	uint16_t wLeftLongWords = wLeftBytes>>2;
	uint16_t wTargetAddress = wNVMTargetAddress;
	uint32_t dwData0,dwData1;
	uint32_t *pdwData = (uint32_t*)pData;
	int32_t  i;
	
	// Check address to see if it is aligned to 4 bytes
	// Global address [1:0] must be 00.
	if(wNVMTargetAddress & 0x03)
	{
		err = FLASH_ERR_INVALID_PARAM;
		return (err);
	}
	// Loop for the two longwords (8 bytes) programming
	for(i = 0; i < w2LongWordCount; i++)
	{
		dwData0 = *pdwData++;
		dwData1 = *pdwData++;
		err = Flash_Program2LongWords(wTargetAddress, dwData0, dwData1);
		if(err)
		{
			goto EndP;
			break;
		}
		wTargetAddress += 8;
	}
	// Loop for the single longword (4 bytes) programming
	for(i = 0; i < wLeftLongWords; i++)
	{
		dwData0 = *pdwData++;
		err = Flash_Program1LongWord(wTargetAddress, dwData0);
		if(err)
		{
			asm(BGND);
			goto EndP;
			break;
		}
		wTargetAddress += 4;
	}
	wLeftBytes = (wLeftBytes-(wLeftLongWords<<2));	// calculate the # of bytes that are not programmed
	dwData0 = 0;
	pData = pdwData;	// pointer to the left bytes
	for(i = wLeftBytes; i >0; i--)
	{
		dwData0 <<= 8;
		dwData0 |= *pData++;	// MSB byte first
	}
	// Calculate how many bytes need to be filled with 0xFFs
	// in order to form a single longword for the left bytes of data
	wLeftBytes = 4 - wLeftBytes;	
	//  
	for(i = wLeftBytes; i >0; i--)
	{
		dwData0 <<= 8;
		dwData0 |= 0xFF;	// MSB byte first
	}	
	// Now program the last longword
	err = Flash_Program1LongWord(wTargetAddress, dwData0);	
EndP:	
	return (err);
}

/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: EEPROM_Program
*
* @brief program EEPROM routine, each program operation supports up to 4 bytes
* 		 programming
*        
* @param  
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/

uint16_t EEPROM_Program(uint16_t wNVMTargetAddress, uint8_t *pData, uint16_t sizeBytes)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	uint8_t  wLeftBytes = (sizeBytes & 0x3);
	uint16_t wLeftLongWords = sizeBytes>>2;
	uint16_t wTargetAddress = wNVMTargetAddress;
	int32_t  i;
	

	// Loop for 4 bytes programming
	for(i = 0; i < wLeftLongWords; i++)
	{
		err = EEPROM_ProgramUpto4Bytes(wTargetAddress, pData, 4);
		if(err)
		{
			goto EndP;
			break;
		}
		wTargetAddress += 4;
		pData += 4;
	}
	if(wLeftBytes>0)
	{
		err = EEPROM_ProgramUpto4Bytes(wTargetAddress, pData, wLeftBytes);	
	}
EndP:	
	return (err);
}


#pragma CODE_SEG FLASH_ROUTINES
uint16_t Flash_Program1LongWord(uint16_t wNVMTargetAddress, uint32_t dwData)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	
	// Check address to see if it is aligned to 4 bytes
	// Global address [1:0] must be 00.
	if(wNVMTargetAddress & 0x03)
	{
		err = FLASH_ERR_INVALID_PARAM;
		return (err);
	}
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[17:16]	
	NVM_FCCOBHI = FLASH_CMD_PROGRAM;// program FLASH command
	NVM_FCCOBLO = 0;// memory address bits[17:16]
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	// Write index to specify the word0 (MSB word) to be programmed
	NVM_FCCOBIX = 0x2;
	// Write the word  0
	NVM_FCCOB = (dwData>>16) & 0xFFFF;
	// Write index to specify the word1 (LSB word) to be programmed
	NVM_FCCOBIX = 0x3;
	// Write the word1 
	NVM_FCCOB = (dwData) & 0xFFFF;	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}	
	
	return (err);
}


uint16_t Flash_Program2LongWords(uint16_t wNVMTargetAddress, uint32_t dwData0, uint32_t dwData1)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	
	// Check address to see if it is aligned to 4 bytes
	// Global address [1:0] must be 00.
	if(wNVMTargetAddress & 0x03)
	{
		err = FLASH_ERR_INVALID_PARAM;
		return (err);
	}
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[17:16]	
	NVM_FCCOBHI = FLASH_CMD_PROGRAM;// program FLASH command
	NVM_FCCOBLO = 0;// memory address bits[17:16]
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	// Write index to specify the word0 (MSB word) to be programmed
	NVM_FCCOBIX = 0x2;
	// Write the word 0
	NVM_FCCOB = (dwData0>>16) & 0xFFFF;
	// Write index to specify the word1 (LSB word) to be programmed
	NVM_FCCOBIX = 0x3;
	// Write word 1
	NVM_FCCOB = (dwData0) & 0xFFFF;
	
	// Write index to specify the word0 (MSB word) to be programmed
	NVM_FCCOBIX = 0x4;
	// Write the word2
	NVM_FCCOB = (dwData1>>16) & 0xFFFF;
	// Write index to specify the word1 (LSB word) to be programmed
	NVM_FCCOBIX = 0x5;
	// Write word 3
	NVM_FCCOB = (dwData1) & 0xFFFF;
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}	
	
	return (err);
}





/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: Flash_EraseSector
*
* @brief erase flash sector, each flash sector is of 512 bytes long,
* global address [1:0] = 00.
*        
* @param  
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
uint16_t Flash_EraseSector(uint16_t wNVMTargetAddress)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	
	// Check address to see if it is aligned to 4 bytes
	// Global address [1:0] must be 00.
	if(wNVMTargetAddress & 0x03)
	{
		err = FLASH_ERR_INVALID_PARAM;
		return (err);
	}
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[23:16]	
	NVM_FCCOBHI = FLASH_CMD_ERASE_SECTOR;// EEPROM FLASH command
	NVM_FCCOBLO = 0;// memory address bits[23:16]
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}		
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}
	
	return (err);
}


/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: NVM_EraseBlock
*
* @brief erase flash/EEPROM block.
*        
* @param  
*	wNVMTargetAddress: target address in NVM
*	bIsEEPROM:		specify the target address is within EEPROM, 
*					1 if in EEPROM block, 0 if in FLASH block 
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
uint16_t NVM_EraseBlock(uint16_t wNVMTargetAddress, uint8_t bIsEEPROM)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[23:16]	
	NVM_FCCOBHI = NVM_CMD_ERASE_BLOCK;// erase FLASH block command
	NVM_FCCOBLO = 0;// memory address bits[23:16] with bit23 = 0 for Flash block, 1 for EEPROM block
	if(bIsEEPROM)
	{
		NVM_FCCOBLO |= 0x80;	// bit 23 = 1 for EEPROM block
	}
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}		
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}
	
	return (err);
}


/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: NVM_EraseVerifyBlock
*
* @brief erase verify flash/EEPROM block.
*        
* @param  
*	wNVMTargetAddress: target address in NVM
*	bIsEEPROM:		specify the target address is within EEPROM, 
*					1 if in EEPROM block, 0 if in FLASH block 
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
uint16_t NVM_EraseVerifyBlock(uint16_t wNVMTargetAddress, uint8_t bIsEEPROM)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[23:16]	
	NVM_FCCOBHI = NVM_CMD_ERASE_VERIFY_BLOCK;// erase FLASH block command
	NVM_FCCOBLO = 0;// memory address bits[23:16] with bit23 = 0 for Flash block, 1 for EEPROM block
	if(bIsEEPROM)
	{
		NVM_FCCOBLO |= 0x80;	// bit 23 = 1 for EEPROM block
	}
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}		
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}
	
	return (err);
}

/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: Flash_EraseVerifySection
*
* @brief erase verify flash section.
*        
* @param  
*	wNVMTargetAddress: target address in NVM, must be longword aligned
*	uiLongWordCount:   # of longwords to be verified
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
uint16_t Flash_EraseVerifySection(uint16_t wNVMTargetAddress, uint16_t uiLongWordCount)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	// Check address to see if it is aligned to 4 bytes
	// Global address [1:0] must be 00.
	if(wNVMTargetAddress & 0x03)
	{
		err = FLASH_ERR_INVALID_PARAM;
		return (err);
	}	
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[23:16]	
	NVM_FCCOBHI = FLASH_CMD_ERASE_VERIFY_SECTION;// erase verify FLASH section command
	NVM_FCCOBLO = 0;// memory address bits[23:16] with bit23 = 0 for Flash block, 1 for EEPROM block
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	// Write index to specify the # of longwords to be verified
	NVM_FCCOBIX = 0x2;
	// Write the # of longwords 
	NVM_FCCOB = uiLongWordCount;
	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}		
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}
#if 0	
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
#endif	
	return (err);
}




/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: NVM_EraseVerifyAll
*
* @brief erase verify all flash and EEPROM blocks.
*        
* @param  
*	
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
uint16_t NVM_EraseVerifyAll(void)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[23:16]	
	NVM_FCCOBHI = NVM_CMD_ERASE_VERIFY_ALL;// erase verify all flash & EEPROM blocks
	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}	
#if 0	
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}

	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
#endif	
	return (err);
}

/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: NVM_EraseAll
*
* @brief erase all flash and EEPROM blocks.
*        
* @param  
*	
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
uint16_t NVM_EraseAll(void)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[23:16]	
	NVM_FCCOBHI = NVM_CMD_ERASE_ALL;// erase  all flash & EEPROM blocks
	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}	
#if 0	
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}
#endif	

	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
	
	return (err);
}
#pragma CODE_SEG DEFAULT

/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: EEPROM_EraseVerifySection
*
* @brief erase verify flash section.
*        
* @param  
*	wNVMTargetAddress: target address in NVM, must be longword aligned
*	uiLongWordCount:   # of bytes to be verified
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
uint16_t EEPROM_EraseVerifySection(uint16_t wNVMTargetAddress, uint16_t uiByteCount)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	// Check address to see if it is aligned to 4 bytes
	// Global address [1:0] must be 00.
	if(wNVMTargetAddress & 0x03)
	{
		err = FLASH_ERR_INVALID_PARAM;
		return (err);
	}
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[23:16]	
	NVM_FCCOBHI = EEPROM_CMD_ERASE_VERIFY_SECTION;// erase verify FLASH section command
	NVM_FCCOBLO = 0;// memory address bits[23:16] with bit23 = 0 for Flash block, 1 for EEPROM block
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	// Write index to specify the # of longwords to be verified
	NVM_FCCOBIX = 0x2;
	// Write the # of longwords 
	NVM_FCCOB = uiByteCount;
	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}
#if 0	
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}	
	
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
#endif	
	return (err);
}

uint16_t EEPROM_Program1Byte(uint16_t wNVMTargetAddress, uint8_t bData)
{
	uint16_t err = FLASH_ERR_SUCCESS;
	
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[17:16]	
	NVM_FCCOBHI = EEPROM_CMD_PROGRAM;// EEPROM FLASH command
	NVM_FCCOBLO = 0;// memory address bits[17:16]
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	// Write index to specify the byte0 (MSB word) to be programmed
	NVM_FCCOBIX = 0x2;
	// Write the byte 0
	NVM_FCCOB = bData;
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}	
#if 0	
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}
#endif	
	
	return (err);
}


uint16_t EEPROM_ProgramUpto4Bytes(uint16_t wNVMTargetAddress, uint8_t *pbData, uint8_t bByteCount)
{
	int i;
	uint16_t err = FLASH_ERR_SUCCESS;
	if(bByteCount >4 || bByteCount == 0)
	{
		err = FLASH_ERR_INVALID_PARAM;
		return (err);		
	}
	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[17:16]	
	NVM_FCCOBHI = EEPROM_CMD_PROGRAM;// EEPROM FLASH command
	NVM_FCCOBLO = 0;// memory address bits[17:16]
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	
	for (i = 0; i < bByteCount; i++)
	{
		// Write index to specify the byte0 (MSB word) to be programmed
		NVM_FCCOBIX = 0x2+i;
		// Write the byte 0
		NVM_FCCOB = *pbData++;
	}
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}	
#if 0	
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}
#endif	
	return (err);
}


/*****************************************************************************//*!
+FUNCTION----------------------------------------------------------------
* @function name: EEPROM_EraseSector
*
* @brief erase EEPROM sector, each EEPROM sector is of 2 bytes long
* 		 
*        
* @param  
*
* @return none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
uint16_t EEPROM_EraseSector(uint16_t wNVMTargetAddress)
{
	uint16_t err = FLASH_ERR_SUCCESS;

	// Clear error flags
	NVM_FSTAT = 0x30;
	
	// Write index to specify the command code to be loaded
	NVM_FCCOBIX = 0x0;
	// Write command code and memory address bits[23:16]	
	NVM_FCCOBHI = EEPROM_CMD_ERASE_SECTOR;// EEPROM FLASH command
	NVM_FCCOBLO = 0;// memory address bits[23:16]
	// Write index to specify the lower byte memory address bits[15:0] to be loaded
	NVM_FCCOBIX = 0x1;
	// Write the lower byte memory address bits[15:0]
	NVM_FCCOB = wNVMTargetAddress;
	
	// Launch the command
	NVM_FSTAT = 0x80;
	// Wait till command is completed
	while (!(NVM_FSTAT & NVM_FSTAT_CCIF_MASK));
	
	// Check error status
	if(NVM_FSTAT & NVM_FSTAT_ACCERR_MASK)
	{
		err |= FLASH_ERR_ACCESS;
	}
	if(NVM_FSTAT & NVM_FSTAT_FPVIOL_MASK)
	{
		err |= FLASH_ERR_PROTECTION;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT0_MASK)
	{
		err |= FLASH_ERR_MGSTAT0;		
	}
	if(NVM_FSTAT & NVM_FSTAT_MGSTAT1_MASK)
	{
		err |= FLASH_ERR_MGSTAT1;		
	}	
#if 0	
	if(NVM_FERSTAT & (NVM_FERSTAT_SFDIF_MASK))
	{
		err |= EEPROM_ERR_SINGLE_BIT_FAULT;
	}
	if(NVM_FERSTAT & (NVM_FERSTAT_DFDIF_MASK))
	{
		err |= EEPROM_ERR_DOUBLE_BIT_FAULT;
	}
#endif	
	return (err);
}
