/***************************************************************************************
 *      Copyright(c) 2014 ECONET Incorporation All rights reserved.
 *
 *      This is unpublished proprietary source code of ECONET Networks Incorporation
 *
 *      The copyright notice above does not evidence any actual or intended
 *      publication of such source code.
 ***************************************************************************************
 */

/*======================================================================================
 * MODULE NAME: spi
 * FILE NAME: spi_nand_flash.c
 * DATE: 2014/11/21
 * VERSION: 1.00
 * PURPOSE: To Provide SPI NAND Access interface.
 * NOTES:
 *
 * AUTHOR :          REVIEWED by
 *
 * FUNCTIONS
 *
 *      SPI_NAND_Flash_Init             To provide interface for SPI NAND init. 
 *      SPI_NAND_Flash_Write_Nbyte      To provide interface for Write N Bytes into SPI NAND Flash. 
 *      SPI_NAND_Flash_Read_Byte        To provide interface for read 1 Bytes from SPI NAND Flash. 
 *      SPI_NAND_Flash_Read_DWord       To provide interface for read Double Word from SPI NAND Flash.  
 *      SPI_NAND_Flash_Read_NByte       To provide interface for Read N Bytes from SPI NAND Flash. 
 *      SPI_NAND_Flash_Erase            To provide interface for Erase SPI NAND Flash. 
 * 
 * DEPENDENCIES
 *
 * * $History: $
 * MODIFICTION HISTORY:
 * Version 1.00 - Date 2014/11/21 
 * ** This is the first versoin for creating to support the functions of
 *    current module.
 *
 *======================================================================================
 */

/* INCLUDE FILE DECLARATIONS --------------------------------------------------------- */
#include <linux/version.h>

#include <flashhal.h>
#include <asm/tc3162.h>
#include <asm/system.h>

#include <asm/io.h>
#include <linux/types.h>

#include <asm/arch-en7523/spi_nand_flash.h>
#include <asm/arch-en7523/spi_controller.h>
#include <asm/arch-en7523/spi_nfi.h>
#include <asm/arch-en7523/spi_ecc.h>
#include <asm/arch-en7523/nand_flash_otp.h>
#include "bmt.h"

/* NAMING CONSTANT DECLARATIONS ------------------------------------------------------ */

/* SPI NAND Command Set */
#define _SPI_NAND_OP_GET_FEATURE					0x0F	/* Get Feature */
#define _SPI_NAND_OP_SET_FEATURE					0x1F	/* Set Feature */
#define _SPI_NAND_OP_PAGE_READ						0x13	/* Load page data into cache of SPI NAND chip */
#define _SPI_NAND_OP_READ_FROM_CACHE_SINGLE			0x03	/* Read data from cache of SPI NAND chip, single speed*/
#define _SPI_NAND_OP_READ_FROM_CACHE_DUAL			0x3B	/* Read data from cache of SPI NAND chip, dual speed*/
#define _SPI_NAND_OP_READ_FROM_CACHE_QUAD			0x6B	/* Read data from cache of SPI NAND chip, quad speed*/
#define _SPI_NAND_OP_WRITE_ENABLE					0x06	/* Enable write data to  SPI NAND chip */
#define _SPI_NAND_OP_WRITE_DISABLE					0x04	/* Reseting the Write Enable Latch (WEL) */
#define _SPI_NAND_OP_PROGRAM_LOAD_SINGLE			0x02	/* Write data into cache of SPI NAND chip with cache reset, single speed */
#define _SPI_NAND_OP_PROGRAM_LOAD_QUAD				0x32	/* Write data into cache of SPI NAND chip with cache reset, quad speed */
#define _SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE		0x84	/* Write data into cache of SPI NAND chip, single speed */
#define _SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD		0x34	/* Write data into cache of SPI NAND chip, quad speed */

#define _SPI_NAND_OP_PROGRAM_EXECUTE		0x10	/* Write data from cache into SPI NAND chip */
#define _SPI_NAND_OP_READ_ID				0x9F	/* Read Manufacture ID and Device ID */
#define _SPI_NAND_OP_BLOCK_ERASE			0xD8	/* Erase Block */
#define _SPI_NAND_OP_RESET					0xFF	/* Reset */
#define _SPI_NAND_OP_DIE_SELECT				0xC2	/* Die Select */

/* SPI NAND register address of command set */
#define _SPI_NAND_ADDR_MANUFACTURE_ID		0x00	/* Address of Manufacture ID */
#define _SPI_NAND_ADDR_DEVICE_ID			0x01	/* Address of Device ID */

/* SPI NAND value of register address of command set */
#define _SPI_NAND_VAL_DISABLE_PROTECTION	0x0		/* Value for disable write protection */
#define _SPI_NAND_VAL_ENABLE_PROTECTION		0x38	/* Value for enable write protection */
#define _SPI_NAND_VAL_OIP					0x1		/* OIP = Operaton In Progress */
#define _SPI_NAND_VAL_WEL					0x2		/* WEL = Write enable bit */
#define _SPI_NAND_VAL_ERASE_FAIL			0x4		/* E_FAIL = Erase Fail */
#define _SPI_NAND_VAL_PROGRAM_FAIL			0x8		/* P_FAIL = Program Fail */

/* Others Define */
#define _SPI_NAND_LEN_ONE_BYTE				(1)
#define _SPI_NAND_LEN_TWO_BYTE				(2)
#define _SPI_NAND_LEN_THREE_BYTE			(3)
#define _SPI_NAND_BLOCK_ROW_ADDRESS_OFFSET	(6)

#define _SPI_NFI_CHECK_ECC_DONE_MAX_TIMES	(1000000)
#define CACHE_LINE_SIZE						(32)

#ifdef BLOCK_SIZE
#undef BLOCK_SIZE
#endif

#ifdef PAGE_SIZE
#undef PAGE_SIZE
#endif

#define BLOCK_SIZE		(_current_flash_info_t.erase_size)
#define PAGE_SIZE		(_current_flash_info_t.page_size)
#define PAGE_CNT_PER_BLOCK	(BLOCK_SIZE / PAGE_SIZE)


#define PAGE_SIZE_MAGIC_FLASH_ADDR			(0XFFB0)
#define PAGE_SIZE_MAGIC						(0x50414745)

#define LINUX_USE_OOB_START_OFFSET		(4)
#define MAX_LINUX_USE_OOB_SIZE			(26)
#define MAX_USE_OOB_SIZE				(LINUX_USE_OOB_START_OFFSET + MAX_LINUX_USE_OOB_SIZE + 2)

#define MIN(a,b)			((a) < (b) ? (a) : (b))

#define K0_TO_K1
#define K1_TO_PHY
#define dma_cache_inv(start,size)

char *rdma_addr;
char *wdma_addr;

#define _SPI_NAND_SEMAPHORE_LOCK()
#define _SPI_NAND_SEMAPHORE_UNLOCK()

unsigned int print_dot = 0;
int test_write_fail_flag = 0;
int en_oob_write = 0;

/* FUNCTION DECLARATIONS ------------------------------------------------------ */
extern void * memset(void * s, int c, size_t count);
static void spi_nand_direct_select_die(u32 die);
SPI_NAND_FLASH_RTN_T spi_nand_erase_block(u32 block_index);
static SPI_NAND_FLASH_RTN_T spi_nand_write_page(u32 page_number, 
												u32 data_offset,
											  	const u8  *ptr_data, 
											  	u32 data_len, 
											  	u32 oob_offset,
												u8  *ptr_oob , 
												u32 oob_len,
											    SPI_NAND_FLASH_WRITE_SPEED_MODE_T speed_mode);


/* MACRO DECLARATIONS ---------------------------------------------------------------- */
#define _SPI_NAND_BLOCK_ALIGNED_CHECK( __addr__,__block_size__) ((__addr__) & ( __block_size__ - 1))
#define _SPI_NAND_GET_DEVICE_INFO_PTR		&(_current_flash_info_t)

/* Porting Replacement */
	#define _SPI_NAND_PRINTF(args...)
	#define _SPI_NAND_DEBUG_PRINTF(args...)
	#define _SPI_NAND_DEBUG_PRINTF_HEX(args...)
	#define _SPI_NAND_DEBUG_PRINTF_ARRAY(args...)
#define _SPI_NAND_ENABLE_MANUAL_MODE		SPI_CONTROLLER_Enable_Manual_Mode
#define _SPI_NAND_WRITE_ONE_BYTE			SPI_CONTROLLER_Write_One_Byte
#define _SPI_NAND_WRITE_ONE_BYTE_WITH_CMD	SPI_CONTROLLER_Write_One_Byte_With_Cmd
#define _SPI_NAND_WRITE_NBYTE				SPI_CONTROLLER_Write_NByte
#define _SPI_NAND_READ_NBYTE				SPI_CONTROLLER_Read_NByte
#define _SPI_NAND_READ_CHIP_SELECT_HIGH		SPI_CONTROLLER_Chip_Select_High
#define _SPI_NAND_READ_CHIP_SELECT_LOW		SPI_CONTROLLER_Chip_Select_Low

#define _SPI_NAND_PRINTF				prom_printf

struct ra_nand_chip ra;
struct nand_info flashInfo;
flashdev_info devinfo;

u8 *dma_read_page = NULL;
u8 *dma_write_page = NULL;

u8 	tmp_dma_read_page[_SPI_NAND_CACHE_SIZE + CACHE_LINE_SIZE];
u8	tmp_dma_write_page[_SPI_NAND_CACHE_SIZE + CACHE_LINE_SIZE];
u8	_current_cache_page[_SPI_NAND_CACHE_SIZE];
u8	_current_cache_page_data[_SPI_NAND_PAGE_SIZE];
u8	_current_cache_page_oob[_SPI_NAND_OOB_SIZE];
u8	_current_cache_page_oob_mapping[_SPI_NAND_OOB_SIZE];

/* TYPE DECLARATIONS ----------------------------------------------------------------- */

/* STATIC VARIABLE DECLARATIONS ------------------------------------------------------ */
static int bmt_pool_size = 0;
static bmt_struct *g_bmt = NULL;
static init_bbt_struct *g_bbt = NULL;
extern int nand_flash_avalable_size;
u32 maximum_bmt_block_count=0;

#define BMT_BAD_BLOCK_INDEX_OFFSET (1)
#define POOL_GOOD_BLOCK_PERCENT 8/100
#define MAX_BMT_SIZE_PERCENTAGE 1/10

/* for coverity(CID:186345), DMA mode only enable or disable status */
typedef enum{
	SPI_DMA_MODE_DISABLE = 0,
	SPI_DMA_MODE_ENABLE,
} SPI_DMA_MODE_T;

u32	_current_page_num	= UNKNOW_PAGE;
NAND_FLASH_OTP_ENABLE_T otp_status = NAND_FLASH_OTP_DISABLE;
u32 reservearea_size = 0;
unsigned char _ondie_ecc_flag=1;    /* Ondie ECC : [ToDo :  Init this flag base on diffrent chip ?] */
SPI_DMA_MODE_T _spi_dma_mode=SPI_DMA_MODE_DISABLE;
unsigned char _plane_select_bit=0;
unsigned char _die_id = 0;

struct SPI_NAND_FLASH_INFO_T	_current_flash_info_t;			/* Store the current flash information */

unsigned char _parallel_nand_mode = 0;
SPI_NAND_FLASH_RTN_T (*nand_probe)( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_device_t ) = NULL;


/* LOCAL SUBPROGRAM BODIES------------------------------------------------------------ */
static int generic_ffs(int x)
{
	int r = 1;

	if (!x)
		return 0;
	if (!(x & 0xffff)) {
		x >>= 16;
		r += 16;
	}
	if (!(x & 0xff)) {
		x >>= 8;
		r += 8;
	}
	if (!(x & 0xf)) {
		x >>= 4;
		r += 4;
	}
	if (!(x & 3)) {
		x >>= 2;
		r += 2;
	}
	if (!(x & 1)) {
		x >>= 1;
		r += 1;
	}
	return r;
}

void spi_nand_protocol_set_otp(NAND_FLASH_OTP_ENABLE_T state)
{
	/* reset for forcing read page */
	_current_page_num = UNKNOW_PAGE;
	/* set OTP status */
	otp_status = state;
}

static SPI_NAND_FLASH_RTN_T spi_nand_protocol_chk_status(u8 mask, u8 value)
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	u32						check_cnt;
	u8						status;
	
	check_cnt = 0;
	do {
		rtn_status = spi_nand_protocol_get_feature(_SPI_NAND_ADDR_STATUS, &status);
		if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			_SPI_NAND_PRINTF("[Error] get status fail!\n");
			return rtn_status;
		}
		if(check_cnt == _SPI_NFI_CHECK_ECC_DONE_MAX_TIMES) {		
			_SPI_NAND_PRINTF("[Error] wait for busy free timeout, status=0x%x!\n", status);
			return SPI_NAND_FLASH_RTN_TIMEOUT;
		}
		check_cnt++;
	} while((status & mask) != value) ;

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_chk_status\n");
	
	return (SPI_NAND_FLASH_RTN_NO_ERROR);
}

