#include <windows.h>
#include <bsp.h>
#include "bsp_cfg.h"
#include "utils.h"

//----------------------------------------------------------------------------------

#define rNFCONF             (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x00))
#define rNFCONT             (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x04))
#define rNFCMMD             (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x08))
#define rNFADDR             (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x0C))
#define rNFDATA             (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x10))
#define rNFDATA8            (*(volatile unsigned char *)(BASE_REG_PA_NFCON+0x10))
#define rNFDATA32           (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x10))
#define rNFMECCD0           (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x14))
#define rNFMECCD1           (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x18))
#define rNFSTAT             (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x28))
#define rNFECCERR0          (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x2C))
#define rNFMECC0            (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x34))
#define rNFMECC1            (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x38))
#define rNFMECC0            (*(volatile unsigned *)(BASE_REG_PA_NFCON+0x34))

#define rNFECCCONF			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0x0))
#define rNFECCCONT			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0x20))
#define rNFECCSTAT			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0x30))
#define rNFECCSECSTAT		(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0x40))


#define rNFECCERL0			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0xC0))
#define rNFECCERL1			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0xC4))
#define rNFECCERL2			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0xC8))
#define rNFECCERL3			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0xCC))

#define rNFECCERP0			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0xF0))
#define rNFECCERP1			(*(volatile unsigned *)(BASE_REG_PA_NFCON_ECC + 0xF4))



/*

#define NF_CMD(cmd)			{rNFCMMD=cmd;}
#define NF_ADDR(addr)		{rNFADDR=addr;}

#define NF_nFCE_L()			{rNFCONT &= ~(1 << 1);}
#define NF_nFCE_H()			{rNFCONT |=  (1 << 1);}

#define NF_RSTECC()			{rNFCONT |=  ((1<<5)|(1<<4));}

#define NF_MECC_UnLock()	{rNFCONT &= ~(1<<7);}
#define NF_MECC_Lock()		{rNFCONT |= (1<<7);}

#define NF_SECC_UnLock()	{rNFCONT &= ~(1<<6);}
#define NF_SECC_Lock()		{rNFCONT |= (1<<6);}

#define NF_CLEAR_RB()		{rNFSTAT = (1 << 4);}
#define NF_DETECT_RB()		{while((rNFSTAT&0x11)!=0x11);}    // RnB_Transdetect & RnB
#define NF_WAITRB()			{while (!(rNFSTAT & (1 << 0)));}

#define NF_RDDATA()			(rNFDATA)
#define NF_RDDATA8()		(unsigned char)(rNFDATA8)
#define NF_RDDATA32()		(rNFDATA32)
#define NF_WRDATA(data)		{rNFDATA=data;}

#define NF_RDMECC0()		(rNFMECC0)
#define NF_RDMECC1()		(rNFMECC1)

#define NF_RDMECCD0()		(rNFMECCD0)
#define NF_RDMECCD1()		(rNFMECCD1)

#define NF_WRMECCD0(data)	{rNFMECCD0 = (data);}
#define NF_WRMECCD1(data)	{rNFMECCD1 = (data);}

*/





#define NF_GET_MID(nid)		((nid & 0x0000FF00) >>8)
#define NF_GET_DID(nid)		(nid & 0x000000FF)




#define NF_CMD(cmd)				{rNFCMMD = (unsigned char)(cmd);}
#define NF_ADDR(addr)			{rNFADDR = (unsigned char)(addr);}    

#define NF_nFCE_L()				{rNFCONT &= ~(1<<1);}
#define NF_nFCE_H()				{rNFCONT |=  (1<<1);}

#define NF_RSTECC()				{rNFCONT |=  ((1<<5) | (1<<4));}

#define NF_MECC_UnLock()		{rNFCONT &= ~(1<<7);}
#define NF_MECC_Lock()			{rNFCONT |= (1<<7);}
#define NF_SECC_UnLock()		{rNFCONT &= ~(1<<6);}
#define NF_SECC_Lock()			{rNFCONT |= (1<<6);}

#define NF_CLEAR_RB()			{rNFSTAT =  (1<<4);}    // Have write '1' to clear this bit.

#define NF_DETECT_RB()			{while((rNFSTAT & 0x11) != 0x11);} // RnB_Transdetect & RnB
#define NF_WAITRB()				{while(!(rNFSTAT & (1<<0)));} 


#define NF_RDDATA_BYTE()		(rNFDATA8)
#define NF_RDDATA_WORD()		(rNFDATA32)

#define NF_WRDATA_BYTE(data)	{rNFDATA8  =  (UINT8)(data);}
#define NF_WRDATA_WORD(data)    {rNFDATA32 = (UINT32)(data);}

