//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//

#include <windows.h>
#include <bsp.h>

#include <bsp_cfg.h>
#include <fmd.h>
#include <nand_reg.h>
#include <bsp_args.h>
#include <ethdbg.h>
#include "Cfnand.h"
#if MAGNETO
#include <image_cfg.h>
#endif

#include <drvmsg.h>

#include <fmd_LB.h>




//#define FMDMSG(x, y) //DBGMSG(x, (TEXT("[FMD] : "))); DBGMSG(x, y);
#define FMDMSG(x, y) //RETAILMSG(1, (TEXT("[FMD] : "))); RETAILMSG(1, y);
#define FMDERR(x, y) ERRMSG((TEXT("[FMD:ERR] : "))); ERRMSG(y);


#define NAND_BASE				(0xB9400000)    // PA:0xB0E00000
#define ECC_BASE				(0xB9420000)	// PA:0xB0E20000


#define RdPage512(x)        	RdPage512R(x, (UINT32)(&(v_pNFCONregs->NFDATA)))
#define RdPage512Unalign(x)		RdPage512UnalignR(x, (UINT32)(&(v_pNFCONregs->NFDATA)))
#define WrPage512(x)			WrPage512R(x, (UINT32)(&(v_pNFCONregs->NFDATA)))
#define WrPage512Unalign(x)		WrPage512UnalignR(x, (UINT32)(&(v_pNFCONregs->NFDATA)))
#define RdPageInfo(x)			RdPageInfoR(x, (UINT32)(&(v_pNFCONregs->NFDATA)))
#define WrPageInfo(x)			WrPageInfoR(x, (UINT32)(&(v_pNFCONregs->NFDATA)))

extern "C"
{
    void RdPage512R(unsigned char *bufPt, UINT32 nNFDATAAddr);
    void RdPage512UnalignR(unsigned char *bufPt, UINT32 nNFDATAAddr);
    void WrPage512R(unsigned char *bufPt, UINT32 nNFDATAAddr);
    void WrPage512UnalignR(unsigned char *bufPt, UINT32 nNFDATAAddr);
    void RdPageInfoR(PBYTE pBuff, UINT32 nNFDATAAddr);
    void WrPageInfoR(PBYTE pBuff, UINT32 nNFDATAAddr);
}

static volatile NAND_REG *v_pNFCONregs = (NAND_REG *)NAND_BASE;
static volatile NAND_ECC_REG *v_pECCregs = (NAND_ECC_REG *)ECC_BASE;


BOOL NEED_EXT_ADDR;

NAND_FLASH_SPEC *g_pNANDSpec = NULL;


// Register backup for PowerUp/PowerDown
DWORD g_dwNFCONF;
DWORD g_dwNFCONT;


// STEPLDR pages & blocks
DWORD g_dwNumberOfPagesSTEPLDR;
DWORD g_dwNumberOfBlocksSTEPLDR;


#if MAGNETO
#define MAX_REGIONS 16
#define DEFAULT_COMPACTION_BLOCKS 4

BSP_ARGS *pBSPArgs;
static BOOL DefineLayout();
static FlashRegion g_pRegionTable[MAX_REGIONS];
static DWORD g_dwNumRegions;
static FlashInfo g_flashInfo;
static BYTE g_pFLSBuffer[NAND_LB_PAGE_SIZE];  // temporary sector size is adapted for large block NAND
#endif



#define ECC8_SPARE_MSG_LENGTH	(4 + 4 + 4 + 4 )		// Spare area 8-bit ECC message length



typedef struct
{
    UINT32    n8MECC0;        // 8MECC0
    UINT32    n8MECC1;        // 8MECC1
    UINT32    n8MECC2;        // 8MECC2
    UINT8     n8MECC3;        // 8MECC3
} MECC8;



// Error correction Capability
// Main data  : 1 bit / 512byte
// Spare data : 8 bit / 
BOOL ECC_CorrectData(SECTOR_ADDR dwSector, LPBYTE pbData, ECC_CORRECT_TYPE nType)
{
	BOOL bReturn = TRUE;

	DWORD dwECC1ErrorResult;
	DWORD dwECC1MainErrorNo;
	DWORD dwECC1MainErrorByte;
	DWORD dwECC1MainErrorBit;

	DWORD dwECC8ErrorNo;
	DWORD dwECC8ErrorByte[8];
	DWORD dwECC8ErrorPattern[8];

	DWORD i;

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	switch(nType)
	{
		case ECC_CORRECT_MAIN:
		{
			// 1-bit ECC

			dwECC1ErrorResult = NF_ECC_ERR0;

			dwECC1MainErrorNo = dwECC1ErrorResult & 0x3;
			dwECC1MainErrorByte = (dwECC1ErrorResult >> 7) & 0x7FF;
			dwECC1MainErrorBit = (dwECC1ErrorResult >> 4) & 0x7;

			switch(dwECC1MainErrorNo)
			{
				case 0:
					FMDMSG(FMD_ECC, (TEXT("[MAIN] No ECC error : sector %d\r\n"), dwSector));
					break;

				case 1:
					FMDMSG(FMD_ECC, (TEXT("[MAIN] Correctable ECC error : sector %d\r\n"), dwSector));
					pbData[dwECC1MainErrorByte] ^= (1 << dwECC1MainErrorBit);
					break;

				case 2:
					FMDERR(TRUE, (TEXT("[MAIN] Uncorrectable ECC error : sector %d\r\n"), dwSector));
					bReturn = FALSE;
					break;

				case 3:
					FMDERR(TRUE, (TEXT("[MAIN] Uncorrectable ECC area error : sector %d\r\n"), dwSector));
					bReturn = FALSE;
					break;
			}
			

			break;
		}
		

		case ECC_CORRECT_SPARE:
		{
			// 8-bit ECC

			dwECC8ErrorNo = NF_ECC8_ERROR();
			
			if(dwECC8ErrorNo == 0)
			{
				FMDMSG(FMD_ECC, (TEXT("[SPARE] No ECC error : sector %d\r\n"), dwSector));
			}
			else if(dwECC8ErrorNo <= 8)
			{
				FMDMSG(FMD_ECC, (TEXT("[SPARE] Correctable ECC error : sector %d\r\n"), dwSector));
						
				dwECC8ErrorByte[0] = (v_pECCregs->NFECCERL0 >> 0) & 0x3FF;
				dwECC8ErrorByte[1] = (v_pECCregs->NFECCERL0 >> 16) & 0x3FF;
				dwECC8ErrorByte[2] = (v_pECCregs->NFECCERL1 >> 0) & 0x3FF;
				dwECC8ErrorByte[3] = (v_pECCregs->NFECCERL1 >> 16) & 0x3FF;
				dwECC8ErrorByte[4] = (v_pECCregs->NFECCERL2 >> 0) & 0x3FF;
				dwECC8ErrorByte[5] = (v_pECCregs->NFECCERL2 >> 16) & 0x3FF;
				dwECC8ErrorByte[6] = (v_pECCregs->NFECCERL3 >> 0) & 0x3FF;
				dwECC8ErrorByte[7] = (v_pECCregs->NFECCERL3 >> 16) & 0x3FF;
			
				dwECC8ErrorPattern[0] = (v_pECCregs->NFECCERP0 >> 0) & 0xFF;
				dwECC8ErrorPattern[1] = (v_pECCregs->NFECCERP0 >> 8) & 0xFF;
				dwECC8ErrorPattern[2] = (v_pECCregs->NFECCERP0 >> 16) & 0xFF;
				dwECC8ErrorPattern[3] = (v_pECCregs->NFECCERP0 >> 24) & 0xFF;
				dwECC8ErrorPattern[4] = (v_pECCregs->NFECCERP1 >> 0) & 0xFF;
				dwECC8ErrorPattern[5] = (v_pECCregs->NFECCERP1 >> 8) & 0xFF;
				dwECC8ErrorPattern[6] = (v_pECCregs->NFECCERP1 >> 16) & 0xFF;
				dwECC8ErrorPattern[7] = (v_pECCregs->NFECCERP1 >> 24) & 0xFF;


				for(i = 0;i < dwECC8ErrorNo;i++)
				{
					pbData[dwECC8ErrorByte[i]] ^= (dwECC8ErrorPattern[i]);
				}
			}
			else
			{
				FMDERR(TRUE, (TEXT("[SPARE] Uncorrectable ECC error : sector %d\r\n"), dwSector));
				bReturn = FALSE;
			}
			break;
		}

		default:
			break;
			
	}

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

	return bReturn;
}


/*
    @func   DWORD | ReadFlashID | Reads the flash manufacturer and device codes.
    @rdesc  Manufacturer and device codes.
    @comm
    @xref
*/
static DWORD ReadFlashID(void)
{
    UINT8  ucMID, ucDID;
    DWORD i;

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    NF_nFCE_L();                        // Deselect the flash chip.
    NF_CMD(CMD_READID);                 // Send flash ID read command.

    NF_ADDR(0);


    for (i = 0; i < 10; i++)
    {
        ucMID = NF_RDDATA_BYTE();

        if (ucMID == 0xEC || ucMID == 0x98)
            break;
    }

    ucDID = NF_RDDATA_BYTE();
#if 0  // read more infomation
    {
        BYTE  c3rd, c4th, c5th;

        c3rd = NF_RDDATA_BYTE();
        c4th = NF_RDDATA_BYTE();
        c5th = NF_RDDATA_BYTE();
        RETAILMSG(1, (TEXT(" ReadFlashID 1st cycle = 0x%x\n\r"), ucManufacturer));
        RETAILMSG(1, (TEXT(" ReadFlashID 2nd cycle = 0x%x\n\r"), Dev));
        RETAILMSG(1, (TEXT(" ReadFlashID 3rd cycle = 0x%x\n\r"), c3rd));
        RETAILMSG(1, (TEXT(" ReadFlashID 4th cycle = 0x%x\n\r"), c4th));
        RETAILMSG(1, (TEXT(" ReadFlashID 5th cycle = 0x%x\n\r"), c5th));
    }
#endif
    NF_nFCE_H();

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return ((DWORD)(ucMID<<8)+ucDID);
}