static SPI_NAND_FLASH_RTN_T spi_nand_protocol_reset( void )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] reset Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();

	/* 2. Send FFh opcode (Reset) */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_SET_FEATURE );

	/* 5. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();	

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_reset\n");
	
	return (rtn_status);	
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_set_feature( u8 addr, u8 protection )
 * PURPOSE : To implement the SPI nand protocol for set status register.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr - register address
 *			 data - The variable of this register.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2017/5/26.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T spi_nand_protocol_set_feature( u8 addr, u8 data )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	u8						status;

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] set feature:0x%x Timeout.\n", addr);
		return (rtn_status);
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();

	/* 2. Send 0Fh opcode (Set Feature) */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_SET_FEATURE );
	
	/* 3. Offset addr */
	_SPI_NAND_WRITE_ONE_BYTE( addr );
	
	/* 4. Write new setting */
	_SPI_NAND_WRITE_ONE_BYTE( data );

	/* 5. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();	

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_set_feature %x: val=0x%x\n", addr, data);
	
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_get_feature( u8 addr, u8 *ptr_rtn_data )
 * PURPOSE : To implement the SPI nand protocol for get status register.
 * AUTHOR  :
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr - register address
 *   OUTPUT: ptr_rtn_protection  - A pointer to the ptr_rtn_protection variable.
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2017/5/26.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T spi_nand_protocol_get_feature( u8 addr, u8 *ptr_rtn_data )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	if (_parallel_nand_mode)
	{
		return rtn_status;
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();

	/* 2. Send 0Fh opcode (Get Feature) */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_GET_FEATURE );
	
	/* 3. Offset addr */
	_SPI_NAND_WRITE_ONE_BYTE_WITH_CMD(_SPI_CONTROLLER_VAL_OP_OS2ID, addr );
	
	/* 4. Read 1 byte data */
	_SPI_NAND_READ_NBYTE( ptr_rtn_data, _SPI_NAND_LEN_ONE_BYTE, SPI_CONTROLLER_SPEED_SINGLE);

	/* 5. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();	

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_get_feature %x: val=0x%x\n", addr, *ptr_rtn_data);
	
	return (rtn_status);
}

SPI_NAND_FLASH_RTN_T spi_nand_protocol_show_status(void)
{
	u8 feature;
	u8 i;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;

	for(i = 0; i < ptr_dev_info_t->die_num; i++) {
		spi_nand_direct_select_die(i);
		_SPI_NAND_PRINTF("Die %d:\n", i);
		rtn_status = spi_nand_protocol_get_feature(_SPI_NAND_ADDR_PROTECTION, &feature);
		if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			_SPI_NAND_PRINTF("spi_nand_protocol_show_status register:0x%02x fail.\n", _SPI_NAND_ADDR_PROTECTION);
			return (rtn_status);
		}
		_SPI_NAND_PRINTF("  0x%02x:0x%x\n", _SPI_NAND_ADDR_PROTECTION, feature);
		
		rtn_status = spi_nand_protocol_get_feature(_SPI_NAND_ADDR_FEATURE, &feature);
		if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			_SPI_NAND_PRINTF("spi_nand_protocol_show_status register:0x%02x fail.\n", _SPI_NAND_ADDR_FEATURE);
			return (rtn_status);
		}
		_SPI_NAND_PRINTF("  0x%02x:0x%x\n", _SPI_NAND_ADDR_FEATURE, feature);
		
		rtn_status = spi_nand_protocol_get_feature(_SPI_NAND_ADDR_STATUS, &feature);
		if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			_SPI_NAND_PRINTF("spi_nand_protocol_show_status register:0x%02x fail.\n", _SPI_NAND_ADDR_STATUS);
			return (rtn_status);
		}
		_SPI_NAND_PRINTF("  0x%02x:0x%x\n", _SPI_NAND_ADDR_STATUS, feature);
	}

	return (rtn_status);
}


/*------------------------------------------------------------------------------------
 * FUNCTION: SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Get_Flash_Info( struct SPI_NAND_FLASH_INFO_T    *ptr_rtn_into_t )
 * PURPOSE : To get system current flash info.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : None
 *   OUTPUT: ptr_rtn_into_t  - A pointer to the structure of the ptr_rtn_into_t variable.
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2015/01/14  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Get_Flash_Info(struct SPI_NAND_FLASH_INFO_T *ptr_rtn_into_t)
{
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;
	
	memcpy( ptr_rtn_into_t, ptr_dev_info_t, sizeof(struct SPI_NAND_FLASH_INFO_T));
	
	return (rtn_status);	
}

SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Set_Flash_Info( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_into_t)
{
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;
	
	memcpy(ptr_dev_info_t, ptr_rtn_into_t, sizeof(struct SPI_NAND_FLASH_INFO_T) );
	
	return (rtn_status);	
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_read_id( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_flash_id )
 * PURPOSE : To implement the SPI nand protocol for read id.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : None
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/12  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_protocol_read_id ( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_flash_id )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;	

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] read id Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select Low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Write op_cmd 0x9F (Read ID) */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_READ_ID );	
	
	/* 3. Write Address Byte (0x00) */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_ADDR_MANUFACTURE_ID );	
	
	/* 4. Read data (Manufacture ID and Device ID) */	
	_SPI_NAND_READ_NBYTE( (u8 *)&(ptr_rtn_flash_id->mfr_id), _SPI_NAND_LEN_ONE_BYTE, SPI_CONTROLLER_SPEED_SINGLE);	
	_SPI_NAND_READ_NBYTE( (u8 *)&(ptr_rtn_flash_id->dev_id), _SPI_NAND_LEN_ONE_BYTE, SPI_CONTROLLER_SPEED_SINGLE);
	
	/* 5. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_read_id : mfr_id=0x%x, dev_id=0x%x\n", ptr_rtn_flash_id->mfr_id, ptr_rtn_flash_id->dev_id);
	
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_read_id_2( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_flash_id )
 * PURPOSE : To implement the SPI nand protocol for read id.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : None
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/12  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_protocol_read_id_2 ( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_flash_id )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;	

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] read id 2 Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select Low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Write op_cmd 0x9F (Read ID) */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_READ_ID );	
	
	/* 3. Read data (Manufacture ID and Device ID) */	
	_SPI_NAND_READ_NBYTE( (u8 *)&(ptr_rtn_flash_id->mfr_id), _SPI_NAND_LEN_ONE_BYTE, SPI_CONTROLLER_SPEED_SINGLE);	
	_SPI_NAND_READ_NBYTE( (u8 *)&(ptr_rtn_flash_id->dev_id), _SPI_NAND_LEN_ONE_BYTE, SPI_CONTROLLER_SPEED_SINGLE);
	
	/* 4. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_read_id_2 : mfr_id=0x%x, dev_id=0x%x\n", ptr_rtn_flash_id->mfr_id, ptr_rtn_flash_id->dev_id);
	
	return (rtn_status);
}

static SPI_NAND_FLASH_RTN_T spi_nand_protocol_die_select_1( u8 die_id)
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] die select 1 Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();

	/* 2. Send C2h opcode (Die Select) */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_DIE_SELECT );

	/* 3. Send Die ID */
	_SPI_NAND_WRITE_ONE_BYTE( die_id );

	/* 5. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_die_select_1\n");

	return (rtn_status);
}

static SPI_NAND_FLASH_RTN_T spi_nand_protocol_die_select_2( u8 die_id)
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	u8 feature;

	rtn_status = spi_nand_protocol_get_feature(_SPI_NAND_ADDR_FEATURE_4, &feature);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("spi_nand_protocol_die_select_2 get die select fail.\n");
		return (rtn_status);
	}
	
	if(die_id == 0) {
		feature &= ~(0x40);
	} else {
		feature |= 0x40;
	}
	rtn_status = spi_nand_protocol_set_feature(_SPI_NAND_ADDR_FEATURE_4, feature);

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_die_select_2\n");

	return (rtn_status);
}

void spi_nand_select_die (u32 page_number)
{
	struct SPI_NAND_FLASH_INFO_T *ptr_dev_info_t;
	u8 die_id = 0;

	ptr_dev_info_t = _SPI_NAND_GET_DEVICE_INFO_PTR;

	if(ptr_dev_info_t->feature & SPI_NAND_FLASH_DIE_SELECT_1_HAVE) {
		if(otp_status == NAND_FLASH_OTP_DISABLE) {
			/* single die = 1024blocks * 64pages */
			die_id = ((page_number >> 16) & 0xff);
		} 
		if (_die_id != die_id) {
			_die_id = die_id;
			spi_nand_protocol_die_select_1(die_id);

			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_protocol_die_select_1: die_id=0x%x\n", die_id);
		}
	} else if(ptr_dev_info_t->feature & SPI_NAND_FLASH_DIE_SELECT_2_HAVE) {
		/* single die = 2plans * 1024blocks * 64pages */
		die_id = ((page_number >> 17) & 0xff);

		if (_die_id != die_id) {
			_die_id = die_id;
			spi_nand_protocol_die_select_2(die_id);

			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_protocol_die_select_2: die_id=0x%x\n", die_id);
		}
	}
}

static void spi_nand_direct_select_die(u32 die)
{
	struct SPI_NAND_FLASH_INFO_T *ptr_dev_info_t;

	ptr_dev_info_t = _SPI_NAND_GET_DEVICE_INFO_PTR;

	if(ptr_dev_info_t->feature & SPI_NAND_FLASH_DIE_SELECT_1_HAVE) {
		if (_die_id != die) {
			_die_id = die;
			spi_nand_protocol_die_select_1(die);

			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_protocol_die_select_1: die_id=0x%x\n", die);
		}
	} else if(ptr_dev_info_t->feature & SPI_NAND_FLASH_DIE_SELECT_2_HAVE) {
		if (_die_id != die) {
			_die_id = die;
			spi_nand_protocol_die_select_2(die);

			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_protocol_die_select_2: die_id=0x%x\n", die);
		}
	}
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_block_aligned_check( u32   addr,
 *                                                                     u32   len  )
 * PURPOSE : To check block align.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr - The addr variable of this function.
 *           len  - The len variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/15  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_block_aligned_check( u32 addr, 
														  u32 len )
{
	struct SPI_NAND_FLASH_INFO_T		*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T				rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;	
	
	
	ptr_dev_info_t = _SPI_NAND_GET_DEVICE_INFO_PTR ;
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_BLOCK_ALIGNED_CHECK_check: addr=0x%x, len=0x%x, block size =0x%x \n", addr, len, (ptr_dev_info_t->erase_size));	

	if (_SPI_NAND_BLOCK_ALIGNED_CHECK(len, (ptr_dev_info_t->erase_size))) 
	{
		len = ( (len/ptr_dev_info_t->erase_size) + 1) * (ptr_dev_info_t->erase_size);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_BLOCK_ALIGNED_CHECK_check: erase block aligned first check OK, addr:%x len:%x\n", addr, len, (ptr_dev_info_t->erase_size));
	}

	if (_SPI_NAND_BLOCK_ALIGNED_CHECK(addr, (ptr_dev_info_t->erase_size)) || _SPI_NAND_BLOCK_ALIGNED_CHECK(len, (ptr_dev_info_t->erase_size)) ) 
	{
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_BLOCK_ALIGNED_CHECK_check: erase block not aligned, addr:0x%x len:0x%x, blocksize:0x%x\n", addr, len, (ptr_dev_info_t->erase_size));		
		rtn_status = SPI_NAND_FLASH_RTN_ALIGNED_CHECK_FAIL;
	}	
	
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: void SPI_NAND_Flash_Clear_Read_Cache_Data( void )
 * PURPOSE : To clear the cache data for read. 
 *           (The next time to read data will get data from flash chip certainly.)
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : None
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2015/01/21  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
void SPI_NAND_Flash_Clear_Read_Cache_Data( void )
{
	_current_page_num	= UNKNOW_PAGE;
}

void SPI_NAND_Flash_Set_DmaMode( SPI_DMA_MODE_T input )
{
	_spi_dma_mode = input;
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_Flash_Set_DmaMode : dma_mode =%d\n", _spi_dma_mode);
}

void SPI_NAND_Flash_Get_DmaMode( SPI_DMA_MODE_T *val )
{
	*val = _spi_dma_mode;
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_Flash_Get_DmaMode : dma_mode =%d\n", _spi_dma_mode);
}

SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Enable_OnDie_ECC( void )
{
	unsigned char feature;
	struct SPI_NAND_FLASH_INFO_T *ptr_dev_info_t;
	u8 die_num;
	u32 die_page_num;
	unsigned long spinand_spinlock_flags;

	if (_parallel_nand_mode)
	{
		_ondie_ecc_flag = 0;
		return (SPI_NAND_FLASH_RTN_NO_ERROR);
	}

	_SPI_NAND_SEMAPHORE_LOCK();
		
	ptr_dev_info_t = _SPI_NAND_GET_DEVICE_INFO_PTR;

	if(((ptr_dev_info_t->feature) & SPI_NAND_FLASH_DIE_SELECT_1_HAVE) ||
	   ((ptr_dev_info_t->feature) & SPI_NAND_FLASH_DIE_SELECT_2_HAVE)) {
		for(die_num = 0; die_num < ptr_dev_info_t->die_num; die_num++) {
			die_page_num = die_num * (ptr_dev_info_t->device_size / ptr_dev_info_t->die_num) / ptr_dev_info_t->page_size;
			spi_nand_select_die(die_page_num);

			spi_nand_protocol_get_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, &feature);
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "before setting : SPI_NAND_Flash_Enable_OnDie_ECC, status reg=0x%x\n", feature);	
			
			if((feature & ptr_dev_info_t->ecc_en.ecc_en_mask) != ptr_dev_info_t->ecc_en.ecc_en_value) {
				feature = (feature & ~(ptr_dev_info_t->ecc_en.ecc_en_mask));
				feature |= (ptr_dev_info_t->ecc_en.ecc_en_value & ptr_dev_info_t->ecc_en.ecc_en_mask);

				spi_nand_protocol_set_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, feature);
			
				/* Value check*/
				spi_nand_protocol_get_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, &feature);
				if(feature & ptr_dev_info_t->ecc_en.ecc_en_mask) {
					_SPI_NAND_SEMAPHORE_UNLOCK();
					return (SPI_NAND_FLASH_RTN_NO_ERROR);
				} else {
					_SPI_NAND_SEMAPHORE_UNLOCK();
					return (SPI_NAND_FLASH_RTN_ENABLE_ECC_FAIL);
				}
			}
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "after setting : SPI_NAND_Flash_Enable_OnDie_ECC, status reg=0x%x\n", feature);	
		}
	} else {
		spi_nand_protocol_get_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, &feature);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "before setting : SPI_NAND_Flash_Enable_OnDie_ECC, status reg=0x%x\n", feature);	
		
		if((feature & ptr_dev_info_t->ecc_en.ecc_en_mask) != ptr_dev_info_t->ecc_en.ecc_en_value) {
			feature = (feature & ~(ptr_dev_info_t->ecc_en.ecc_en_mask));
			feature |= (ptr_dev_info_t->ecc_en.ecc_en_value & ptr_dev_info_t->ecc_en.ecc_en_mask);
			
			spi_nand_protocol_set_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, feature);

			/* Value check*/
			spi_nand_protocol_get_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, &feature);
			if(feature & ptr_dev_info_t->ecc_en.ecc_en_mask) {
				_SPI_NAND_SEMAPHORE_UNLOCK();
				return (SPI_NAND_FLASH_RTN_NO_ERROR);
			} else {
				_SPI_NAND_SEMAPHORE_UNLOCK();
				return (SPI_NAND_FLASH_RTN_ENABLE_ECC_FAIL);
			}
		}
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "after setting : SPI_NAND_Flash_Enable_OnDie_ECC, status reg=0x%x\n", feature);	
	}

	_ondie_ecc_flag = 1;

	_SPI_NAND_SEMAPHORE_UNLOCK();

	return (SPI_NAND_FLASH_RTN_NO_ERROR);
}

SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Disable_OnDie_ECC( void )
{
	unsigned char feature;
	struct SPI_NAND_FLASH_INFO_T *ptr_dev_info_t;
	u8 die_num;
	u32 die_page_num;
	unsigned long spinand_spinlock_flags;

	if (_parallel_nand_mode)
	{
		_ondie_ecc_flag = 0;
		return (SPI_NAND_FLASH_RTN_NO_ERROR);
	}

	_SPI_NAND_SEMAPHORE_LOCK();

	ptr_dev_info_t = _SPI_NAND_GET_DEVICE_INFO_PTR;

	if(((ptr_dev_info_t->feature) & SPI_NAND_FLASH_DIE_SELECT_1_HAVE) ||
	   ((ptr_dev_info_t->feature) & SPI_NAND_FLASH_DIE_SELECT_2_HAVE)) {
		for(die_num = 0; die_num < ptr_dev_info_t->die_num; die_num++) {
			die_page_num = die_num * (ptr_dev_info_t->device_size / ptr_dev_info_t->die_num) / ptr_dev_info_t->page_size;
			spi_nand_select_die(die_page_num);

			spi_nand_protocol_get_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, &feature);

			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "before setting : SPI_NAND_Flash_Disable_OnDie_ECC, status reg=0x%x\n", feature);	

			if((feature & ptr_dev_info_t->ecc_en.ecc_en_mask) == ptr_dev_info_t->ecc_en.ecc_en_value) {
				feature = (feature & ~(ptr_dev_info_t->ecc_en.ecc_en_mask));
				feature |= ((~(ptr_dev_info_t->ecc_en.ecc_en_value)) & ptr_dev_info_t->ecc_en.ecc_en_mask);
			
				spi_nand_protocol_set_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, feature);

				/* Value check*/
				spi_nand_protocol_get_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, &feature);
				if(feature & ptr_dev_info_t->ecc_en.ecc_en_mask) {
					_SPI_NAND_SEMAPHORE_UNLOCK();
					return (SPI_NAND_FLASH_RTN_DISABLE_ECC_FAIL);
				} else {
					_SPI_NAND_SEMAPHORE_UNLOCK();
					return (SPI_NAND_FLASH_RTN_NO_ERROR);
				}
			}
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "after setting : SPI_NAND_Flash_Disable_OnDie_ECC, status reg=0x%x\n", feature);
		}
	} else {
		spi_nand_protocol_get_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, &feature);

		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "before setting : SPI_NAND_Flash_Disable_OnDie_ECC, status reg=0x%x\n", feature);	

		if((feature & ptr_dev_info_t->ecc_en.ecc_en_mask) == ptr_dev_info_t->ecc_en.ecc_en_value) {
			feature = (feature & ~(ptr_dev_info_t->ecc_en.ecc_en_mask));
			feature |= ((~(ptr_dev_info_t->ecc_en.ecc_en_value)) & ptr_dev_info_t->ecc_en.ecc_en_mask);
			
			spi_nand_protocol_set_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, feature);

			/* Value check*/
			spi_nand_protocol_get_feature(ptr_dev_info_t->ecc_en.ecc_en_addr, &feature);
			if(feature & ptr_dev_info_t->ecc_en.ecc_en_mask) {
				_SPI_NAND_SEMAPHORE_UNLOCK();
				return (SPI_NAND_FLASH_RTN_DISABLE_ECC_FAIL);
			} else {
				_SPI_NAND_SEMAPHORE_UNLOCK();
				return (SPI_NAND_FLASH_RTN_NO_ERROR);
			}
		}
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "after setting : SPI_NAND_Flash_Disable_OnDie_ECC, status reg=0x%x\n", feature);
	}

	_ondie_ecc_flag = 0;

	_SPI_NAND_SEMAPHORE_UNLOCK();

	return (SPI_NAND_FLASH_RTN_NO_ERROR);
}

void enable_quad(void)
{
	unsigned char					feature;
	unsigned char					die;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	
	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1,"SPI NAND Chip Init : Enable Quad Mode\n"); 

	for(die = 0; die < ptr_dev_info_t->die_num; die++) {
		spi_nand_direct_select_die(die);

		/* Enable Qual mode */
		if(ptr_dev_info_t->quad_en.quad_en_mask != 0x00) {
			spi_nand_protocol_get_feature(_SPI_NAND_ADDR_FEATURE, &feature);
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "Before enable qual mode setup, the status register2 =0x%x\n", feature);

			feature = (feature & ~(ptr_dev_info_t->quad_en.quad_en_mask));
			feature |= ptr_dev_info_t->quad_en.quad_en_value;
			spi_nand_protocol_set_feature(_SPI_NAND_ADDR_FEATURE, feature);
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "enable qual mode setup, the feature = 0x%x\n", feature);

			spi_nand_protocol_get_feature(_SPI_NAND_ADDR_FEATURE, &feature);
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "After enable qual mode setup, the status register2 =0x%x\n", feature);
		} else {
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "No need enable qual mode setup.\n");
		}
	}

	/* modify share pin for quad mode */
	set_spi_quad_mode_shared_pin();
	
	_SPI_NAND_PRINTF("enable SPI Quad mode\n");
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_page_read( u32    page_number )
 * PURPOSE : To implement the SPI nand protocol for page read.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : page_number - The page_number variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/17  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_protocol_page_read ( u32 page_number )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] page read Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Send 13h opcode */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_PAGE_READ );
	
	/* 3. Send page number */
	_SPI_NAND_WRITE_ONE_BYTE( ((page_number >> 16 ) & 0xff) );
	_SPI_NAND_WRITE_ONE_BYTE( ((page_number >> 8  ) & 0xff) );
	_SPI_NAND_WRITE_ONE_BYTE( ((page_number)        & 0xff) );
	
	/* 4. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();	
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_load_page_into_cache: value=0x%x\n", page_number);	
	
	return (rtn_status);
	
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_read_from_cache( u32  data_offset,
 *                                                                          u32  len,
 *                                                                          u8   *ptr_rtn_buf,
 *																			u32	 read_mode,
 *																			SPI_NAND_FLASH_READ_DUMMY_BYTE_T dummy_mode)
 * PURPOSE : To implement the SPI nand protocol for read from cache with single speed.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : data_offset  - The data_offset variable of this function.
 *           len          - The len variable of this function.
 *   OUTPUT: ptr_rtn_buf  - A pointer to the ptr_rtn_buf variable.
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_protocol_read_from_cache( u32 data_offset, 
																u32 len, 
																u8 *ptr_rtn_buf, 
																u32 read_mode,
																SPI_NAND_FLASH_READ_DUMMY_BYTE_T dummy_mode )
{			

	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	
	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;	

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] read from cache Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Send opcode */
	switch (read_mode)
	{
		/* 03h */
		case SPI_NAND_FLASH_READ_SPEED_MODE_SINGLE:
			_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_READ_FROM_CACHE_SINGLE );
			break;

		/* 3Bh */
		case SPI_NAND_FLASH_READ_SPEED_MODE_DUAL:
			_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_READ_FROM_CACHE_DUAL );
			break;										

		/* 6Bh */
		case SPI_NAND_FLASH_READ_SPEED_MODE_QUAD:
			_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_READ_FROM_CACHE_QUAD );
			break;

		default:
			break;
	}
	
	/* 3. Send data_offset addr */
	if( dummy_mode == SPI_NAND_FLASH_READ_DUMMY_BYTE_PREPEND )
	{
		_SPI_NAND_WRITE_ONE_BYTE(0xff);		/* dummy byte */
	}
	
	if( ((ptr_dev_info_t->feature) & SPI_NAND_FLASH_PLANE_SELECT_HAVE) )
	{
		if( _plane_select_bit == 0)
		{
			_SPI_NAND_WRITE_ONE_BYTE( ((data_offset >> 8 ) &(0xef)) );
		}
		if( _plane_select_bit == 1)
		{
			_SPI_NAND_WRITE_ONE_BYTE( ((data_offset >> 8 ) | (0x10)) );
		}				
	}	
	else
	{
		_SPI_NAND_WRITE_ONE_BYTE( ((data_offset >> 8 ) &(0xff)) );
	}

	if((dummy_mode == SPI_NAND_FLASH_READ_DUMMY_BYTE_PREPEND) && 
	  (read_mode == SPI_NAND_FLASH_READ_SPEED_MODE_SINGLE)) {
		_SPI_NAND_WRITE_ONE_BYTE_WITH_CMD(_SPI_CONTROLLER_VAL_OP_OS2IS, ((data_offset      ) &(0xff)) );	
	} else {
		_SPI_NAND_WRITE_ONE_BYTE( ((data_offset      ) &(0xff)) );	
	}
	
	if( dummy_mode == SPI_NAND_FLASH_READ_DUMMY_BYTE_APPEND )
	{
		/* dummy byte */
		switch(read_mode)
		{
			case SPI_NAND_FLASH_READ_SPEED_MODE_SINGLE:
				_SPI_NAND_WRITE_ONE_BYTE_WITH_CMD(_SPI_CONTROLLER_VAL_OP_OS2IS, 0xff);	
				break;

			case SPI_NAND_FLASH_READ_SPEED_MODE_DUAL:
				_SPI_NAND_WRITE_ONE_BYTE_WITH_CMD(_SPI_CONTROLLER_VAL_OP_OS2ID, 0xff);	
				break;										

			case SPI_NAND_FLASH_READ_SPEED_MODE_QUAD:
				_SPI_NAND_WRITE_ONE_BYTE_WITH_CMD(_SPI_CONTROLLER_VAL_OP_OS2IQ, 0xff);	
				break;

			default:
				break;
		}
	}

	if(dummy_mode == SPI_NAND_FLASH_READ_DUMMY_BYTE_PREPEND)
	{
		/* dummy byte */
		switch(read_mode)
		{
			case SPI_NAND_FLASH_READ_SPEED_MODE_DUAL:
				_SPI_NAND_WRITE_ONE_BYTE_WITH_CMD(_SPI_CONTROLLER_VAL_OP_OS2ID, 0xff);	
				break;										

			case SPI_NAND_FLASH_READ_SPEED_MODE_QUAD:
				_SPI_NAND_WRITE_ONE_BYTE_WITH_CMD(_SPI_CONTROLLER_VAL_OP_OS2IQ, 0xff);	
				break;

			default:
				break;
		}
	}
	
	/* 4. Read n byte (len) data */
	switch (read_mode)
	{
		case SPI_NAND_FLASH_READ_SPEED_MODE_SINGLE:
			_SPI_NAND_READ_NBYTE( ptr_rtn_buf, len, SPI_CONTROLLER_SPEED_SINGLE);
			break;

		case SPI_NAND_FLASH_READ_SPEED_MODE_DUAL:
			_SPI_NAND_READ_NBYTE( ptr_rtn_buf, len, SPI_CONTROLLER_SPEED_DUAL);
			break;										

		case SPI_NAND_FLASH_READ_SPEED_MODE_QUAD:
			_SPI_NAND_READ_NBYTE( ptr_rtn_buf, len, SPI_CONTROLLER_SPEED_QUAD);
			break;

		default:
			break;
	}
		
	/* 5. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_read_from_cache : data_offset=0x%x, buf=0x%x\n", data_offset, ptr_rtn_buf);	
	
	return (rtn_status);
}

static SPI_NAND_FLASH_RTN_T ecc_fail_check( u32 page_number )
{
	u8								status;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	
	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;

	spi_nand_protocol_get_feature(_SPI_NAND_ADDR_STATUS, &status);

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "[spinand_ecc_fail_check]: status=0x%x\n", status);

	if((ptr_dev_info_t->feature & SPI_NAND_FLASH_NO_ECC_STATUS_HAVE) == 0) {
		if((status & ptr_dev_info_t->ecc_fail_check_info.ecc_check_mask) == ptr_dev_info_t->ecc_fail_check_info.ecc_uncorrected_value) {
			rtn_status = SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK;
			_SPI_NAND_PRINTF("[spinand_ecc_fail_check] : ECC cannot recover detected !, page=0x%x\n", page_number);
		}
	}

	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_load_page_into_cache( long  page_number )
 * PURPOSE : To load page into SPI NAND chip.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : page_number - The page_number variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/16  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_load_page_into_cache( u32 page_number)
{
	u8						status;
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	if (_parallel_nand_mode)
	{
		return rtn_status;
	}
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_load_page_into_cache: page number =0x%x\n", page_number);
		
	if(_current_page_num == page_number) {
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_load_page_into_cache: page number == _current_page_num\n");
	} else {
		spi_nand_select_die(page_number);

		spi_nand_protocol_page_read(page_number);

		/*  Checking status for load page/erase/program complete */
		do{
			spi_nand_protocol_get_feature(_SPI_NAND_ADDR_STATUS, &status);
		} while(status & _SPI_NAND_VAL_OIP) ;

		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_load_page_into_cache : status =0x%x\n", status);

		if(!isSpiNandAndCtrlECC)
		{
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_load_page_into_cache: check flash ecc status.\n");
			rtn_status = ecc_fail_check(page_number);
		}
	}
	
	return (rtn_status);
}

