//*****************************************************************************
//
// bl_flash.c    : Functions to erase/program the MCU embedded flash
// Author      : QJ Wang. qjwang@ti.com
// Date        : 5-9-2013
//
// Copyright (c) 2008-2011 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
//
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
//
//*****************************************************************************

#include "bl_config.h"
#include "bl_flash.h"
#include "f035.h"
#include "hw_sci.h"
#include "flash470.h"
#include "flash_defines.h"
#include "reg_flash.h"

#define REGISTER(mRegister) (* (volatile UINT32 *)(mRegister))

//*****************************************************************************
//
//  Returns the size of the 1st sector size of the flash in bytes.
// 
//*****************************************************************************
uint32 BLInternalFlashFirstSectorSizeGet(void)
{
	uint32 firstSectorSize;
	firstSectorSize = sector[0].length;
    return (firstSectorSize);
}
//*****************************************************************************
//
//  Returns the size of the internal flash in bytes.
// 
//  This function returns the total number of bytes of internal flash in the
//  current part.  No adjustment is made for any sections reserved via
//  options defined in bl_config.h.
// 
//  \return Returns the total number of bytes of internal flash.
//
//*****************************************************************************
uint32 BLInternalFlashSizeGet(void)
{
	uint32 flashSize;
	flashSize = (uint32)sector[NUMBEROFSECTORS-1].start + sector[NUMBEROFSECTORS-1].length;

    return (flashSize);
}

//*****************************************************************************
//
//  Checks whether a given start address is valid for a download.
// 
//  This function checks to determine whether the given address is a valid
//  download image start address given the options defined in bl_config.h.
// 
//  \return Returns non-zero if the address is valid or 0 otherwise.
//
//*****************************************************************************
uint32 BLInternalFlashStartAddrCheck(uint32 ulAddr, uint32 ulImgSize)
{
    uint32 count=0, i;

	uint32 ulWholeFlashSize;

    //
    // Determine the size of the flash available on the part in use.
    //
    ulWholeFlashSize = (uint32)sector[NUMBEROFSECTORS-1].start + sector[NUMBEROFSECTORS-1].length;

	/* The start address must be at the begining of the sector */
    /* starting from #1 sector means you cannot erase/program sector 0 which has bootloader */
    for (i = 1; i < NUMBEROFSECTORS-1; i++){
		if (ulAddr == (uint32)(sector[i].start))
		{
			count++;
		}
	}
    if (count == 0){
#ifdef DEBUG_MSG_L3
    UART_putString(UART, "\r Application Address should be aligned with flash sector start address!\r");
#endif
    	return(0);
    }

    //
    // Is the address we were passed a valid start address?  We allow:
    //
    // 1. Address 0 if configured to update the boot loader.
    // 2. The start of the reserved block if parameter space is reserved (to
    //    allow a download of the parameter block contents).
    // 3. The application start address specified in bl_config.h.
    //
    // The function fails if the address is not one of these, if the image
    // size is larger than the available space or if the address is not word
    // aligned.
    //
    if((
#ifdef ENABLE_BL_UPDATE
                       (ulAddr != 0) &&
#endif
                        (ulAddr != APP_START_ADDRESS)) ||
                       ((ulAddr + ulImgSize) > ulWholeFlashSize) ||
                       ((ulAddr & 3) != 0))
    {
#ifdef DEBUG_MSG_L3
    UART_putString(UART, "\r Flash address and size check failed!\r");
#endif
    	return(0);
    }
    else
    {
#ifdef DEBUG_MSG_L3
    UART_putString(UART, "\r Flash address and size check passed!\r");
#endif
        return(1);
    }
}

//*****************************************************************************
//
//  Returns the size of the internal flash in bytes.
//
//  This function returns the total number of bytes of internal flash in the
//  current part.  No adjustment is made for any sections reserved via
//  options defined in bl_config.h.
//
//  \return Returns the total number of bytes of internal flash.
//
//*****************************************************************************
uint32 BL_ECC_FlashSizeGet(void)
{
	uint32 flashSize;
	flashSize = (uint32)ecc_parity[NUMBEROFECC_PARITYBANKS-1].start + ecc_parity[NUMBEROFECC_PARITYBANKS-1].length;

    return (flashSize);
}