/*
    @func   PVOID | FMD_Init | Initializes the Smart Media NAND flash controller.
    @rdesc  Pointer to S5PV210 NAND controller registers.
    @comm
    @xref
*/
PVOID FMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)
{
    // Caller should have specified NAND controller address.
    //
    UINT8 nMID, nDID;
    int nCnt;
    volatile DWORD rdid;

	DWORD dwTACLS;
	DWORD dwTWRPH0;
	DWORD dwTWRPH1;
	
    DWORD dwTotalNumberOfPages;

	
	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));


#if MAGNETO
    pBSPArgs = ((BSP_ARGS *) IMAGE_SHARE_ARGS_UA_START);
#endif

    if (pRegIn && pRegIn->MemBase.Num && pRegIn->MemBase.Reg[0])
        v_pNFCONregs = (NAND_REG *)(pRegIn->MemBase.Reg[0]);
    else
        v_pNFCONregs = (NAND_REG *)NAND_BASE;



	// ECC registers
	v_pECCregs = (NAND_ECC_REG *)ECC_BASE;
	

    // Set up initial flash controller configuration.
    //
    v_pNFCONregs->NFCONF = (NAND_TACLS << 12) | (NAND_TWRPH0 << 8) | (NAND_TWRPH1 << 4);
    v_pNFCONregs->NFCONT = (0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(1<<7)|(1<<6)|(1<<5)|(1<<4)|(0x3<<1)|(1<<0);
    v_pNFCONregs->NFSTAT = (1<<4);



    // Get the device information
    rdid = ReadFlashID();

    nMID = (UINT8)(rdid >> 8);
    nDID = (UINT8)(rdid & 0xff);

    for (nCnt = 0; g_supportedNAND[nCnt].ucMID != 0; nCnt++)
    {
        if (nDID == g_supportedNAND[nCnt].ucDID && nMID == g_supportedNAND[nCnt].ucMID )
        {
			g_pNANDSpec = &(g_supportedNAND[nCnt]);
            break;
        }
    }

	if(g_pNANDSpec == NULL)
	{
		FMDERR(TRUE, (TEXT("Failed to find a supported NAND flash memory.\r\n")));
		FMDERR(FMD_INFO, (TEXT("Manufacturer code         : 0x%2X\r\n"), nMID));
		FMDERR(FMD_INFO, (TEXT("Device code               : 0x%2X\r\n"), nDID));

		// error return
		v_pNFCONregs = NULL;
		goto CleanUp;
	}

	// 
	// NFCON configuration
	// 
	
	dwTotalNumberOfPages = g_pNANDSpec->usNumberOfBlocks * g_pNANDSpec->usPagesPerBlock;

	// TACLS, TWRPH0, TWRPH1 timing parameter should be calculated based on the spec to achieve the maximum performance.
//	dwTACLS = NAND_TACLS;
//	dwTWRPH0 = NAND_TWRPH0;
//	dwTWRPH1 = NAND_TWRPH1;
	dwTACLS = g_pNANDSpec->dwTACLS;
	dwTWRPH0 = g_pNANDSpec->dwTWRPH0;
	dwTWRPH1 = g_pNANDSpec->dwTWRPH1;

	
	// -- SLC or MLC
	// -- FMD driver supports SLC NAND flash memory only.
	if(g_pNANDSpec->ucType == SLC_NAND)
	{
		if(g_pNANDSpec->usMainBytesPerPage == 2048)
		{
			if(dwTotalNumberOfPages > (1 << 16))
			{
				// SLC, Large block, 5 address cycle
				v_pNFCONregs->NFCONF = (dwTACLS  <<  12) | (dwTWRPH0 <<  8) | (dwTWRPH1 <<  4)
										| ECC_1BIT | SLC_NAND | LARGE_BLOCK | EXT_ADDR;
				NEED_EXT_ADDR = TRUE;
			}
			else
			{
				// SLC, Large block, 4 address cycle
				v_pNFCONregs->NFCONF = (dwTACLS  <<  12) | (dwTWRPH0 <<  8) | (dwTWRPH1 <<  4)
										| ECC_1BIT | SLC_NAND | LARGE_BLOCK;
				NEED_EXT_ADDR = FALSE;
			}
		}
		else if(g_pNANDSpec->usMainBytesPerPage == 512)
		{
			// Small block NAND
			FMDERR(TRUE, (TEXT("FMD doesn't support the small block NAND flash memory.\r\n")));

			v_pNFCONregs = NULL;
			goto CleanUp;
		}
		else
		{
			// page size > 2KB
			FMDERR(TRUE, (TEXT("FMD doesn't support the NAND flash memory whose page size is larger than 2KB\r\n")));
			
			v_pNFCONregs = NULL;
			goto CleanUp;
		}
	}
	else
	{
		// MLC NAND flash memory
		FMDERR(TRUE, (TEXT("FMD doesn't support the MLC NAND flash memory.\r\n")));

		v_pNFCONregs = NULL;
		goto CleanUp;
	}


	FlashInfo fi;

	FMD_GetInfo(&fi);


#if MAGNETO
//    DefineLayout();
#endif

CleanUp:

	if(v_pNFCONregs == NULL)
	{
		FMDERR(TRUE, (TEXT("FMD_Init() was failed.\r\n")));
	}

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    
    return((PVOID)v_pNFCONregs);
}


/*
    @func   BOOL | FMD_ReadSector | Reads the specified sector(s) from NAND flash.
    @rdesc  TRUE = Success, FALSE = Failure.
    @comm
    @xref
*/
BOOL FMD_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    BOOL bRet = TRUE;


//	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	bRet = FMD_LB_ReadSector(startSectorAddr, pSectorBuff, pSectorInfoBuff, dwNumSectors);

//	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bRet;
}


BOOL FMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff,
                        DWORD dwNumSectors)
{
    BOOL    bRet = TRUE;
	//RETAILMSG(1,(TEXT("++++FMD_WriteSector\r\n")));

//	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	bRet = FMD_LB_WriteSector(startSectorAddr, pSectorBuff, pSectorInfoBuff, dwNumSectors);

//	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bRet;
}

/*
    @func   BOOL | FMD_EraseBlock | Erases the specified flash block.
    @rdesc  TRUE = Success, FALSE = Failure.
    @comm
    @xref
*/
BOOL FMD_EraseBlock(BLOCK_ID blockID)
{
	BOOL    bRet = TRUE;

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	bRet = FMD_LB_EraseBlock(blockID);

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bRet;
}

BOOL FMD_Deinit(PVOID hFMD)
{
    return(TRUE);
}


/*
    @func   BOOL | FMD_GetInfo | Provides information on the NAND flash.
    @rdesc  TRUE = Success, FALSE = Failure.
    @comm
    @xref
*/
BOOL FMD_GetInfo(PFlashInfo pFlashInfo)
{
	BOOL bReturn = TRUE;
	
	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    if(pFlashInfo == NULL)
    {
    	FMDERR(TRUE, (TEXT("pFlashInfo is NULL.\r\n")));
    	
    	bReturn = FALSE;
    	goto CleanUp;
    }

	if(g_pNANDSpec == NULL)
	{
		bReturn = FALSE;
		goto CleanUp;
	}
	
	
    pFlashInfo->flashType = NAND;
    pFlashInfo->dwNumBlocks = g_pNANDSpec->usNumberOfBlocks;
    pFlashInfo->wSectorsPerBlock = g_pNANDSpec->usPagesPerBlock;
    pFlashInfo->wDataBytesPerSector = g_pNANDSpec->usMainBytesPerPage;
    pFlashInfo->dwBytesPerBlock = (pFlashInfo->wSectorsPerBlock * pFlashInfo->wDataBytesPerSector);


	// STEPLDR block
	g_dwNumberOfPagesSTEPLDR = STEPLDR_LENGTH / g_pNANDSpec->usMainBytesPerPage;
	g_dwNumberOfPagesSTEPLDR += (STEPLDR_LENGTH % g_pNANDSpec->usMainBytesPerPage) ? 1 : 0;

	g_dwNumberOfBlocksSTEPLDR = g_dwNumberOfPagesSTEPLDR / g_pNANDSpec->usPagesPerBlock;
	g_dwNumberOfBlocksSTEPLDR += (g_dwNumberOfPagesSTEPLDR % g_pNANDSpec->usPagesPerBlock) ? 1 : 0;

	
	FMDMSG(FMD_INFO, (TEXT("-- NAND flash memory information --\r\n")));
	FMDMSG(FMD_INFO, (TEXT("Manufacturer code         : 0x%2X\r\n"), g_pNANDSpec->ucMID));
	FMDMSG(FMD_INFO, (TEXT("Device code               : 0x%2X\r\n"), g_pNANDSpec->ucDID));
	FMDMSG(FMD_INFO, (TEXT("Number of Blocks          : %d\r\n"), pFlashInfo->dwNumBlocks));
	FMDMSG(FMD_INFO, (TEXT("Sectors per block         : %d\r\n"), pFlashInfo->wSectorsPerBlock));
	FMDMSG(FMD_INFO, (TEXT("Bytes per sector          : %d\r\n"), pFlashInfo->wDataBytesPerSector));
	FMDMSG(FMD_INFO, (TEXT("Bytes per block           : %d\r\n"), pFlashInfo->dwBytesPerBlock));

	FMDMSG(FMD_INFO, (TEXT("Number of STEPLDR pages   : %d\r\n"), g_dwNumberOfPagesSTEPLDR));
	FMDMSG(FMD_INFO, (TEXT("Number of STEPLDR blocks  : %d\r\n"), g_dwNumberOfBlocksSTEPLDR));




	
CleanUp:

   
	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return TRUE;
}

