/*
 * Copyright (c) 2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/**
 * \file  : nandflash_test.c
 *
 * \brief : Support routines to test the NAND Flash
 *
 * \b \Descr: This file contains the suppoort routines for testing the NAND
 * \n         Flash on the DM8127 EVM
 *
 */

/*
 *====================
 * Includes
 *====================
 */

#include "dm8127Board.h"
#include "dm8127_platform.h"
#include "dm8127_types.h"
#include "stdio.h"
#include "nandflash.h"
#include "gpio.h"


/*
 *=======================
 * Funnction definition
 *=======================
 */

/***
 * \brief: NAND Flash #wp test function.
 *
 * \b \Descr: This will send the command to the NAND flash to check the status
 * \n         for the NAND flash write protect or not.
 *
 * \param: VOID
 * \return:VOID
 */

void nand_isWriteProtect
(
	void
)
{

    UINT32 status;

	// Check erase status
	NANDFLASH_CLE = CMD_STATUS;
	status = NANDFLASH_DATA;

	NANDFLASH_CLE = CMD_RESET;
	if (status & 0x80)
	{
#ifdef DEBUG
		printf("Write protect disable\n");
#else
		Uart_stringSend("Write protect disable\n\r");
#endif

	}
	else
	{
#ifdef DEBUG
		printf("Write protect Enable\n");
#else
		Uart_stringSend("Write protect Enable\n\r");
#endif
	}
}
#undef SPARE_CHECK

/***
 * \brief : Routine to Test Nandflash device .
 *
 * \b \Descr: This routine tests first 4 blocks of device without Spare area.
 * \n         Uncommenting the  loop tests with spare area.This routine first
 * \n         erases the blocks.Writes into the device and read the same and
 * \n         Compares both.
 *
 * \param   VOID
 * \return : NAND_TEST_PASS  Successfully pass
 *           NAND_TEST_FAIL  NAND test Failed
 */
STATUS NAND_Flash_Test
(
		void *testargs
)
{
	STATUS u32RetVal;
    SINT16 status;
    UINT32 i, j, errors_Cnt, No_Of_blocks_test, u32Count;
	 Uart_stringSend("\r\n==================================="
					"\r\n          NAND Flash Test          "
					"\r\n===================================\r\n");
	static UINT16 src[NAND_FLASH_PAGESIZE + NAND_FLASH_SPARESIZE] = { };
	static UINT16 dst[NAND_FLASH_PAGESIZE + NAND_FLASH_SPARESIZE] = { };

	/* Variable init */
	u32RetVal = FAILED;
	errors_Cnt = INIT_VALUE;
	u32Count= INIT_VALUE;
	No_Of_blocks_test = NAND_FLASH_BLOCKS_TEST_CNT;

	GPIO_1_init();
    /* Initialize NAND */
	u32RetVal = nand_init();

    if(SUCCESS != u32RetVal)
    {
    	u32RetVal = FAILED;
        return u32RetVal;
    }

    No_Of_blocks_test = NAND_FLASH_BLOCKS_TEST_CNT;  /* NANDFLASH_BLOCKS */


#ifdef DEBUG
    printf("Testing %d blocks\n", No_Of_blocks_test);
#else
    platform_write("Testing %d blocks\n", No_Of_blocks_test);
#endif

#ifdef DEBUG
    printf("Erasing NAND blocks...\r\n");
#else
    Uart_stringSend("Erasing NAND blocks...\r\n");
#endif
    /* Erase the NAND flash uptp No_Of_blocks_test */


	/* Write protect DISABLE*/
	set_gpio_bank1 (BIT_SEL_GPIO, 1);
	delay(100);
/***************************************************************************************/
	/**
	 * The chip is already erased when manufactur, avoid erase when writing
	 * first time.
	 */
    for (u32Count = 0; u32Count < No_Of_blocks_test ; u32Count++)
    {
    	u32RetVal = nand_eraseBlock(u32Count);
        if (FAILED == u32RetVal)
		{
            errors_Cnt ++;

        }
    }


#ifdef DEBUG
    printf(" -- Total  %d block failed to erase \n", errors_Cnt);
#else
    platform_write("Total  %d block failed to erase \n", errors_Cnt);
#endif

    /* Fill the source buffer */
#ifndef SPARE_CHECK
    for (i = 0; i < (NAND_FLASH_PAGE_SIZE_WITH_SPARE_AREA); i++)
#else
    for (i = 0; i < NAND_FLASH_PAGESIZE; i++)
#endif
     src[i] = 0xA5A5;

    /* Clear the destination buffer */
#ifndef SPARE_CHECK
    for (i = 0; i < (NAND_FLASH_PAGE_SIZE_WITH_SPARE_AREA); i++)
#else
    for (i = 0; i < NAND_FLASH_PAGESIZE; i++)
#endif
        dst[i] = 0x0000;

    /* Program all pages */
#ifdef DEBUG
    printf("Programming NAND pages\n");
#else
    Uart_stringSend("Programming NAND pages\n\r");
#endif

    for (j = 0; j < (No_Of_blocks_test * NAND_FLASH_PAGESPERBLCK); j++)
    {

#ifdef SPARE_CHECK
	src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 4] =  j & 0x000000ff;
	src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 3] = (j & 0x0000ff00) >> 8;
	src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 2] = (j & 0x00ff0000) >> 16;
	src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 1] = (j & 0xff000000) >> 24;
