/*
 *  NAND Flash 
 *
 */

#include "stdio.h"
#include "C6747.h"
#include "nandflash.h"


static Uint8 rx[NAND_PAGESIZE + NAND_SPARESIZE];
static Uint8 tx[NAND_PAGESIZE + NAND_SPARESIZE];

/*
 *  Invalid Block & Page list
 */
static Uint16 invalid_blk_count = 0;
static Uint16 invalid_blks[80];

static Uint16 invalid_pg_count = 0;
static Uint16 invalid_pgs[80];

/*
 *  MFG & Device IDs
 */
static Uint8 mfg_id;
static Uint8 dev_id;
static Uint8 num_id;
static Uint8 size_id;
static Uint8 plane_id;


/*
 *  NAND Flash timeout
 */
static Uint32 nand_timeout = ( 0x00100000 );

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  nandflash_test( )                                                       *
 *                                                                          *
 * ------------------------------------------------------------------------ */

void FLASHGPIO_init()
{
	PINMUX13 = 0x11111111;
	PINMUX14 = 0x88111111;
	PINMUX15 = 0x11888888;
    PINMUX16 = 0x11111111;
    PINMUX18 = 0x11111110;

}

Int16 nandflash_test( )
{
    Int32 i;
    Int16 errors = 0;

    Uint32 nblocks;
    Uint32 npages;
    Uint32 nbytes;

    Uint32* p32;

    Uint32 page;
    Uint32 pageaddr;
    Int32 bad_pages = 0;
    Int32 allowed_bad_pages = 25;

    /* ---------------------------------------------------------------- *
     *  Initialize NAND Flash                                           *
     * ---------------------------------------------------------------- */
 //   EVMOMAPL137_FLASH_init( );

    /* Determine the total number of pages */
    npages =NANDFLASH_getTotalPages( );

    if ( npages == 0 )
    {
        printf( "     >> Error: Cannot find NAND Flash pages.\n" );
        return 1;
    }
    else
    {
        nblocks = npages >> 7;//NAND_PAGES_PER_BLOCK_POW2;
        nbytes  = (npages>> 20) * NAND_PAGESIZE;
     
        printf( "     %d Blocks\n", nblocks);
        printf( "     %d Pages\n", npages);
        printf( "     %d MBytes\n", nbytes);
    }

    /* ---------------------------------------------------------------- *
     *  Erase                                                           *
     * ---------------------------------------------------------------- */
    printf( "     Erasing Flash [%d-%d] blocks\n", 0, nblocks );

    /* Cycle through NAND Flash Blocks */
   
    for ( i = 0 ; i < 2 ; i++ )//nblocks
    {
        errors = NANDFLASH_erase( i, 1 );

        if ( errors == NAND_ERR_TIMEOUT )
        {
            printf( "     >> Error: Timeout occured.\n" );
            return errors;
        }
        else if ( errors != 0 )
        {
            printf( "     Found bad block: %d\n", i );
            if ( ( ++bad_pages ) > allowed_bad_pages )
            {
                printf( "     >> Error: Erasing NAND Flash %d\n", bad_pages );
                return errors;
            }
        }
	}

    bad_pages = 0;
	printf( "     erasing is ok\n" );

    /* ---------------------------------------------------------------- *
     *  Write                                                           *
     * ---------------------------------------------------------------- */
    printf( "     Writing Flash [%d-%d] pages\n", 0, npages );

    /* Iterate through all pages */

	for ( page = 0 ; page < 128 ; page++ )
    {

	    	pageaddr = page << 12;//13

        p32 = ( Uint32* )tx;
        for ( i = 0 ; i < NAND_PAGESIZE / 4 ; i ++ )
            *p32++ = ( pageaddr + i );

        // Write to Flash
        errors = NANDFLASH_writePage( ( Uint32 )tx, pageaddr, 1 );

        if ( errors )
        {
            printf( "     Found bad page: %d\n", page );
            if ( ( ++bad_pages ) > allowed_bad_pages )
            {
                printf( "     >> Error: Too many bad pages %d\n", bad_pages );
                return bad_pages;
            }
        }

    }

    bad_pages = 0;
	printf( "     writing is ok\n" );

    /* ---------------------------------------------------------------- *
     *  Read                                                            *
     * ---------------------------------------------------------------- */
    printf( "     Reading Flash [%d-%d] pages\n", 0, npages );

    /* Iterate through all pages */

   	for ( page = 0 ; page < 128 ; page++ )
    {

	   	   	pageaddr = page << 12;


        /* Read from Flash */
		p32 = ( Uint32* )rx;

        for ( i = 0 ; i < (NAND_PAGESIZE) / 4 ; i ++ )
            *p32++ = 0;
        errors = NANDFLASH_readPage( pageaddr, ( Uint32 )rx, 1 );
		//printf( "     good page: %d\n", page );
        /* Check pattern */
        p32 = ( Uint32* )rx;
        for ( i = 0; i < NAND_PAGESIZE / 4; i ++ )
            if ( *p32++ != ( pageaddr + i ) )
                errors = 1;

        if ( errors )
        {
            printf( "     Found bad page: %d\n", page );
            if ( ( ++bad_pages ) > allowed_bad_pages )
            {
                printf( "     >> Error: Too many bad pages %d\n", bad_pages );
                return bad_pages;
            }
        }


    }

    bad_pages = 0;
	printf( "     Reading is ok\n" );
    /* ---------------------------------------------------------------- *
     *  Erase                                                           *
     * ---------------------------------------------------------------- */
    printf( "     Erasing Flash [%d-%d] blocks\n", 0, nblocks );

    /* Cycle through NAND Flash Blocks */
    for ( i = 0 ; i < 2 ; i++ )
    {
        errors = NANDFLASH_erase( i, 1 );

        if ( errors == NAND_ERR_TIMEOUT )
        {
            printf( "     >> Error: Timeout occured.\n" );
            return errors;
        }
        else if ( errors != 0 )
        {
            printf( "     Found bad block: %d\n", i );
            if ( ( ++bad_pages ) > allowed_bad_pages )
            {
                printf( "     >> Error: Erasing NAND Flash %d\n", bad_pages );
                return errors;
            }
        }
    }

    return 0;
}