#if MAGNETO
BOOL  FMD_GetInfoEx(PFlashInfoEx pFlashInfo, PDWORD pdwNumRegions)
{
	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    if (!pdwNumRegions)
    {
        return FALSE;
    }

    if (!pFlashInfo)
    {
        // Return required buffer size to caller
        *pdwNumRegions = g_dwNumRegions;
        return TRUE;
    }

    if (*pdwNumRegions < g_dwNumRegions)
    {
        *pdwNumRegions = g_dwNumRegions;
        DEBUGMSG (1, (TEXT("FMD_GetInfoEx: Insufficient buffer for number of regions")));
        return FALSE;
    }

    memcpy (pFlashInfo->region, g_pRegionTable, g_dwNumRegions * sizeof(FlashRegion));

    // Temp
    for (DWORD iRegion = 0; iRegion < g_dwNumRegions; iRegion++) {
        RETAILMSG(1, (L"Type=%d, StartP=0x%x, NumP=0x%x, NumL=0x%x, Sec/Blk=0x%x, B/Blk=0x%x, Compact=%d.\r\n",
            g_pRegionTable[iRegion].regionType,
            g_pRegionTable[iRegion].dwStartPhysBlock,
            g_pRegionTable[iRegion].dwNumPhysBlocks,
            g_pRegionTable[iRegion].dwNumLogicalBlocks,
            g_pRegionTable[iRegion].dwSectorsPerBlock,
            g_pRegionTable[iRegion].dwBytesPerBlock,
            g_pRegionTable[iRegion].dwCompactBlocks));

    }

    *pdwNumRegions = g_dwNumRegions;

    pFlashInfo->cbSize                  = sizeof(FlashInfoEx);
    pFlashInfo->flashType = NAND;
    pFlashInfo->dwNumBlocks = g_pNANDSpec->usNumberOfBlocks;
    pFlashInfo->dwDataBytesPerSector = g_pNANDSpec->usMainBytesPerPage;
    pFlashInfo->dwNumRegions            = g_dwNumRegions;

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return(TRUE);
}


#if 0
BOOL FMD_GetOEMReservedByte(SECTOR_ADDR physicalSectorAddr, PBYTE pOEMReserved)
{
	FMD_LB_GetOEMReservedByte( physicalSectorAddr,  pOEMReserved);

    return TRUE;
}

//  FMD_SetOEMReservedByte
//
//  Sets the OEM reserved byte (for metadata) for the specified physical sector.
//
BOOL FMD_SetOEMReservedByte(SECTOR_ADDR physicalSectorAddr, BYTE bOEMReserved)
{
    BOOL bRet = TRUE;

	bRet = FMD_LB_SetOEMReservedByte(physicalSectorAddr, bOEMReserved);

    return bRet;
}
#endif


#endif // MAGNETO



#ifndef NOSYSCALL
//  We don't have to build the following interface functions for the
//  bootloader.
//

//  FMD_PowerUp
//
//  Performs any necessary powerup procedures...
//
VOID FMD_PowerUp(VOID)
{
	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    // Set up initial flash controller configuration.
    v_pNFCONregs->NFCONF = g_dwNFCONF;
    v_pNFCONregs->NFCONT = g_dwNFCONT;
    v_pNFCONregs->NFSTAT = (1<<4);
	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}

//  FMD_PowerDown
//
//  Performs any necessary powerdown procedures...
//
VOID FMD_PowerDown(VOID)
{
	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	g_dwNFCONF = v_pNFCONregs->NFCONF;
	g_dwNFCONT = v_pNFCONregs->NFCONT;
	
	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
}

//  FMD_OEMIoControl
//
//  Used for any OEM defined IOCTL operations
//
BOOL FMD_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned)
{
	BOOL bReturn = TRUE;
	
#if    MAGNETO
    BSP_ARGS *pBSPArgs = ((BSP_ARGS *) IMAGE_SHARE_ARGS_UA_START);
#endif

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    switch(dwIoControlCode)
    {

        case IOCTL_FMD_GET_INTERFACE:
        {
        	FMDMSG(FMD_DBG, (TEXT("IOCTL_FMD_GET_INTERFACE\r\n")));

            if (!pOutBuf || nOutBufSize < sizeof(FMDInterface))
            {
            	FMDERR(TRUE, (TEXT("An invalid OEMIoControl parameter\r\n")));
            	bReturn = FALSE;
            	break;
            }

            PFMDInterface pInterface = (PFMDInterface)pOutBuf;

            pInterface->cbSize = sizeof(FMDInterface);
            pInterface->pInit = FMD_Init;
            pInterface->pDeInit = FMD_Deinit;
            pInterface->pGetInfo = FMD_GetInfo;
//            pInterface->pGetInfoEx = FMD_GetInfoEx;
            pInterface->pGetInfoEx = NULL;
            pInterface->pGetBlockStatus = FMD_GetBlockStatus;
            pInterface->pSetBlockStatus = FMD_SetBlockStatus;
            pInterface->pReadSector = FMD_ReadSector;
            pInterface->pWriteSector = FMD_WriteSector;
            pInterface->pEraseBlock = FMD_EraseBlock;
            pInterface->pPowerUp = FMD_PowerUp;
            pInterface->pPowerDown = FMD_PowerDown;
            pInterface->pGetPhysSectorAddr = NULL;

            break;
        }

    	case IOCTL_FMD_LOCK_BLOCKS:
	        FMDMSG(FMD_DBG, (TEXT("IOCTL_FMD_LOCK_BLOCKS is not supported.\r\n")));
	        bReturn = FALSE;
			break;
			
	    case IOCTL_FMD_UNLOCK_BLOCKS:
	        FMDMSG(FMD_DBG,  (TEXT("IOCTL_FMD_UNLOCK_BLOCKS is not supported.\r\n")));
	        bReturn = FALSE;
	        break;

	    case IOCTL_FMD_READ_RESERVED:
	        FMDMSG(FMD_DBG,  (TEXT("IOCTL_FMD_READ_RESERVED is not supported.\r\n")));
	        bReturn = FALSE;
	        break;
	        
	    case IOCTL_FMD_WRITE_RESERVED:
	        FMDMSG(FMD_DBG,  (TEXT(" IOCTL_FMD_WRITE_RESERVED is not supported.\r\n")));
	        bReturn = FALSE;
	        break;
	        
	    case IOCTL_FMD_GET_RESERVED_TABLE:
	        FMDMSG(FMD_DBG,  (TEXT(" IOCTL_FMD_GET_RESERVED_TABLE is not supported.\r\n")));
	        bReturn = FALSE;
	        break;

	    case IOCTL_FMD_GET_XIPMODE:
	        FMDMSG(FMD_DBG,  (TEXT(" IOCTL_FMD_GET_XIPMODE is not supported.\r\n")));
	        bReturn = FALSE;
	        break;	        
	        
	    case IOCTL_FMD_SET_SECTORSIZE:
	        FMDMSG(FMD_DBG,  (TEXT(" IOCTL_FMD_SET_SECTORSIZE is not supported.\r\n")));
	        bReturn = FALSE;
	        break;
	        
	    case IOCTL_FMD_RAW_WRITE_BLOCKS:
	        FMDMSG(FMD_DBG,  (TEXT(" IOCTL_FMD_RAW_WRITE_BLOCKS is not supported.\r\n")));
	        bReturn = FALSE;
	        break;
	        
	    case IOCTL_FMD_GET_RAW_BLOCK_SIZE:
	        FMDMSG(FMD_DBG,  (TEXT(" IOCTL_FMD_GET_RAW_BLOCK_SIZE is not supported.\r\n")));
	        bReturn = FALSE;
	        break;
	        
	    case IOCTL_FMD_GET_INFO:
	        FMDMSG(FMD_DBG,  (TEXT(" IOCTL_FMD_GET_INFO is not supported.\r\n")));
	        bReturn = FALSE;
	        break;
	        
	    case  IOCTL_FMD_SET_XIPMODE    :
	        FMDMSG(FMD_DBG,  (TEXT(" IOCTL_FMD_SET_XIPMODE is not supported.\r\n")));
	        bReturn = FALSE;
	        break;

	    case  IOCTL_DISK_FLUSH_CACHE:
	        FMDMSG(FMD_DBG, (TEXT("[FMD] FMD_OEMIoControl() : IOCTL_DISK_FLUSH_CACHE\r\n")));
			break;

			
	    case IOCTL_DISK_GET_STORAGEID:
	        FMDMSG(FMD_DBG, (TEXT("[FMD:ERR] FMD_OEMIoControl() : IOCTL_DISK_GET_STORAGEID is unsupported\r\n")));
			break;

		default:
			FMDERR(TRUE, (TEXT("Unknown IOCTL = 0x%X\r\n"), dwIoControlCode));
			bReturn = FALSE;
			break;
	}



	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

	return bReturn;
	




        
#if 0
        case IOCTL_FMD_LOCK_BLOCKS:
            // LOCK is not supported.!!!!
            BlockLockInfo * pLockInfo;
            pLockInfo = (BlockLockInfo *)pInBuf;
            RETAILMSG(1, (TEXT("IOCTL_FMD_LOCK_BLOCKS!!!!(0x%x,0x%x) \r\n"), pLockInfo->StartBlock, pLockInfo->NumBlocks));

            if ( IS_LB )        // Large Block
            {
                if ( READ_REGISTER_BYTE(pNFSBLK) >> 6 < (ULONG)(pLockInfo->StartBlock + pLockInfo->NumBlocks) )
                    WRITE_REGISTER_USHORT(pNFSBLK, (pLockInfo->StartBlock + pLockInfo->NumBlocks)<<6);
            }
            else    // Small Block
            {
                if ( READ_REGISTER_BYTE(pNFSBLK) >> 5 < (ULONG)(pLockInfo->StartBlock + pLockInfo->NumBlocks)*8 )
                {
//                    RETAILMSG(1, (TEXT("Write value (0x%x) \r\n"), ((ULONG)(pLockInfo->StartBlock + pLockInfo->NumBlocks)*8)<<5));
                    WRITE_REGISTER_ULONG(pNFSBLK, ((ULONG)(pLockInfo->StartBlock + pLockInfo->NumBlocks)*8)<<5);
//                    RETAILMSG(1, (TEXT("Read value  (0x%x) \r\n"), READ_REGISTER_ULONG(pNFSBLK)));
                }
            }
            pBSPArgs->nfsblk = pLockInfo->StartBlock + pLockInfo->NumBlocks;

            break;
        case IOCTL_FMD_UNLOCK_BLOCKS:
            RETAILMSG(1, (TEXT("IOCTL_FMD_UNLOCK_BLOCKS!!!!(0x%x,0x%x) \r\n"), pLockInfo->StartBlock, pLockInfo->NumBlocks));
            RETAILMSG(1, (TEXT("IOCTL_FMD_UNLOCK_BLOCKS is not supported!!!! \r\n")));
            return(FALSE);
#endif


}

