/***************************************************************************
	FILE
		main.c
	DESCRIPTION:
		For ARM7 EV40 board's ATMEL flash access,
		without address limitation
	HISTORY:
	    first done: 02.03.17
	COPYRIGHT
		(c) 2002 MICETEK International Inc.,
		All rights reserved.				By SHENG 2002.03.11
***************************************************************************/


#include "flash.h"

/*********************************************************************
 * External  Array declare
 *********************************************************************/
//extern unsigned long FlashParaRam[0x10];
//extern unsigned short FlashBuffer[0x2000];

/*********************************************************************
 * Function declare
 *********************************************************************/
void burn_boot (unsigned int *FlashParaRam,unsigned short *FlashBuffer);
void EraseAll(unsigned int *FlashParaRam);
void EraseFlash(unsigned int *FlashParaRam);
void EraseBlock(unsigned int *FlashParaRam);

/*
 *	main function of Programming the FlashROM
 */
int DoMain (unsigned long argc)
{
    unsigned int *FlashParaRam;	// From start address to start address+0x3f, total 0x40 bytes
    unsigned short *FlashBuffer;	// From start address+0x40 to start address+0x403f, total 0x4000 bytes
    
    FlashParaRam = (unsigned int *) argc;
    FlashBuffer = (unsigned short *) (argc + 0x40);
 
    FlashParaRam[ErrorInfos] = OnRUN;
    switch(FlashParaRam[Command])
    {
		case Erasing:                /* 0x00: erase the flash at chip or sector mode*/
             EraseFlash(FlashParaRam);
             break;
		case Programming:            /* 0x01: programming the flash*/
             burn_boot(FlashParaRam,FlashBuffer);
             break;
/*		case Verifying:              /* 0x02: verifying the programmed flash*/
/*             verify_boot(FlashParaRam);
             break;*/
		default:
             FlashParaRam[ErrorInfos]=NoSupport;
             break;
    }
    if (FlashParaRam[ErrorInfos] == OnRUN)
       
       	FlashParaRam[ErrorInfos] = ExecOK;
    return 1;
}

/*
 * Erase the flash by chip or by block(sector)
 */
void EraseFlash(unsigned int *FlashParaRam)
{
    unsigned long FlashAddr;
    unsigned short *s16,temp0,temp1;
    FlashAddr = FlashParaRam[FlashStartAddress];
    s16 = (unsigned short *) FlashAddr;
    s16[0x555] = 0xAA;
    s16[0x2AA] = 0x55;
    s16[0x555] = 0x90;
    temp0 = *s16;	   /* Manufacturer Code*/
    temp1 = *(s16+1);  /*Device Code:*/
    s16[0x555] = 0xAA;
    s16[0x2AA] = 0x55;
    s16[0x555] = 0xF0;
    if((temp0!=0x001F)||(temp1!=0x00C2))
    {
        FlashParaRam[ErrorInfos] = TypeError;
        return;
    }

    if ( FlashParaRam[FlashSize] == 0x200000)             /* 0x80000 = 512k*/
        EraseAll(FlashParaRam);
    else
        EraseBlock(FlashParaRam);
}

/*
 * Erase sector flash, return value is the error address of blank check
 */