#define NF_RDMECC0()			(rNFMECC0)
#define NF_RDMECC1()			(rNFMECC1)

#define NF_RDMECCD0()			(rNFMECCD0)
#define NF_RDMECCD1()			(rNFMECCD1)

#define NF_ECC_ERR0				(rNFECCERR0)

#define NF_WRMECCD0(data)		{rNFMECCD0 = (data);}
#define NF_WRMECCD1(data)		{rNFMECCD1 = (data);}

#define NF_RDSTAT				(rNFSTAT)


#define NF_ECC8_DECODE()			(rNFECCCONT = (0 << 16) | (1 << 2))
#define NF_ECC8_ENCODE()			(rNFECCCONT = (1 << 16) | (1 << 2))

#define NF_ECC8_SETUP(MsgLength)	{rNFCONF |= (0x3 << 23); (rNFECCCONF = ((MsgLength - 1) << 16) | 0x3);}
#define NF_ECC1_SETUP()				{rNFECCCONF &= ~(0x7); rNFCONF &= ~(0x3 << 23);}


#define NF_ECC8_WAIT_DECODE_DONE()	{while(rNFECCSTAT & (1 << 24) == 0);}
#define NF_ECC8_WAIT_ENCODE_DONE()	{while(rNFECCSTAT & (1 << 25) == 0);}
#define NF_ECC8_CLEAR_DECODE_DONE()	{rNFECCSTAT = (1 << 24);}
#define NF_ECC8_CLEAR_ENCODE_DONE()	{rNFECCSTAT = (1 << 25);}
#define NF_ECC8_CLEAR_DONE()		{rNFECCSTAT = (1 << 25) | (1 << 24);}

#define NF_ECC8_ERROR()				(rNFECCSECSTAT & 0x1F)

#define NF_ECC8_ERROR_BYTE0			((rNFECCERL0 >> 0) & 0x3FF)
#define NF_ECC8_ERROR_BYTE1			((rNFECCERL0 >> 16) & 0x3FF)
#define NF_ECC8_ERROR_BYTE2			((rNFECCERL1 >> 0) & 0x3FF)
#define NF_ECC8_ERROR_BYTE3			((rNFECCERL1 >> 16) & 0x3FF)
#define NF_ECC8_ERROR_BYTE4			((rNFECCERL2 >> 0) & 0x3FF)
#define NF_ECC8_ERROR_BYTE5			((rNFECCERL2 >> 16) & 0x3FF)
#define NF_ECC8_ERROR_BYTE6			((rNFECCERL3 >> 0) & 0x3FF)
#define NF_ECC8_ERROR_BYTE7			((rNFECCERL3 >> 16) & 0x3FF)

#define NF_ECC8_ERROR_PATTERN0		((rNFECCERP0 >> 0) & 0xFF)
#define NF_ECC8_ERROR_PATTERN1		((rNFECCERP0 >> 8) & 0xFF)
#define NF_ECC8_ERROR_PATTERN2		((rNFECCERP0 >> 16) & 0xFF)
#define NF_ECC8_ERROR_PATTERN3		((rNFECCERP0 >> 24) & 0xFF)
#define NF_ECC8_ERROR_PATTERN4		((rNFECCERP1 >> 0) & 0xFF)
#define NF_ECC8_ERROR_PATTERN5		((rNFECCERP1 >> 8) & 0xFF)
#define NF_ECC8_ERROR_PATTERN6		((rNFECCERP1 >> 16) & 0xFF)
#define NF_ECC8_ERROR_PATTERN7		((rNFECCERP1 >> 24) & 0xFF)

//----------------------------------------------------------------------------------

#define CMD_READ		(0x00)    //  Read
#define CMD_READ1		(0x01)    //  Read1
#define CMD_READ2		(0x50)    //  Read2
#define CMD_READ3		(0x30)    //  Read3
#define CMD_RDO			(0x05)    //  Random Data Output
#define CMD_RDO2        (0xE0)    //  Random Data Output

//----------------------------------------------------------------------------------

#define TACLS			(NAND_TACLS)
#define TWRPH0			(NAND_TWRPH0)
#define TWRPH1			(NAND_TWRPH1)

//----------------------------------------------------------------------------------

typedef enum {
    ECC_CORRECT_MAIN = 0,  // correct Main ECC
    ECC_CORRECT_SPARE = 1,  // correct Main ECC
} ECC_CORRECT_TYPE;

//----------------------------------------------------------------------------------

BOOL g_bLargeBlock = FALSE;
BOOL NEED_EXT_ADDR;

static void NF_Reset(void);
static BOOL ReadFlashID(DWORD *pID);
extern void _Read_512Byte(BYTE *bufPt);        // in "nand_opt.s"