#endif // NOSYSCALL



/*
    @func   DWORD | FMD_GetBlockStatus | Returns the status of the specified block.
    @rdesc  Block status (see fmd.h).
    @comm
    @xref
*/
DWORD FMD_GetBlockStatus(BLOCK_ID blockID)
{
    DWORD dwResult = 0;

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	dwResult = FMD_LB_GetBlockStatus(blockID);

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return dwResult;
}



/*
    @func   BOOL | FMD_SetBlockStatus | Marks the block with the specified block status.
    @rdesc  TRUE = Success, FALSE = Failure.
    @comm
    @xref
*/
BOOL FMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus)
{
    BOOL    bRet = TRUE;

	bRet = FMD_LB_SetBlockStatus(blockID, dwStatus);

    return bRet;
}





BOOL FMD_LB_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    DWORD   i;
    BOOL    bReturn = TRUE;
    UINT32 nSpareAddr = BYTES_PER_SECTOR;
    UINT32 dwColAddr = 0;

	DWORD dwECC1[4];
	DWORD dwECCLoop;
	DWORD dwTemp;

	// 2 back-up copies are used for wReserved2 and bOEMReserved (SectorInfo)
	DWORD dwSectorInfo[3][2];
	
	DWORD dwBuf[(ECC8_SPARE_MSG_LENGTH + 16) / 4];				// Including 13-bytes 8-bit ECC itself
	
	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));


    NF_nFCE_L();

    if (!pSectorBuff && !pSectorInfoBuff)
    {
    	FMDERR(TRUE, (TEXT("pSectorBuff and pSectorInfoBuff are NULL.\r\n")));
    	bReturn = FALSE;
    	goto CleanUp;
    }

    if(dwNumSectors > 1)
    {
        FMDERR(TRUE, (TEXT("FMD doesn't support writing multiple sectors at once. dwNumSectors must be same as 1.\r\n")));
        bReturn = FALSE;
        goto CleanUp;
    }


	// STEPLDR has a different spare area layout
	if(startSectorAddr < g_dwNumberOfPagesSTEPLDR)
	{
		bReturn = FMD_LB_ReadSector_Steploader(startSectorAddr, pSectorBuff, pSectorInfoBuff, dwNumSectors);
		goto CleanUp;
	}
		

    NF_CLEAR_RB();

    NF_CMD(CMD_READ);                            // Send read command.

    NF_ADDR(nSpareAddr & 0xFF);
    NF_ADDR((nSpareAddr >> 8) & 0xFF);
    NF_ADDR(startSectorAddr & 0xFF);
    NF_ADDR((startSectorAddr >> 8) & 0xFF);
    if (NEED_EXT_ADDR)
        NF_ADDR((startSectorAddr >> 16) & 0xFF);

    NF_CMD(CMD_READ3);							// 2nd command

    NF_DETECT_RB();								// Wait for ready to read

	// tRR
    NF_CLEAR_RB();
    NF_CLEAR_RB();

    
	// read SectorInfo
	// For the performance, read 4bytes at once.
	// 1st copy of SectorInfo
	dwSectorInfo[0][0]	= NF_RDDATA_WORD();
	dwSectorInfo[0][1]	= NF_RDDATA_WORD();
		
	dwSectorInfo[1][0]	= NF_RDDATA_WORD();
	dwSectorInfo[1][1]	= NF_RDDATA_WORD();

	dwSectorInfo[2][0]	= NF_RDDATA_WORD();
	dwSectorInfo[2][1]	= NF_RDDATA_WORD();


	if(pSectorInfoBuff != NULL)
	{
		// bBadBlock(1)      bOEMReserved(1)     wReserved2(2)     dwReserved1(4)
		if(dwSectorInfo[0][0] == dwSectorInfo[1][0] && dwSectorInfo[0][1] == dwSectorInfo[1][1])
		{
			pSectorInfoBuff->bBadBlock		= (BYTE)((dwSectorInfo[0][0]) & 0xFF);
			pSectorInfoBuff->bOEMReserved	= (BYTE)((dwSectorInfo[0][0] >> 8) & 0xFF);
			pSectorInfoBuff->wReserved2		= (WORD)((dwSectorInfo[0][0] >> 16) & 0xFFFF);
			pSectorInfoBuff->dwReserved1	= (DWORD)(dwSectorInfo[0][1]);
		}
		else if(dwSectorInfo[0][0] == dwSectorInfo[2][0] && dwSectorInfo[0][1] == dwSectorInfo[2][1])
		{
			pSectorInfoBuff->bBadBlock		= (BYTE)((dwSectorInfo[0][0]) & 0xFF);
			pSectorInfoBuff->bOEMReserved	= (BYTE)((dwSectorInfo[0][0] >> 8) & 0xFF);
			pSectorInfoBuff->wReserved2		= (WORD)((dwSectorInfo[0][0] >> 16) & 0xFFFF);
			pSectorInfoBuff->dwReserved1	= (DWORD)(dwSectorInfo[0][1]);
		}
		else if(dwSectorInfo[1][0] == dwSectorInfo[2][0] && dwSectorInfo[1][1] == dwSectorInfo[2][1])
		{
			pSectorInfoBuff->bBadBlock		= (BYTE)((dwSectorInfo[1][0]) & 0xFF);
			pSectorInfoBuff->bOEMReserved	= (BYTE)((dwSectorInfo[1][0] >> 8) & 0xFF);
			pSectorInfoBuff->wReserved2		= (WORD)((dwSectorInfo[1][0] >> 16) & 0xFFFF);
			pSectorInfoBuff->dwReserved1	= (BYTE)(dwSectorInfo[1][1]);
		}
		else
		{
		   	FMDERR(TRUE, (TEXT("SectorInfo is invalid, sector = %d\r\n"), startSectorAddr));
        	bReturn = FALSE;
        	goto CleanUp;
		}

		// Bad block check
		// If pSectorBuff is NULL, we assume that this function call is only for checking the sector status.
		if(pSectorInfoBuff->bBadBlock != 0xFF)
		{
			// A bad block
			FMDMSG(FMD_INFO, (TEXT("Bad sector %d\r\n"), startSectorAddr));
			
			bReturn = FALSE;
			goto CleanUp;
		}
	}
	else
	{
		// Nothing to do
	}


    if (!pSectorBuff)
    {
    	// No need to read main data
    	goto CleanUp;
    }


	// start of spare ECC decoding
	NF_ECC8_SETUP(ECC8_SPARE_MSG_LENGTH);
	NF_ECC8_CLEAR_DONE();
	NF_ECC8_DECODE();
	NF_MECC_UnLock();


	// 1-bit ECC for main data
	dwBuf[0] = NF_RDDATA_WORD();
	dwBuf[1] = NF_RDDATA_WORD();
	dwBuf[2] = NF_RDDATA_WORD();
	dwBuf[3] = NF_RDDATA_WORD();

	// 8-bit ECC  (Spare data ECC)
	dwBuf[4] = NF_RDDATA_WORD();
	dwBuf[5] = NF_RDDATA_WORD();
	dwBuf[6] = NF_RDDATA_WORD();
	dwBuf[7] = NF_RDDATA_BYTE();

	// end of spare ECC decoding
	NF_ECC8_WAIT_DECODE_DONE();
	NF_ECC8_CLEAR_DECODE_DONE();
	NF_MECC_Lock();
	
	// check & correct ECC errors
	if(ECC_CorrectData(startSectorAddr, (LPBYTE)dwBuf, ECC_CORRECT_SPARE) == FALSE)
	{
		bReturn = FALSE;
		goto CleanUp;
	}	

	// 1-bit ECC / 512bytes  (Main data ECC)
    dwECC1[0] = dwBuf[0];
	dwECC1[1] = dwBuf[1];
	dwECC1[2] = dwBuf[2];
	dwECC1[3] = dwBuf[3];



	NF_CMD(CMD_RDO);    // Send read command.
    NF_ADDR((dwColAddr) & 0xff);
    NF_ADDR((dwColAddr >> 8) & 0xff);
    NF_CMD(CMD_RDO2);   // 2nd command


	NF_ECC1_SETUP();

	for(dwECCLoop = 0;dwECCLoop < (DWORD)(BYTES_PER_SECTOR >> 9);dwECCLoop++)
	{
	    NF_RSTECC();
	    NF_MECC_UnLock();
	
		if((DWORD)pSectorBuff & 0x3)
		{
			// Un-aligned buffer address
			FMDMSG(FMD_INFO, (TEXT("Unaligned buffer for WriteSector, 0x%X\r\n"), pSectorBuff));
			
			for(i = 0;i < 512;i += 4)
			{
				dwTemp = NF_RDDATA_WORD();

				*pSectorBuff = (BYTE)(dwTemp & 0xFF);			pSectorBuff++;
				*pSectorBuff = (BYTE)((dwTemp >> 8) & 0xFF);	pSectorBuff++;
				*pSectorBuff = (BYTE)((dwTemp >> 16) & 0xFF);	pSectorBuff++;
				*pSectorBuff = (BYTE)((dwTemp >> 24) & 0xFF);	pSectorBuff++;
			}
		}
		else
		{	
	    	RdPage512(pSectorBuff);                    // Read page/sector data.
	    	pSectorBuff += 512;
		}

		NF_MECC_Lock();

	    NF_WRMECCD0((((dwECC1[dwECCLoop] >> 8) & 0xFF) << 16) | (dwECC1[dwECCLoop] & 0xff));
	    NF_WRMECCD1((((dwECC1[dwECCLoop] >> 24) & 0xFF) << 16) | (dwECC1[dwECCLoop] >> 16) & 0xFF);
	   
		if(ECC_CorrectData(startSectorAddr, pSectorBuff - 512, ECC_CORRECT_MAIN) == FALSE)
		{
	        FMDERR(TRUE, (TEXT("ECC error, Sector = %d, NFECCERR0 = 0x%X\r\n"), startSectorAddr,  NF_ECC_ERR0));

			FMDERR(TRUE, (TEXT("pSectorBuff = 0x%X, pSectorInfoBuff = 0x%X, dwNumSectors = 0x%X\r\n"),
							pSectorBuff, pSectorInfoBuff, dwNumSectors));


			FMDERR(1, (TEXT("ECC = 0x%X  0x%X  0x%X  0x%X\r\n"), dwECC1[0], dwECC1[1], dwECC1[2], dwECC1[3]));

			RETAILMSG(1, (TEXT("--------------------- MAIN ---------------------\r\n")));
			
			for(i = 1;i <= 2048;i++)
			{
				RETAILMSG(1, (TEXT("%02X "), pSectorBuff[i - 1]));
			
				if((i % 0x10) == 0)
				{
					RETAILMSG(1, (TEXT("\r\n")));
				}
			}

			
	        bReturn = FALSE;
	        goto CleanUp;
		}

	}