uint32 Fapi_BlockErase(uint32 FlashStartAddress, uint32 Size_In_Bytes)
{
	uint32  i;
	uint32 eraseEndAddr;
	uint32 eraseStartAddr;
	uint32 eraseStartSector;
	uint32 eraseEndSector;
	uint32 pstatus;
	FLASH_STATUS_ST status;
	BOOL temp;
    uint32 tdelay = HCLK_FREQ/2;

	pstatus = 0x0;
	status.stat1 = 0;

	eraseStartAddr = (uint32)FlashStartAddress;
	eraseEndAddr   = (uint32)FlashStartAddress + (uint32)Size_In_Bytes;


	for (i=0; i<NUMBEROFSECTORS-1; i++){
        if(eraseEndAddr < ((uint32)(sector[i].start) + sector[i].length)){
           eraseEndSector = i;
           break;
        }
	}

	for (i=0; i<NUMBEROFSECTORS-1; i++){
        if(eraseStartAddr < ((uint32)(sector[i].start) + sector[i].length)){
           eraseStartSector = i;
           break;
        }
	}

	for (i = eraseStartSector; i < (eraseEndSector+1); i++)
	{
		temp = Flash_Compact_B(sector[i].start, sector[i].bank, (FLASH_SECT)sector[i].sectorNumber, tdelay, (FLASH_ARRAY_ST)sector[i].FlashBaseAddress,&status);
		if(!temp)
		{
			pstatus=1;
			break;
		}
	}
#ifdef DEBUG_MSG_L3
  	if(pstatus) UART_putString(sciREG1, "Compact Failed\r");
		else UART_putString(sciREG1, "Compact Passed\r");
#endif

	for (i = eraseStartSector; i < (eraseEndSector+1); i++){
		temp = Flash_Erase_B(sector[i].start, sector[i].length, sector[i].bank, (FLASH_SECT)sector[i].sectorNumber, tdelay, (FLASH_ARRAY_ST)sector[i].FlashBaseAddress, &status);
		if(!temp)
		{
			pstatus=1;
			break;
		}
	}

	if (pstatus == 0 ){
//		pstatus = Flash_Erase_Verify(eraseStartSector, eraseEndSector);
	}

#ifdef DEBUG_MSG_L3
  	if(pstatus){
  		UART_putString(sciREG1, "Erase Failed\r");
  	}else {
  		UART_putString(sciREG1, "Erase Passed\r");
  	}
#endif
	return (pstatus);

}

uint32 Fapi_BlockProgram( uint32* puAddress, uint32* puData, uint32 numBytes)
{
    uint32 tdelay, pstatus, i, myCore;
    FLASH_STATUS_ST status;
	BOOL temp;

    tdelay =  HCLK_FREQ/2;
    pstatus = 0;

    for (i=0; i < NUMBEROFBANKS; i++){
    	if ((uint32)puAddress < ((uint32)(bank[i].start) + (uint32)(bank[i].length)))
    		myCore = bank[i].bankNumber;
    	    break;
    }

    temp = Flash_Prog_B(puAddress, puData, numBytes >> 2, (FLASH_CORE)myCore, tdelay, (FLASH_ARRAY_ST)sector[1].FlashBaseAddress, &status);
	if(!temp)
	{
		pstatus=1;
	}
    if (pstatus == 0)
    	 temp = Flash_Program_Verify(puAddress, puData, numBytes, myCore);

	if(!temp)
	{
		pstatus=1;
	}

#ifdef DEBUG_MSG_L3
   	if(pstatus) UART_putString(sciREG1, "Program data Failed\r");
		else UART_putString(sciREG1, "Program data Passed\r");
#endif
	return (pstatus);     //pstatus=0 --> pass
}

uint32 Flash_Program_Verify(uint32* pProgramStartAddress, uint32* pDataAddress, uint32 numBytes, uint32 myCore)
{
    uint32 pstatus;
    FLASH_STATUS_ST status;
	BOOL temp;

	temp = Flash_Verify_B(pProgramStartAddress, pDataAddress, numBytes >> 2, (FLASH_CORE)myCore, (FLASH_ARRAY_ST)sector[1].FlashBaseAddress, &status);
	if(!temp)
	{
		pstatus=0x1;
	}

#ifdef DEBUG_MSG_L3
	if(pstatus) UART_putString(sciREG1, "Verify data Failed\r");
	else UART_putString(sciREG1, "Verify data Passed\r");
#endif
	return (pstatus);
}	


uint32 Flash_Erase_Verify(uint32 eraseStartSector, uint32 eraseEndSector)
{
	uint32  i;
	uint32 pstatus;
	FLASH_STATUS_ST status;
	BOOL temp;

	pstatus = 0x0;
	status.stat1 = 0;

	for (i = eraseStartSector; i < (eraseEndSector+1); i++){
		temp=Flash_Blank_B(sector[i].start, sector[i].length, sector[i].bank, (FLASH_ARRAY_ST)sector[i].FlashBaseAddress, &status);
    	if(!temp)
		{
			pstatus=1;
			break;
		}
  	}

#ifdef DEBUG_MSG_L3
  	if(pstatus) UART_putString(sciREG1, "Blank Check Failed\r");
		else UART_putString(sciREG1, "Blank Check Passed\r");
#endif
   	return (pstatus);

}