// Error correction Capability
// Main data  : 1 bit / 512byte
// Spare data : 8 bit / 
BOOL ECC_CorrectData(DWORD 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];

	int i;

	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] = NF_ECC8_ERROR_BYTE0;
				dwECC8ErrorByte[1] = NF_ECC8_ERROR_BYTE1;
				dwECC8ErrorByte[2] = NF_ECC8_ERROR_BYTE2;
				dwECC8ErrorByte[3] = NF_ECC8_ERROR_BYTE3;
				dwECC8ErrorByte[4] = NF_ECC8_ERROR_BYTE4;
				dwECC8ErrorByte[5] = NF_ECC8_ERROR_BYTE5;
				dwECC8ErrorByte[6] = NF_ECC8_ERROR_BYTE6;
				dwECC8ErrorByte[7] = NF_ECC8_ERROR_BYTE7;
			
				dwECC8ErrorPattern[0] = NF_ECC8_ERROR_PATTERN0;
				dwECC8ErrorPattern[1] = NF_ECC8_ERROR_PATTERN1;
				dwECC8ErrorPattern[2] = NF_ECC8_ERROR_PATTERN2;
				dwECC8ErrorPattern[3] = NF_ECC8_ERROR_PATTERN3;
				dwECC8ErrorPattern[4] = NF_ECC8_ERROR_PATTERN4;
				dwECC8ErrorPattern[5] = NF_ECC8_ERROR_PATTERN5;
				dwECC8ErrorPattern[6] = NF_ECC8_ERROR_PATTERN6;
				dwECC8ErrorPattern[7] = NF_ECC8_ERROR_PATTERN7;

				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;
			
	}

	return bReturn;
}




#if 0



BOOL ECC_CorrectData(unsigned int sectoraddr, UINT8* pData, UINT32 nRetEcc, ECC_CORRECT_TYPE nType)
{
    DWORD  nErrStatus;
    DWORD  nErrDataNo;
    DWORD  nErrBitNo;
    UINT32 nErrDataMask;
    UINT32 nErrBitMask = 0x7;
    BOOL bRet = TRUE;

    if (nType == ECC_CORRECT_MAIN)
    {
        nErrStatus   = 0;
        nErrDataNo   = 7;
        nErrBitNo    = 4;
        nErrDataMask = 0x7ff;
    }
    else if (nType == ECC_CORRECT_SPARE)
    {
        nErrStatus   = 2;
        nErrDataNo   = 21;
        nErrBitNo    = 18;
        nErrDataMask = 0xf;
    }
    else
    {
        return FALSE;
    }

    switch((nRetEcc>>nErrStatus) & 0x3)
    {
        case 0:    // No Error
            bRet = TRUE;
            break;
        case 1:    // 1-bit Error(Correctable)
            (pData)[(nRetEcc>>nErrDataNo)&nErrDataMask] ^= (1<<((nRetEcc>>nErrBitNo)&nErrBitMask));
            bRet = TRUE;
            break;
        case 2:    // Multiple Error
            //Uart_SendString("M ECC Err\n");
            //Uart_SendDWORD(sectoraddr,1);
            bRet = FALSE;
            break;
        case 3:    // ECC area Error
            //Uart_SendString("ECC Err\n");
        default:
            bRet = FALSE;
            break;
    }

    return bRet;
}


#endif