CleanUp:

    NF_nFCE_H();

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bReturn;
}


BOOL RAW_LB_ReadSector(UINT32 startSectorAddr, LPBYTE pSectorBuff, LPBYTE pSectorInfoBuff)
{
	BOOL bReturn = TRUE;
	
    UINT32 nPageAddr = startSectorAddr;
    DWORD  i;
    UINT32 nRetEcc = 0;
    UINT32 dwColAddr = 0;

	DWORD *pdwMainData = (DWORD *)pSectorBuff;
	DWORD *pdwSpareData = (DWORD *)pSectorInfoBuff;

	DWORD dwTemp;
	

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));


	RETAILMSG(1, (TEXT("pSectorInfoBuff = 0x%X   pdwSpareData = 0x%X\r\n"), pSectorInfoBuff, pdwSpareData));
	

	if(pSectorBuff == NULL && pSectorInfoBuff == NULL)
	{
    	FMDERR(TRUE, (TEXT("pSectorBuff and pSectorInfoBuff are NULL.\r\n")));
		bReturn = FALSE;
		goto CleanUp;
	}

    if(pSectorBuff == NULL)
    {
    	dwColAddr = BYTES_PER_SECTOR;
    }

    NF_nFCE_L();

    NF_CLEAR_RB();

    NF_CMD(CMD_READ);                           // Send read command.

    NF_ADDR((dwColAddr)&0xff);
    NF_ADDR((dwColAddr>>8)&0xff);
    NF_ADDR((nPageAddr)&0xff);
    NF_ADDR((nPageAddr>>8)&0xff);
    if (NEED_EXT_ADDR)
        NF_ADDR((nPageAddr>>16)&0xff);

    NF_CMD(CMD_READ3);  // 2nd command
    NF_DETECT_RB();                             // Wait for command to complete.


	if(pSectorBuff != NULL)
	{
		for(i = 0;i < (DWORD)(g_pNANDSpec->usMainBytesPerPage >> 2);i++)
		{
			*(pdwMainData + i) = (DWORD)NF_RDDATA_WORD();
		}
	}
		
	if(pSectorInfoBuff != NULL)
	{
		if((DWORD)pSectorInfoBuff & 0x3)
		{
			// Un-aligned buffer address
			FMDMSG(FMD_INFO, (TEXT("Unaligned pSectorInfoBuff for RAW_LB_ReadSector, 0x%X\r\n"), pSectorInfoBuff));
			
			for(i = 0;i < g_pNANDSpec->usSpareBytesPerPage;i += 4)
			{
				dwTemp = NF_RDDATA_WORD();

				*(pSectorInfoBuff + i) = (BYTE)(dwTemp & 0xFF);
				*(pSectorInfoBuff + i + 1) = (BYTE)((dwTemp >> 8) & 0xFF);
				*(pSectorInfoBuff + i + 2) = (BYTE)((dwTemp >> 16) & 0xFF);
				*(pSectorInfoBuff + i + 3) = (BYTE)((dwTemp >> 24) & 0xFF);
			}
		}
		else
		{	
			for(i = 0;i < (DWORD)(g_pNANDSpec->usSpareBytesPerPage >> 2);i++)
			{
				*(pdwSpareData + i) = (DWORD)NF_RDDATA_WORD();
			}
		}
	}
	

CleanUp:

    NF_nFCE_H();

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bReturn;
}





BOOL RAW_LB_WriteSector(UINT32 startSectorAddr, LPBYTE pSectorBuff, LPBYTE pSectorInfoBuff)
{
	BOOL bReturn = TRUE;
	
    UINT32 nPageAddr = startSectorAddr;
    DWORD  i;
    UINT32 nRetEcc = 0;
    UINT32 dwColAddr = 0;

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	if(pSectorBuff == NULL && pSectorInfoBuff == NULL)
	{
		bReturn = FALSE;
		goto CleanUp;
	}

    NF_nFCE_L();

    NF_CLEAR_RB();

    NF_CMD(CMD_WRITE);                           // Send write command.

    NF_ADDR((dwColAddr)&0xff);
    NF_ADDR((dwColAddr>>8)&0xff);
    NF_ADDR((nPageAddr)&0xff);
    NF_ADDR((nPageAddr>>8)&0xff);
    if (NEED_EXT_ADDR)
        NF_ADDR((nPageAddr>>16)&0xff);

	
	if(pSectorBuff != NULL)
	{
		DWORD *pdwMainData = (DWORD *)pSectorBuff;
		for(i = 0;i < (DWORD)(g_pNANDSpec->usMainBytesPerPage >> 2);i++)
		{
			NF_WRDATA_WORD(*(pdwMainData + i));
		}
	}

	if(pSectorInfoBuff != NULL)
	{
		DWORD *pdwSpareData = (DWORD *)pSectorInfoBuff;
		for(i = 0;i < (DWORD)(g_pNANDSpec->usSpareBytesPerPage >> 2);i++)
		{
			NF_WRDATA_WORD(*(pdwSpareData + i));
		}
	}

    NF_CMD(CMD_WRITE2);			// 2nd command
    NF_DETECT_RB();				// Wait for command to complete.

	

CleanUp:

    NF_nFCE_H();

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bReturn;
}



BOOL FMD_LB_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    DWORD   i;
    BOOL    bReturn = TRUE;
    volatile UINT32 wrdata;
    UINT32 dwColAddr = 0;
    UINT32 nPageAddr = startSectorAddr;


	DWORD dwECCLoop;
	DWORD dwECC1[4];			// 1bit ECC x 4(each 512 bytes of main data)
	//DWORD dwECC1;

	DWORD dwTemp;

	
	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    //  Sanity check
    if (!pSectorBuff && !pSectorInfoBuff)
    {
    	FMDERR(TRUE, (TEXT("pSectorBuff and pSectorInfoBuff are NULL.\r\n")));
    	bReturn = FALSE;
    	goto CleanUp;
    }
    
    if ( dwNumSectors > 1 )
    {
        FMDERR(TRUE, (TEXT("FMD doesn't support writing multiple sectors at once. dwNumSectors must be same as 1.\r\n")));
        bReturn = FALSE;
        goto CleanUp;
    }

	if(pSectorBuff == NULL)
    {
		// An empty 1-bit MECC because of no main data
    	dwECC1[0] = 0xFFFFFFFF;
    	dwECC1[1] = 0xFFFFFFFF;
    	dwECC1[2] = 0xFFFFFFFF;
    	dwECC1[3] = 0xFFFFFFFF;
			
    	dwColAddr = BYTES_PER_SECTOR;
    }


    //  Enable Chip
    NF_nFCE_L();

	NF_CLEAR_RB();
	
    //  Issue command
    NF_CMD(CMD_WRITE);

    //  Setup address
    NF_ADDR((dwColAddr)&0xff);
    NF_ADDR((dwColAddr>>8)&0xff);
    NF_ADDR((nPageAddr)&0xff);
    NF_ADDR((nPageAddr>>8)&0xff);
    if (NEED_EXT_ADDR)
        NF_ADDR((nPageAddr>>16)&0xff);


	// Write main data (2KBytes) and encode 1-bit ECC for the main data
	if(pSectorBuff != NULL)
	{
		NF_ECC1_SETUP();

		// 1-bit ECC / 512bytes main data
		for(dwECCLoop = 0;dwECCLoop < (DWORD)(BYTES_PER_SECTOR >> 9);dwECCLoop++)
		{
		    //  Initialize ECC register
		    NF_RSTECC();
		    NF_MECC_UnLock();

			if((DWORD)pSectorBuff & 0x3)
			{
				// Un-aligned buffer address
				FMDMSG(FMD_INFO, (TEXT("Unaligned buffer for WriteSector, 0x%X\r\n"), pSectorBuff));
		
				for(i = 0;i < 512;i += 4)
				{
					wrdata = *pSectorBuff;			pSectorBuff++;
					wrdata |= *pSectorBuff << 8;	pSectorBuff++;
					wrdata |= *pSectorBuff << 16;	pSectorBuff++;
					wrdata |= *pSectorBuff << 24;	pSectorBuff++;

					NF_WRDATA_WORD(wrdata);
				}
			}
			else
			{	
				WrPage512(pSectorBuff);

				pSectorBuff += 512;
			}

			//  Read out the ECC value generated by HW
			NF_MECC_Lock();

			dwECC1[dwECCLoop] = NF_RDMECC0();
		}
	}




	// SectorInfo is not included for calculating the spare ECC.

	// wReserved2 is written multiple times for a sector write by FAL so that a related ECC is changed.
	// But, the changed ECC can't be written without erasing the block.
	// Because of this, wReserved2 is excluded from generating an ECC for spare data.
	// Intead of using the ECC, two of copies are used for reliability.
	
	if(pSectorInfoBuff)
	{
		// bBadBlock(1)      bOEMReserved(1)     wReserved2(2)     dwReserved1(4)

		dwTemp = (pSectorInfoBuff->wReserved2 << 16) | (pSectorInfoBuff->bOEMReserved << 8) | pSectorInfoBuff->bBadBlock;

		// 1st copy
		NF_WRDATA_WORD(dwTemp);
		NF_WRDATA_WORD(pSectorInfoBuff->dwReserved1);

		// 2nd copy
		NF_WRDATA_WORD(dwTemp);
		NF_WRDATA_WORD(pSectorInfoBuff->dwReserved1);

		// 3rd copy
		NF_WRDATA_WORD(dwTemp);
		NF_WRDATA_WORD(pSectorInfoBuff->dwReserved1);
	}
	else
	{
		// This code is only for exception handling.
		// 
		//
		// Windows Mobile Documentation says,
		// 
		//	pSectorInfoBuff 
		//		[in] Buffer for an array of sector information structures. 
		//		There must be one sector information entry for each sector that is to be written. 
		//		Set to NULL if this data is not written.


		// 1st copy
		NF_WRDATA_WORD(0xFFFFFFFF);
		NF_WRDATA_WORD(0xFFFFFFFF);

		// 2nd copy
		NF_WRDATA_WORD(0xFFFFFFFF);
		NF_WRDATA_WORD(0xFFFFFFFF);

		// 3rd copy
		NF_WRDATA_WORD(0xFFFFFFFF);
		NF_WRDATA_WORD(0xFFFFFFFF);
	}

	// Write spare data	and 8-bit ECC
	if(pSectorBuff != NULL)
	{
		NF_ECC8_SETUP(ECC8_SPARE_MSG_LENGTH);
		NF_ECC8_CLEAR_DONE();
		NF_ECC8_ENCODE();
		// Start ECC encoding
		NF_MECC_UnLock();

		NF_WRDATA_WORD(dwECC1[0]);								// 1-bit MECC
		NF_WRDATA_WORD(dwECC1[1]);								// 1-bit MECC
		NF_WRDATA_WORD(dwECC1[2]);								// 1-bit MECC
		NF_WRDATA_WORD(dwECC1[3]);								// 1-bit MECC
		
		// wait for encoding done
		NF_ECC8_WAIT_ENCODE_DONE();
		// clear EncodeDone
		NF_ECC8_CLEAR_ENCODE_DONE();
		// Finish ECC encoding
		NF_MECC_Lock();

		// Write 8-bit ECC (13Bytes)		
		NF_WRDATA_WORD(v_pECCregs->NFECCPRGECC0);
		NF_WRDATA_WORD(v_pECCregs->NFECCPRGECC1);
		NF_WRDATA_WORD(v_pECCregs->NFECCPRGECC2);
		NF_WRDATA_BYTE(v_pECCregs->NFECCPRGECC3);		
	}
	else
	{
		// If the main data buffer is empty, the data will be written later by FAL.
		// Because the NAND flash memory can be written from 1 to 0 (only one time),
		//   the spare ECC data must not be written.
	}

	// TODO : Is the dirty bit needed? (we are using 8-bit ECC for the spare area)

    //  Finish up the write operation
    NF_CMD(CMD_WRITE2);

    //  Wait for RB
    NF_DETECT_RB();     // Wait tR(max 12us)

    if(NF_RDSTAT & STATUS_ILLACC)
    {
        FMDERR(TRUE, (TEXT("Failed to write a sector #%d. (ILLEGAL ACCESS)\r\n"), startSectorAddr));
		v_pNFCONregs->NFSTAT =  STATUS_ILLACC;    // Write 1 to clear.
		bReturn = FALSE;
		goto CleanUp;
    }
    else
    {
        //  Check the status of program
        NF_CMD(CMD_STATUS);

        if(NF_RDDATA_BYTE() & STATUS_ERROR)
        {
            FMDERR(1, (TEXT("Failed to write a sector #%d. (STATUS FAIL)\r\n"), startSectorAddr));
            bReturn = FALSE;
            goto CleanUp;
        }
    }