static SPI_NAND_FLASH_RTN_T spi_nand_read_page (u32 page_number, SPI_NAND_FLASH_READ_SPEED_MODE_T speed_mode)
{
	u32			 					idx=0;
	u32								i, j;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	SPI_NFI_RTN_T					nfi_status;
	struct spi_nand_flash_oobfree 	*ptr_oob_entry_idx;	
	u16 							read_addr;
	SPI_NFI_MISC_SPEDD_CONTROL_T	dma_speed_mode;
	u32								read_cmd;

	SPI_NFI_CONF_T			spi_nfi_conf_t;
	SPI_ECC_DECODE_CONF_T	spi_ecc_decode_conf_t;
	u32 					check_cnt;

	SPI_ECC_DECODE_STATUS_T decode_status_t;
	SPI_CONTROLLER_CONF_T	spi_conf_t;
	u32 					offset1, offset2, offset3, dma_sec_size;

	if(_current_page_num != page_number) {
		ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;

		/* read from read_addr index in the page */
		read_addr = 0;

		/* Switch to manual mode*/
		_SPI_NAND_ENABLE_MANUAL_MODE();

		/* 1. Load Page into cache of NAND Flash Chip */
		if(spi_nand_load_page_into_cache(page_number) == SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK) {
			_SPI_NAND_PRINTF("spi_nand_read_page: Bad Block, ECC cannot recovery detecte, page=0x%x\n", page_number);
			rtn_status = SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK;
		}

		/* 2. Read whole data from cache of NAND Flash Chip */
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_read_page: curren_page_num=0x%x, page_number=0x%x\n", _current_page_num, page_number);

		/* No matter what status, we must read the cache data to dram */
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_read_page: before read, _current_cache_page:\n");
		if(_spi_dma_mode == SPI_DMA_MODE_DISABLE) {
			_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page[0], ptr_dev_info_t->page_size + ptr_dev_info_t->oob_size);
		} else {
			_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &dma_read_page[0], ptr_dev_info_t->page_size + ptr_dev_info_t->oob_size);
		}
		
		if(ptr_dev_info_t->feature & SPI_NAND_FLASH_PLANE_SELECT_HAVE) {
			_plane_select_bit = ((page_number >> 6)& (0x1));

			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1,"spi_nand_read_page: plane select = 0x%x\n",  _plane_select_bit);			
		}
		
		if(_spi_dma_mode == SPI_DMA_MODE_ENABLE) {	
			SPI_CONTROLLER_Get_Configure(&spi_conf_t);

			spi_conf_t.dummy_byte_num = 0;

			switch (speed_mode)
			{
				case SPI_NAND_FLASH_READ_SPEED_MODE_SINGLE:
					dma_speed_mode = SPI_NFI_MISC_CONTROL_X1;
					read_cmd = _SPI_NAND_OP_READ_FROM_CACHE_SINGLE;
					break;

				case SPI_NAND_FLASH_READ_SPEED_MODE_DUAL:
					dma_speed_mode = SPI_NFI_MISC_CONTROL_X2;
					read_cmd = _SPI_NAND_OP_READ_FROM_CACHE_DUAL;
					if(ptr_dev_info_t->dummy_mode == SPI_NAND_FLASH_READ_DUMMY_BYTE_PREPEND) {
						spi_conf_t.dummy_byte_num = 1;
					}
					break;										
					
				case SPI_NAND_FLASH_READ_SPEED_MODE_QUAD:
					dma_speed_mode = SPI_NFI_MISC_CONTROL_X4;
					read_cmd = _SPI_NAND_OP_READ_FROM_CACHE_QUAD;
					if(ptr_dev_info_t->dummy_mode == SPI_NAND_FLASH_READ_DUMMY_BYTE_PREPEND) {
						spi_conf_t.dummy_byte_num = 1;
					}
					break;

				default:
					_SPI_NAND_PRINTF("[Error] Read DMA : read speed setting error:%d!\n", speed_mode);
					dma_speed_mode = SPI_NFI_MISC_CONTROL_X1;
					read_cmd = _SPI_NAND_OP_READ_FROM_CACHE_SINGLE;
					break;
			}
			
			spi_conf_t.mode = SPI_CONTROLLER_MODE_DMA;
			SPI_CONTROLLER_Set_Configure(&spi_conf_t);

			/* Reset NFI statemachie is neccessary */
			SPI_NFI_Reset();
			
			SPI_NFI_Get_Configure(&spi_nfi_conf_t);
			SPI_NFI_Set_Configure(&spi_nfi_conf_t);

			SPI_ECC_Decode_Get_Configure(&spi_ecc_decode_conf_t);
			SPI_ECC_Decode_Set_Configure(&spi_ecc_decode_conf_t);
			
			if(spi_nfi_conf_t.hw_ecc_t == SPI_NFI_CON_HW_ECC_Enable) {
				SPI_ECC_Decode_Get_Configure(&spi_ecc_decode_conf_t);
				
				if(spi_ecc_decode_conf_t.decode_en == SPI_ECC_DECODE_ENABLE) {
					spi_ecc_decode_conf_t.decode_block_size = ((spi_nfi_conf_t.fdm_ecc_num + 512) * 8) + (spi_ecc_decode_conf_t.decode_ecc_abiliry * 13);

					SPI_ECC_Decode_Set_Configure(&spi_ecc_decode_conf_t);
					
					_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_read_page: decode block size=0x%x, ecc_num=0x%x, ecc_ab=0x%x\n", get_spi_ecc_deccnfg(), (spi_nfi_conf_t.fdm_ecc_num), (spi_ecc_decode_conf_t.decode_ecc_abiliry));
					_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_read_page : SPI_ECC_Decode_Enable \n");

					SPI_ECC_Decode_Disable();
					SPI_ECC_Encode_Disable();					
					SPI_ECC_Decode_Enable();
				}			
			}						

			/* Set plane select */
			if(ptr_dev_info_t->feature & SPI_NAND_FLASH_PLANE_SELECT_HAVE) {
				if(_plane_select_bit == 0) {
					read_addr &= ~(0x1000);
				} else if(_plane_select_bit == 1) {
					read_addr |= (0x1000);
				}				
			}

			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_read_page: dma_speed_mode:%d, read_cmd:%x\n", dma_speed_mode, read_cmd);

			/* Invalid dma_read_page */
			flush_dcache_range((unsigned long)dma_read_page, (unsigned long)(dma_read_page + _SPI_NAND_CACHE_SIZE));
			rdma_addr = K1_TO_PHY(&dma_read_page[0]);

			mb();
			{
				nfi_status = SPI_NFI_Read_SPI_NAND_Page(dma_speed_mode, read_cmd, read_addr, (u32 *)rdma_addr);
				if(nfi_status != SPI_NFI_RTN_NO_ERROR) {
					rtn_status = SPI_NAND_FLASH_RTN_NFI_FAIL;
				}
			}

			if(spi_nfi_conf_t.hw_ecc_t == SPI_NFI_CON_HW_ECC_Enable) {
				if(spi_ecc_decode_conf_t.decode_en == SPI_ECC_DECODE_ENABLE) {
					/* Check Decode done or not */
					for(check_cnt = 0 ; check_cnt < _SPI_NFI_CHECK_ECC_DONE_MAX_TIMES; check_cnt++) {
						SPI_ECC_Decode_Check_Done(&decode_status_t);
						
						if(decode_status_t == SPI_ECC_DECODE_STATUS_DONE) {
							break;
						}
					}
					if(check_cnt == _SPI_NFI_CHECK_ECC_DONE_MAX_TIMES) {		
						_SPI_NAND_PRINTF("[Error] Read ECC : Check Decode Done Timeout ! \n");

						SPI_NAND_Flash_Clear_Read_Cache_Data();

						/*  return  somthing ? */
						rtn_status = SPI_NAND_FLASH_RTN_ECC_DECODE_FAIL;
					}

					if(SPI_ECC_DECODE_Check_Correction_Status() == SPI_ECC_RTN_CORRECTION_ERROR) {
						_SPI_NAND_PRINTF("[Error] Read ECC : ECC Fail! page:0x%x\n", page_number);
						SPI_NAND_Flash_Clear_Read_Cache_Data();

						/* Switch to manual mode*/
						_SPI_NAND_ENABLE_MANUAL_MODE();
						
						/*  return somthing ?*/
						rtn_status = SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK;
					}
				}
			}

			/* Switch to manual mode*/
			_SPI_NAND_ENABLE_MANUAL_MODE();
		} else {
			spi_nand_protocol_read_from_cache(read_addr, ((ptr_dev_info_t->page_size)+(ptr_dev_info_t->oob_size)), &_current_cache_page[0], speed_mode, ptr_dev_info_t->dummy_mode );
		}

		/* Divide read page into data segment and oob segment  */
		if((_spi_dma_mode == SPI_DMA_MODE_DISABLE) || 
		  ((_spi_dma_mode == SPI_DMA_MODE_ENABLE) && (!isSpiNandAndCtrlECC) && (spi_nfi_conf_t.auto_fdm_t == SPI_NFI_CON_AUTO_FDM_Disable) && (spi_nfi_conf_t.hw_ecc_t == SPI_NFI_CON_HW_ECC_Disable)))
		{
			
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_read_page: after read, _current_cache_page:\n");

			if(_spi_dma_mode == SPI_DMA_MODE_DISABLE) {
				_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page[0], ptr_dev_info_t->page_size + ptr_dev_info_t->oob_size);
				memcpy(&_current_cache_page_data[0], &_current_cache_page[0], ptr_dev_info_t->page_size);
				memcpy(&_current_cache_page_oob[0],  &_current_cache_page[ptr_dev_info_t->page_size], ptr_dev_info_t->oob_size);
			} else {
				_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &dma_read_page[0], ptr_dev_info_t->page_size + ptr_dev_info_t->oob_size);
				memcpy(&_current_cache_page_data[0], &dma_read_page[0], ptr_dev_info_t->page_size);
				memcpy(&_current_cache_page_oob[0],  &dma_read_page[ptr_dev_info_t->page_size], ptr_dev_info_t->oob_size);
			}
			
			idx = 0;
			ptr_oob_entry_idx = (struct spi_nand_flash_oobfree*) &((ptr_dev_info_t->oob_free_layout)->oobfree);

			if(_ondie_ecc_flag == 1)   /*  When OnDie ecc is enable,  mapping oob area is neccessary */
			{
				/* Transter oob area from physical offset into logical offset */
				for( i=0; (i<SPI_NAND_FLASH_OOB_FREE_ENTRY_MAX) && (ptr_oob_entry_idx[i].len) && (idx< ((ptr_dev_info_t->oob_free_layout)->oobsize)) ; i++)
				{
					for(j=0; (j< (ptr_oob_entry_idx[i].len)) && (idx<(ptr_dev_info_t->oob_free_layout->oobsize)) ; j++)
					{
						/* _SPI_NAND_PRINTF("i=%d , j=%d, len=%d, idx=%d, size=%d\n", i, j,(ptr_oob_entry_idx[i].len), idx, (ptr_dev_info_t->oob_free_layout->oobsize) ); */
						_current_cache_page_oob_mapping[idx] = _current_cache_page_oob[(ptr_oob_entry_idx[i].offset)+j];
						idx++;
					}
				}	
			}
			else
			{
				memcpy( &_current_cache_page_oob_mapping[0],  &_current_cache_page_oob[0], (ptr_dev_info_t->oob_size) );	
			}
		}
		else
		{	
			
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_read_page: after read, _current_cache_page:\n");
			_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &dma_read_page[0], ptr_dev_info_t->page_size + ptr_dev_info_t->oob_size);

			if(spi_nfi_conf_t.auto_fdm_t == SPI_NFI_CON_AUTO_FDM_Disable)  
			{
				offset1 = 0;
				offset2 = 0;
				offset3 = 0;
				dma_sec_size = ((spi_nfi_conf_t.page_size_t)/ (spi_nfi_conf_t.sec_num));
				for(i = 0; i < spi_nfi_conf_t.sec_num; i++) {
					memcpy( &_current_cache_page_data[offset1], &dma_read_page[offset2], dma_sec_size);
					memcpy( &_current_cache_page_oob[offset3], &dma_read_page[offset2+dma_sec_size], (spi_nfi_conf_t.spare_size_t) );
					offset1 += dma_sec_size;
					offset2 += (dma_sec_size+ (spi_nfi_conf_t.spare_size_t));
					offset3 += (spi_nfi_conf_t.spare_size_t);					
				}

				offset1 = 0;
				offset2 = 0;
				for(i = 0; i < spi_nfi_conf_t.sec_num; i++) {
					memcpy( &_current_cache_page_oob_mapping[offset1], &_current_cache_page_oob[offset2], spi_nfi_conf_t.fdm_num);
					offset1 += spi_nfi_conf_t.fdm_num;
					offset2 += spi_nfi_conf_t.spare_size_t;
				}
			}
			else /* Auto FDM enable : Data and oob alternate ,  Data inside DRAM , oob inside NFI register */  
			{
				memcpy( &_current_cache_page_data[0], &dma_read_page[0], (ptr_dev_info_t->page_size) );
				SPI_NFI_Read_SPI_NAND_FDM(&_current_cache_page_oob[0], (ptr_dev_info_t->oob_size));
				memcpy( &_current_cache_page_oob_mapping[0], &_current_cache_page_oob[0], ptr_dev_info_t->oob_size);
			}
		}
		
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_read_page: _current_cache_page:\n");
		if(_spi_dma_mode == SPI_DMA_MODE_DISABLE) {
			_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page[0], ptr_dev_info_t->page_size + ptr_dev_info_t->oob_size);
		} else {
			_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &dma_read_page[0], ptr_dev_info_t->page_size + ptr_dev_info_t->oob_size);
		}
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_read_page: _current_cache_page_data:\n");
		_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page_data[0], ptr_dev_info_t->page_size);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_read_page: _current_cache_page_oob:\n");
		_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page_oob[0], ptr_dev_info_t->oob_size);		
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_read_page: _current_cache_page_oob_mapping:\n");
		_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page_oob_mapping[0], (ptr_dev_info_t->oob_free_layout)->oobsize);
		
		_current_page_num = page_number; 
	}	

	return rtn_status;
}

static SPI_NAND_FLASH_RTN_T spi_nand_boundary_check(
													u32 addr,
													u32 len)
{
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t = _SPI_NAND_GET_DEVICE_INFO_PTR;
	u32								end_addr = 0;

	end_addr = (addr + len);

	if (end_addr> ptr_dev_info_t->device_size)
	{
		rtn_status = SPI_NAND_FLASH_RTN_ALIGNED_CHECK_FAIL;

		_SPI_NAND_PRINTF("[Error] Over Device Size : end_addr=0x%x, device_size=0x%x!\n", end_addr, ptr_dev_info_t->device_size);
	}

	return rtn_status;
		
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_read_internal( u32     addr,
 *                                                               u32     len,
 *                                                               u8      *ptr_rtn_buf )
 * PURPOSE : To read flash internally.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr         - The addr variable of this function.
 *           len          - The len variable of this function.
 *   OUTPUT: ptr_rtn_buf  - A pointer to the ptr_rtn_buf variable.
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/19  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_read_internal(
													u32 addr,
													u32 len, 
													u8 *ptr_rtn_buf, 
													SPI_NAND_FLASH_READ_SPEED_MODE_T speed_mode,
													SPI_NAND_FLASH_RTN_T *status)
{
	u32			 					page_number, data_offset;
	u32								read_addr, physical_read_addr;
	u32			 					remain_len, logical_block, physical_block;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	SPI_ECC_RTN_T					ecc_status = SPI_ECC_RTN_NO_ERROR;
	unsigned long					spinand_spinlock_flags;
	SPI_NFI_CONF_T					spi_nfi_conf_t;
	SPI_ECC_DECODE_CONF_T			spi_ecc_decode_conf_t;
	
    unsigned short phy_block_bbt;
	unsigned long  addr_offset_in_block;
	/* for exceed 64bits address */
	u64	physical_block_tmp, erase_size_tmp;

	if((0xbc000000 <= addr) && (addr <= 0xbfffffff)) {		/* Reserver address area for system */
		if( (addr & 0xbfc00000) == 0xbfc00000) {
			addr &= 0x003fffff;
		} else {
			addr &= 0x03ffffff;
		}
	}

	SPI_NFI_Get_Configure(&spi_nfi_conf_t);
	SPI_ECC_Decode_Get_Configure(&spi_ecc_decode_conf_t);
		
	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;			
	read_addr  		= addr;
	remain_len 		= len;

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "\nspi_nand_read_internal : addr=0x%lx, len=0x%x\n", addr, len );
	rtn_status = spi_nand_boundary_check(read_addr, remain_len);
	if (rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR)
		return rtn_status;

	_SPI_NAND_SEMAPHORE_LOCK();

	*status = SPI_NAND_FLASH_RTN_NO_ERROR;

	while(remain_len > 0) {
		physical_read_addr = read_addr;

		if(otp_status == NAND_FLASH_OTP_DISABLE) {
            addr_offset_in_block = (read_addr % ptr_dev_info_t->erase_size);
			logical_block = (read_addr / (ptr_dev_info_t->erase_size));
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1,"read_addr=0x%x, erase size =0x%x, logical_block =0x%x\n", read_addr, (ptr_dev_info_t->erase_size), logical_block);
			physical_block = get_mapping_block_index(logical_block, &phy_block_bbt);
			/* for coverity(CID:186222), avoid overflow */
			physical_block_tmp = physical_block;
			erase_size_tmp = ptr_dev_info_t->erase_size;
			physical_read_addr = (physical_block_tmp * erase_size_tmp) + addr_offset_in_block;
			if(physical_block != logical_block) {			
				_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "Bad Block Mapping, from %d block to %d block\n", logical_block, physical_block);
			}
		}
		/* Caculate page number */
        data_offset = (physical_read_addr % (ptr_dev_info_t->page_size));
		page_number = (physical_read_addr / (ptr_dev_info_t->page_size));

		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_read_internal: read_addr=0x%x, page_number=0x%x, data_offset=0x%x\n", physical_read_addr, page_number, data_offset);

		if(len == 1 && _current_page_num == page_number) {
			ptr_rtn_buf[0] = _current_cache_page_data[data_offset];
			_SPI_NAND_SEMAPHORE_UNLOCK();
			return rtn_status;
		}

		rtn_status = spi_nand_read_page(page_number, speed_mode);
		if(_spi_dma_mode == SPI_DMA_MODE_ENABLE) {
			if((spi_nfi_conf_t.hw_ecc_t == SPI_NFI_CON_HW_ECC_Enable) &&
			   (spi_ecc_decode_conf_t.decode_en == SPI_ECC_DECODE_ENABLE)) {
				ecc_status = SPI_ECC_DECODE_Check_Correction_Status();
				if(ecc_status == SPI_ECC_RTN_NO_ERROR ) {
					*status = SPI_NAND_FLASH_RTN_NO_ERROR;
				} else {
					*status = SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK;
					_SPI_NAND_SEMAPHORE_UNLOCK();
					return (rtn_status);
				}
			} else {
				if(rtn_status == SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK) {
					*status = SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK;
					_SPI_NAND_SEMAPHORE_UNLOCK();
					return (rtn_status);
				}
			}
		} else {
			if(rtn_status == SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK) {
				*status = SPI_NAND_FLASH_RTN_DETECTED_BAD_BLOCK;
				_SPI_NAND_SEMAPHORE_UNLOCK();
				return (rtn_status);
			}
		}

		/* 3. Retrieve the request data */
		if((data_offset + remain_len) < ptr_dev_info_t->page_size) {
			memcpy( &ptr_rtn_buf[len - remain_len], &_current_cache_page_data[data_offset], remain_len);
			remain_len =0;
		} else {
			memcpy( &ptr_rtn_buf[len - remain_len], &_current_cache_page_data[data_offset], ptr_dev_info_t->page_size - data_offset);
			remain_len -= (ptr_dev_info_t->page_size - data_offset);
			read_addr += (ptr_dev_info_t->page_size - data_offset);
		}
	}
	
	_SPI_NAND_SEMAPHORE_UNLOCK();

	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: int SPI_NAND_Flash_Read_NByte( long     addr,
 *                                          long     len,
 *                                          long     *retlen,
 *                                          char     *buf    )
 * PURPOSE : To provide interface for Read N Bytes from SPI NAND Flash.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr    - The addr variable of this function.
 *           len     - The len variable of this function.
 *           retlen  - The retlen variable of this function.
 *           buf     - The buf variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/12  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