void EraseBlock(unsigned int *FlashParaRam)
{
	unsigned long FlashAddr,AddrPtr,BlockAddrStart,BlockAddrEnd,NextEdge;
	unsigned short *s16;
	unsigned long Block,i,ErrAddress;
	i=0;
	AddrPtr = FlashParaRam[FlashSize];                      // temp save the length will be erased
	FlashAddr = FlashParaRam[FlashStartAddress];            // chip start address

	BlockAddrStart = FlashParaRam[CurrentSAddress]+FlashAddr;// The begin address to erase
	BlockAddrEnd =BlockAddrStart+AddrPtr;					// The end address to erase
	NextEdge=FlashParaRam[FlashStartAddress]+0X1F0000;					
	if(BlockAddrStart<NextEdge)
		{
			BlockAddrStart &= 0xFFFF0000;  						// Align for 64K
		Block=1;}
	else                         
		{
			BlockAddrStart &= 0XFFFFE000;				// Align for 8K
		Block=0;
		}
	if (BlockAddrEnd >=NextEdge)   
		{
		if ((BlockAddrEnd & 0x00001FFF)!=0)                      
      	 	{
      	 		 BlockAddrEnd = (BlockAddrEnd & 0xFFFFE000) + 0x00002000;
      	 		 }
			else
       		{
       			 BlockAddrEnd = (BlockAddrEnd & 0xFFFFE000);
       		}
       	}
	else{
			if ((BlockAddrEnd & 0x0000FFFF)!=0)                      
      	 	 {
      	 	 	BlockAddrEnd = (BlockAddrEnd & 0xFFFF0000) + 0x00010000;
      	 	 	if(BlockAddrEnd >=NextEdge)
      	 	 	{
      	 	 		BlockAddrEnd =NextEdge;
      	 	 	}
      	 	 }
			else
       		 BlockAddrEnd = (BlockAddrEnd & 0xFFFF0000);
		}	
	if (FlashParaRam[PortSize]==Port16)
	{	
        AddrPtr = BlockAddrStart;	                          // the pointer for current address,
        NextEdge = BlockAddrStart + 0x1F0000;             // Align for 64k block
       			
		s16 = (unsigned short *) FlashAddr;
                s16[0x555] = 0xAA;
		s16[0x2AA] = 0x55;
		s16[0x555] = 0x80;
		s16[0x555] = 0xAA;
		s16[0x2AA] = 0x55;
	    while (AddrPtr < BlockAddrEnd)
		{
				s16 = (unsigned short *) (AddrPtr);		// Now using sector erase
			   	*s16 = 0x30;	 	                   
				/* Data Polling, the Address needn't care */
			/*while (!((*s16) & 0x80))*/
			if(Block==1)
				{
					AddrPtr += 0x10000;
					if(AddrPtr>=NextEdge)
					{
						AddrPtr=NextEdge;
						Block=0;
					}
				}
				else
				{
					AddrPtr += 0x2000;
					}	
			}
			while (!((*s16) & 0x80))
				{	
					if ((i++)>0x15000000)
					{
						FlashParaRam[ErrorInfos]=EreaseError;
						return;
					}
				}
		}
	else
	{
		FlashParaRam[ErrorInfos]=NoSupport;
		return;
	}
		// Blank check after block or sector erase
	/*s16 = (unsigned short *)(FlashParaRam[CurrentSAddress]+FlashAddr);
	i = FlashParaRam[FlashSize];
	ErrAddress =FlashParaRam[CurrentSAddress]+FlashAddr;*/

	s16 = (unsigned short *)(BlockAddrStart);
		AddrPtr = BlockAddrEnd-BlockAddrStart;
		for (;(unsigned long) s16 < BlockAddrEnd;s16++)
		{
			if(* s16 != 0xffff)
			{
				FlashParaRam[ErrorInfos]=BlankCError;
				FlashParaRam[ErrorAddress]=(unsigned long) s16;
				return;
			}
		}
		
}

/*
 * Erase all chip flash, return value is the error address of blank check
 */