#endif

#ifdef SPARE_CHECK
	nand_writePage(j, src, NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE);
#else
    nand_writePage(j, src, NAND_FLASH_PAGESIZE);
#endif
    }

#ifdef DEBUG
    printf("Comparing data\n");
#else
    Uart_stringSend("Comparing data\n\r");
#endif
    /* Read and compare all pages */
    errors_Cnt = 0;
    for (j = 0; j < (No_Of_blocks_test * NAND_FLASH_PAGESPERBLCK); j++)
    {
#ifdef SPARE_CHECK
    nand_readPage(j, dst, NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE);
#else
    nand_readPage(j, dst, NAND_FLASH_PAGESIZE);
#endif

    /** Validating the data  */
    status = 0;
    for (i = 0; i < 100; i++)
    {

    	if(src[i] != dst[i])
    	{
    		status = 1;
    		break;
    	}

    }

	if (status)
    {

#ifdef DEBUG
		printf("Error in data\n\n");
#else
		Uart_stringSend("Error in data\n\n\r");
#endif
	    break;
    }
}
    if (i == 100)
    {
#ifdef DEBUG
    	printf("No Error in data\n\n");
#else
    	Uart_stringSend("No Error in data\n\n\r");
    	u32RetVal = SUCCESS;
#endif
    }

/*****************************************************************************/
    /* Write protect Enable*/
    nand_isWriteProtect();
#ifdef DEBUG
    printf("Enabling the write protect\n");
#else
    Uart_stringSend("Enabling the write protect\n\r");
#endif

	set_gpio_bank1 (BIT_SEL_GPIO, 0);
	nand_isWriteProtect();
	delay(100);

	errors_Cnt = 0;
	for (u32Count = 0; u32Count < No_Of_blocks_test ; u32Count++)
	{
		u32RetVal = nand_eraseBlock(u32Count);
		if (FAILED == u32RetVal)
		{
			errors_Cnt ++;
		}
	}
#ifdef DEBUG
	printf(" -- Total  %d block failed to erase \n", errors_Cnt);
#else
	platform_write("Total  %d block failed to erase \n\r", errors_Cnt);
#endif


          /* Fill the source buffer */
#ifndef SPARE_CHECK
	  for (i = 0; i < (NAND_FLASH_PAGE_SIZE_WITH_SPARE_AREA); i++)
#else
	  for (i = 0; i < NAND_FLASH_PAGESIZE; i++)
#endif
	   src[i] = 0x1234;

	  /* Clear the destination buffer */
#ifndef SPARE_CHECK
	  for (i = 0; i < (NAND_FLASH_PAGE_SIZE_WITH_SPARE_AREA); i++)
#else
	  for (i = 0; i < NAND_FLASH_PAGESIZE; i++)
#endif
		  dst[i] = 0x0000;

	  /* Program all pages */
#ifdef DEBUG
	  printf("Programming NAND pages\n");
#else
	  Uart_stringSend("Programming NAND pages\n\r");
#endif

	  for (j = 0; j < (No_Of_blocks_test * NAND_FLASH_PAGESPERBLCK); j++)
	  {

      #ifdef SPARE_CHECK
      	src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 4] =  j & 0x000000ff;
      	src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 3] = (j & 0x0000ff00) >> 8;
      	src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 2] = (j & 0x00ff0000) >> 16;
      	src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 1] = (j & 0xff000000) >> 24;
      #endif

      #ifdef SPARE_CHECK
      	nand_writePage(j, src, NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE);
      #else
          nand_writePage(j, src, NAND_FLASH_PAGESIZE);
      #endif
          }
          /* Read and compare all pages */
#ifdef DEBUG
	  printf("Comparing data\n");
#else
	  Uart_stringSend("Comparing data\n\r");