CleanUp:

    //  Disable the chip
    NF_nFCE_H();

  
	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));
    return bReturn;
}



BOOL FMD_LB_EraseBlock(BLOCK_ID blockID)
{
    BOOL bReturn = TRUE;
    DWORD dwPage;

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	if(g_pNANDSpec == NULL)
	{
		FMDERR(TRUE, (TEXT("No NAND flash memory was detected.\r\n")));

		bReturn = FALSE;
		goto CleanUp;
	}

	dwPage = blockID * g_pNANDSpec->usPagesPerBlock;

    //  Enable the chip
    NF_nFCE_L();                        // Select the flash chip.

    NF_CLEAR_RB();

    //  Issue command
    NF_CMD(CMD_ERASE);

    //  Set up address
    NF_ADDR((dwPage) & 0xff);
    NF_ADDR((dwPage >> 8) & 0xff);
    if (NEED_EXT_ADDR)
        NF_ADDR((dwPage >> 16) & 0xff);

    //  Complete erase operation
    NF_CMD(CMD_ERASE2);

    //  Wait for ready bit
    NF_DETECT_RB();     // Wait tR(max 12us)

    if ( NF_RDSTAT & STATUS_ILLACC )
    {
        FMDERR(TRUE, (TEXT("Failed to erase a block #%d. (ILLEGAL ACCESS)\r\n"), blockID));
        v_pNFCONregs->NFSTAT =  STATUS_ILLACC;    // Write 1 to clear.
        bReturn = FALSE;
    }
    else
    {
        //  Check the status
        NF_CMD(CMD_STATUS);

        if( NF_RDDATA_BYTE() & STATUS_ERROR)
        {
            FMDERR(1, (TEXT("Failed to erase a block #%d. (STATUS FAIL)\r\n"), blockID));
            bReturn = FALSE;
        }
    }


CleanUp:

    NF_nFCE_H();                        // Select the flash chip.

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bReturn;
}


DWORD FMD_LB_GetBlockStatus(BLOCK_ID blockID)
{
	SECTOR_ADDR nPageAddr;
	SectorInfo si;
	DWORD dwResult = 0;

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	nPageAddr = blockID * SECTORS_PER_BLOCK;


    if(!FMD_LB_ReadSector(nPageAddr, NULL, &si, 1))
    {
    	dwResult = BLOCK_STATUS_BAD;
    	goto CleanUp;
    }

    if(!(si.bOEMReserved & OEM_BLOCK_READONLY))
    {
        dwResult |= BLOCK_STATUS_READONLY;
    }

#if    MAGNETO
// TODO : Block Lock
//    if( nPageAddr < pBSPArgs->nfsblk )
//    {
//        dwResult |= BLOCK_STATUS_READONLY;
//    }
#endif

    if (!(si.bOEMReserved & OEM_BLOCK_RESERVED))
    {
        dwResult |= BLOCK_STATUS_RESERVED;
    }


    if(si.bBadBlock != 0xFF)
    {
        dwResult |= BLOCK_STATUS_BAD;
    }

CleanUp:

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return dwResult;
}


BOOL FMD_LB_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus)
{
	BOOL bReturn = TRUE;
	
	DWORD dwPage = blockID * g_pNANDSpec->usPagesPerBlock;
	DWORD dwCol = BYTES_PER_SECTOR;

	BYTE bOEMReserved;
	BYTE bBadBlock;

	DWORD dwTemp;
	

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

	if(!(dwStatus & (BLOCK_STATUS_BAD | BLOCK_STATUS_READONLY | BLOCK_STATUS_RESERVED)))
	{
		FMDERR(TRUE, (TEXT("No valid status was requested to be set\r\n")));
		bReturn = FALSE;
		goto CleanUp;
	}

	// STEPLDR has a different spare area layout
	if(blockID < g_dwNumberOfBlocksSTEPLDR)
	{
		return FALSE;
	}
	
	bBadBlock = (dwStatus & BLOCK_STATUS_BAD) ? 0x0 : 0xFF;
	
	bOEMReserved = 0xFF;
	bOEMReserved &= (dwStatus & BLOCK_STATUS_READONLY) ? ~OEM_BLOCK_READONLY : 0xFF;
	bOEMReserved &= (dwStatus & BLOCK_STATUS_RESERVED) ? ~OEM_BLOCK_RESERVED : 0xFF;
	
    //  Enable chip
    NF_nFCE_L();
    NF_CLEAR_RB();

    //  Issue command
    //  We are dealing with spare area
    NF_CMD(CMD_WRITE);

    //  Set up address
    NF_ADDR(dwCol & 0xFF);
    NF_ADDR((dwCol >> 8) & 0xFF);
    NF_ADDR(dwPage & 0xFF);
    NF_ADDR((dwPage >> 8) & 0xFF);
    if (NEED_EXT_ADDR)
        NF_ADDR((dwPage >> 16) & 0xff);


	// 3 copies for SectorInfo
	dwTemp = (0xFFFF << 16) | (bOEMReserved << 8) | bBadBlock;
	
	NF_WRDATA_WORD(dwTemp);			// bBadBlock(1)  bOEMReserved(1)   wReserved2(2)
	NF_WRDATA_WORD(0xFFFFFFFF);		// dwReserved1(4)

	NF_WRDATA_WORD(dwTemp);			// bBadBlock(1)  bOEMReserved(1)   wReserved2(2)
	NF_WRDATA_WORD(0xFFFFFFFF);		// dwReserved1(4)

	NF_WRDATA_WORD(dwTemp);			// bBadBlock(1)  bOEMReserved(1)   wReserved2(2)
	NF_WRDATA_WORD(0xFFFFFFFF);		// dwReserved1(4)

			
    //  Complete the write
    NF_CMD(CMD_WRITE2);

    //  Wait for RB
    NF_DETECT_RB();     // Wait tR(max 12us)

    if(NF_RDSTAT & STATUS_ILLACC)
    {
        FMDERR(TRUE, (TEXT("Failed to set a block status #%d. (ILLEGAL ACCESS)\r\n"), blockID));
		v_pNFCONregs->NFSTAT =  STATUS_ILLACC;    // Write 1 to clear.
		bReturn = FALSE;
    }
    else
    {
        //  Check the status of program
        NF_CMD(CMD_STATUS);

        if(NF_RDDATA_BYTE() & STATUS_ERROR)
        {
            FMDERR(1, (TEXT("Failed to set a block status #%d. (STATUS FAIL)\r\n"), blockID));
            bReturn = FALSE;
        }
    }


CleanUp:
    //  Disable chip select
    NF_nFCE_H();

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bReturn;
}