void EraseAll(unsigned int *FlashParaRam)
{
	unsigned long ErrAddress,i;
	unsigned short *s16;

	i=0;
	ErrAddress=FlashParaRam[FlashStartAddress];

	s16 = (unsigned short *) ErrAddress;
	if (FlashParaRam[PortSize]==Port16)
	{		//	ATMEL Flash erase ;
        s16[0x555] = 0xAA;
	s16[0x2AA] = 0x55;
	s16[0x555] = 0x80;
	s16[0x555] = 0xAA;
	s16[0x2AA] = 0x55;
	s16[0x555] = 0x10;

		/* Data Polling, the Address needn't care */
		while (!( (*s16) & 0x80))
		{
			if ((i++)>0xFFFFF0)
			{
				FlashParaRam[ErrorInfos]=EreaseError;
				return;
			}
		}
	}
	else
	{
		FlashParaRam[ErrorInfos]=NoSupport;
		return;
	}

    //Blank check all chip flash, return value is the error address
 	s16 = (unsigned short *)FlashParaRam[FlashStartAddress];
	i = FlashParaRam[FlashSize];
	ErrAddress = FlashParaRam[FlashStartAddress];

	for ( ; i-=2; ErrAddress++, s16++ )
	{
		if ( *s16 != 0xffff)
		{
			FlashParaRam[ErrorInfos] = BlankCError;
			FlashParaRam[ErrorAddress] = ErrAddress;
			return;
		}
	}
}

/*
 * Program flash, return value is the error address of programming
 */
void burn_boot (unsigned int *FlashParaRam,unsigned short *FlashBuffer)
{

	unsigned long FlashStart,counter,TotalProgramSize,FlashAddr,i;
	unsigned short *s16,*ptr16,temp0,temp1;
    FlashAddr = FlashParaRam[FlashStartAddress];
/*	FlashStart = FlashParaRam[FlashType] & 0xFF000000;
	if ( FlashStart != Fujitsu)		*/				// Check flash type
   	s16 = (unsigned short *) FlashAddr;
   	s16[0x555] = 0xAA;
   	s16[0x2AA] = 0x55;
	s16[0x555] = 0x90;
	temp0 = *s16;
	temp1 = *(s16+1);
	s16[0x555] = 0xAA;
   	s16[0x2AA] = 0x55;
	if((temp0!=0x001F)||(temp1!=0x00C))
	{
		FlashParaRam[ErrorInfos] = TypeError;
		return;
	}
	
	i=0;
	counter = 0;
	FlashStart = FlashParaRam[FlashStartAddress];
	ptr16 = (unsigned short *) (FlashParaRam[CurrentSAddress]+FlashStart);
    TotalProgramSize = FlashParaRam[CurrentLength]/2;

	if (FlashParaRam[PortSize]==Port16)
	{
        s16 = (unsigned short *) FlashStart;
		while (counter < TotalProgramSize)
		{
            		s16[0x555] = 0xAA;
			s16[0x2AA] = 0x55;
			s16[0x555] = 0xA0;

			*ptr16 = FlashBuffer[counter];

			do{
				temp0 = *s16;
				temp0 &= 0x40;
				temp1 = *s16;
				temp1 &= 0x40;
			}while ((temp0 != temp1)&&(i++ < 0xFFFFF));                                     /* Polling */
			if ((i++)>0xFFFFF0)
			{
				FlashParaRam[ErrorInfos]=ProgError;
				return;
			}

			if (*ptr16 != FlashBuffer[counter])                          /* Verify the flash image */
			{
				FlashParaRam[ErrorInfos]=ProgError;
				FlashParaRam[ErrorAddress]=counter+FlashParaRam[FlashStartAddress];
				return;
			}
			counter++;
			ptr16++;
		}
	}
	else
	{
		FlashParaRam[ErrorInfos]=NoSupport;
		return;
	}
}

/*
 * Verify the programmed flash, return value is the error address of verification
 */
/* void verify_boot(void)
{
	unsigned short *s16;
	unsigned long i,ErrAddress;

	s16=(unsigned short *)(FlashParaRam[CurrentSAddress]+FlashParaRam[FlashStartAddress]);
	i=FlashParaRam[CurrentLength]/2;
	ErrAddress=0;

	for(;i--;ErrAddress++,s16++)
	{
		if(* s16 != FlashBuffer[ErrAddress])
		{
			FlashParaRam[ErrorInfos]=VerifyError;
			FlashParaRam[ErrorAddress]=ErrAddress*2+FlashParaRam[FlashStartAddress];
			return;
		}
	}
}*/
