//
// 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.
//
//------------------------------------------------------------------------------
//
//  Copyright (C) 2004-2007, Freescale Semiconductor, Inc. All Rights Reserved.
//  THIS SOURCE CODE, AND ITS USE AND DISTRIBUTION, IS SUBJECT TO THE TERMS
//  AND CONDITIONS OF THE APPLICABLE LICENSE AGREEMENT
//
//------------------------------------------------------------------------------
#include <fmd.h>
#include "bsp.h"
#include "nandfmd.h"
#pragma warning(disable: 4101 4189)
//-----------------------------------------------------------------------------
// External Functions
extern "C" void RdPageAlign1(LPBYTE pSectorBuff, USHORT SectorSize);
extern "C" void RdPageAlign2(LPBYTE pSectorBuff, USHORT SectorSize);
extern "C" void RdPageAlign3(LPBYTE pSectorBuff, USHORT SectorSize);
extern "C" void RdPageAlign4(LPBYTE pSectorBuff, USHORT SectorSize);
extern "C" void WrPageAlign1(LPBYTE pSectorBuff, USHORT SectorSize); 
extern "C" void WrPageAlign2(LPBYTE pSectorBuff, USHORT SectorSize); 
extern "C" void WrPageAlign3(LPBYTE pSectorBuff, USHORT SectorSize); 
extern "C" void WrPageAlign4(LPBYTE pSectorBuff, USHORT SectorSize); 
extern BOOL NFCAlloc(VOID);
extern VOID NFCWait(BOOL bPoll);
extern BOOL NFCSetClock(BOOL bEnabled);
extern BOOL NFCSetPagesize(BOOL bLargePage);


//-----------------------------------------------------------------------------
// External Variables


//-----------------------------------------------------------------------------
// Defines

//-----------------------------------------------------------------------------
// Types


//-----------------------------------------------------------------------------
// Global Variables
extern "C" PCSP_NANDFC_REGS g_pNFC = NULL;


//-----------------------------------------------------------------------------
// Local Variables
static UINT16 g_NandDeviceID;


//-----------------------------------------------------------------------------
// Local Functions

#ifdef SWAP_BBI_INFO

#define BBI_MAIN_ADDR		464        

void SwapBBI()
{
	UINT16 MainData,TempMainData;
	UINT16 SpareData,TempSpareData;

	MainData = INREG16(&g_pNFC->MAIN[3][BBI_MAIN_ADDR/2]);
	SpareData = INREG16(&g_pNFC->SPARE[3][2]);

	TempMainData = (MainData & 0xFF00) | (SpareData >>8);
	TempSpareData = (SpareData & 0xFF) | (MainData <<8);

	OUTREG16(&g_pNFC->MAIN[3][BBI_MAIN_ADDR/2], TempMainData);
	OUTREG16(&g_pNFC->SPARE[3][2], TempSpareData);
}
#endif

//-----------------------------------------------------------------------------
//
//  Function: NFCReadMain
//
//  This function reads the NAND flash controller main area and provides this
//  sector data to the upper FMD layers.
//
//  Parameters:
//      pSectorBuff 
//          [out] Buffer containing sector data read.
//
//  Returns:
//      None.
//
//-----------------------------------------------------------------------------
static VOID NFCReadMain(LPBYTE pSectorBuff)
{    
    if((UINT32)pSectorBuff & 0x1)        
    {
        // Byte-aligned on BYTE[3]
        if((UINT32)pSectorBuff & 0x2)        
        {
            RdPageAlign3(pSectorBuff, (USHORT)NAND_PAGE_SIZE);
        }

        // Byte-aligned on BYTE[1]
        else
        {
            RdPageAlign1(pSectorBuff, (USHORT)NAND_PAGE_SIZE);
        }
    }
    else if ((UINT32)pSectorBuff & 0x2)
    {
        // Half-word aligned on BYTE[2]
        RdPageAlign2(pSectorBuff, (USHORT)NAND_PAGE_SIZE);
    } 
    else
    {
        // Word-aligned
        RdPageAlign4(pSectorBuff, (USHORT)NAND_PAGE_SIZE);
    }

}