u32 SPI_NAND_Flash_Read_NByte(	
								u32 							addr,
								u32								 len, 
								u32								 *retlen, 
								u8								 *buf, 
								SPI_NAND_FLASH_READ_SPEED_MODE_T speed_mode,
								SPI_NAND_FLASH_RTN_T *status)
{
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_Flash_Read_NByte\n");
	
	return spi_nand_read_internal(addr, len, buf, speed_mode, status);	
}

/*------------------------------------------------------------------------------------
 * FUNCTION: char SPI_NAND_Flash_Read_Byte( long     addr )
 * PURPOSE : To provide interface for read 1 Bytes from SPI NAND Flash.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr - The addr variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/12  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
unsigned char SPI_NAND_Flash_Read_Byte(
										u32 addr,
										SPI_NAND_FLASH_RTN_T *status)
{
	unsigned char 					ch;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	int 							ret = 0;
	size_t 							retlen;

	ptr_dev_info_t  = _SPI_NAND_GET_DEVICE_INFO_PTR;

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_Flash_Read_Byte\n");

	if(addr >= 0xFF00 && addr <= 0xFFFF) {
		addr += 0x2FF00;
	}

	spi_nand_read_internal(addr, 1, &ch, ptr_dev_info_t->read_mode, status);

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_Flash_Read_Byte : buf=0x%x\n", ch);
	
	return ch;
}

/*------------------------------------------------------------------------------------
 * FUNCTION: long SPI_NAND_Flash_Read_DWord( long    addr )
 * PURPOSE : To provide interface for read Double Word from SPI NAND Flash.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr - The addr variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/12  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
unsigned long SPI_NAND_Flash_Read_DWord(
										u32 addr,
										SPI_NAND_FLASH_RTN_T *status)
{
	u32		 ret_val=0;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	size_t							retlen;
	int								ret = 0;

	ptr_dev_info_t  = _SPI_NAND_GET_DEVICE_INFO_PTR;
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_Flash_Read_DWord\n");

	spi_nand_read_internal(addr, 4, &ret_val, ptr_dev_info_t->read_mode, status);

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "SPI_NAND_Flash_Read_DWord : ret_val=0x%x\n", ret_val);
	
	return ret_val;
}

int nandflash_read(unsigned long from, unsigned long len, u32 *retlen, unsigned char *buf, SPI_NAND_FLASH_RTN_T *status)
{
	struct SPI_NAND_FLASH_INFO_T	 *ptr_dev_info_t;
	 
	ptr_dev_info_t  = _SPI_NAND_GET_DEVICE_INFO_PTR;

	if( SPI_NAND_Flash_Read_NByte(from, len, retlen, buf, ptr_dev_info_t->read_mode, status) == SPI_NAND_FLASH_RTN_NO_ERROR) {
		return 0;
	} else {
		return -1;
	}	
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_write_enable( void )
 * PURPOSE : To implement the SPI nand protocol for write enable.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : None
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/17  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T spi_nand_protocol_write_enable( void )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	SPI_NAND_FLASH_RTN_T	feature_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	u8						feature;

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] write enable Timeout.\n");
		return (rtn_status);
	}

	if (_parallel_nand_mode)
	{
		return rtn_status;
	}

	/* 1. Chip Select Low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Write op_cmd 0x06 (Write Enable (WREN)*/
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_WRITE_ENABLE );
	
	/* 3. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();

	/*	Checking status for write enable */
	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_WEL, _SPI_NAND_VAL_WEL);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		feature_status = spi_nand_protocol_get_feature(_SPI_NAND_ADDR_STATUS, &feature);
		if(feature_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			_SPI_NAND_PRINTF("[Error] get status fail!\n");
			return feature_status;
		}
		_SPI_NAND_PRINTF("[Error] wait for WEL bit = 1 Timeout, status=0x%x!\n", feature);
		return (rtn_status);
	}
	
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_write_disable( void )
 * PURPOSE : To implement the SPI nand protocol for write disable.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : None
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/17  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T spi_nand_protocol_write_disable( void )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	SPI_NAND_FLASH_RTN_T	feature_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	u8						feature;

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] write disable Timeout.\n");
		return (rtn_status);
	}

	if (_parallel_nand_mode)
	{
		return rtn_status;
	}

	/* 1. Chip Select Low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Write op_cmd 0x04 (Write Disable (WRDI)*/
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_WRITE_DISABLE );
	
	/* 3. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();

	/*	Checking status for write disable */
	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_WEL, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		feature_status = spi_nand_protocol_get_feature(_SPI_NAND_ADDR_STATUS, &feature);
		if(feature_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			_SPI_NAND_PRINTF("[Error] get status fail!\n");
			return feature_status;
		}
		_SPI_NAND_PRINTF("[Error] wait for WEL bit = 0 Timeout, status=0x%x!\n", feature);
		return (rtn_status);
	}
	
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_program_load( u32     addr,
 *                                                                       u8      *ptr_data,
 *                                                                       u32     len,
 *																		 u32 write_mode)
 * PURPOSE : To implement the SPI nand protocol for program load, with single speed.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr      - The addr variable of this function.
 *           ptr_data  - A pointer to the ptr_data variable.
 *           len       - The len variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_protocol_program_load ( u32 addr, 
																		  u8 *ptr_data, 
																		  u32 len,
																		  u32 write_mode)
{

	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	
	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_program_load: addr=0x%x, len=0x%x\n", addr, len );

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] program load Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Send opcode */
	switch (write_mode)
	{
		/* 02h */
		case SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE:
			_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_PROGRAM_LOAD_SINGLE );
			break;

		/* 32h */
		case SPI_NAND_FLASH_WRITE_SPEED_MODE_QUAD:
			_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_PROGRAM_LOAD_QUAD );
			break;										

		default:
			break;
	}
	
	/* 3. Send address offset */
	if( ((ptr_dev_info_t->feature) & SPI_NAND_FLASH_PLANE_SELECT_HAVE) )
	{
		if( _plane_select_bit == 0)
		{
			_SPI_NAND_WRITE_ONE_BYTE( ((addr >> 8 ) & (0xef)) );
		}
		if( _plane_select_bit == 1)
		{
			_SPI_NAND_WRITE_ONE_BYTE( ((addr >> 8 ) | (0x10)) );
		}				
	}	
	else
	{
		_SPI_NAND_WRITE_ONE_BYTE( ((addr >> 8 ) & (0xff)) );
	}

	_SPI_NAND_WRITE_ONE_BYTE( ((addr)        & (0xff)) );
	
	/* 4. Send data */
	switch (write_mode)
	{
		case SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE:
			_SPI_NAND_WRITE_NBYTE( ptr_data, len, SPI_CONTROLLER_SPEED_SINGLE);
			break;

		case SPI_NAND_FLASH_WRITE_SPEED_MODE_QUAD:
			_SPI_NAND_WRITE_NBYTE( ptr_data, len, SPI_CONTROLLER_SPEED_QUAD);
			break;										

		default:
			break;
	}
	
	/* 5. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();	
	
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_program_load_random( u32     addr,
 *                                                                       u8      *ptr_data,
 *                                                                       u32     len,
 *																		 u32 write_mode)
 * PURPOSE : To implement the SPI nand protocol for program load, with single speed.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr      - The addr variable of this function.
 *           ptr_data  - A pointer to the ptr_data variable.
 *           len       - The len variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/17  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_protocol_program_load_random ( u32 addr, 
																  u8 *ptr_data, 
																  u32 len,
																  u32 write_mode)
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_program_load_random: addr=0x%x, len=0x%x\n", addr, len );

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] program load random Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Send opcode */
	switch (write_mode)
	{
		/* 84 */
		case SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE:
			_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE );
			break;

		/* 34h */
		case SPI_NAND_FLASH_WRITE_SPEED_MODE_QUAD:
			_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD );
			break;										

		default:
			break;
	}
	
	/* 3. Send address offset */
	_SPI_NAND_WRITE_ONE_BYTE( ((addr >> 8  ) & 0xff) );
	_SPI_NAND_WRITE_ONE_BYTE( ((addr)        & 0xff) );
	
	/* 4. Send data */
	switch (write_mode)
	{
		case SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE:
			_SPI_NAND_WRITE_NBYTE( ptr_data, len, SPI_CONTROLLER_SPEED_SINGLE);
			break;

		case SPI_NAND_FLASH_WRITE_SPEED_MODE_QUAD:
			_SPI_NAND_WRITE_NBYTE( ptr_data, len, SPI_CONTROLLER_SPEED_QUAD);
			break;										

		default:
			break;
	}
	
	/* 5. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();	
	
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_program_execute( u32  addr )
 * PURPOSE : To implement the SPI nand protocol for program execute.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr - The addr variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/17  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T spi_nand_protocol_program_execute ( u32 addr )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	u8						status;
	u32 					check_cnt;
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_program_execute: addr=0x%x\n", addr);	

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] program execute Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Send 10h opcode */
	_SPI_NAND_WRITE_ONE_BYTE(_SPI_NAND_OP_PROGRAM_EXECUTE);
	
	/* 3. Send address offset */
	_SPI_NAND_WRITE_ONE_BYTE( ((addr >> 16) & 0xff) );
	_SPI_NAND_WRITE_ONE_BYTE( ((addr >> 8 ) & 0xff) );
	_SPI_NAND_WRITE_ONE_BYTE( ((addr)       & 0xff) );
	
	/* 4. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();	

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] program execute Timeout.\n");
		return (rtn_status);
	}

	/*	Check Program Fail Bit */
	rtn_status = spi_nand_protocol_get_feature(_SPI_NAND_ADDR_STATUS, &status);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] get status fail!\n");
		return rtn_status;
	}

	if(status & _SPI_NAND_VAL_PROGRAM_FAIL) {
		_SPI_NAND_PRINTF("spi_nand_write_page : Program Fail at addr=0x%x, status=0x%x\n", addr, status);
		rtn_status = SPI_NAND_FLASH_RTN_PROGRAM_FAIL;
	}
	
	return (rtn_status);
}

static SPI_NAND_FLASH_RTN_T spi_nand_dma_program_load(u32 addr, u32 oob_len, SPI_NAND_FLASH_WRITE_SPEED_MODE_T speed_mode)
{
	SPI_CONTROLLER_CONF_T	spi_conf_t; 	
	SPI_NFI_CONF_T			spi_nfi_conf_t;
	SPI_ECC_ENCODE_CONF_T	spi_ecc_encode_conf_t;
	SPI_NAND_FLASH_RTN_T	rtn_status;
	u16 					write_addr;
	u32						write_cmd;
	SPI_NFI_MISC_SPEDD_CONTROL_T	dma_speed_mode;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;

	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;	

	/* Set plane select */
	write_addr = addr;
	if(ptr_dev_info_t->feature & SPI_NAND_FLASH_PLANE_SELECT_HAVE) {
		if(_plane_select_bit == 0) {
			write_addr &= ~(0x1000);
		} else if(_plane_select_bit == 1) {
			write_addr |= (0x1000);
		}				
	}

	switch (speed_mode)
	{
		case SPI_NAND_FLASH_WRITE_SPEED_MODE_SINGLE:
			dma_speed_mode = SPI_NFI_MISC_CONTROL_X1;
			write_cmd = _SPI_NAND_OP_PROGRAM_LOAD_SINGLE;
			break;
						
		case SPI_NAND_FLASH_WRITE_SPEED_MODE_QUAD:
			dma_speed_mode = SPI_NFI_MISC_CONTROL_X4;
			write_cmd = _SPI_NAND_OP_PROGRAM_LOAD_QUAD;
			break;

		default:
			_SPI_NAND_PRINTF("[Error] Write DMA : write speed setting error:%d!\n", speed_mode);
			dma_speed_mode = SPI_NFI_MISC_CONTROL_X1;
			write_cmd = _SPI_NAND_OP_PROGRAM_LOAD_SINGLE;
			break;
	}

	SPI_CONTROLLER_Get_Configure(&spi_conf_t);
	spi_conf_t.dummy_byte_num = 0 ;
	spi_conf_t.mode = SPI_CONTROLLER_MODE_DMA;
	SPI_CONTROLLER_Set_Configure(&spi_conf_t);

	/* Reset NFI statemachie is neccessary */
	SPI_NFI_Reset();

	SPI_NFI_Get_Configure(&spi_nfi_conf_t);
	SPI_NFI_Set_Configure(&spi_nfi_conf_t);

	SPI_ECC_Encode_Get_Configure(&spi_ecc_encode_conf_t);
	SPI_ECC_Encode_Set_Configure(&spi_ecc_encode_conf_t);
	
	if(spi_nfi_conf_t.hw_ecc_t == SPI_NFI_CON_HW_ECC_Enable) {
		SPI_ECC_Encode_Get_Configure(&spi_ecc_encode_conf_t);
		
		if(spi_ecc_encode_conf_t.encode_en == SPI_ECC_ENCODE_ENABLE) {
			spi_ecc_encode_conf_t.encode_block_size = (spi_nfi_conf_t.fdm_ecc_num + 512) ;
			SPI_ECC_Encode_Set_Configure(&spi_ecc_encode_conf_t);

			SPI_ECC_Encode_Disable();
			SPI_ECC_Decode_Disable();
			SPI_ECC_Encode_Enable();
		}
	}

	if(spi_nfi_conf_t.auto_fdm_t == SPI_NFI_CON_AUTO_FDM_Enable) {	/* Data and oob alternate */
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_dma_program_load: set fdm\n");
		SPI_NFI_Write_SPI_NAND_FDM(&_current_cache_page_oob_mapping[0], oob_len);
	}

	rtn_status = SPI_NFI_Write_SPI_NAND_page(dma_speed_mode, write_cmd, write_addr, (u32 *)wdma_addr);

	/* Switch to manual mode*/
	_SPI_NAND_ENABLE_MANUAL_MODE();	

	return rtn_status;
}

