/* --------------------------------------------------------------------------
    FILE        : nandwriter.c                                                   
    PURPOSE     : NAND writer main program
    PROJECT     : DM35x CCS NAND Flashing Utility
    AUTHOR      : Daniel Allred
    DESC        : CCS-based utility to flash the DM644x in preparation for 
                  NAND booting
 ----------------------------------------------------------------------------- */

// C standard I/O library
#include "stdio.h"

// General type include
#include "tistdtypes.h"

// Device specific CSL
#include "device.h"

// This module's header file 
#include "nandwriter.h"

// NAND driver include
#include "nand.h"
#include "device_nand.h"

// Misc. utility function include
#include "util.h"


/************************************************************
* Explicit External Declarations                            *
************************************************************/


/************************************************************
* Local Macro Declarations                                  *
************************************************************/


/************************************************************
* Local Typedef Declarations                                *
************************************************************/


/************************************************************
* Local Function Declarations                               *
************************************************************/

static Uint32 nandwriter(void);
static Uint32 LOCAL_writeHeaderAndData(NAND_InfoHandle hNandInfo, NANDWRITER_Boot *nandBoot, Uint8 *srcBuf);


/************************************************************
* Global Variable Definitions
************************************************************/
int write_header = 0;
int dspais = 0;
int armubl = 0;
int uboot = 0;

//#pragma DATA_SECTION(DDRStart,".ddrram");
//VUint32 __FAR__ DDRStart;

#pragma DATA_SECTION(NANDStart,".aemif_mem");
VUint32 __FAR__ NANDStart;

// Global variables for page buffers 
static Uint8* gNandTx;
static Uint8* gNandRx;

/************************************************************
* Global Function Definitions                               *
************************************************************/

void main( void )
{
  int status;

  // Init memory alloc pointer
  UTIL_setCurrMemPtr(0);

#if 0
  // System init
  if (DEVICE_init() !=E_PASS)
  {
    exit();
  }
#endif

  // Execute the NAND flashing
  status = nandwriter();

  if (status != E_PASS)
  {
    DEBUG_printString("\n\nNAND flashing failed!\r\n");
  }
  else
  {
    DEBUG_printString( "\n\nNAND boot preparation was successful!\r\n" );
  }
}


/************************************************************
* Local Function Definitions                                *
************************************************************/