//-----------------------------------------------------------------------------
//
//  Function: NFCWriteMain
//
//  This function writes the sector data provided by the upper FMD layers
//  to the NAND flash controller main area.
//
//  Parameters:
//      pSectorBuff 
//          [in] Buffer containing sector data to be written.
//
//  Returns:
//      None.
//
//-----------------------------------------------------------------------------
static VOID NFCWriteMain(LPBYTE pSectorBuff)
{
    if((UINT32)pSectorBuff & 0x1)        
    {
        // Byte-aligned on BYTE[3]
        if((UINT32)pSectorBuff & 0x2)        
        {
            WrPageAlign3(pSectorBuff, (USHORT)NAND_PAGE_SIZE);
        }

        // Byte-aligned on BYTE[1]
        else
        {
            WrPageAlign1(pSectorBuff, (USHORT)NAND_PAGE_SIZE);
        }
    }
    else if ((UINT32)pSectorBuff & 0x2)
    {
        // Half-word aligned on BYTE[2]
        WrPageAlign2(pSectorBuff, (USHORT)NAND_PAGE_SIZE);
    } 
    else
    {
        // Word-aligned
        WrPageAlign4(pSectorBuff, (USHORT)NAND_PAGE_SIZE);
    }
}


//-----------------------------------------------------------------------------
//
//  Function: NFCReadSpare
//
//  This function reads the NAND flash controller spare area and provides this
//  information to the upper FMD layers.
//
//  Parameters:
//      pSectorInfoBuff 
//          [out] Buffer containing sector information read.
//
//  Returns:
//      None.
//
//-----------------------------------------------------------------------------
static VOID NFCReadSpare(PSectorInfo pSectorInfoBuff)
{
#ifdef SWAP_BBI_INFO
    pSectorInfoBuff->dwReserved1 = INREG32((UINT32 *) &g_pNFC->SPARE[3][0]);
    *((UINT16 *) (&pSectorInfoBuff->bOEMReserved)) = INREG16(&g_pNFC->SPARE[3][2]);
    pSectorInfoBuff->wReserved2 = INREG16(&g_pNFC->SPARE[3][7]);
#else
    pSectorInfoBuff->dwReserved1 = INREG32((UINT32 *) &g_pNFC->SPARE[0][0]);
    *((UINT16 *) (&pSectorInfoBuff->bOEMReserved)) = INREG16(&g_pNFC->SPARE[0][2]);
    pSectorInfoBuff->wReserved2 = INREG16(&g_pNFC->SPARE[0][7]);
#endif	
}


//-----------------------------------------------------------------------------
//
//  Function: NFCWriteSpare
//
//  This function writes the spare data provided by the upper FMD layers
//  to the NAND flash controller spare area.
//
//  Parameters:
//      pSectorInfoBuff 
//          [in] Buffer containing sector information to be written.
//
//  Returns:
//      None.
//
//-----------------------------------------------------------------------------
static VOID NFCWriteSpare(PSectorInfo pSectorInfoBuff)
{
    int i;

    for(i = 0; i < NAND_PAGE_SIZE / NANDFC_MAIN_BUFFER_SIZE; i++){
    OUTREG32((UINT32 *) &g_pNFC->SPARE[i][0], pSectorInfoBuff->dwReserved1);
    OUTREG16(&g_pNFC->SPARE[i][2], *((UINT16 *) (&pSectorInfoBuff->bOEMReserved)));
    OUTREG16(&g_pNFC->SPARE[i][3], 0xFFFF);
    OUTREG32((UINT32 *) &g_pNFC->SPARE[i][4], 0xFFFFFFFF);
    OUTREG16(&g_pNFC->SPARE[i][6], 0xFFFF);
    OUTREG16(&g_pNFC->SPARE[i][7], pSectorInfoBuff->wReserved2);
    }
}