static SPI_NAND_FLASH_RTN_T spi_nand_write_page(u32 page_number, 
												u32 data_offset,
											  	const u8  *ptr_data, 
											  	u32 data_len, 
											  	u32 oob_offset,
												u8  *ptr_oob , 
												u32 oob_len,
											    SPI_NAND_FLASH_WRITE_SPEED_MODE_T speed_mode)
{
	u8 status_2;
	u32		i = 0, j = 0, idx = 0;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	struct spi_nand_flash_oobfree 	*ptr_oob_entry_idx;	
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	SPI_NAND_FLASH_RTN_T			prog_exe_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	u16 							write_addr;
	SPI_NFI_CONF_T					spi_nfi_conf_t;
	u32								offset1, offset2, offset3, dma_sec_size;
	SPI_NAND_FLASH_DEBUG_LEVEL_T	ubiDbgLv = SPI_NAND_FLASH_DEBUG_LEVEL_2;
	static int						isUbifsBlankPageFix = 0;
	unsigned long					block_idx;
	u32								rc;

	/* write to write_addr index in the page */
	write_addr = 0;

	SPI_NFI_Get_Configure(&spi_nfi_conf_t);
	
	/* Switch to manual mode*/
	_SPI_NAND_ENABLE_MANUAL_MODE();

	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;
	/* Read Current page data to software cache buffer */
	spi_nand_read_page(page_number, speed_mode);
	flush_dcache_range((unsigned long)dma_write_page, (unsigned long)(dma_write_page + _SPI_NAND_CACHE_SIZE));
	wdma_addr = K1_TO_PHY(&dma_write_page[0]);


	/* Write data & OOB */
	if((_spi_dma_mode == SPI_DMA_MODE_DISABLE) ||
	  ((_spi_dma_mode == SPI_DMA_MODE_ENABLE) && (!isSpiNandAndCtrlECC) && (spi_nfi_conf_t.auto_fdm_t == SPI_NFI_CON_AUTO_FDM_Disable) && (spi_nfi_conf_t.hw_ecc_t == SPI_NFI_CON_HW_ECC_Disable)))
	{
		if(data_len > 0) {
			memcpy( &_current_cache_page_data[data_offset], &ptr_data[0], data_len);
		}
		
		if(_spi_dma_mode == SPI_DMA_MODE_DISABLE) {
			memcpy(&_current_cache_page[data_offset], &_current_cache_page_data[0], PAGE_SIZE);
		} else {
			memcpy((u8 *)K0_TO_K1(&dma_write_page[data_offset]), &_current_cache_page_data[0], PAGE_SIZE);
		}
		
		if(oob_len > 0) {
			if(_ondie_ecc_flag == 1) {	/*  When OnDie ecc is enable,  mapping oob area is neccessary */
				ptr_oob_entry_idx = (struct spi_nand_flash_oobfree*) &( ptr_dev_info_t->oob_free_layout->oobfree );
						
				for(i = 0; (i < SPI_NAND_FLASH_OOB_FREE_ENTRY_MAX) && (ptr_oob_entry_idx[i].len) && ((idx < ptr_dev_info_t->oob_free_layout->oobsize) && (idx < oob_len)); i++) {
					for(j = 0; (j < ptr_oob_entry_idx[i].len) && (idx < ptr_dev_info_t->oob_free_layout->oobsize) && ((idx < ptr_dev_info_t->oob_free_layout->oobsize) && (idx < oob_len)) ;j++) {
						_current_cache_page_oob[(ptr_oob_entry_idx[i].offset)+j] &= ptr_oob[idx];
						idx++;
					}
				}			
			} else {
				memcpy( &_current_cache_page_oob[0], &ptr_oob[0], oob_len);
			}
		}
		
		if(_spi_dma_mode == SPI_DMA_MODE_DISABLE) {
			memcpy( &_current_cache_page[ptr_dev_info_t->page_size], &_current_cache_page_oob[0], ptr_dev_info_t->oob_size);
		} else {
			memcpy((u8 *)K0_TO_K1(&dma_write_page[ptr_dev_info_t->page_size]), &_current_cache_page_oob[0], ptr_dev_info_t->oob_size);
		}
	} else {
		if(data_len > 0) {	
			memcpy(&_current_cache_page_data[data_offset], &ptr_data[0], data_len);
		}
		
		if(spi_nfi_conf_t.auto_fdm_t== SPI_NFI_CON_AUTO_FDM_Disable) {	/* Data and oob alternate */
			if(oob_len > 0) {
				offset1 = 0;
				offset2 = 0;
				for(i = 0; i < spi_nfi_conf_t.sec_num; i++) {
					memcpy( &_current_cache_page_oob[offset1], &ptr_oob[offset2], spi_nfi_conf_t.spare_size_t);
					offset1 += spi_nfi_conf_t.spare_size_t;
					offset2 += spi_nfi_conf_t.spare_size_t;
				}
			}

			offset1 = 0;
			offset2 = 0;
			offset3 = 0;
			dma_sec_size = (spi_nfi_conf_t.page_size_t / spi_nfi_conf_t.sec_num);
			for(i = 0 ; i < spi_nfi_conf_t.sec_num; i++) {													
				memcpy((u8 *)K0_TO_K1(&dma_write_page[offset2]), &_current_cache_page_data[offset1],	dma_sec_size );
				memcpy((u8 *)K0_TO_K1(&dma_write_page[offset2+dma_sec_size]), &_current_cache_page_oob[offset3] , (spi_nfi_conf_t.spare_size_t) );
				offset1 += dma_sec_size;
				offset2 += (dma_sec_size+ (spi_nfi_conf_t.spare_size_t));
				offset3 += (spi_nfi_conf_t.spare_size_t);					
			}
		} else {  /* Data inside DRAM , oob inside NFI register */
			if(oob_len > 0) {
				memcpy( &_current_cache_page_oob_mapping[0], &ptr_oob[0], oob_len);
			}
			/* Set data */
			memcpy((u8 *)K0_TO_K1(&dma_write_page[0]), &_current_cache_page_data[0], ptr_dev_info_t->page_size);
		}
	}

	if(_spi_dma_mode == SPI_DMA_MODE_ENABLE) {	
		mb();
	}
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_write_page: page=0x%x, data_offset=0x%x, data_len=0x%x, oob_offset=0x%x, oob_len=0x%x\n", page_number, data_offset, data_len, oob_offset, oob_len);
	if(_spi_dma_mode == SPI_DMA_MODE_DISABLE) {
		_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page[0], ((ptr_dev_info_t->page_size) + (ptr_dev_info_t->oob_size)));
	} else {
		_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, K0_TO_K1(&dma_write_page[0]), ptr_dev_info_t->page_size + ptr_dev_info_t->oob_size);
	}

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_write_page: _current_cache_page_data:\n");
	_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page_data[0], ptr_dev_info_t->page_size);
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_write_page: _current_cache_page_oob:\n");
	_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page_oob[0], ptr_dev_info_t->oob_size);		
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_write_page: _current_cache_page_oob_mapping:\n");
	_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &_current_cache_page_oob_mapping[0], (ptr_dev_info_t->oob_free_layout)->oobsize);

	if(ptr_dev_info_t->feature & SPI_NAND_FLASH_PLANE_SELECT_HAVE) {
		_plane_select_bit = ((page_number >> 6) & (0x1));

		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "spi_nand_write_page: _plane_select_bit=0x%x\n", _plane_select_bit );
	}

	{
		spi_nand_select_die ( page_number );
	
		/* Different Manafacture have different prgoram flow and setting */
		if(ptr_dev_info_t->write_en_type == SPI_NAND_FLASH_WRITE_LOAD_FIRST) {
			if(_spi_dma_mode == SPI_DMA_MODE_ENABLE) {
				rtn_status = spi_nand_dma_program_load(write_addr, oob_len, speed_mode);
				if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
					SPI_NAND_Flash_Clear_Read_Cache_Data();
					/* Switch to manual mode*/
					_SPI_NAND_ENABLE_MANUAL_MODE();
					return (rtn_status);
				}
			} else {
				spi_nand_protocol_program_load(write_addr, &_current_cache_page[0], ((ptr_dev_info_t->page_size) + (ptr_dev_info_t->oob_size)), speed_mode);
			}
	
			/*	Enable write_to flash */
			rtn_status = spi_nand_protocol_write_enable();
			if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
				SPI_NAND_Flash_Clear_Read_Cache_Data();
				/* Switch to manual mode*/
				_SPI_NAND_ENABLE_MANUAL_MODE();
				return (rtn_status);
			}
		} else {
			/*	Enable write_to flash */
			rtn_status = spi_nand_protocol_write_enable();
			if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
				SPI_NAND_Flash_Clear_Read_Cache_Data();
				/* Switch to manual mode*/
				_SPI_NAND_ENABLE_MANUAL_MODE();
				return (rtn_status);
			}
		
			if(_spi_dma_mode == SPI_DMA_MODE_ENABLE) {
				rtn_status = spi_nand_dma_program_load(write_addr, oob_len, speed_mode);
				if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
					SPI_NAND_Flash_Clear_Read_Cache_Data();
					/* Switch to manual mode*/
					_SPI_NAND_ENABLE_MANUAL_MODE();
					return (rtn_status);
				}
			} else {
				/* Proram data into buffer of SPI NAND chip */
				spi_nand_protocol_program_load(write_addr, &_current_cache_page[0], ((ptr_dev_info_t->page_size) + (ptr_dev_info_t->oob_size)), speed_mode);
			}
		}
	
		/*	Execute program data into SPI NAND chip  */
		prog_exe_status = spi_nand_protocol_program_execute ( page_number );
	
		/*. Disable write_flash */
		rtn_status = spi_nand_protocol_write_disable();
		if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			SPI_NAND_Flash_Clear_Read_Cache_Data();
			/* Switch to manual mode*/
			_SPI_NAND_ENABLE_MANUAL_MODE();
			return (rtn_status);
		}
		if(prog_exe_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			SPI_NAND_Flash_Clear_Read_Cache_Data();
			/* Switch to manual mode*/
			_SPI_NAND_ENABLE_MANUAL_MODE();
			return (prog_exe_status);
		}
		spi_nand_protocol_get_feature(_SPI_NAND_ADDR_PROTECTION, &status_2);
	
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "[spi_nand_write_page]: status 1 = 0x%x\n", status_2);
	}
	print_dot++;
	if((print_dot % 20) == 0 ) {
		_SPI_NAND_PRINTF(".");
	}

	SPI_NAND_Flash_Clear_Read_Cache_Data();
	
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_write_internal( u32    dst_addr,
 *                                                                u32    len,
 *                                                                u32    *ptr_rtn_len,
 *                                                                u8*    ptr_buf      )
 * PURPOSE : To write flash internally.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : dst_addr     - The dst_addr variable of this function.
 *           len          - The len variable of this function.
 *           ptr_buf      - A pointer to the ptr_buf variable.
 *   OUTPUT: ptr_rtn_len  - A pointer to the ptr_rtn_len variable.
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/19  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_write_internal( u32 dst_addr, 
													 u32 len, 
													 u32 *ptr_rtn_len, 
													 u8* ptr_buf, 
													 SPI_NAND_FLASH_WRITE_SPEED_MODE_T speed_mode )
{
	u32					 			remain_len, write_addr, data_len, page_number, physical_dst_addr;
	u32					 			addr_offset;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	unsigned long					spinand_spinlock_flags;
		
    unsigned short	phy_block_bbt;
	unsigned long	addr_offset_in_block;
	u32				logical_block, physical_block;
	u8				oob_buf[_SPI_NAND_OOB_SIZE]={0xff};

	*ptr_rtn_len 	= 0;
	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;	
	remain_len 		= len;
	write_addr 		= dst_addr;

	rtn_status = spi_nand_boundary_check(write_addr, remain_len);
	if (rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR)
		return rtn_status;
	
	_SPI_NAND_SEMAPHORE_LOCK();	

	SPI_NAND_Flash_Clear_Read_Cache_Data();

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_write_internal: remain_len =0x%x\n", remain_len);
	
	while(remain_len > 0)	
	{
		physical_dst_addr = write_addr;
		
		memset(oob_buf, 0xff, _SPI_NAND_OOB_SIZE);

		if(otp_status == NAND_FLASH_OTP_DISABLE) {
			addr_offset_in_block = (write_addr %(ptr_dev_info_t->erase_size) );
			logical_block = (write_addr / (ptr_dev_info_t->erase_size));
			physical_block = get_mapping_block_index(logical_block, &phy_block_bbt);
			physical_dst_addr = (physical_block * (ptr_dev_info_t->erase_size))+ addr_offset_in_block;

			if(physical_block != logical_block) {			
				_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "Bad Block Mapping, from %d block to %d block\n", logical_block, physical_block);
			}
		}
	
		/* Caculate page number */
		addr_offset = (physical_dst_addr % (ptr_dev_info_t->page_size));
		page_number = (physical_dst_addr / (ptr_dev_info_t->page_size));		
		
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "\nspi_nand_write_internal: addr_offset =0x%x, page_number=0x%x, remain_len=0x%x, page_size=0x%x\n", addr_offset, page_number, remain_len,(ptr_dev_info_t->page_size) );		
		if(((addr_offset + remain_len) > ptr_dev_info_t->page_size)) {  /* data cross over than 1 page range */
			data_len = ((ptr_dev_info_t->page_size) - addr_offset);			
		} else {
			data_len = remain_len;
		}

		if(otp_status == NAND_FLASH_OTP_DISABLE) {
			if(block_is_in_bmt_region(physical_block)) {
				memcpy(oob_buf + OOB_INDEX_OFFSET, &phy_block_bbt, OOB_INDEX_SIZE);
			}
		}

		if(test_write_fail_flag == 0) {
			rtn_status = spi_nand_write_page(page_number, addr_offset, &(ptr_buf[len - remain_len]), data_len, 0, &oob_buf[0], ptr_dev_info_t->oob_size , speed_mode);
		} else {
			rtn_status = SPI_NAND_FLASH_RTN_PROGRAM_FAIL;
		}

		if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		    _SPI_NAND_PRINTF("write fail at page: %d \n", page_number);
            if(update_bmt(page_number * ptr_dev_info_t->page_size, UPDATE_WRITE_FAIL, &(ptr_buf[len - remain_len]), oob_buf)) {
                _SPI_NAND_PRINTF("Update BMT success\n");
				rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
           
            } else {
                _SPI_NAND_PRINTF("Update BMT fail\n");
				_SPI_NAND_SEMAPHORE_UNLOCK();	
                return -1;
            }		
		}
		
		/* 8. Write remain data if neccessary */
		write_addr	+= data_len;
		remain_len  -= data_len;				
		*ptr_rtn_len += data_len;	 //COV, //COV, CID 139184
	}
	
	_SPI_NAND_SEMAPHORE_UNLOCK();
		
	return (rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Write_Nbyte( u32    dst_addr,
 *                                                            u32    len,
 *                                                            u32    *ptr_rtn_len,
 *                                                            u8*    ptr_buf      )
 * PURPOSE : To provide interface for Write N Bytes into SPI NAND Flash.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : dst_addr - The dst_addr variable of this function.
 *           len      - The len variable of this function.
 *           buf      - The buf variable of this function.
 *   OUTPUT: rtn_len  - The rtn_len variable of this function.
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/15  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Write_Nbyte( u32						dst_addr, 
												 u32								len, 
												 u32								*ptr_rtn_len, 
												 u8									*ptr_buf, 
												 SPI_NAND_FLASH_WRITE_SPEED_MODE_T	speed_node )
{		
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;	
	
	rtn_status = spi_nand_write_internal(dst_addr, len, ptr_rtn_len, ptr_buf, speed_node);
	
	*ptr_rtn_len = len ;  /* , tmp modify */
	
	return (rtn_status);
}

int nandflash_write(unsigned long to, unsigned long len, u32 *retlen, unsigned char *buf)
{
	struct SPI_NAND_FLASH_INFO_T	 *ptr_dev_info_t;
	 
	ptr_dev_info_t  = _SPI_NAND_GET_DEVICE_INFO_PTR;
	
	if( SPI_NAND_Flash_Write_Nbyte(to, len, retlen, buf, ptr_dev_info_t->write_mode) == SPI_NAND_FLASH_RTN_NO_ERROR )
	{
		return 0;
	}
	else
	{
		return -1;
	}	
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_protocol_block_erase( u32   block_idx )
 * PURPOSE : To implement the SPI nand protocol for block erase.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : block_idx - The block_idx variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/17  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_protocol_block_erase( u32 block_idx )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	rtn_status = spi_nand_protocol_chk_status(_SPI_NAND_VAL_OIP, 0);
	if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
		_SPI_NAND_PRINTF("[Error] erase Timeout.\n");
		return (rtn_status);
	}
	
	/* 1. Chip Select Low */
	_SPI_NAND_READ_CHIP_SELECT_LOW();
	
	/* 2. Write op_cmd 0xD8 (Block Erase) */
	_SPI_NAND_WRITE_ONE_BYTE( _SPI_NAND_OP_BLOCK_ERASE );

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_protocol_block_erase : block idx =0x%x\n", block_idx);
	
	/* 3. Write block number */
	block_idx = block_idx << _SPI_NAND_BLOCK_ROW_ADDRESS_OFFSET; 	/*Row Address format in SPI NAND chip */
	
	_SPI_NAND_WRITE_ONE_BYTE( (block_idx >> 16) & 0xff );		/* dummy byte */
	_SPI_NAND_WRITE_ONE_BYTE( (block_idx >> 8)  & 0xff );
	_SPI_NAND_WRITE_ONE_BYTE(  block_idx & 0xff );
	
	/* 4. Chip Select High */
	_SPI_NAND_READ_CHIP_SELECT_HIGH();
			
	return (rtn_status);
}