void NAND_Init(void)
{
    DWORD ReadID;

	BYTE cMID, cDID;
	
    // Configure BUS Width and Chip Select for NAND Flash

    // Initialize NAND Flash Controller
    rNFCONF = (TACLS << 12) | (TWRPH0 <<  8) | (TWRPH1 <<  4) | (0<<0);
    rNFCONT = (0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(1<<7)|(1<<6)|(1<<5)|(1<<4)|(0x3<<1)|(1<<0);
    rNFSTAT = (1<<4);

    NF_Reset();

    // Get manufacturer and device codes.
    if (!ReadFlashID(&ReadID))
    {
        //Uart_SendString("ID Err\n");
        //Uart_SendDWORD(ReadID,1);
        //Uart_SendString("!\n");
    }

    cMID = NF_GET_MID(ReadID);
    cDID = NF_GET_DID(ReadID);

    switch(cMID)
    {
    	case 0xEC:
    	{
    		switch(cDID)
    		{
    			case 0xDA:
    			{
    				g_bLargeBlock = TRUE;
    				NEED_EXT_ADDR = TRUE;
    				break;
    			}

		    	case 0xF1:
    			{
    				g_bLargeBlock = TRUE;
    				NEED_EXT_ADDR = FALSE;
    				break;
    			}
				//added by terry 20110925
		    	case 0xDC:
    			{
    				g_bLargeBlock = TRUE;
    				NEED_EXT_ADDR = TRUE;
    				break;
    			}				

    			default:
    				// Not supported NAND
					while(1);
    				break;
    		}

			break;    		
    	}

    	default:
    		// Not supported NAND
    		while(1);
    		break;
    }
}



#define BYTES_PER_SECTOR 2048
#define ECC8_SPARE_MSG_LENGTH (4+4+4+4)


BOOL NAND_ReadSector(DWORD dwSector, LPBYTE pSectorBuff)
{
	BOOL bReturn			= TRUE;
	DWORD dwSpareAddr		= 2048;
	DWORD dwColAddr			= 0;
	
	DWORD dwECC1[4];
	DWORD dwECC8[4];
	DWORD dwECCError;
	DWORD dwECCLoop;

	DWORD dwBuf[(ECC8_SPARE_MSG_LENGTH + 16)/ 4];
	
	DWORD dwTemp;
	

	int i;

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

    
    NF_nFCE_L();

    NF_CLEAR_RB();

    NF_CMD(CMD_READ);                            // Send read command.

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

    NF_CMD(CMD_READ3);							// 2nd command

    NF_DETECT_RB();								// Wait for ready to read


	// read out the spare data to check 8-bit ECC.
	// 1st copy of SectorInfo
	dwTemp = NF_RDDATA_WORD();
	dwTemp = NF_RDDATA_WORD();

	// 2nd copy of SectorInfo
	dwTemp = NF_RDDATA_WORD();
	dwTemp = NF_RDDATA_WORD();

	// 3rd copy of SectorInfo
	dwTemp = NF_RDDATA_WORD();
	dwTemp = NF_RDDATA_WORD();
	

	NF_ECC8_SETUP(ECC8_SPARE_MSG_LENGTH);
	NF_ECC8_CLEAR_DONE();
	NF_ECC8_DECODE();
	
	NF_MECC_UnLock();

	// 1-bit ECC / 512bytes  (Main data ECC)
	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();
	
	NF_ECC8_WAIT_DECODE_DONE();
	NF_ECC8_CLEAR_DECODE_DONE();
	NF_MECC_Lock();

			
	if(ECC_CorrectData(dwSector, (LPBYTE)dwBuf, ECC_CORRECT_SPARE) == FALSE)
	{
		bReturn = FALSE;
		goto CleanUp;
	}

	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 < BYTES_PER_SECTOR / 512;dwECCLoop++)
	{
	    NF_RSTECC();
	    NF_MECC_UnLock();
	
		if((DWORD)pSectorBuff & 0x3)
		{
			// Un-aligned buffer address
			for(i = 0;i < 512;i += 4)
			{
				dwTemp = NF_RDDATA_WORD();

				*pSectorBuff = dwTemp & 0xFF;			pSectorBuff++;
				*pSectorBuff = (dwTemp >> 8) & 0xFF;	pSectorBuff++;
				*pSectorBuff = (dwTemp >> 16) & 0xFF;	pSectorBuff++;
				*pSectorBuff = (dwTemp >> 24) & 0xFF;	pSectorBuff++;
			}
		}
		else
		{	
	    	_Read_512Byte(pSectorBuff);
	    	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);
	   
		dwECCError = NF_ECC_ERR0;
		
		if(ECC_CorrectData(dwSector, pSectorBuff - 512, ECC_CORRECT_MAIN) == FALSE)
	    {
	        bReturn = FALSE;
	        goto CleanUp;
		}

	}


CleanUp:

    NF_nFCE_H();

	return bReturn;

}




static void NF_Reset(void)
{
    volatile int i;

    NF_nFCE_L();

    NF_CLEAR_RB();
    NF_CMD(0xFF);    //reset command

    for(i=0; i<10; i++);  //tWB = 100ns. //??????

    NF_DETECT_RB();

    NF_nFCE_H();
}

static BOOL ReadFlashID(DWORD *pID)
{
    BYTE MID, DID, i;

    NF_nFCE_L();

    NF_CLEAR_RB();

    NF_CMD(0x90);            // Send flash ID read command.

    NF_ADDR(0x00);

    for (i=0; i<100; i++);

    for (i=0; i<10; i++)
    {
        MID = (BYTE)NF_RDDATA_BYTE();    // Manufacturer ID

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

    DID = (BYTE)NF_RDDATA_BYTE();        // Device ID

    NF_nFCE_H();

    if (MID == 0xEC)        // Samsung NAND
    {
        if (DID >= 0xA0) g_bLargeBlock = TRUE;

        *pID = (DWORD)((MID<<8) | (DID));

        return TRUE;
    }
    else if (MID == 0x98)    // xD-Picture Card
    {
        *pID = (DWORD)((MID<<8) | (DID));

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