#if 0
#if MAGNETO
BOOL FMD_LB_GetOEMReservedByte(SECTOR_ADDR physicalSectorAddr, PBYTE pOEMReserved)
{
    UINT32 nSpareAddr = BYTES_PER_SECTOR;
    UINT32 nPageAddr = physicalSectorAddr;

    RETAILMSG(1, (TEXT("FMD_GetOEMReservedByte 0x%x \n"), nPageAddr));
    BOOL bLastMode = SetKMode(TRUE);

    //  Enable chip select
    NF_nFCE_L();
    NF_CLEAR_RB();

    //  Issue command
    NF_CMD(CMD_READ);

    //  Set up address
    NF_ADDR((nSpareAddr+POS_OEMRESERVED)&0xff);
    NF_ADDR(((nSpareAddr+POS_OEMRESERVED)>>8)&0xff);
    NF_ADDR((nPageAddr)&0xff);
    NF_ADDR((nPageAddr>>8)&0xff);
    if (NEED_EXT_ADDR)
        NF_ADDR((nPageAddr>>16)&0xff);

    NF_CMD(CMD_READ3);

    //  Wait for the ready bit
    NF_DETECT_RB();     // Wait tR(max 12us)

    //  Read the data
    *pOEMReserved = (BYTE) NF_RDDATA_BYTE();        // read and discard

    //  Disable chip select
    NF_nFCE_H();

    SetKMode(bLastMode);
    return TRUE;

}




//  FMD_SetOEMReservedByte
//
//  Sets the OEM reserved byte (for metadata) for the specified physical sector.
//
BOOL FMD_LB_SetOEMReservedByte(SECTOR_ADDR physicalSectorAddr, BYTE bOEMReserved)
{
    BOOL   bRet = TRUE;
    UINT32 nSpareAddr = BYTES_PER_SECTOR;
    UINT32 nPageAddr = physicalSectorAddr;

    RETAILMSG(1, (TEXT("FMD_SetOEMReservedByte 0x%x \n"), nPageAddr));

    BOOL bLastMode = SetKMode(TRUE);

    //  Enable chip select
    NF_nFCE_L();
    NF_CLEAR_RB();

    //  Issue command
    NF_CMD(CMD_WRITE);

    //  Set up address
    NF_ADDR((nSpareAddr+POS_OEMRESERVED)&0xff);
    NF_ADDR(((nSpareAddr+POS_OEMRESERVED)>>8)&0xff);
    NF_ADDR((nPageAddr)&0xff);
    NF_ADDR((nPageAddr>>8)&0xff);
    if (NEED_EXT_ADDR)
        NF_ADDR((nPageAddr>>16)&0xff);

    //  Write the data
    bOEMReserved = NF_RDDATA_BYTE() ;

    //  Complete the write
    NF_CMD(CMD_WRITE2);

    //  Wait for the ready bit
    NF_DETECT_RB();     // Wait tR(max 12us)

    if ( NF_RDSTAT & STATUS_ILLACC )
    {
        RETAILMSG(1, (TEXT("FMD_LB_SetOEMReservedByte() ######## Error Programming page (Illigar Access) %d!\n"), nPageAddr));
        v_pNFCONregs->NFSTAT =  STATUS_ILLACC;    // Write 1 to clear.
           bRet = FALSE;
    }
    else
    {
        //  Check the status of program
        NF_CMD(CMD_STATUS);

        if( NF_RDDATA_BYTE() & STATUS_ERROR) {
            RETAILMSG(1, (TEXT("FMD_LB_SetOEMReservedByte() ######## Error Programming page %d!\n"), nPageAddr));
            bRet = FALSE;
        }
    }

    //  Disable chip select
    NF_nFCE_H();

    SetKMode(bLastMode);
    return bRet;
}


#endif	// MAGNETO
#endif




#if MAGNETO
static BOOL DefineLayout()
{
    PFlashRegion pRegion = NULL;
    DWORD dwBlock = 0;

    if (!FMD_GetInfo (&g_flashInfo))
        return FALSE;

    // Find the MBR to determine if there is a flash layout sector
    g_dwNumRegions = 0;


    // Find the first usuable block
    while (dwBlock < g_flashInfo.dwNumBlocks) {
        if (!(FMD_GetBlockStatus(dwBlock) & (BLOCK_STATUS_BAD | BLOCK_STATUS_RESERVED))) {
            break;
        }
        dwBlock++;
    }

    RETAILMSG(1, (TEXT("DefineLayout: dwBlock = 0x%x \r\n"), dwBlock));

    // Find the first usuable sector
    DWORD dwSector = dwBlock * g_flashInfo.wSectorsPerBlock;
    RETAILMSG(1, (TEXT("DefineLayout: dwSector = 0x%x \r\n"), dwSector));
    if (!FMD_ReadSector (dwSector, g_pFLSBuffer, NULL, 1)) {
        return FALSE;
    }

    // compare the signatures
    if (IS_VALID_BOOTSEC(g_pFLSBuffer))
    {
        if (!FMD_ReadSector (dwSector+1, g_pFLSBuffer, NULL, 1)) {
            return FALSE;
        }
        if (IS_VALID_FLS(g_pFLSBuffer))
        {
            PFlashLayoutSector pFLS = (PFlashLayoutSector)(g_pFLSBuffer);

            // Cache the flash layout sector information
            g_dwNumRegions = pFLS->cbRegionEntries / sizeof(FlashRegion);
            RETAILMSG(1, (TEXT("DefineLayout: g_dwNumRegions = 0x%x \r\n"), g_dwNumRegions));

            // FlashRegion table starts after the ReservedEntry table.
            if (g_dwNumRegions)
            {
                pRegion = (PFlashRegion)((LPBYTE)pFLS + sizeof(FlashLayoutSector) + pFLS->cbReservedEntries);
            RETAILMSG(1, (TEXT("DefineLayout: sizeof(FlashLayoutSector) = %x cdReservedEntries = %x  \r\n"),
                sizeof(FlashLayoutSector),pFLS->cbReservedEntries));
            }
        }
    }

    if (!g_dwNumRegions)
    {
        g_dwNumRegions = 1;
    }

    if (g_dwNumRegions > MAX_REGIONS)
        return FALSE;


    if (pRegion)
    {
        memcpy (g_pRegionTable, pRegion, g_dwNumRegions * sizeof(FlashRegion));
    }
    else

    {
        g_pRegionTable[0].dwStartPhysBlock = 0;
        g_pRegionTable[0].dwNumPhysBlocks = g_flashInfo.dwNumBlocks;
        g_pRegionTable[0].dwNumLogicalBlocks = FIELD_NOT_IN_USE;
        g_pRegionTable[0].dwBytesPerBlock = g_flashInfo.dwBytesPerBlock;
        g_pRegionTable[0].regionType = FILESYS;
        g_pRegionTable[0].dwSectorsPerBlock = g_flashInfo.wSectorsPerBlock;
        g_pRegionTable[0].dwCompactBlocks = DEFAULT_COMPACTION_BLOCKS;
    }

    RETAILMSG(1, (TEXT("DefineLayout: g_pRegionTable[0].dwNumPhysBlocks = 0x%x\r\ndwBytesperBlock = %x \r\ndwSectorsPerBlock = %x\r\n")
                        ,g_pRegionTable[0].dwNumPhysBlocks,g_pRegionTable[0].dwBytesPerBlock
                        ,g_pRegionTable[0].dwSectorsPerBlock));
    RETAILMSG(1, (TEXT("DefineLayout: g_flashInfo.dwNumPhysBlocks = 0x%x\r\ndwBytesperBlock = %x \r\ndwSectorsPerBlock = %x\r\n")
                        ,g_flashInfo.dwNumBlocks,g_flashInfo.dwBytesPerBlock
                        ,g_flashInfo.wSectorsPerBlock));

    return TRUE;
}
#endif