#endif

          errors_Cnt = 0;
          for (j = 0; j < (No_Of_blocks_test * NAND_FLASH_PAGESPERBLCK); j++)
          {
      #ifdef SPARE_CHECK
          nand_readPage(j, dst, NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE);
      #else
          nand_readPage(j, dst, NAND_FLASH_PAGESIZE);
      #endif

#ifdef SPARE_CHECK
	if (dst[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 1] != ((j & 0xff000000) >> 24))
		status = 1;
	if (dst[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 2] != ((j & 0x00ff0000) >> 16))
		status = 1;
	if (dst[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 3] != ((j & 0x0000ff00) >> 8))
		status = 1;
	if (dst[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 4] != (j & 0x000000ff))
		status = 1;
#endif
	  /** Validating the data  */
	    status = 0;
	    for (i = 0; i < 100; i++)
	    {

	    	if(src[i] != dst[i])
	    	{
	    		status = 1;
	    		break;
	    	}
	    }
		if (status)
	    {

	#ifdef DEBUG
			printf("Error in data\n\n");
	#else
			Uart_stringSend("Error in data\n\n\r");
	#endif
			u32RetVal = SUCCESS;
		    break;
	    }
	}
	    if (i == 100)
	    {
	#ifdef DEBUG
	    	printf("No Error in data\n\n");
	#else
	    	Uart_stringSend("No Error in data\n\n\r");
	#endif
	    }
/*****************************************************************************/

	  /* Write protect Enable*/
	  nand_isWriteProtect();
#ifdef DEBUG
	  printf("Disabling the write protect\n");
#else
	  Uart_stringSend("Disabling the write protect\n\r");
#endif

	  set_gpio_bank1 (BIT_SEL_GPIO, 1);
	  nand_isWriteProtect();
	  delay(100);

	errors_Cnt = 0;
	for (u32Count = 0; u32Count < No_Of_blocks_test ; u32Count++)
	{
		u32RetVal = nand_eraseBlock(u32Count);
		if (FAILED == u32RetVal)
		{
			errors_Cnt ++;
		}
	}
#ifdef DEBUG
	printf(" -- Total  %d block failed to erase \n", errors_Cnt);
#else
	platform_write("Total  %d block failed to erase \n\r", errors_Cnt);
#endif

			/* Fill the source buffer */
  #ifndef SPARE_CHECK
	  for (i = 0; i < (NAND_FLASH_PAGE_SIZE_WITH_SPARE_AREA); i++)
  #else
	  for (i = 0; i < NAND_FLASH_PAGESIZE; i++)
  #endif
	   src[i] = 0x1234;

	  /* Clear the destination buffer */
  #ifndef SPARE_CHECK
	  for (i = 0; i < (NAND_FLASH_PAGE_SIZE_WITH_SPARE_AREA); i++)
  #else
	  for (i = 0; i < NAND_FLASH_PAGESIZE; i++)
  #endif
		  dst[i] = 0x0000;

	  /* Program all pages */
#ifdef DEBUG
	  printf("Programming NAND pages\n");
#else
	  Uart_stringSend("Programming NAND pages\n\r");
#endif

	  for (j = 0; j < (No_Of_blocks_test * NAND_FLASH_PAGESPERBLCK); j++)
	  {

		#ifdef SPARE_CHECK
			src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 4] =  j & 0x000000ff;
			src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 3] = (j & 0x0000ff00) >> 8;
			src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 2] = (j & 0x00ff0000) >> 16;
			src[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 1] = (j & 0xff000000) >> 24;
		#endif

		#ifdef SPARE_CHECK
			nand_writePage(j, src, NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE);
		#else
			nand_writePage(j, src, NAND_FLASH_PAGESIZE);
		#endif
			}

			/* Read and compare all pages */
#ifdef DEBUG
	  printf("Comparing data\n");
#else
	  Uart_stringSend("Comparing data\n\r");
#endif
			errors_Cnt = 0;
			for (j = 0; j < (No_Of_blocks_test * NAND_FLASH_PAGESPERBLCK); j++)
			{
		#ifdef SPARE_CHECK
			nand_readPage(j, dst, NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE);
		#else
			nand_readPage(j, dst, NAND_FLASH_PAGESIZE);
		#endif

  #ifdef SPARE_CHECK
	if (dst[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 1] != ((j & 0xff000000) >> 24))
		status = 1;
	if (dst[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 2] != ((j & 0x00ff0000) >> 16))
		status = 1;
	if (dst[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 3] != ((j & 0x0000ff00) >> 8))
		status = 1;
	if (dst[NANDFLASH_PAGESIZE + NANDFLASH_SPARESIZE - 4] != (j & 0x000000ff))
		status = 1;
  #endif
	  /** Validating the data  */
		status = 0;
		for (i = 0; i < 100; i++)
		{

			if(src[i] != dst[i])
			{
				status = 1;
				break;
			}

		}


		if (status)
	    {

	#ifdef DEBUG
			printf("Error in data\n\n");
	#else
			Uart_stringSend("Error in data\n\r");
	#endif
			u32RetVal = SUCCESS;
		    break;
	    }
	}
	    if (i == 100)
	    {
	#ifdef DEBUG
	    	printf("No Error in data\n\n");
	#else
	    	Uart_stringSend("No Error in data\n\n\r");
	#endif
	    }
#ifdef DEBUG
	    printf("NAND flash test performed\n");
#else
	    Uart_stringSend("NAND flash test performed\n\r");
#endif
	    Uart_stringSend("\r\n-------------------------X-----------------------------");
    return (u32RetVal);
}