static Uint32 nandwriter()
{
  Uint32 numPages;

  NANDWRITER_Boot  gNandBoot;
  NAND_InfoHandle  hNandInfo;

  FILE  *fPtr;
  Uint8  *ptr;
  Int32  fileSize = 0,allocSize = 0;
  Int8  fileName[256], itype[20];
  Int32 i=0;

  DEBUG_printString("Starting DA8xx_NANDWriter.\r\n");

  // Initialize NAND Flash
  hNandInfo = NAND_open((Uint32)&NANDStart);
  
  if (hNandInfo == NULL)
  {
    DEBUG_printString( "\tERROR: NAND Initialization failed.\r\n" );
    return E_FAIL;
  }

#ifdef __CCSv4_BUILD__  

  	// Read the file from host
  	fPtr = fopen("C:\\aisname.dat", "rb");
  	if(fPtr == NULL)
  	{
		DEBUG_printString("\tERROR: File C:\\aisname.dat open failed.\n"
      		"Please create a file by this name containing the path to the AIS binary to be flashed\n");
		return E_FAIL;
  	}
  	fscanf(fPtr, "%s", fileName);
  	DEBUG_printString("Opening file %s to write to the NAND\n", fileName);
  	fclose(fPtr);  

	dspais = 1;	/* Always burn DSP AIS image only with CCSv4 */

#else
	printf("Enter the image type (one of \"dspais\" \"armubl\" \"uboot\")\n");
	scanf("%s", itype);
	if(!strcmp(itype, "dspais")) { 
		dspais = 1;
	} else if(!strcmp(itype, "armubl")) {
		armubl = 1;
		write_header = 1;
	} else if(!strcmp(itype, "uboot")) {
		uboot = 1;
		write_header = 1;
	}
  	// Read the file from host
	printf("Enter the file Name\n");
	scanf("%s", fileName);
	fflush(stdin);
#endif
	
  if (strcmp(fileName,"none") != 0)
  {
  	// Open an File from the hard drive
  	fPtr = fopen(fileName, "rb");
  	if(fPtr == NULL)
  	{
      DEBUG_printString("\tERROR: File ");
      DEBUG_printString(fileName);
      DEBUG_printString(" open failed.\r\n");
      return E_FAIL;
    }

    // Read file size
    fseek(fPtr,0,SEEK_END);
    fileSize = ftell(fPtr);

    if(fileSize == 0)
    {
      DEBUG_printString("\tERROR: File read failed.. Closing program.\r\n");
      fclose (fPtr);
      return E_FAIL;
    }

    numPages = 0;
    while ( (numPages * hNandInfo->dataBytesPerPage)  < fileSize )
    {
      numPages++;
    }

    //We want to allocate an even number of pages.
    allocSize = numPages * hNandInfo->dataBytesPerPage;

    // Setup pointer in RAM
    ptr = (Uint8 *) UTIL_allocMem(allocSize);
	if(ptr == NULL) {
		DEBUG_printString("\tERROR: Unable to allocate memory sized %d bytes for file read\n", allocSize);
		return E_FAIL;
	}

    for (i=0; i<allocSize; i++)
      ptr[i]=0x00;

    fseek(fPtr,0,SEEK_SET);

    if (fileSize != fread(ptr, 1, fileSize, fPtr))
    {
      DEBUG_printString("\tWARNING: File Size mismatch.\r\n");
    }

    fclose (fPtr);

	gNandBoot.page        = 0;
	gNandBoot.numPage     = numPages;

	if (dspais) {
		gNandBoot.magicNum    = UBL_MAGIC_SAFE;
		gNandBoot.block       = DEVICE_NAND_RBL_SEARCH_START_BLOCK;
		gNandBoot.entryPoint  = 0x0100;       
		gNandBoot.ldAddress   = 0;            
	} else if (armubl == 1) {
		gNandBoot.magicNum    = UBL_MAGIC_BIN_IMG;
		gNandBoot.block       = DEVICE_NAND_ARM_UBL_SEARCH_START_BLOCK;
		gNandBoot.entryPoint  = 0x80000000;       
		gNandBoot.ldAddress   = 0x80000000;   
	} else if (uboot == 1) {
		gNandBoot.magicNum    = UBL_MAGIC_DMA;
		gNandBoot.block       = DEVICE_NAND_UBOOT_SEARCH_START_BLOCK;
		gNandBoot.entryPoint  = 0xc1080000;      
		gNandBoot.ldAddress   = 0xc1080000;
	}

    if (LOCAL_writeHeaderAndData(hNandInfo,&gNandBoot,ptr) != E_PASS)
    {
		printf("\tERROR: Write failed.\r\n");
		return E_FAIL;
    }
  }
  
  return E_PASS;
}