//-----------------------------------------------------------------------------
//
//  Function: NFCClearSpare
//
//  This function clears the NAND flash controller spare area (all
//  ones written to the spare area will prevent the spare area of
//  the NAND memory device from being updated since only zeros are
//  written during a NAND program cycle).
//
//  Parameters:
//      None.
//
//  Returns:
//      None.
//
//-----------------------------------------------------------------------------
static VOID NFCClearSpare(VOID)
{
    UINT32 *dest;
    int i;


    for(i = 0; i < NAND_PAGE_SIZE / NANDFC_MAIN_BUFFER_SIZE; i++){
        dest = (UINT32 *) &g_pNFC->SPARE[i];

    OUTREG32(&dest[0], 0xFFFFFFFF);
    OUTREG32(&dest[1], 0xFFFFFFFF);
    OUTREG32(&dest[2], 0xFFFFFFFF);
    OUTREG32(&dest[3], 0xFFFFFFFF);
    }
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_Init
//
//  This function initializes the flash memory of a device.
//
//  Parameters:
//      lpActiveReg
//          [in] Pointer to the active registry string used to find device 
//          information from the registry. Set to NULL if not needed. 
//
//      pRegIn
//          [in] Pointer to a PCI_REG_INFO structure. Used to find flash 
//          hardware on PCI hardware. Set to NULL if not needed.
//
//      pRegOut
//          [in/out] Pointer to a PCI_REG_INFO structure. Used to return 
//          flash information. Set to NULL if not needed.
//
//  Returns:
//      A handle that can be used in a call to FMD_Deinit. It is the 
//      responsibility of the specific flash media driver (FMD) implementation 
//      to determine what this value represents. A value of zero (0) 
//      represents failure.
//
//-----------------------------------------------------------------------------
PVOID FMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)
{
    PVOID rc = NULL;
    
    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(lpActiveReg);
    UNREFERENCED_PARAMETER(pRegIn);
    UNREFERENCED_PARAMETER(pRegOut);
    
    NFCSetClock(TRUE);
    
    // Perform NFC allocations, mappings, and initializations
    if (!NFCAlloc())
    {
        ERRORMSG(TRUE, (_T("FMD_Init: failed memory allocation/mapping\r\n")));
        goto cleanUp;
    }
    
#ifdef NAND_LARGE_PAGE
    // For large page size nand flash, the NFMS bit in register RCSR(CCM module) 
    // must be set
    if(!NFCSetPagesize(TRUE))
    {
        ERRORMSG(TRUE, (_T("FMD_Init: failed setting page size\r\n")));
        goto cleanUp;
    }
#endif
    
    // Unlock buffer pages
    INSREG16BF(&g_pNFC->NFC_CONFIGURATION, NANDFC_NFC_CONFIGURATION_BLS,
        NANDFC_NFC_CONFIGURATION_BLS_UNLOCKED);

    // Use NFC buffer #0 for all operations
    OUTREG16(&g_pNFC->RAM_BUFF_ADDRESS, 0);
    
    // Configure the NFC
    //  - Enable hardware ECC
    //  - Mask NFC interrupt
    //  - Little endian mode
    //  - Reset NFC state machine
    //  - CE signal operates normally
    OUTREG16(&g_pNFC->NAND_FLASH_CONFIG1, 
        CSP_BITFVAL(NANDFC_NAND_FLASH_CONFIG1_ECC_EN, NANDFC_NAND_FLASH_CONFIG1_ECC_EN_ENABLE) |
        CSP_BITFVAL(NANDFC_NAND_FLASH_CONFIG1_INT_MSK, NANDFC_NAND_FLASH_CONFIG1_INT_MSK_MASK) |
        CSP_BITFVAL(NANDFC_NAND_FLASH_CONFIG1_NF_BIG, NANDFC_NAND_FLASH_CONFIG1_NF_BIG_LITTLE) |
        CSP_BITFVAL(NANDFC_NAND_FLASH_CONFIG1_NF_CE, NANDFC_NAND_FLASH_CONFIG1_NF_CE_UNFORCE) );

    NF_CMD(CMD_RESET);     // Send flash reset command

    // Get manufacturer and device codes.
    NF_CMD(CMD_READID);     // Read ID command
    NF_ADDR(0);             // Required address cycle
    NF_RD_ID();             // Read ID into NFC buffer

    g_NandDeviceID = INREG16(&g_pNFC->MAIN[0]);
    
    if (g_NandDeviceID != NAND_ID_CODE)
    {
        ERRORMSG(TRUE, (_T("FMD_Init: invalid device ID (%x)\r\n"), g_NandDeviceID));
        goto cleanUp;
    }

    // Unlock all blocks
    OUTREG16(&g_pNFC->UNLOCK_START_BLK_ADD, 0);
    OUTREG16(&g_pNFC->UNLOCK_END_BLK_ADD, NAND_BLOCK_CNT-1);
    OUTREG16(&g_pNFC->NF_WR_PROT, NANDFC_NF_WR_PROT_WPC_UNLOCK);

    rc = g_pNFC;

cleanUp:
    NFCSetClock(FALSE);
    return rc;
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_Deinit
//
//  This function de-initializes the flash chip.
//
//  Parameters:
//      hFMD 
//          [in] The handle returned from FMD_Init. 
//
//  Returns:  
//      Returns TRUE on success. Returns FALSE on failure.
//
//-----------------------------------------------------------------------------
BOOL FMD_Deinit(PVOID hFMD)
{
    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(hFMD);
    return(TRUE);
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_ReadSector
//
//  This function reads the requested sector data and metadata from the 
//  flash media.
//
//  Parameters:
//      startSectorAddr 
//          [in] The starting physical sector address to read.
//
//      pSectorBuff 
//          [out] Pointer to the buffer that contains the sector data read 
//          from flash memory. Set to NULL if this data is not needed.
//
//      pSectorInfoBuff 
//          [out] Buffer for an array of sector information structures. There 
//          is one sector information entry for every sector that is to be read. 
//          Set to NULL if this data is not needed. 
//
//      dwNumSectors 
//          [in] Number of sectors to read.
//
//  Returns:  
//      Returns TRUE on success. Returns FALSE on failure.
//
//-----------------------------------------------------------------------------
BOOL FMD_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, 
    PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    SECTOR_ADDR SectorAddr = startSectorAddr;
    BOOL bSeqRead = FALSE;
    BOOL rc = FALSE;
    int i;
	BOOL bSwapBBI = FALSE;
	
#ifdef SWAP_BBI_INFO	
	bSwapBBI = TRUE;		
#endif

    NFCSetClock(TRUE);
    
    while (dwNumSectors--)
    {
        UINT32 addr = SectorAddr * NAND_PAGE_SIZE;
    
        // RETAILMSG(TRUE, (_T("FMD_ReadSector(0x%x)\r\n"), addr));
    
        if (pSectorBuff != NULL || bSwapBBI)
        {
            // If read access requires command and address to be sent
            if (!bSeqRead)
            {
            	//KITLOutputDebugString("Send page read command.\r\n");
                NF_CMD(CMD_READ);             // Send page read command.
                NF_ADDR_COL(addr);      // Send column address
                NF_ADDR_PAGE(addr);     // Send page address
                
#ifdef NAND_LARGE_PAGE
                NF_CMD(CMD_READ_2CYCLE);      // Send 2nd cycle read command
#endif
            }
			
            //KITLOutputDebugString("Read page data into NFC buffer.\r\n");
            // Read page data into NFC buffer
            for(i = 0; i < NAND_PAGE_SIZE / NANDFC_MAIN_BUFFER_SIZE; i++){
                NF_BUF_ADDR(i);
            	NF_RD_PAGE();
            
                // Check for uncorrectable ECC errors in main and spare areas
            if (EXTREG16BF(&g_pNFC->ECC_STATUS_RESULT, NANDFC_ECC_STATUS_RESULT_ERM) == 
                NANDFC_ECC_STATUS_RESULT_ERM_2BIT_ERR)
                {
                    //ERRORMSG(TRUE, (_T("Uncorrectable ECC error at Sector address: 0x%x.\r\n"),SectorAddr));					
                    goto cleanUp;
                }

            } 
			
#ifdef SWAP_BBI_INFO			
			SwapBBI();
#endif			
            //KITLOutputDebugString("Move page data from NFC buffer sector buffer.\r\n");
            // Move page data from NFC buffer sector buffer
            if (pSectorBuff != NULL)
            {
            	//KITLOutputDebugString("read main data.\r\n");
            	NFCReadMain(pSectorBuff);
				//KITLOutputDebugString("read main data finished.\r\n");
                pSectorBuff += NAND_PAGE_SIZE;
            }

            if (pSectorInfoBuff != NULL)
            {
                NFCReadSpare(pSectorInfoBuff);

                pSectorInfoBuff++;
            }		
            
        }

        else if (pSectorInfoBuff != NULL)
        {
            NF_BUF_ADDR(0);
#ifndef NAND_LARGE_PAGE
            NF_CMD(CMD_READ2);                        // Send spare read command.
            NF_ADDR_COL(addr);           // Send column address
            NF_ADDR_PAGE(addr);          // Send page address
#else
            NF_CMD(CMD_READ);                         // Send read command
            NF_ADDR_COL(addr+512);      // Send column address
            NF_ADDR_PAGE(addr);         // Send page address
            NF_CMD(CMD_READ_2CYCLE);                  // Send 2nd cycle read command
#endif

            // Read page data into NFC buffer
            NF_RD_SPARE();
            NFCReadSpare(pSectorInfoBuff);

            pSectorInfoBuff++;
        }

        ++SectorAddr;

        bSeqRead = NF_READ_SEQ(SectorAddr);
            
    }

    rc = TRUE;

cleanUp:
    NFCSetClock(FALSE);
    return rc;
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_EraseBlock
//
//  This function erases the specified flash block.
//
//  Parameters:
//      blockID 
//          [in] The block number to erase.
//
//  Returns:  
//      Returns TRUE on success. Returns FALSE on failure.
//
//-----------------------------------------------------------------------------
BOOL FMD_EraseBlock(BLOCK_ID blockID)
{
    UINT16 Status;
    UINT32 addr = (blockID * NAND_BLOCK_SIZE);

    //RETAILMSG(TRUE, (_T("Erase Block No. = 0x%x\r\n"), blockID));

    NFCSetClock(TRUE);

#ifndef NAND_LARGE_PAGE   
    NF_CMD(CMD_READ);           // Set address pointer to main area
#endif

    NF_CMD(CMD_ERASE);          // Send block erase setup command
    NF_ADDR_PAGE(addr);         // Send page address
    NF_CMD(CMD_ERASE2);         // Send block erase command

    NF_BUF_ADDR(0);
    NF_CMD(CMD_STATUS)          // Send status command
    NF_RD_STATUS();             // Read status into NFC buffer
    Status = INREG16(&g_pNFC->MAIN[0][0]);     // Read status from NFC buffer
    
    NFCSetClock(FALSE);
        
    if (Status & NAND_STATUS_MASK_ERROR)
    {
        ERRORMSG(TRUE, (_T("NAND erase failed (Block = 0x%x)\r\n"), blockID)); 
        return FALSE;
    }

    return TRUE;
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_WriteSector
//
//  This function writes the requested sector data and metadata to the 
//  flash media.
//
//  Parameters:
//      startSectorAddr 
//          [in] The starting physical sector address to write to.
//
//      pSectorBuff 
//          [in] Pointer to the buffer that contains the sector data to write. 
//          Set to NULL if no data is to be written.
//
//      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.
//
//      dwNumSectors 
//          [in] Number of sectors to write.
//
//  Returns:  
//      Returns TRUE on success. Returns FALSE on failure.
//
//-----------------------------------------------------------------------------
BOOL FMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, 
    PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    UINT16 Status;
    SECTOR_ADDR SectorAddr = startSectorAddr;
    BOOL rc = FALSE;
    int i;

    NFCSetClock(TRUE);
    
    if (!pSectorBuff && !pSectorInfoBuff)
    {
        ERRORMSG(TRUE, (_T("Sector/Info buffers are NULL\r\n")));
        goto cleanUp;
    }

    while (dwNumSectors--)
    {
        UINT32 addr = SectorAddr * NAND_PAGE_SIZE;

        // RETAILMSG(TRUE, (_T("FMD_WriteSector(0x%x)\r\n"), SectorAddr));
    
        if (pSectorBuff != NULL)
        {
            if (pSectorInfoBuff)
            {
                // Update spare area with sector information provided
                NFCWriteSpare(pSectorInfoBuff);

                pSectorInfoBuff++;
            }
            else
            {
                // Clear out the spare area
                NFCClearSpare();
            }

            // Move page data from sector buffer to NFC buffer
            NFCWriteMain(pSectorBuff);
#ifdef SWAP_BBI_INFO	
			SwapBBI();
#endif				
            pSectorBuff += NAND_PAGE_SIZE;     // Next sector
		            
#ifndef NAND_LARGE_PAGE
            NF_CMD(CMD_READ);           // Set address pointer to main area
#endif

            NF_CMD(CMD_WRITE);          // Send sequential data input command
            NF_ADDR_COL(addr);      // Send column address
            NF_ADDR_PAGE(addr);     // Send page address

             // Write out the page data
            for(i = 0; i < NAND_PAGE_SIZE / NANDFC_MAIN_BUFFER_SIZE; i++){
                NF_BUF_ADDR(i);   
                NF_WR_PAGE();          
            }

        }

        else
        {
            // Update spare area with sector information provided
            NFCWriteSpare(pSectorInfoBuff);
            NF_BUF_ADDR(0);

#ifndef NAND_LARGE_PAGE            
            NF_CMD(CMD_READ2);          // Set address pointer to spare area
            NF_CMD(CMD_WRITE);          // Send sequential data input command
            NF_ADDR_COL(addr);            // Send column address
            NF_ADDR_PAGE(addr);           // Send page address            
#else
            NF_CMD(CMD_WRITE);          // Send sequential data input command
            NF_ADDR_COL(addr + 512);      // Send column address
            NF_ADDR_PAGE(addr);           // Send page address
#endif

            NF_WR_SPARE();          // Write out the spare data

            pSectorInfoBuff++;
        }
            
        NF_CMD(CMD_WRITE2)          // Send program command
        
        NF_BUF_ADDR(0);        
        NF_CMD(CMD_STATUS)          // Send status command
        NF_RD_STATUS();             // Read status into NFC buffer
        Status = INREG16(&g_pNFC->MAIN[0][0]);     // Read status from NFC buffer
        
        if (Status & NAND_STATUS_MASK_ERROR)
        {
            ERRORMSG(TRUE, (_T("NAND program failed (Sectoraddr = 0x%x)\r\n"), SectorAddr)); 
            goto cleanUp;
        }

        ++SectorAddr;
    }


    rc = TRUE;

cleanUp:
    NFCSetClock(FALSE);
    return  rc;
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_PowerUp
//
//  This function restores power to the flash memory device, if applicable.
//
//  Parameters:
//      None
//
//  Returns:  
//      None
//
//-----------------------------------------------------------------------------
VOID FMD_PowerUp(VOID)
{
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_PowerDown
//
//  This function suspends power to the flash memory device, if applicable.
//
//  Parameters:
//      None
//
//  Returns:  
//      None
//
//-----------------------------------------------------------------------------
VOID FMD_PowerDown(VOID)
{
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_OEMIoControl
//
//  This function implements user-defined commands for the flash memory device. 
//
//  Parameters:
//      dwIoControlCode 
//          [in] The control code specifying the command to execute.
//
//      pInBuf
//          [in] Long pointer to a buffer that contains the data required to 
//          perform the operation. Set to NULL if the dwIoControlCode parameter 
//          specifies an operation that does not require input data.
//                        
//      nInBufSize
//          [in] Size, in bytes, of the buffer pointed to by pInBuf. 
//
//      pOutBuf
//          [out] Long pointer to a buffer that receives the output data for 
//          the operation. Set to NULL if the dwIoControlCode parameter 
//          specifies an operation that does not produce output data.
//
//      nOutBufSize
//          [in] Size, in bytes, of the buffer pointed to by pOutBuf.
//
//      pBytesReturned
//          [out] Long pointer to a variable that receives the size, in bytes, 
//          of the data stored into the buffer pointed to by pOutBuf. Even 
//          when an operation produces no output data and pOutBuf is set to 
//          NULL, the DeviceIoControl function uses the variable pointed to 
//          by pBytesReturned. After such an operation, the value of the 
//          variable has no meaning. 
//
//  Returns:  
//      Returns TRUE on success. Returns FALSE on failure.
//
//-----------------------------------------------------------------------------
BOOL FMD_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, 
    PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned)
{
    BOOL rc = FALSE;

    // Remove-W4: Warning C4100 workaround
    UNREFERENCED_PARAMETER(dwIoControlCode);
    UNREFERENCED_PARAMETER(pInBuf);
    UNREFERENCED_PARAMETER(nInBufSize);
    UNREFERENCED_PARAMETER(pOutBuf);
    UNREFERENCED_PARAMETER(nOutBufSize);
    UNREFERENCED_PARAMETER(pBytesReturned);

    return(rc);
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_GetInfo
//
//  This function determines the size characteristics for the flash memory 
//  device.
//
//  Parameters:
//      pFlashInfo 
//          [out] A pointer to a structure that contains the size 
//          characteristics for the flash memory device.
//
//  Returns:  
//      Returns TRUE on success. Returns FALSE on failure.
//
//-----------------------------------------------------------------------------
BOOL FMD_GetInfo(PFlashInfo pFlashInfo)
{

    if (!pFlashInfo)
        return(FALSE);

    pFlashInfo->flashType           = NAND;
    pFlashInfo->wDataBytesPerSector = NAND_PAGE_SIZE;
    pFlashInfo->dwNumBlocks         = NAND_BLOCK_CNT;
    pFlashInfo->wSectorsPerBlock    = NAND_PAGE_CNT;
    pFlashInfo->dwBytesPerBlock     = (pFlashInfo->wSectorsPerBlock * pFlashInfo->wDataBytesPerSector);

    return(TRUE);
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_GetBlockStatus
//
//  This function returns the status of a block.
//
//  Parameters:
//      blockID 
//          [in] The block number used to check status.
//
//  Returns:  
//      Flags to describe the status of the block.
//
//-----------------------------------------------------------------------------
DWORD FMD_GetBlockStatus(BLOCK_ID blockID)
{
    SECTOR_ADDR Sector = (blockID * NAND_PAGE_CNT);
    SectorInfo SI;
    DWORD dwResult = 0;
	
    // Samsung makes sure that either the 1st or 2nd page of every initial 
    // invalid block has non-FFh data at the column address of 517.  Read
    // first two page spare areas and to determine block status.
    if (!FMD_ReadSector(Sector+NAND_BBI_PAGE2_OFFSET, NULL, &SI, 1))
    {
        dwResult =BLOCK_STATUS_BAD;// BLOCK_STATUS_UNKNOWN;
        goto cleanUp;
    }
	
	if(SI.bBadBlock != 0xFF)
	{
        dwResult = BLOCK_STATUS_BAD;
        goto cleanUp;
	} 

    if (!FMD_ReadSector(Sector+NAND_BBI_PAGE1_OFFSET, NULL, &SI, 1))
    {
        dwResult = BLOCK_STATUS_BAD;//BLOCK_STATUS_UNKNOWN;
        goto cleanUp;
    }

	if(SI.bBadBlock != 0xFF)
	{
        dwResult = BLOCK_STATUS_BAD;
        goto cleanUp;
	} 

    if (!(SI.bOEMReserved & OEM_BLOCK_READONLY))  
        dwResult |= BLOCK_STATUS_READONLY;

    if (!(SI.bOEMReserved & OEM_BLOCK_RESERVED))  
        dwResult |= BLOCK_STATUS_RESERVED;

cleanUp:

    return(dwResult);
}


//-----------------------------------------------------------------------------
//
//  Function: FMD_SetBlockStatus
//
//  This function sets the status of a block.
//
//  Parameters:
//      blockID 
//          [in] The block number used to set status. 
//
//      dwStatus
//          [in] The status value to set.
//
//  Returns:  
//      Returns TRUE on success. Returns FALSE on failure.
//
//-----------------------------------------------------------------------------
BOOL FMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus)
{
    SECTOR_ADDR Sector = (blockID*NAND_PAGE_CNT+NAND_BBI_PAGE1_OFFSET);
    SectorInfo SI;
	BYTE SectorBuff[NAND_PAGE_SIZE];
	memset(SectorBuff, 0xFF, NAND_PAGE_SIZE);			

	
    if (dwStatus & (BLOCK_STATUS_BAD | BLOCK_STATUS_READONLY | BLOCK_STATUS_RESERVED)) {        
        if (!FMD_ReadSector(Sector, NULL, &SI, 1)) {
            return FALSE;
        }

        if (dwStatus & BLOCK_STATUS_BAD)
        {
            SI.bBadBlock = 0x00;
        }
            
        if (dwStatus & BLOCK_STATUS_READONLY) {
            SI.bOEMReserved &= ~OEM_BLOCK_READONLY;
        }
        
        if (dwStatus & BLOCK_STATUS_RESERVED) {
            SI.bOEMReserved &= ~OEM_BLOCK_RESERVED;
        }
		
        if (!FMD_WriteSector (Sector, (LPBYTE)&SectorBuff, &SI, 1)) {
            return FALSE;
        }

    }    
    
    return(TRUE);
}
