/*******************************************************************************
*                                                                              *
* Copyright 2010 Rheinmetall Canada Inc.                                  	   *
*                                                                              *
* No part of this document may be reproduced, stored in                        *
* a retrieval system, or transmitted, in any form or by any means,             *
* electronic, mechanical, photocopying, recording, or otherwise,               *
* without the prior written permission of Rheinmetall Canada Inc.              *
*                                                                              *
*******************************************************************************/


/*---- example include file ---------------------------------------------------*/
#include "define.h"
#include "FlashInterface.h"

stDataSector_t CurrentDataSector;

void InitFlashInterface(void)
{
	
}



//see section 5 of PIC32 manual
unsigned int FlashUnlock(unsigned int Operation)
{

	//Setup operation to do
	NVMCON = Operation;	
	
	int i =0;
	for(i=0;i<500;i++)
		Nop();
		
	//Unlock
	NVMKEY = 0xAA996655;
	NVMKEY = 0x556699AA;

	//Execute operation
	NVMCONSET = 0x8000;
	
	//Wait for it to finish
	while(NVMCON & 0x8000);
	Nop();

	
	NVMCONCLR = 0x4000;
	
	//Return NVMERR et LVDERR status bits
	return (NVMCON & 0x3000);
	
}

unsigned int FlashProgramWord(void *Address, unsigned int DataWord)
{
	unsigned int res;
	
	//Load Data into NVMDATA register
	NVMDATA = DataWord;
	
	//Load Address (Physical address)
	NVMADDR = KVA_TO_PA((unsigned int)Address);
	
	//Unlock Flash and init operation
	res = FlashUnlock(FLASH_PROGRAM_WORD_CMD);
	
	return res;
	
}
	
unsigned int FlashProgramRow(void *Address, void *DataPtr)
{
	unsigned int res;
	
	//Load Address (Physical address)
	NVMADDR = KVA_TO_PA((unsigned int)Address);
	
	// Set pointer to data buffer
	NVMSRCADDR = KVA_TO_PA((unsigned int)DataPtr);
	
	//Unlock Flash and init operation
	res = FlashUnlock(FLASH_PROGRAM_ROW_CMD);
	
	return res;	
}

unsigned int FlashProgramData(void *FlashStartAddress, unsigned int *DataPtr, unsigned int Size)	
{
	//The Size parameter is the number of WORDS.
	unsigned int SizeInBytes = Size * 4;
	void *CurFlashAddress;
	unsigned int i;
	unsigned int res = 0;
	
	unsigned int curData;
	curData = *DataPtr;
	
	
	CurFlashAddress = FlashStartAddress;
	
	//1 row = 512 bytes --> 0x80.  We must always program an entire row (cannot start in the middle).
	//if this happens, we must write words individually before and after the rows.
	
	if(Size < FLASH_ROW_SIZE)	//if we don't have enough data to fill a row, program words...
	{
		for(i = 0; i < Size; i++)
		{
			curData = *DataPtr++;
			res |= FlashProgramWord(CurFlashAddress, curData /*DataPtr++*/);
			CurFlashAddress+=4;
		}
		
		if(res)
			return FLASH_STATUS_ERROR;
		else
			return FLASH_STATUS_OK;		
	}
	else	//we have at least 1 row
	{
		unsigned int NbRow;
		
		NbRow = Size / FLASH_ROW_SIZE;
		int ProgramFinished = 0;
		do
		{
			//check if the start address is aligned with a row.
			if((unsigned int)CurFlashAddress & 0x1FF)	//data is misaligned.  Write all words to the next row.
			{
				do
				{
					res |= FlashProgramWord(CurFlashAddress, *DataPtr++);
					CurFlashAddress+=4;
					Size--;
				}
				while(((unsigned int)CurFlashAddress & 0x1FF) && Size != 0);
			}
			else	//data is aligned with the row.
			{
				if(Size % FLASH_ROW_SIZE == 0)	//we have an integer number of rows.  Program all of them and return.
				{
					for(i = 0; i < NbRow; i++)
					{
						res |= FlashProgramRow(CurFlashAddress, (void*)DataPtr);
						CurFlashAddress += FLASH_ROW_SIZE_IN_BYTES;
						DataPtr += FLASH_ROW_SIZE;
					}	
					
					if(res)
						return FLASH_STATUS_ERROR;
					else
						return FLASH_STATUS_OK;		
				}
				else	//non integer number of rows...
				{
					res |= FlashProgramRow(CurFlashAddress, (void*)DataPtr);
					CurFlashAddress += FLASH_ROW_SIZE_IN_BYTES;
					DataPtr += FLASH_ROW_SIZE;
					Size -= FLASH_ROW_SIZE;
				}
			}
			
			if(Size < FLASH_ROW_SIZE)	//if we don't have enough data to fill a row, program words...
			{
				for(i = 0; i < Size; i++)
				{
					res |= FlashProgramWord(CurFlashAddress, *DataPtr++);
					CurFlashAddress+=4;
				}
				
				if(res)
					return FLASH_STATUS_ERROR;
				else
					return FLASH_STATUS_OK;		
			}
			
			if(Size == 0)
			{
				if(res)
					return FLASH_STATUS_ERROR;
				else
					return FLASH_STATUS_OK;	
			}
		}
		while(1);

	
	}
	
	
	

}

unsigned int FlashErase(void)
{
	unsigned int res = 0;
	unsigned int i;
	void* PageAddress;
		
	PageAddress = (void*)APPLICATION_FLASH_START_ADDRESS;
	
	for(i = 0; i < FLASH_NB_APPLICATION_PAGES; i++)
	{
		res |= FlashErasePage(PageAddress);
//		if(res)
//		{
//			int page = PageAddress;
//		}	
		PageAddress += FLASH_PAGE_SIZE_IN_BYTES;
	}
	
	if(res)
		return FLASH_STATUS_ERROR;
	else
		return FLASH_STATUS_OK;	
}	

unsigned int FlashErasePage(void *Address)
{
	unsigned int res;
	
	NVMADDR = KVA_TO_PA((unsigned int)Address);
	res = FlashUnlock(FLASH_PAGE_ERASE_CMD);
	
	return res;
}



//EOF


