/*
	이 프로그램은 필히 File Specific Options에서 Opt Lavel을 None으로 선택을 하여야 함.
*/

#include "fm_spansion.h"

//#define FLASH_SPANSION_DATA	  		*(volatile short int *)0xA0040000
static Int32 device_mfg = 0;
static Int32 device_devid[3];
static Int32 device_flash_pages = 0;
static Int32 device_flash_page_length = 0;
static Int32 device_flash_page_len_pow2 = 0;
Int16 _FLASH_waitWhileErasing( Uint32 addr, Int32 timeout )
{
    Uint8* pdata = ( Uint8* )addr;
    if ( _FLASH_getMfg( ) == MFG_SPANSION )
    {
        while ( timeout-- > 0 )
            if ( *pdata == 0xFF )
                /* Good, erase completed */
                return 0;
		
        /* Timeout occured */
        FLASH_BASE_PTR8 = FLASH_RESET;
        return 1;

    }
//    return 1;
}

Int16 _FLASH_waitWhileProgramming( Uint32 addr, Uint8 data, Int32 timeout )
{
    volatile Uint8* pdata = ( Uint8* )addr;

    if ( _FLASH_getMfg( ) == MFG_SPANSION )
    {
        while ( timeout-- > 0 )
		{
            if ( *pdata == data )
			{
                /* Good, programming completed */
                return 0;
			}
		}
        /* Timeout occured */
        FLASH_BASE_PTR8 = FLASH_RESET;
        return 1;
    }
    return -1;
}

Uint32 FLASH_checksum( Uint32 start, Uint32 length )
{
    Uint32 i;
    Uint32 checksum32 = 0;
    Uint8* ptr = ( Uint8* )start;

    /*
     *  Set to Read Mode
     */
        if ( _FLASH_getMfg( ) == MFG_SPANSION )
            FLASH_BASE_PTR8 = FLASH_RESET;

    /*
     *  Compute Checksum by adding each byte up
     */
    for ( i = 0 ; i < length ; i++ )
        checksum32 += *ptr++;

    return checksum32;
}