// Generic function to write a UBL or Application header and the associated data
static Uint32 LOCAL_writeHeaderAndData(NAND_InfoHandle hNandInfo, NANDWRITER_Boot *nandBoot, Uint8 *srcBuf)
 {
  Uint32    endBlockNum = NANDWRITER_END_APP_BLOCK_NUM;
  Uint32    *headerPtr;
  Uint32    blockNum;
  Uint32    count;
  Uint32    countMask;
  Uint32    numBlks;
  Uint32    pageNum;
  Uint32    i;
  Uint8     *dataPtr;


  gNandTx = (Uint8 *) UTIL_allocMem(NAND_MAX_PAGE_SIZE);
  gNandRx = (Uint8 *) UTIL_allocMem(NAND_MAX_PAGE_SIZE);

  for (i=0; i<NAND_MAX_PAGE_SIZE; i++)  
  {
    gNandTx[i]=0xff;
    gNandRx[i]=0xff;
  }  
  
  // Get total number of blocks needed
  numBlks = 0;
  while ( (numBlks * hNandInfo->pagesPerBlock)  < (nandBoot->numPage + 1) )
  {
    numBlks++;
  }
  DEBUG_printString("Number of blocks needed for data: ");
  DEBUG_printHexInt(numBlks);
  DEBUG_printString("\r\n");

  // Check whether writing UBL or APP (based on destination block)
  blockNum = nandBoot->block;

NAND_WRITE_RETRY:
  if (blockNum > endBlockNum)
  {
	DEBUG_printString("Size out of range. Cannot write upto block number %d\n", blockNum); 	
    return E_FAIL;
  }
  DEBUG_printString("Attempting to start write in block number ");
  DEBUG_printHexInt(blockNum);
  DEBUG_printString(".\r\n");

  // Unprotect all needed blocks of the Flash 
  if (NAND_unProtectBlocks(hNandInfo,blockNum,numBlks) != E_PASS)
  {
    blockNum++;
    DEBUG_printString("Unprotect failed.\r\n");
    goto NAND_WRITE_RETRY;
  }
  
  // Setup header to be written
  headerPtr = (Uint32 *) gNandTx;
  headerPtr[0] = nandBoot->magicNum;          //Magic Number
  headerPtr[1] = nandBoot->entryPoint;        //Entry Point
  headerPtr[2] = nandBoot->numPage;           //Number of Pages
  headerPtr[3] = blockNum;                    //Starting Block Number 
  headerPtr[4] = 1;                           //Starting Page Number - always start data in page 1 (this header goes in page 0)

  if ( (blockNum>=DEVICE_NAND_RBL_SEARCH_START_BLOCK) &&  (blockNum <= DEVICE_NAND_SEARCH_END_BLOCK) && dspais)
  {
    headerPtr[5] = 0;
  }
  else if ( (blockNum>=DEVICE_NAND_ARM_UBL_SEARCH_START_BLOCK) &&  (blockNum<=DEVICE_NAND_SEARCH_END_BLOCK))
  {
    headerPtr[5] = nandBoot->ldAddress;         //nandBoot->ldAddress;
  }
  else
  {
    // Block number is out of range
    return E_FAIL; 
  }
  pageNum = 0;
    
  // Erase the block where the header goes and the data starts
  if (NAND_eraseBlocks(hNandInfo,blockNum,numBlks) != E_PASS)
  {
    blockNum++;
    DEBUG_printString("Erase failed\n");
    goto NAND_WRITE_RETRY;
  }

  if (write_header) {
	// Start writing in page 0 of current block (data will be in page 1)
	count = 0;
  	if (NAND_writePage(hNandInfo, blockNum, pageNum, gNandTx) != E_PASS)
  	{
    	blockNum++;
    	DEBUG_printString("Write failed\n");
    	NAND_reset(hNandInfo);
    	goto NAND_WRITE_RETRY;
  	}
    
  	UTIL_waitLoop(200);

  	// Verify the page just written
  	if (NAND_verifyPage(hNandInfo, blockNum, pageNum, gNandTx, gNandRx) != E_PASS)
  	{
    	DEBUG_printString("Verify failed. Attempting to clear page\n");
    	NAND_reset(hNandInfo);
    	NAND_eraseBlocks(hNandInfo,blockNum,numBlks);
    
    	blockNum++;
    	NAND_reset(hNandInfo);

    	goto NAND_WRITE_RETRY;
  	}
	count++;
  } /* write_header */

  // The following assumes power of 2 pagesPerBlock -  *should* always be valid 
  countMask = (Uint32) hNandInfo->pagesPerBlock - 1;
  dataPtr = srcBuf;

  do
  {
    DEBUG_printString((Uint8 *)"Writing image data to Block ");
    DEBUG_printHexInt(blockNum);
    DEBUG_printString((Uint8 *)", Page ");
    DEBUG_printHexInt(count & countMask);
    DEBUG_printString((Uint8 *)"\r\n");

    // Write the UBL or APP data on a per page basis
    if (NAND_writePage(hNandInfo, blockNum,  (count & countMask), dataPtr) != E_PASS)
    {
      blockNum++;
      DEBUG_printString("Write failed\n");
      goto NAND_WRITE_RETRY;
    }
    
    UTIL_waitLoop(200);
    
    // Verify the page just written
    if (NAND_verifyPage(hNandInfo, blockNum, (count & countMask), dataPtr, gNandRx) != E_PASS)
    {
      DEBUG_printString("Verify failed. Attempting to clear page\n");
      NAND_reset(hNandInfo);
      NAND_eraseBlocks(hNandInfo,blockNum,numBlks);
      blockNum++;
      goto NAND_WRITE_RETRY;
    }
    
    count++;
    dataPtr +=  hNandInfo->dataBytesPerPage;
    if (!(count & countMask))
    {
      do
      {
        blockNum++;
      }
      while (NAND_badBlockCheck(hNandInfo,blockNum) != E_PASS);
    }
  } while (count <= nandBoot->numPage);

  NAND_protectBlocks(hNandInfo);

  return E_PASS;
}


/***********************************************************
* End file                                                 
************************************************************/

/* --------------------------------------------------------------------------
  HISTORY
    v1.00 - DJA - 24-Mar-2008
      Initial release
-------------------------------------------------------------------------- */