// Write 0xFFFFFFFF to the 1st word of the APPLICATION Start Address
uint32 Fapi_UpdateStatusProgram(uint32* puAddress, uint32 data)
{
	uint32 temp, tdelay, myCore, i;
	uint32 pstatus;
	FLASH_STATUS_ST status;

	pstatus = 0x0;
	status.stat1 = 0;
    tdelay =  HCLK_FREQ/2;

    for (i=0; i < NUMBEROFBANKS; i++){
    	if ((uint32)puAddress < ((uint32)(bank[i].start) + (uint32)(bank[i].length)))
    		myCore = bank[i].bankNumber;
    	    break;
    }

    temp = Flash_Prog_B(puAddress, (uint32*)&data, 1, (FLASH_CORE)myCore, tdelay, (FLASH_ARRAY_ST)sector[1].FlashBaseAddress, &status);
	if(!temp)
	{
		pstatus=1;
	}
    if (pstatus == 0)
    	temp = Flash_Verify_B(puAddress, (uint32*)&data, 1, (FLASH_CORE)myCore, (FLASH_ARRAY_ST)sector[1].FlashBaseAddress, &status);

	if(!temp)
	{
		pstatus=1;
	}

#ifdef DEBUG_MSG_L3
   	if(pstatus) UART_putString(sciREG1, "Program stack top word Failed\r");
		else UART_putString(sciREG1, "Program stack top word Passed\r");
#endif
	return (pstatus);     //pstatus=0 --> pass
}


/* try to calculate ECC from the data writing to flash */
uint32 Fapi_ECC_BlockProgram( uint32* pFlashAddress, uint32* pFlashData, uint32 numBytes)
{
    uint32 tdelay, pstatus, i, myCore, eccDst;
    FLASH_STATUS_ST status;
	BOOL temp;
	uint32 bytes_remain;
	uint32 pu32EccData[128];
    uint8  u8EccData[2];

	eccDst = 0x400000 + (uint32)(pFlashAddress)/2;

	tdelay =  HCLK_FREQ/2;
    pstatus = 0;
    bytes_remain = numBytes >> 2;

    for (i=0; i < NUMBEROFBANKS; i++){
    	if ((uint32)pFlashAddress < ((uint32)(bank[i].start) + (uint32)(bank[i].length)))
    		myCore = bank[i].bankNumber;
    	    break;
    }

    i=0;

	while( bytes_remain > 0)
    {
		u8EccData[0] = Flash_ECC_Calc(pFlashAddress, pFlashData);                 // calculate  ECC for flash data
		u8EccData[1] = Flash_ECC_Calc((pFlashAddress + 2), (pFlashData + 2));     // calculate  ECC for flash data

        pu32EccData[i] = (u8EccData[0] << 24) | (u8EccData[1] << 16) | (u8EccData[0] << 8) | (u8EccData[1] << 0);
        pu32EccData[i+1] =  pu32EccData[i];

        pFlashAddress += 0x4;
        pFlashData += 0x4;
		bytes_remain -= 0x4;
		i += 2;
    }

	if 	( bytes_remain <= 0){
		UART_putString(sciREG1, " <= 0\r");
	}


    temp = Flash_Prog_B((uint32 *)eccDst, (uint32 *)&pu32EccData[0], numBytes>>3, (FLASH_CORE)myCore, tdelay, (FLASH_ARRAY_ST)ecc_parity[1].FlashBaseAddress, &status);
	if(!temp)
	{
		pstatus=1;
	}

    if (pstatus == 0)
    	temp = Flash_Verify_B((uint32*)eccDst, (uint32*)&pu32EccData[0], numBytes >> 3, (FLASH_CORE)myCore, (FLASH_ARRAY_ST)sector[1].FlashBaseAddress, &status);

	if(!temp)
	{
		pstatus=1;
	}

#ifdef DEBUG_MSG_L3
   	if(pstatus) UART_putString(sciREG1, "Program data Failed\r");
		else UART_putString(sciREG1, "Program data Passed\r");
#endif
	return (pstatus);     //pstatus=0 --> pass
}


static uint8 Flash_ECC_Calc(uint32* Flash_Address, uint32* pdata)
{

	uint8 ECC;
	uint32 femuAddr, femuDataLSW, femuDataMSW;

	//Disable the Diagnostic mode
	flashWREG->F035_FDIAGCTRL = 0x0;

    do{
		flashWREG->F035_FEMUADDR = (uint32)Flash_Address;
		femuAddr = flashWREG->F035_FEMUADDR;
    }while(femuAddr != (uint32)Flash_Address);

	//write known data into Emulation data registers
    do{
		flashWREG->F035_FEMUDLSW = (uint32)( *pdata);
		femuDataLSW = flashWREG->F035_FEMUDLSW;
    }while(femuDataLSW != *pdata);

    pdata++;

	do{
		flashWREG->F035_FEMUDMSW = (uint32)( *pdata);
		femuDataMSW = flashWREG->F035_FEMUDMSW;
	}while(femuDataMSW != *pdata);

	ECC = flashWREG->F035_FEMUECC & 0xFF;
	return ECC;
}