SPI_NAND_FLASH_RTN_T spi_nand_erase_block(u32 block_index)
{
	u8								status;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status	= SPI_NAND_FLASH_RTN_NO_ERROR;
	int								i;

	ptr_dev_info_t = _SPI_NAND_GET_DEVICE_INFO_PTR ;
	{
		spi_nand_select_die (block_index * (ptr_dev_info_t->erase_size / ptr_dev_info_t->page_size));

		/* 2.2 Enable write_to flash */
		rtn_status = spi_nand_protocol_write_enable();
		if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			return (SPI_NAND_FLASH_RTN_ERASE_FAIL);
		}

		/* 2.3 Erasing one block */
		spi_nand_protocol_block_erase(block_index);

		/* 2.4 Checking status for erase complete */
		do {
			spi_nand_protocol_get_feature(_SPI_NAND_ADDR_STATUS, &status);
		} while(status & _SPI_NAND_VAL_OIP) ;
				
		/* 2.5 Disable write_flash */
		rtn_status = spi_nand_protocol_write_disable();
		if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
			return (SPI_NAND_FLASH_RTN_ERASE_FAIL);
		}

		/* 2.6 Check Erase Fail Bit */
		if(status & _SPI_NAND_VAL_ERASE_FAIL) {
			_SPI_NAND_PRINTF("spi_nand_erase_block : erase block fail, block=0x%x, status=0x%x\n", block_index, status);
			rtn_status = SPI_NAND_FLASH_RTN_ERASE_FAIL;
		}
	}

	return rtn_status;
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_erase_internal( u32     addr,
 *                                               				  u32     len )
 * PURPOSE : To erase flash internally.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : addr - The addr variable of this function.
 *           len - The size variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/15  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_erase_internal( u32 addr, 
													 u32 len )
{		
	u32						block_index = 0;
	u32						erase_len	= 0;
	SPI_NAND_FLASH_RTN_T	rtn_status  = SPI_NAND_FLASH_RTN_NO_ERROR;
	unsigned long			spinand_spinlock_flags;
				
    unsigned short	phy_block_bbt;
	u32				logical_block, physical_block;
		
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "\nspi_nand_erase_internal (in): addr=0x%x, len=0x%x\n", addr, len );		

	rtn_status = spi_nand_boundary_check(addr, len);
	if (rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR)
		return rtn_status;

	_SPI_NAND_SEMAPHORE_LOCK();	
	
	/* Switch to manual mode*/
	_SPI_NAND_ENABLE_MANUAL_MODE();

	SPI_NAND_Flash_Clear_Read_Cache_Data();
	
	/* 1. Check the address and len must aligned to NAND Flash block size */
	if( spi_nand_block_aligned_check( addr, len) == SPI_NAND_FLASH_RTN_NO_ERROR) {
		/* 2. Erase block one by one */
		while( erase_len < len ) {
			/* 2.1 Caculate Block index */
			block_index = (addr/(_current_flash_info_t.erase_size));
			logical_block = block_index;
			physical_block = get_mapping_block_index(logical_block, &phy_block_bbt);		
			if( physical_block != logical_block) {			
				_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "Bad Block Mapping, from %d block to %d block\n", logical_block, physical_block);
			}
			block_index = physical_block;
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_erase_internal: addr=0x%x, len=0x%x, block_idx=0x%x\n", addr, len, block_index );

			rtn_status = spi_nand_erase_block(block_index);

			/* 2.6 Check Erase Fail Bit */
			if(rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR) {
				if (update_bmt((block_index * BLOCK_SIZE),UPDATE_ERASE_FAIL, NULL, NULL)) {
					_SPI_NAND_PRINTF("Erase fail at block: %d, update BMT success\n", addr/(_current_flash_info_t.erase_size));
					rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
				} else {
					_SPI_NAND_PRINTF("Erase fail at block: %d, update BMT fail\n", addr/(_current_flash_info_t.erase_size));
					rtn_status = SPI_NAND_FLASH_RTN_ERASE_FAIL;
					break;
				}
			}
			
			/* 2.7 Erase next block if needed */
			addr		+= _current_flash_info_t.erase_size;
			erase_len	+= _current_flash_info_t.erase_size;
		}		
	} else {
		rtn_status = SPI_NAND_FLASH_RTN_ALIGNED_CHECK_FAIL;
	}	
	
	_SPI_NAND_SEMAPHORE_UNLOCK();
		
	return 	(rtn_status);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Erase( u32  dst_addr,
 *                                                      u32  len      )
 * PURPOSE : To provide interface for Erase SPI NAND Flash.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : dst_addr - The dst_addr variable of this function.
 *           len      - The len variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/17  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Erase(u32 dst_addr, u32 len)
{
	SPI_NAND_FLASH_RTN_T rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	
	rtn_status = spi_nand_erase_internal(dst_addr, len);
	
	return (rtn_status);
}

int nandflash_erase(unsigned long offset, unsigned long len)
{
	if(SPI_NAND_Flash_Erase(offset, len) == SPI_NAND_FLASH_RTN_NO_ERROR) {
		return 0;
	} else {
		return -1;
	}
}

int en7512_nand_exec_read_page(u32 page, u8* date, u8* oob)
{	
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	unsigned long					spinand_spinlock_flags;

	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;

	rtn_status = spi_nand_read_page(page, ptr_dev_info_t->read_mode);
	
	if(rtn_status == SPI_NAND_FLASH_RTN_NO_ERROR) {
		/* Get data segment and oob segment  */
		memcpy( date, &_current_cache_page_data[0], ptr_dev_info_t->page_size );
		memcpy( oob,  &_current_cache_page_oob_mapping[0], ptr_dev_info_t->oob_size );
		
		return 0;
	} else {
		 _SPI_NAND_PRINTF( "en7512_nand_exec_read_page: read error, page=0x%x\n", page);
		return -1;
	}
}
int en7512_nand_check_block_bad(u32 offset, u32 bmt_block)
{
	u32								page_number;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	u8								bad_block_indicator[28];
	unsigned long					spinand_spinlock_flags;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;

	ptr_dev_info_t  = _SPI_NAND_GET_DEVICE_INFO_PTR;	

	if((0xbc000000 <= offset) && (offset <= 0xbfffffff)) {		/* Reserver address area for system */
		if( (offset & 0xbfc00000) == 0xbfc00000) {
			offset &= 0x003fffff;
		} else {
			offset &= 0x03ffffff;
		}
	}	 

	/* Caculate page number */
	page_number = (offset / (ptr_dev_info_t->page_size));		 

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "en7512_nand_check_block_bad: read_addr=0x%x, page_number=0x%x\n", offset, page_number);

	SPI_NAND_Flash_Clear_Read_Cache_Data();

	rtn_status = spi_nand_read_page(page_number, ptr_dev_info_t->read_mode);
	memcpy(bad_block_indicator, _current_cache_page_oob_mapping, 2);
	if(bmt_block) {
		if(bad_block_indicator[BMT_BAD_BLOCK_INDEX_OFFSET] != 0xff) {
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "Bad block detected at page_addr 0x%x, oob_buf[%d] is 0x%x\n", page_number, BMT_BAD_BLOCK_INDEX_OFFSET,_current_cache_page_oob_mapping[BMT_BAD_BLOCK_INDEX_OFFSET]);
			return 1;
		}
	}
	else {
		if(bad_block_indicator[0] != 0xff) {
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "Bad block detected at page_addr 0x%x, oob_buf[0] is 0x%x\n", page_number, _current_cache_page_oob_mapping[0]);
			return 1;
		}
	}
	return 0;  /* Good Block*/
}

int en7512_nand_erase(u32 offset)
{
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	unsigned long					spinand_spinlock_flags;
	
	ptr_dev_info_t	= _SPI_NAND_GET_DEVICE_INFO_PTR;   

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "en7512_nand_erase: offset =0x%x, erase_size=0x%x\n", offset, (ptr_dev_info_t->erase_size));

	SPI_NAND_Flash_Clear_Read_Cache_Data();
	
	rtn_status = spi_nand_erase_block((offset / ptr_dev_info_t->erase_size));
	
	if(rtn_status == SPI_NAND_FLASH_RTN_NO_ERROR)
	{
		return 0;		
	}
	else
	{
		_SPI_NAND_PRINTF("en7512_nand_erase : Fail \n");
		return -1;
	}
}

int en7512_nand_mark_badblock(u32 offset, u32 bmt_block)
{
	u32 							page_number;
	u8 buf[8];
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	unsigned long					spinand_spinlock_flags;

	ptr_dev_info_t  = _SPI_NAND_GET_DEVICE_INFO_PTR;	

	/* Caculate page number */
	page_number = (((offset / BLOCK_SIZE) * BLOCK_SIZE) / PAGE_SIZE);	

	memset(buf, 0xFF, 8);
	if(bmt_block)
	{
		buf[BMT_BAD_BLOCK_INDEX_OFFSET] = 0;
	}
	else
	{
		buf[0] = 0;
	}

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_2, "en7512_nand_mark_badblock: buf info:\n");
	_SPI_NAND_DEBUG_PRINTF_ARRAY(SPI_NAND_FLASH_DEBUG_LEVEL_2, &buf[0], 8);

	_SPI_NAND_PRINTF("en7512_nand_mark_badblock: page_num=0x%x\n", page_number);

	spi_nand_erase_block((offset / (ptr_dev_info_t->erase_size)));
	rtn_status = spi_nand_write_page(page_number, 0, NULL, 0, 0, &buf[0], 8, ptr_dev_info_t->write_mode);
	
	if( rtn_status == SPI_NAND_FLASH_RTN_NO_ERROR)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}
int en7512_nand_exec_write_page(u32 page, u8 *dat, u8 *oob)
{

	SPI_NAND_FLASH_RTN_T			rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
	struct SPI_NAND_FLASH_INFO_T	*ptr_dev_info_t;
	unsigned long					spinand_spinlock_flags;

	ptr_dev_info_t  = _SPI_NAND_GET_DEVICE_INFO_PTR;

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "en7512_nand_exec_write_page: page=0x%x\n", page);

	rtn_status = spi_nand_write_page(page, 0, dat, ptr_dev_info_t->page_size, 0, oob, ptr_dev_info_t->oob_size, ptr_dev_info_t->write_mode);

	if( rtn_status == SPI_NAND_FLASH_RTN_NO_ERROR)
	{
		return 0;
	}
	else
	{
		return -1;
	}
}

int calc_bmt_pool_size(struct ra_nand_chip *ra)
{
    unsigned int chip_size = 1 << ra->flash->chip_shift;
    unsigned int block_size = 1 << ra->flash->erase_shift;
    unsigned int total_block = chip_size / block_size;
    unsigned int last_block = total_block - 1;
    u16 valid_block_num = 0;
    u16 need_valid_block_num = total_block * POOL_GOOD_BLOCK_PERCENT;

	maximum_bmt_block_count = total_block * MAX_BMT_SIZE_PERCENTAGE;

	nand_flash_avalable_size = chip_size - (maximum_bmt_block_count * block_size);

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "\r\navalable block = %d\n", nand_flash_avalable_size / block_size);

    for(;last_block > 0; --last_block) {
        if(en7512_nand_check_block_bad((last_block*block_size), BAD_BLOCK_RAW)) {
            continue;  
        } else {
            valid_block_num++;
            if(valid_block_num == need_valid_block_num) {
                break;
            }
        }
    }

    return (total_block - last_block);   
}

bmt_struct *get_g_bmt(void)
{
	return g_bmt;
}

init_bbt_struct *get_g_bbt(void)
{
	return g_bbt;
}

u32 get_maximum_bmt_block_count(void)
{
	return maximum_bmt_block_count;
}

void set_dma_write_addr(u8 *addr)
{
	dma_write_page = addr;
}

void reset_dma_write_addr()
{
	set_dma_write_addr(tmp_dma_write_page + (CACHE_LINE_SIZE - (((u32)tmp_dma_write_page) % CACHE_LINE_SIZE)));
}

SPI_NAND_FLASH_RTN_T get_spi_nand_protocol_read_id (struct SPI_NAND_FLASH_INFO_T *ptr_rtn_flash_id)
{
	return spi_nand_protocol_read_id(ptr_rtn_flash_id);
}

SPI_NAND_FLASH_RTN_T get_spi_nand_protocol_read_id_2 (struct SPI_NAND_FLASH_INFO_T *ptr_rtn_flash_id)
{
	return spi_nand_protocol_read_id_2(ptr_rtn_flash_id);
}

void set_test_write_fail_flag(u32 val)
{
	test_write_fail_flag = val;
}

void set_dma_read_addr(u8 *addr)
{
	dma_read_page = addr;
}

void reset_dma_read_addr()
{
	set_dma_read_addr(tmp_dma_read_page + (CACHE_LINE_SIZE - (((u32)tmp_dma_read_page) % CACHE_LINE_SIZE)));
}

SPI_NAND_FLASH_RTN_T set_spi_nand_load_page_into_cache(u32 page_number)
{
	return spi_nand_load_page_into_cache(page_number);
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static void spi_nand_manufacute_init( struct SPI_NAND_FLASH_INFO_T *ptr_device_t )
 * PURPOSE : To init SPI NAND Flash chip
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : None
 *   OUTPUT: None.
 * RETURN  : None.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2015/05/15  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static void spi_nand_manufacute_init(struct SPI_NAND_FLASH_INFO_T *ptr_device_t)
{
	unsigned char	feature;
	unsigned char	die;

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1,"spi_nand_manufacute_init: Unlock all block and Enable Quad Mode\n"); 

	for(die = 0; die < ptr_device_t->die_num; die++) {
		spi_nand_direct_select_die(die);

		/* 1. Unlock All block */
		if(ptr_device_t->unlock_block_info.unlock_block_mask != 0x00) {	
			spi_nand_protocol_get_feature(_SPI_NAND_ADDR_PROTECTION, &feature);
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "Before Unlock all block setup, the status register1 = 0x%x\n", feature);
			
			feature = (feature & ~(ptr_device_t->unlock_block_info.unlock_block_mask));
			feature |= (ptr_device_t->unlock_block_info.unlock_block_value & ptr_device_t->unlock_block_info.unlock_block_mask);
			spi_nand_protocol_set_feature(_SPI_NAND_ADDR_PROTECTION, feature);
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "Unlock all block setup, the feature = 0x%x\n", feature);

			spi_nand_protocol_get_feature(_SPI_NAND_ADDR_PROTECTION, &feature);
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "After Unlock all block setup, the status register1 = 0x%x\n", feature);
		} else {
			_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "No need Unlock all block setup.\n");
		}
	}
}