BOOL FMD_LB_ReadSector_Steploader(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
	DWORD	i;
    BOOL    bReturn = TRUE;
    UINT32  nSpareAddr = BYTES_PER_SECTOR;
    UINT32  dwColAddr = 0;
    UINT32  nPageAddr = startSectorAddr;

	DWORD	dwECC8[4];						// 8-bit ECC (13Bytes)
	
	DWORD	dwTemp;

		
	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));


    if (!pSectorBuff && !pSectorInfoBuff)
    {
    	FMDERR(TRUE, (TEXT("pSectorBuff and pSectorInfoBuff are NULL.\r\n")));
    	bReturn = FALSE;
    	goto CleanUp;
    }

    if(dwNumSectors > 1)
    {
        FMDERR(TRUE, (TEXT("FMD doesn't support writing multiple sectors at once. dwNumSectors must be same as 1.\r\n")));
        bReturn = FALSE;
        goto CleanUp;
    }


	if(pSectorInfoBuff != NULL)
	{
		pSectorInfoBuff->bBadBlock = 0xFF;
		pSectorInfoBuff->bOEMReserved = ~(OEM_BLOCK_RESERVED | OEM_BLOCK_READONLY);
		pSectorInfoBuff->wReserved2 = 0xFFFF;
		pSectorInfoBuff->dwReserved1 = 0xFFFFFFFF;
	}


    NF_nFCE_L();

    NF_CLEAR_RB();

    NF_CMD(CMD_READ);                            // Send read command.

    NF_ADDR(0 & 0xFF);
    NF_ADDR((0 >> 8) & 0xFF);
    NF_ADDR(nPageAddr & 0xFF);
    NF_ADDR((nPageAddr >> 8) & 0xFF);
    if (NEED_EXT_ADDR)
        NF_ADDR((nPageAddr >> 16) & 0xFF);

    NF_CMD(CMD_READ3);    // 2nd command
    NF_DETECT_RB();                                // Wait for command to complete.


	// 8-bit ECC decode
	NF_ECC8_SETUP(512);
	NF_ECC8_CLEAR_DONE();
	NF_ECC8_DECODE();
	NF_MECC_UnLock();

	if(pSectorBuff != NULL)
	{
		for(DWORD dwECCLoop = 0;dwECCLoop < 4;dwECCLoop++)
		{
			NF_RSTECC();
			NF_ECC8_DECODE();
			NF_MECC_UnLock();

			if((DWORD)pSectorBuff & 0x3)
			{
				// Un-aligned buffer address
				FMDMSG(FMD_INFO, (TEXT("Unaligned buffer for FMD_ReadSector_Stepldr, 0x%X\r\n"), pSectorBuff));
				
				for(i = 0;i < 512;i += 4)
				{
					dwTemp = NF_RDDATA_WORD();

					*pSectorBuff = (BYTE)(dwTemp & 0xFF);			pSectorBuff++;
					*pSectorBuff = (BYTE)((dwTemp >> 8) & 0xFF);	pSectorBuff++;
					*pSectorBuff = (BYTE)((dwTemp >> 16) & 0xFF);	pSectorBuff++;
					*pSectorBuff = (BYTE)((dwTemp >> 24) & 0xFF);	pSectorBuff++;
				}
			}
			else
			{	
		    	RdPage512(pSectorBuff);                    // Read page/sector data.
		    	pSectorBuff += 512;
			}

			dwColAddr = BYTES_PER_SECTOR + (dwECCLoop * 13);

#if (S5PV210_EVT > 0)
			// skip first 12 bytes of the spare area.
			NF_CMD(CMD_RDO);	// Send read command.
			NF_ADDR((BYTES_PER_SECTOR + 12 + (dwECCLoop * 13)) & 0xff);
			NF_ADDR(((BYTES_PER_SECTOR + 12 + (dwECCLoop * 13)) >> 8) & 0xff);
			NF_CMD(CMD_RDO2);	// 2nd command
#else
			NF_CMD(CMD_RDO);    // Send read command.
		    NF_ADDR((dwColAddr) & 0xff);
		    NF_ADDR((dwColAddr >> 8) & 0xff);
		    NF_CMD(CMD_RDO2);   // 2nd command
#endif

			// read out 8-bit ECC
			dwECC8[0] = NF_RDDATA_WORD();
			dwECC8[1] = NF_RDDATA_WORD();
			dwECC8[2] = NF_RDDATA_WORD();
			dwECC8[3] = NF_RDDATA_BYTE();

			NF_MECC_Lock();

			FMDMSG(FMD_DBG, (TEXT("READ ECC = 0x%X 0x%X 0x%X 0x%X\r\n"), dwECC8[0], dwECC8[1], dwECC8[2], dwECC8[3]));

			// end of 8-bit ECC decoding
			NF_ECC8_WAIT_DECODE_DONE();
			NF_ECC8_CLEAR_DECODE_DONE();
			NF_MECC_Lock();


			// check ECC error
			if(ECC_CorrectData(startSectorAddr, pSectorBuff - 512, ECC_CORRECT_SPARE) == FALSE)
			{
				FMDERR(TRUE, (TEXT("ECC error, Sector = %d, NFECCERR0 = 0x%X\r\n"), startSectorAddr,  NF_ECC_ERR0));
				bReturn = FALSE;

				goto CleanUp;
			}
				
			dwColAddr = 512 * (dwECCLoop + 1);

			NF_CMD(CMD_RDO);    // Send read command.
		    NF_ADDR((dwColAddr) & 0xff);
		    NF_ADDR((dwColAddr >> 8) & 0xff);
		    NF_CMD(CMD_RDO2);   // 2nd command

		}
	}

CleanUp:

    NF_nFCE_H();

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bReturn;
}




BOOL FMD_LB_WriteSector_Steploader(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
	BOOL bReturn = TRUE;
    DWORD i;
    DWORD dwECCLoop;
    volatile DWORD    wrdata;
    int NewSpareAddr = 0x0;
    int NewDataAddr = 0;
    int NewSectorAddr = startSectorAddr;
    MECC8 t8MECC[4];

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));


    if(!pSectorBuff)
    {
    	FMDERR(TRUE, (TEXT("pSectorBuff is NULL.\r\n")));
    	bReturn = FALSE;
    	goto CleanUp;
    }

    if(dwNumSectors > 1)
    {
        FMDERR(TRUE, (TEXT("FMD doesn't support writing multiple sectors at once. dwNumSectors must be same as 1.\r\n")));
        bReturn = FALSE;
        goto CleanUp;
    }



    //  Enable Chip
    NF_nFCE_L();    // Force nFCE to low

    //  Issue command
    NF_CMD(CMD_WRITE);    //0x80

    //  Setup address to write Main data
    NF_ADDR((NewDataAddr)&0xff);    // 2bytes for column address
    NF_ADDR((NewDataAddr>>8)&0xff);
    NF_ADDR((NewSectorAddr)&0xff);    // 3bytes for row address
    NF_ADDR((NewSectorAddr>>8)&0xff);
    if (NEED_EXT_ADDR)
	    NF_ADDR((NewSectorAddr>>16)&0xff);


    // initialize variable.
    for(i = 0; i < 4; i++) {
        t8MECC[i].n8MECC0 = 0x0;
        t8MECC[i].n8MECC1 = 0x0;
        t8MECC[i].n8MECC2 = 0x0;
        t8MECC[i].n8MECC3 = 0x0;
    }

    // Write each Sector in the Page. (4 Sector per Page, Loop 4 times.)
    for (dwECCLoop = 0; dwECCLoop < (DWORD)(g_pNANDSpec->usMainBytesPerPage >> 9); dwECCLoop++)
    {
        //  Initialize ECC register

		NF_ECC8_SETUP(512);
		NF_ECC8_CLEAR_DONE();
		NF_ECC8_ENCODE();
		// Start ECC encoding
		NF_MECC_UnLock();

        // Special case to handle un-aligned buffer pointer.
        if( ((DWORD) (pSectorBuff + (dwECCLoop * 512))) & 0x3)
        {
            //  Write the data
            for(i=0; i < 512 / sizeof(DWORD); i++)
            {
                wrdata = (pSectorBuff + (dwECCLoop * 512))[i*4+0];
                wrdata |= (pSectorBuff + (dwECCLoop * 512))[i*4+1]<<8;
                wrdata |= (pSectorBuff + (dwECCLoop * 512))[i*4+2]<<16;
                wrdata |= (pSectorBuff + (dwECCLoop * 512))[i*4+3]<<24;
                NF_WRDATA_WORD(wrdata);
            }
        }
        else
        {
            WrPage512(pSectorBuff + (dwECCLoop * 512));
        }

        NF_MECC_Lock();
		NF_ECC8_WAIT_ENCODE_DONE();
		NF_ECC8_CLEAR_ENCODE_DONE();

        //  Read out the ECC value generated by HW
        t8MECC[dwECCLoop].n8MECC0 = v_pECCregs->NFECCPRGECC0;
        t8MECC[dwECCLoop].n8MECC1 = v_pECCregs->NFECCPRGECC1;
        t8MECC[dwECCLoop].n8MECC2 = v_pECCregs->NFECCPRGECC2;
        t8MECC[dwECCLoop].n8MECC3 = v_pECCregs->NFECCPRGECC3 & 0xff;


        /*
          //Debug Code :: Print Write Data
        RETAILMSG(1, (TEXT("\r\n===================WRITE DATA=====================")));
        for(i=0;i<512;i++)
        {
            if(i%16 == 0)
                RETAILMSG(1, (TEXT("\r\n")));
            RETAILMSG(1, (TEXT(" 0x%x "),*(pSectorBuff+(nSectorLoop*SECTOR_SIZE) + i)));
        }
        RETAILMSG(1, (TEXT("\r\n===================================================\r\n")));

        RETAILMSG(1, (TEXT("FMD_LB_WriteSector_Steploader() : Sdata : 0x%x, 0x%x, 0x%x, 0x%x\r\n")
                , t8MECC[nSectorLoop].n8MECC0
                , t8MECC[nSectorLoop].n8MECC1
                , t8MECC[nSectorLoop].n8MECC2
                , t8MECC[nSectorLoop].n8MECC3));
        */
    }

#if (S5PV210_EVT > 0)
	// skip first 12 bytes of the spare area.

    NF_CMD(CMD_RDI);								//0x85 : Random Data Input

    //  Setup address to write Main data
    NF_ADDR((BYTES_PER_SECTOR + 12)&0xff);			// 2bytes for column address
    NF_ADDR(((BYTES_PER_SECTOR + 12)>>8)&0xff);
#endif

    for(dwECCLoop = 0; dwECCLoop < 4; dwECCLoop++)
    {
        NF_WRDATA_WORD(t8MECC[dwECCLoop].n8MECC0); // 4 byte n8MECC0
        NF_WRDATA_WORD(t8MECC[dwECCLoop].n8MECC1); // 4 byte n8MECC1
        NF_WRDATA_WORD(t8MECC[dwECCLoop].n8MECC2); // 4 byte n8MECC2
        NF_WRDATA_BYTE((t8MECC[dwECCLoop].n8MECC3) & 0xff); // 1 byte n8MECC3
    }

	NF_CLEAR_RB();

    //  Finish up the write operation
    NF_CMD(CMD_WRITE2);    // 0x10

    //  Wait for RB.
    NF_DETECT_RB();     // Wait tR(max 12us)

    if(NF_RDSTAT & STATUS_ILLACC)
    {
        FMDERR(TRUE, (TEXT("Failed to write a sector #%d. (ILLEGAL ACCESS)\r\n"), startSectorAddr));
		v_pNFCONregs->NFSTAT =  STATUS_ILLACC;    // Write 1 to clear.
		bReturn = FALSE;
		goto CleanUp;
    }
    else
    {
        //  Check the status of program
        NF_CMD(CMD_STATUS);

        if(NF_RDDATA_BYTE() & STATUS_ERROR)
        {
            FMDERR(1, (TEXT("Failed to write a sector #%d. (STATUS FAIL)\r\n"), startSectorAddr));
            bReturn = FALSE;
            goto CleanUp;
        }
    }

CleanUp:

	NF_nFCE_H();

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bReturn;

    
}

BOOL FMD_WriteSector_Stepldr(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    BOOL bRet = TRUE;

	FMDMSG(FMD_FUNC, (TEXT("+%s\r\n"), _T(__FUNCTION__)));

    bRet = FMD_LB_WriteSector_Steploader(startSectorAddr, pSectorBuff, pSectorInfoBuff, dwNumSectors);

	FMDMSG(FMD_FUNC, (TEXT("-%s\r\n"), _T(__FUNCTION__)));

    return bRet;
}