Int32 _FLASH_getId( )
{
	volatile int i;
       Uint32 addr = FLASH_BASE;


        volatile Uint8* addr8   = ( Uint8* )addr;
        volatile Uint8* pmfgid  = ( Uint8* )addr;
        volatile Uint8* pdevid1 = ( Uint8* )( addr + 0x01 );
        volatile Uint8* pdevid2 = ( Uint8* )( addr + 0x1C );
        volatile Uint8* pdevid3 = ( Uint8* )( addr + 0x1E );
        volatile Uint8* flash_ctlAAA = ( Uint8* )( addr + 0xAAA );
        volatile Uint8* flash_ctl555 = ( Uint8* )( addr + 0x555 );

    /*------------------------------------------------------------------*
     *                                                                  *
     *  Am I Spansion based Flash?                                      *
     *                                                                  *
     *------------------------------------------------------------------*/
            *addr8 = FLASH_RESET;               // Read Mode
            *flash_ctlAAA = FLASH_CMD_AA;
            *flash_ctl555 = FLASH_CMD_55;
            *flash_ctlAAA = FLASH_ID;           // Read ID Mode

			for(i = 0; i < 1000; i ++);			
            device_mfg      = *pmfgid;          // Read MFG_ID
			for(i = 0; i < 10; i ++);
            device_devid[0] = *pdevid1;         // Read DEV_ID
            device_devid[1] = *pdevid2;
            device_devid[2] = *pdevid3;

            *addr8 = FLASH_RESET;


        if ( device_mfg == MFG_SPANSION )
            return MFG_SPANSION;                // Spansion Flash
            
    return 0;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _FLASH_getMfg( )                                                        *
 *                                                                          *
 *      Who am I?   Intel/Micron based Flash?                               *
 *                  Spansion based Flash?                                   *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Int32 _FLASH_getMfg( )
{
    if ( device_mfg != 0 )
        return device_mfg;
    else
        return _FLASH_getId( );
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _FLASH_getTotalPages( number_devices )                                  *
 *      Compute the total number of Flash pages                             *
 *                                                                          *
 *      number_devices  <- number of Flash device chips ( hardcoded to 1 )  *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Uint32 FLASH_getTotalPages( Uint16 number_devices )
{
    number_devices = number_devices;

    device_flash_pages = 0;
    device_flash_page_length = 0;
    device_flash_page_len_pow2 = 0;


        /*------------------------------------------------------------------*
         *                                                                  *
         *  SPANSION Flash                                                  *
         *                                                                  *
         *------------------------------------------------------------------*/
        if ( _FLASH_getMfg( ) == MFG_SPANSION )
        {
            device_flash_pages += 64;          // 64 sectors
            device_flash_page_length = 0x10000; // 64-KB sector size
            device_flash_page_len_pow2 = 16;    // Pow 2 size
        }
    /* Return total pages of Flash */
    return device_flash_pages;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _FLASH_getPageLength( )                                                 *
 *                                                                          *
 *      What is the page Length?                                            *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Int32 _FLASH_getPageLength( )
{
    return device_flash_page_length;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _FLASH_nextPage( )                                                      *
 *                                                                          *
 *      Get Next Page of Flash.  This is only used b/c the Intel Flash being*
 *      contains 4 smaller pages in the TOP or BOTTOM page.  This breaks    *
 *      the assumption that each page is the same size, thus we need to     *
 *      compute what the next page is.                                      *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Uint32 _FLASH_nextPage( Uint32 addr )
{
    Uint32 flash_offset;        // Offset in Flash
    Uint32 flash_major_page;    // Full size page

    /* Go to first page */
    if ( addr < FLASH_BASE )
        return FLASH_BASE;

    /* Last Page, no where left to go */
    if ( addr >= FLASH_END_OR_RANGE )
        return FLASH_END_OR_RANGE;

    /* ---------------------------------------------------------------- *
     *                                                                  *
     *  Spansion Flash                                                  *
     *                                                                  *
     * ---------------------------------------------------------------- */

        if ( _FLASH_getMfg( ) == MFG_SPANSION )
        {
            /* Compute offset and page */
            flash_offset = addr - FLASH_BASE;
            flash_major_page = flash_offset >> device_flash_page_len_pow2;

            addr = FLASH_BASE;
            addr += ( flash_major_page + 1 ) << device_flash_page_len_pow2;

            return addr;
        }
    return 0xFFFFFFFF;
}

int FLASH_read( Uint32 dst, Uint32 page, Uint32 length )
{
	Uint32 i;
	Uint8* psrc8;
	Uint8* pdst8 = ( Uint8* )dst;
	psrc8 = ( Uint8* )(FLASH_BASE + (page*FLASH_PAGESIZE));

    /*
     *  Set to Read Mode
     */
	if ( _FLASH_getMfg( ) == MFG_SPANSION )
	    FLASH_BASE_PTR8 = FLASH_RESET;
    /*
     *  Read Data to Buffer
     */
        for ( i = 0 ; i < length ; i++ )
            *pdst8++ = *psrc8++;

    return 1;
}

int FLASH_write( Uint32 write_data, Uint32 page, Uint32 length )
{
	Uint32 i;
	Uint32 src;
	Uint32 dst;
	unsigned char srcd, dstd;
	Uint8* psrc8;
	Uint8* pdst8;
	dst = FLASH_BASE + (page*FLASH_PAGESIZE);
	psrc8 = ( Uint8* )write_data;
	pdst8 = ( Uint8* )dst;


	/* ---------------------------------------------------------------- *
	 *                                                                  *
	 *  SPANSION Flash                                                  *
	 *                                                                  *
	 * ---------------------------------------------------------------- */

		if ( _FLASH_getMfg( ) == MFG_SPANSION )
		{


			for ( i = 0 ; i < length ; i++ )
			{
				/* Program one 8-bit byte */
				FLASH_CTLAAA = FLASH_CMD_AA;
				FLASH_CTL555 = FLASH_CMD_55;
				FLASH_CTLAAA = FLASH_PROGRAM;
				*pdst8 = *psrc8;

				//_waitusec( 30 );
				// Wait for programming to complete 
				if ( _FLASH_waitWhileProgramming( ( Uint32 )pdst8, *psrc8, 0x00500000 ) )
					return 0;
				pdst8++;
				psrc8++;
			}
		}
	return 1;
}

int FLASH_erase( int page)
{
    Uint32 addr;
    Uint32 end;
    Uint32 start;
    volatile Uint8*  addr8;
	int wait = 0x08000000;
	int sleep = 0x00040000;
    start =  FLASH_BASE + (page*FLASH_PAGESIZE);
    end = start+FLASH_PAGESIZE-1;       // Calculate end of range
    start &= 0xFFFFFFFE;            // Align to 16-bit words

    /* ---------------------------------------------------------------- *
     *                                                                  *
     *  SPANSION Flash                                                  *
     *                                                                  *
     * ---------------------------------------------------------------- */

        if ( _FLASH_getMfg( ) == MFG_SPANSION )
        {
            /*
             *  Erase each Flash block in the range
             */
//            for ( addr = start ; addr <= end ; addr = _FLASH_nextPage( addr ) )
//            {
                    addr8 = ( volatile Uint8* )start;
                    *addr8 = FLASH_RESET;               // Reset Flash
                    FLASH_CTLAAA = FLASH_CMD_AA;
                    FLASH_CTL555 = FLASH_CMD_55;
                    FLASH_CTLAAA = FLASH_ERASE;         // Erase sector command
                    FLASH_CTLAAA = FLASH_CMD_AA;
                    FLASH_CTL555 = FLASH_CMD_55;
                    *addr8 = FLASH_ERASE_SECTOR;        // Erase sector confirm

			        while ( wait-- > 0 )
			            if ( *addr8 == 0xFF )
			                /* Good, erase completed */
			                return 1;
					
			        /* Timeout occured */
			        FLASH_BASE_PTR8 = FLASH_RESET;
			        return 0;

/*
                    if ( _FLASH_waitWhileErasing( start, wait ) )
                    {
                        *addr8 = FLASH_RESET;
                        return 0;                      // Timeout/Error occured
                    }
*/
 //           }
        }
 //   return 1;
}