/*------------------------------------------------------------------------------------
 * FUNCTION: static SPI_NAND_FLASH_RTN_T spi_nand_probe( struct SPI_NAND_FLASH_INFO_T  *ptr_rtn_device_t )
 * PURPOSE : To probe SPI NAND flash id.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : None
 *   OUTPUT: rtn_index  - The rtn_index variable of this function.
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/12  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
static SPI_NAND_FLASH_RTN_T spi_nand_probe( struct SPI_NAND_FLASH_INFO_T *ptr_rtn_device_t )
{
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_PROBE_ERROR;
	unsigned long			spinand_spinlock_flags;

	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: start \n");

	/* Protocol for read id */
	_SPI_NAND_SEMAPHORE_LOCK();	
	spi_nand_protocol_read_id(ptr_rtn_device_t );
	_SPI_NAND_SEMAPHORE_UNLOCK();	

	rtn_status = scan_spi_nand_table(ptr_rtn_device_t);

	if ( rtn_status != SPI_NAND_FLASH_RTN_NO_ERROR )
	{
		/* Another protocol for read id  (For example, the GigaDevice SPI NADN chip for Type C */
		_SPI_NAND_SEMAPHORE_LOCK();	
		spi_nand_protocol_read_id_2( (struct SPI_NAND_FLASH_INFO_T *)ptr_rtn_device_t );
		_SPI_NAND_SEMAPHORE_UNLOCK();	

		rtn_status = scan_spi_nand_table(ptr_rtn_device_t);
	}

	_SPI_NAND_PRINTF("spi_nand_probe: mfr_id=0x%x, dev_id=0x%x\n", ptr_rtn_device_t->mfr_id, ptr_rtn_device_t->dev_id);

	if(rtn_status == SPI_NAND_FLASH_RTN_NO_ERROR)
	{
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: device size:0x%x \n", ptr_rtn_device_t->device_size);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: erase size :0x%x \n", ptr_rtn_device_t->erase_size);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: page size  :0x%x \n", ptr_rtn_device_t->page_size);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: oob size   :%d \n", ptr_rtn_device_t->oob_size);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: dummy mode :%d \n", ptr_rtn_device_t->dummy_mode);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: read mode  :%d \n", ptr_rtn_device_t->read_mode);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: write mode :%d \n", ptr_rtn_device_t->write_mode);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: feature    :0x%x \n", ptr_rtn_device_t->feature);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: die num    :%d \n", ptr_rtn_device_t->die_num);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: write type    :%d \n", ptr_rtn_device_t->write_en_type);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: ecc check info mask     :%x \n", ptr_rtn_device_t->ecc_fail_check_info.ecc_check_mask);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: ecc check info value    :%x \n", ptr_rtn_device_t->ecc_fail_check_info.ecc_uncorrected_value);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: unlock block info mask  :%x \n", ptr_rtn_device_t->unlock_block_info.unlock_block_mask);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: unlock block info value :%x \n", ptr_rtn_device_t->unlock_block_info.unlock_block_value);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: quad en info mask       :%x \n", ptr_rtn_device_t->quad_en.quad_en_mask);
		_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: quad en info value      :%x \n", ptr_rtn_device_t->quad_en.quad_en_value);
		
		_SPI_NAND_SEMAPHORE_LOCK();
		spi_nand_manufacute_init(ptr_rtn_device_t);		
		_SPI_NAND_SEMAPHORE_UNLOCK();	

		if((ptr_rtn_device_t->write_mode == SPI_NAND_FLASH_WRITE_SPEED_MODE_QUAD) || 
			(ptr_rtn_device_t->read_mode == SPI_NAND_FLASH_READ_SPEED_MODE_QUAD)) {
			enable_quad();
		}
	}	
	
	_SPI_NAND_DEBUG_PRINTF(SPI_NAND_FLASH_DEBUG_LEVEL_1, "spi_nand_probe: end \n");
	
	return (rtn_status);
}

int enable_dma(SPI_NFI_CONF_T *spi_nfi_conf_t,
				SPI_ECC_ENCODE_CONF_T *encode_conf_t,
				SPI_ECC_DECODE_CONF_T *decode_conf_t)
{
	unsigned long spinand_spinlock_flags;
	int i;
	
	_SPI_NAND_SEMAPHORE_LOCK();

	if(isSpiNandAndCtrlECC) {
		_current_flash_info_t.oob_free_layout = &ooblayout_spi_controller_ecc;
		_current_flash_info_t.oob_free_layout->oobsize = spi_nfi_conf_t->sec_num * 8;
		
		for(i = 0; i < spi_nfi_conf_t->sec_num; i++) {
			_current_flash_info_t.oob_free_layout->oobfree[i].offset = i * spi_nfi_conf_t->spare_size_t;
			_current_flash_info_t.oob_free_layout->oobfree[i].len= 8;
		}
	}

	/* Set controller to DMA mode */
	SPI_NAND_Flash_Set_DmaMode(SPI_DMA_MODE_ENABLE);

	/* Init DMA */
	SPI_NFI_Init();

	/* config DMA */
	SPI_NFI_Set_Configure(spi_nfi_conf_t);

	/* config ECC */
	if(spi_nfi_conf_t->hw_ecc_t == SPI_NFI_CON_HW_ECC_Enable) {
		
		/* Init Decode, Encode */
		SPI_ECC_Encode_Init();
		SPI_ECC_Decode_Init();
			
		/* Setup Encode */
		SPI_ECC_Encode_Set_Configure(encode_conf_t);
		/* Setup Decode */
		SPI_ECC_Decode_Set_Configure(decode_conf_t);
	}

	_SPI_NAND_SEMAPHORE_UNLOCK();
	return 0;
}

int init_bmt_bbt(struct ra_nand_chip *ra)
{
	unsigned long spinand_spinlock_flags;
	
	_SPI_NAND_SEMAPHORE_LOCK();
	
	bmt_pool_size = calc_bmt_pool_size(ra);

	if(bmt_pool_size > maximum_bmt_block_count) {
		_SPI_NAND_PRINTF("Error : bmt pool size: %d > maximum size %d\n", bmt_pool_size, maximum_bmt_block_count);
		_SPI_NAND_PRINTF("Error: init bmt failed \n");
		_SPI_NAND_SEMAPHORE_UNLOCK();
		return -1;
	}

	if(bmt_pool_size > MAX_BMT_SIZE) {
		bmt_pool_size = MAX_BMT_SIZE;
	}

	_SPI_NAND_PRINTF("bmt pool size: %d \n", bmt_pool_size);
	
	if(!g_bmt) {
		if (!(g_bmt = init_bmt(ra, bmt_pool_size)))
		{
			_SPI_NAND_PRINTF("Error: init bmt failed \n");
			_SPI_NAND_SEMAPHORE_UNLOCK();
			return -1;
		}
	}

	if(!g_bbt) {
		if (!(g_bbt = start_init_bbt())) {
			_SPI_NAND_PRINTF("Error: init bbt failed \n");
			_SPI_NAND_SEMAPHORE_UNLOCK();
			return -1;
		}
	}

	if(write_bbt_or_bmt_to_flash() != 0) {				
		_SPI_NAND_PRINTF("Error: save bbt or bmt to nand failed \n");
		_SPI_NAND_SEMAPHORE_UNLOCK();
		return -1;
	}
	
	if(create_badblock_table_by_bbt()) {
		_SPI_NAND_PRINTF("Error: create bad block table failed \n");
		_SPI_NAND_SEMAPHORE_UNLOCK();
		return -1;
	}

	_SPI_NAND_PRINTF("BMT & BBT Init Success \n");

	_SPI_NAND_SEMAPHORE_UNLOCK();

	return 0;
}

/*------------------------------------------------------------------------------------
 * FUNCTION: SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Init( long  rom_base )
 * PURPOSE : To provide interface for SPI NAND init.
 * AUTHOR  : 
 * CALLED BY
 *   -
 * CALLS
 *   -
 * PARAMs  :
 *   INPUT : rom_base - The rom_base variable of this function.
 *   OUTPUT: None
 * RETURN  : SPI_RTN_NO_ERROR - Successful.   Otherwise - Failed.
 * NOTES   :
 * MODIFICTION HISTORY:
 * Date 2014/12/12  - The first revision for this function.
 *
 *------------------------------------------------------------------------------------
 */
SPI_NAND_FLASH_RTN_T SPI_NAND_Flash_Init(u32 rom_base)
{
	SPI_NFI_CONF_T			spi_nfi_conf_t;
	SPI_ECC_ENCODE_CONF_T	encode_conf_t;
	SPI_ECC_DECODE_CONF_T	decode_conf_t;
	int						sec_num;
	unsigned long 			spi_nand_info;
	SPI_ECC_ENCODE_ABILITY_T	encode_ecc_abiliry;
	SPI_ECC_DECODE_ABILITY_T	decode_ecc_abiliry;
	SPI_NFI_CONF_SPARE_SIZE_T   spare_size_t;
	
	SPI_NAND_FLASH_RTN_T	rtn_status = SPI_NAND_FLASH_RTN_PROBE_ERROR;	
	{
		nand_probe = spi_nand_probe;
	}

	/* 1. set SFC Clock to 50MHZ  */
	spi_set_clock_speed(50);
	
	/* 2. Enable Manual Mode */
	_SPI_NAND_ENABLE_MANUAL_MODE();	

 	/* 3. Probe flash information */
	if ( nand_probe(  &_current_flash_info_t) != SPI_NAND_FLASH_RTN_NO_ERROR ) {
		_SPI_NAND_PRINTF("SPI NAND Flash Detected Error !\n");
	} else {

		/* for 32bytes alignment */
		if(dma_read_page == NULL) {
			dma_read_page = tmp_dma_read_page + (CACHE_LINE_SIZE - (((u32)tmp_dma_read_page) % CACHE_LINE_SIZE));
		}
		
		/* for 32bytes alignment */
		if(dma_write_page == NULL) {
			dma_write_page = tmp_dma_write_page + (CACHE_LINE_SIZE - (((u32)tmp_dma_write_page) % CACHE_LINE_SIZE));
		}

		if(_current_flash_info_t.page_size == _SPI_NAND_PAGE_SIZE_2KBYTE) {
			sec_num = 4;
		} else if(_current_flash_info_t.page_size == _SPI_NAND_PAGE_SIZE_4KBYTE) {
			sec_num = 8;
		} else {
			sec_num = 1;
		}

		if(isSpiNandAndCtrlECC) {
			/* Disable OnDie ECC */
			SPI_NAND_Flash_Disable_OnDie_ECC();

				spi_nand_info		= VPint(SPI_NAND_CONTROLLER_ECC_INFO_SRAM_ADDR_INFO);
				spare_size_t		= (spi_nand_info & 0xFFFF);
				encode_ecc_abiliry	= ((spi_nand_info >> 16) & 0xFFFF);
				decode_ecc_abiliry	= ((spi_nand_info >> 16) & 0xFFFF);

			_SPI_NAND_PRINTF("Using SPI controller ECC.\n");
			_SPI_NAND_PRINTF("  ECC ability:%d\n", encode_ecc_abiliry);
			_SPI_NAND_PRINTF("  NFI spare area size:%d\n", spare_size_t);

			/* Setup NFI */
			spi_nfi_conf_t.auto_fdm_t			= SPI_NFI_CON_AUTO_FDM_Enable;
			spi_nfi_conf_t.hw_ecc_t 			= SPI_NFI_CON_HW_ECC_Enable;
			spi_nfi_conf_t.dma_burst_t			= SPI_NFI_CON_DMA_BURST_Enable;
			spi_nfi_conf_t.fdm_num				= 8;
			spi_nfi_conf_t.fdm_ecc_num			= 8;
			spi_nfi_conf_t.page_size_t			= _current_flash_info_t.page_size;
			spi_nfi_conf_t.sec_num				= sec_num;
			spi_nfi_conf_t.cus_sec_size_en_t	= SPI_NFI_CONF_CUS_SEC_SIZE_Disable;
			spi_nfi_conf_t.sec_size 			= 0;
			spi_nfi_conf_t.spare_size_t 		= spare_size_t;

			/* Setup Encode */
			encode_conf_t.encode_en 			= SPI_ECC_ENCODE_ENABLE;
			encode_conf_t.encode_ecc_abiliry	= encode_ecc_abiliry;
			encode_conf_t.encode_block_size 	= 512 + spi_nfi_conf_t.fdm_ecc_num;
					
			/* Setup Decode */
			decode_conf_t.decode_en 			= SPI_ECC_DECODE_ENABLE;
			decode_conf_t.decode_ecc_abiliry	= decode_ecc_abiliry;
			decode_conf_t.decode_block_size 	= (((spi_nfi_conf_t.fdm_ecc_num) + 512) * 8) + ((decode_conf_t.decode_ecc_abiliry) * 13);

			/* enable DMA */
			enable_dma(&spi_nfi_conf_t, &encode_conf_t, &decode_conf_t);
		} else {
			_SPI_NAND_PRINTF("Using Flash ECC.\n");
			SPI_NAND_Flash_Enable_OnDie_ECC();
		}

		_SPI_NAND_PRINTF("Detected SPI NAND Flash : %s, Flash Size=0x%x\n", _current_flash_info_t.ptr_name,  _current_flash_info_t.device_size);

		rtn_status = SPI_NAND_FLASH_RTN_NO_ERROR;
		
		devinfo.blocksize = (_current_flash_info_t.erase_size)/1024;
		devinfo.totalsize = ((_current_flash_info_t.device_size)/(1024*1024));

		/*  For bootloader to caculate flash size information */
		memset((void*) &ra, 0, sizeof(struct ra_nand_chip));
		memset((void*) &flashInfo, 0, sizeof(struct nand_info));
		
		flashInfo.chip_shift = generic_ffs(_current_flash_info_t.device_size) - 1;
		flashInfo.erase_shift = generic_ffs(_current_flash_info_t.erase_size) - 1;
		flashInfo.page_shift = generic_ffs(_current_flash_info_t.page_size) - 1;
		flashInfo.oob_shift = generic_ffs(MAX_LINUX_USE_OOB_SIZE) - 1;
		ra.flash = &flashInfo;					

reservearea_size = _current_flash_info_t.erase_size;

		if(init_bmt_bbt(&ra) == -1) {
			return -1;
		}
	}

   return (rtn_status);
}

int nandflash_init(int rom_base)
{	
	if(SPI_NAND_Flash_Init(rom_base) == SPI_NAND_FLASH_RTN_NO_ERROR) {
		return 0;
	} else {
		return -1;
	}
}

/* End of [spi_nand_flash.c] package */