/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NAND_readECC( )                                                        *
 *                                                                          *
 *      Read ECC calcualtions                                               *
 *                                                                          *
 * ------------------------------------------------------------------------ */
#ifdef USE_ECC
    inline Uint32 _NAND_readECC( void )
    {
        return AEMIF_NANDECC2;
    }
#endif

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NAND_startECC( )                                                       *
 *                                                                          *
 *      Start ECC calcualtions                                              *
 *                                                                          *
 * ------------------------------------------------------------------------ */
#ifdef USE_ECC
    inline void _NAND_startECC( void )
    {
        _NAND_readECC( );
        AEMIF_NANDFCR |= 0x0100;
    }
#endif

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NAND_getBadPages( pages )                                              *
 *                                                                          *
 *      Get the last invaild pages                                          *
 *                                                                          *
 * ------------------------------------------------------------------------ */
void _NAND_getBadPages( Uint32* pages )
{
    Int16 i;
    for ( i = 0 ; invalid_pg_count ; i++ )
        pages[i] = invalid_pgs[i];

    if ( invalid_pg_count == 0 )
        pages[0] = 0;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NAND_getBadBlocks( blocks )                                            *
 *                                                                          *
 *      Get the last invaild blocks                                         *
 *                                                                          *
 * ------------------------------------------------------------------------ */
void _NAND_getBadBlocks( Uint32* blocks )
{
    Int16 i;
    for ( i = 0 ; invalid_blk_count ; i++ )
        blocks[i] = invalid_blks[i];

    if ( invalid_blk_count == 0 )
        blocks[0] = 0;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NAND_busywait( timeout )                                               *
 *                                                                          *
 *      Poll the external RDY/BSY pin                                       *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Int16 _NAND_busywait( Int32 timeout )
{
    /*
     *  Short delay to let the Ready/Busy signal go LOW
     */
    _wait( 200 );

    /*
     *  Wait while the device is busy
     */
    while( ( ! NAND_READ_RB ) && ( timeout-- > 0 ) );

    if ( timeout == 0 )
        return NAND_ERR_TIMEOUT;
    else
        return 0;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NANDFLASH_init( )                                                      *
 *      Initialize the NAND Flash                                           *
 *                                                                          *
 *  Note:                                                                   *
 *      The Write Protect on the NAND Flash is disabled.  This allows the   *
 *      erase & write NAND flash to work.                                   *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Int16 FLASH_init( )
{   
	Uint32 acfg2;

    /*------------------------------------------------------------------*
     *                                                                  *
     *  NAND Flash timing parameters                                    *
     *                                                                  *
     *      EMIF.CLK freq   = PLL1/6 = 594/6 = 99 MHz                   *
     *      EMIF.CLK period = 1/99 MHz = 10.1 ns
     *      应该是工作在100MHZ							                    *
     *                                                                  *
     *------------------------------------------------------------------*/
/*	acfg2=0x00a00504;*/
 acfg2 = 0
		| ( 0 << 31 )           // selectStrobe
       	| ( 0 << 30 )           // extWait
      	| ( 2 << 26 )           // writeSetup      //   0 ns//0
      	| ( 8 << 20 )           // writeStrobe     //  50 ns
      	| ( 2 << 17 )           // writeHold       //   0 ns
      	| ( 2 << 13 )           // readSetup       //   0 ns//0
      	| ( 6 << 7 )            // readStrobe      //  80 ns
      	| ( 2 << 4 )            // readHold        //  30 ns
       	| ( 3 << 2 )            // turnAround      //  10 ns//2
       	| ( 0 << 0 );           // asyncSize       // 8-bit bus
	_resetEMIF( EMIF_CS3 );
    _setupEMIF( EMIF_CS3, acfg2, EMIF_NAND_MODE );

    /* ---------------------------------------------------------------- *
     *                                                                  *
     *  Note: If NANDFLASH_CE_DO_CARE is defined, then CE will be low   *
     *        during read/write/erase operations.                       *
     *                                                                  *
     *        Else if NANDFLASH_CE_DO_NOT_CARE is defined, then CE will *
     *        only be low during accesses to the NAND Flash device.     *
     *                                                                  *
     * ---------------------------------------------------------------- */



    NAND_CMD( CMD_RESET );              // Reset NAND Flash

    if ( _NAND_busywait( nand_timeout ) )
        return NAND_ERR_TIMEOUT;   //对flash读写超时判断


    return 0;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NANDFLASH_getTotalPages( )                                             *
 *                                                                          *
 *      Determine the total number of pages of NAND Flash                   *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Uint32 NANDFLASH_getTotalPages( )
{
    Uint32 pages = 0;


    NAND_CMD( CMD_READID );             // Issue Read ID command
 
 	NAND_ADDR_1( 0 );

    mfg_id = NAND_DATA;                 // Read MFG Id   
    dev_id = NAND_DATA;                 // Read Device Id
	num_id = NAND_DATA;                 // Read internal chip number
	size_id = NAND_DATA;                // Read sizes
	plane_id = NAND_DATA;               // Read plane number, plane size

    /*
     *  Compared MFG ID & Device ID to the supported devices
     */
    if ( mfg_id == MFG_SAMSUNG )
    {
        switch ( dev_id )
        {
            case 0xDC:
				if(size_id == num_id)
					printf( "The number of internal chip is 2\n" );
				if(size_id == 0xB6)
					printf( "Page size is 4KB\nBlock size is 512KB\nOrganization is x8\n" );
				if(plane_id == 0x78)
					printf( "The number of plane is 4\nSize of each plane is 8Gb\n" );
                pages = 0x40000;//100000
                break;
        }
    }


    return pages;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NANDFLASH_erase( start, block_count )                                  *
 *                                                                          *
 *      Erase the blocks of Flash memory                                    *
 *          start = destination address [ block address ]                   *
 *          block_count = # of blocks   [ 1 block = 128*1024 bytes ]           *
 *                                                                          *
 *      Returns:    0   pass                                                *
 *                  1+  failed - bad blocks                                 *
 *                 -1   timeout                                             *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Int16 NANDFLASH_erase( Uint32 start, Uint32 block_count )
{
    Uint32 i;
	Uint32 add;
    invalid_blk_count = 0;

    for ( i = 0 ; i < block_count ; i++ )
    {
    
        add = start * 0x40000;
        NAND_CMD( CMD_ERASE );          // Erase block
		NAND_ADDR_2( add );
        NAND_CMD( CMD_ERASE_CONFIRM );  // Confirm Erase

		// Check Status
		
        if ( _NAND_busywait( nand_timeout ) )
        {
         
            return NAND_ERR_TIMEOUT;    // Timeout Error
        }
		NAND_CMD( CMD_STATUS );
        if ( NAND_DATA & CMD_STATUS_ERROR )
            invalid_blks[invalid_blk_count++] = start;	

      	start++;

    }
    return invalid_blk_count;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NANDFLASH_readPage( src, dst, page_count )                             *
 *                                                                          *
 *      Read data from NAND Flash by pages ( 2*1024 bytes )                    *
 *                                                                          *
 *          src = source address      [ must be aligned to start of page ]  *
 *          dst = destination address [ any address ]                       *
 *          page_count = # of pages   [ a page is 512 bytes ]               *
 *                                                                          *
 *      Note does not return spare array data                               *
 *                                                                          *
 *      Returns:    0   pass                                                *
 *                  1+  failed - bad pages                                  *
 *                 -1   timeout                                             *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Int16 NANDFLASH_readPage( Uint32 src, Uint32 dst, Uint32 page_count )
{
    Uint32 i, j;
    volatile Uint8* dst8 = ( volatile Uint8* )dst;


    invalid_pg_count = 0;

    for ( i = 0 ; i < page_count ; i++ )
    {


        NAND_CMD( CMD_READ );           // Read page
        NAND_ADDR_4( src );
		NAND_CMD( CMD_READ_CONFIRM );

        if ( _NAND_busywait( nand_timeout ) )
        {

            return NAND_ERR_TIMEOUT;    // Timeout Error
        }

		// Read MAIN array
		for ( j = 0 ; j < NAND_PAGESIZE ; j++ )
            *dst8++ = NAND_DATA;        
		// Read SPARE leftovers
        for ( j = 0 ; j < NAND_SPARESIZE ; j++ )
		{
			*dst8++ = NAND_DATA;
        }


		src += 0x1000;
    }

    return invalid_pg_count;
}

/* ------------------------------------------------------------------------ *
 *                                                                          *
 *  _NANDFLASH_writePage( src, dst, page_count )                            *
 *                                                                          *
 *      Program NAND Flash.                                                 *
 *          src = source address      [ any address ]                       *
 *          dst = destination address [ must be aligned to start of page ]  *
 *          page_count = # of pages   [ a page is 512 bytes ]               *
 *                                                                          *
 *      Note does not program SPARE arrays                                  *
 *                                                                          *
 *      Returns:    0   pass                                                *
 *                  1+  failed - bad pages                                  *
 *                 -1   timeout                                             *
 *                                                                          *
 * ------------------------------------------------------------------------ */
Int16 NANDFLASH_writePage( Uint32 src, Uint32 dst, Uint32 page_count )
{
    Uint32 i, j = 0;
    volatile Uint8* src8 = ( volatile Uint8* )src;

    invalid_pg_count = 0;

    for ( i = 0 ; i < page_count ; i++ )
    {
      //NAND_ASSERT_CE( );              // Assert CE ( for CE-care devices )

        NAND_CMD( CMD_PROGRAM );        // Program page
        NAND_ADDR_4( dst );

		// Write MAIN array
		for ( j = 0 ; j < NAND_PAGESIZE ; j++ )                  
			NAND_DATA = *src8++;			
		// Write Spare array
		for ( j = 0 ; j < NAND_SPARESIZE ; j++ ) 
            NAND_DATA = 0xFF;
               		
		// Confirm Program
        NAND_CMD( CMD_PROGRAM_CONFIRM );
        if ( _NAND_busywait( nand_timeout ) )
        {
  
            return NAND_ERR_TIMEOUT;    // Timeout Error
        }

		// Check Status
        NAND_CMD( CMD_STATUS );         
        if ( NAND_DATA & CMD_STATUS_ERROR )
            invalid_pgs[invalid_pg_count++] = dst;

  
        dst += 0x1000;           
    }

    return invalid_pg_count;
}




