/*
	이 프로그램은 필히 File Specific Options에서 Opt Lavel을 None으로 선택을 하여야 함.
*/

#include "fm_samsung.h"
#include "fm_samsung_ecc.h"
#include "flash.h"
/* Flash Control Register */
#define FCR_RESET			0
#define FCR_CLE_HIGH		1
#define FCR_ALE_HIGH		2
#define FCR_WP_HIGH			4
#define FCR_SE_HIGH			8
#define FCR_CE_HIGH			16

/*Flash Command Define */
#define SEQ_DATA_INPUT		0x80
#define READ_1ST_HALF	  	0x00
#define READ_2ND_HALF	  	0x30
#define READ_SPARE		  	0x50
#define RESET_CMD		    0xFF
#define PAGE_PROGRAM	  	0x10
#define BLOCK_ERASE_1	  	0x60
#define BLOCK_ERASE_2	  	0xD0
#define ERASE_SUSPEND	  	0xB0
#define BLOCK_RESUME	  	0xD0
#define READ_STATUS		0x70
#define READ_ECC_STATUS	0x7A
#define READ_PRODUCT_ID	0x90

TFFSize ffsize;
unsigned char flash_id;
static unsigned char pageData[BYTES_PER_PAGE_1G];
int ecc_error = 0;
void FlashReset(void)
{
	FLASH_CONTROL = FCR_CE_HIGH | FCR_CLE_HIGH;		// 0x11
    	FLASH_DATA = RESET_CMD;         		
	FLASH_CONTROL = FCR_CE_HIGH;					// 0x10
	while (!(FLASH_STATUS & 0x02));

	FLASH_CONTROL = FCR_RESET;						// 0x00
}

void FlashMarkInvalidBlock(int block)
{
	unsigned int j;
	int page;
	if(flash_id == FLASH_TYPE_1G)
	{
		page = block * (PAGES_PER_BLOCK+1);
	}
	else
	{
		page = block * PAGES_PER_BLOCK;
	}	

	for (j = 0; j < 2; j++)
	{
		FlashReset();

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = READ_SPARE;
		FLASH_DATA = SEQ_DATA_INPUT;
		
		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_ALE_HIGH; 	// 0x1E
		FLASH_DATA = 0x05;
		FLASH_DATA = (page | j) & 0xFF;
		FLASH_DATA = (page >> 8) & 0xFF;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
		FLASH_DATA = 0;
		
		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = PAGE_PROGRAM;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
		while (!(FLASH_STATUS & 0x02));
		
		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = READ_STATUS;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
	}
	FLASH_CONTROL = FCR_RESET;										// 0x00
}

int FlashCheckInvalidBlock(int block)
{
	int j, page;
	unsigned char buf[2];
	
	if(flash_id == FLASH_TYPE_1G)
	{
		page = block * (PAGES_PER_BLOCK+1);
	}
	else
	{
		page = block * PAGES_PER_BLOCK;
	}	

	for (j = 0; j < 2; j++)
	{
		FlashReset();

		FLASH_CONTROL = FCR_CE_HIGH | FCR_CLE_HIGH; // 0x19
		FLASH_DATA = READ_SPARE;
		
		FLASH_CONTROL = FCR_CE_HIGH | FCR_ALE_HIGH;	// 0x1A
		FLASH_DATA = 0x05;
		FLASH_DATA = (page | j) & 0xFF;
		FLASH_DATA = (page >> 8) & 0xFF;
		
		FLASH_CONTROL = FCR_CE_HIGH;				// 0x18
		while (!(FLASH_STATUS & 0x02));
		buf[j] = FLASH_DATA;
			
		FLASH_CONTROL = FCR_RESET;					// 0x00
	}

	return (buf[0] == 0xFF && buf[1] == 0xFF);
}

unsigned char FlashReadStatus(void)
{
 	FlashReset();
                                
	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x15
 	FLASH_DATA = READ_STATUS;

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH;					// 0x14
 	return FLASH_DATA; 
}

unsigned char FlashReadProductId(void)
{
 	FlashReset();
                                
	FLASH_CONTROL = FCR_CE_HIGH | FCR_CLE_HIGH;	// 0x15
 	FLASH_DATA = READ_PRODUCT_ID;

	FLASH_CONTROL = FCR_CE_HIGH | FCR_ALE_HIGH;	// 0x14
	FLASH_DATA = 0x00;
	FLASH_CONTROL = FCR_CE_HIGH;
	flash_id = FLASH_DATA;
	flash_id = FLASH_DATA;
	flash_id = FLASH_DATA;
	if(flash_id == FLASH_TYPE_1G)
	{
		ffsize.byte_for_page = BYTES_PER_PAGE_1G;
		ffsize.page_per_block = PAGES_PER_BLOCK_1G;
		ffsize.block_per_deviec	=BLOCKS_PER_DEIVCE_1G;
		ffsize.block_max_size = BYTES_PER_PAGE_1G*PAGES_PER_BLOCK_1G;
	}
	else
	{
		ffsize.byte_for_page = BYTES_PER_PAGE;
		ffsize.page_per_block = PAGES_PER_BLOCK;
		ffsize.block_per_deviec	 = BLOCKS_PER_DEIVCE;
		ffsize.block_max_size = BYTES_PER_PAGE*PAGES_PER_BLOCK;
	}
 	return flash_id; 	
}


int FlashEraseBlock(int block)
{
	int page;
	unsigned char row_add_low, row_add_high;
	if(flash_id == FLASH_TYPE_1G)
	{
		page = ((block)*64*(2048));//(block) * (ffsize.page_per_block);
		row_add_low = (page>>11)&0xFF;
		row_add_high = (page>>19)&0xFF;
	}
	else
	{
		page = block * PAGES_PER_BLOCK;
	}

	FlashReset();

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x15
	FLASH_DATA = BLOCK_ERASE_1;
	
	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_ALE_HIGH;	// 0x16
	if(flash_id == FLASH_TYPE_1G)
	{
		FLASH_DATA = row_add_low;
		FLASH_DATA = row_add_high;
	}
	else
	{
		FLASH_DATA = (unsigned char)(page & 0xFF);
		FLASH_DATA = (unsigned char)((page >> 8) & 0xFF);
	}
	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x15
	FLASH_DATA = BLOCK_ERASE_2;

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH;					// 0x14
	while (!(FLASH_STATUS & 0x02));

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x15
	FLASH_DATA = READ_STATUS;

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH;					// 0x14
	if (FLASH_DATA & 0x01)
	{
		FLASH_CONTROL = FCR_RESET;								// 0x00
		return 0;			//실패의 경우
	} 

	FLASH_CONTROL = FCR_RESET;									// 0x00
	return 1;
}

static void FlashReadECC(int page, unsigned char *ecc)
{
	FlashReset();

	FLASH_CONTROL = FCR_CE_HIGH | FCR_CLE_HIGH; // 0x19
	FLASH_DATA = READ_SPARE;
		
	FLASH_CONTROL = FCR_CE_HIGH | FCR_ALE_HIGH;	// 0x1A
	FLASH_DATA = 6;
	FLASH_DATA = page & 0xFF;
	FLASH_DATA = (page >> 8) & 0xFF;
	
	FLASH_CONTROL = FCR_CE_HIGH;				// 0x18
	while (!(FLASH_STATUS & 0x02));
	ecc[0] = (unsigned char)(FLASH_DATA & 0xFF);
	ecc[1] = (unsigned char)(FLASH_DATA & 0xFF);
	ecc[2] = (unsigned char)(FLASH_DATA & 0xFF);

	FLASH_CONTROL = FCR_RESET;					// 0x00
}


int FlashReadECCStatus()
{
	int ecc_status=0;
	FlashReset();

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH; // 0x19
	FLASH_DATA = READ_ECC_STATUS;
		
	FLASH_CONTROL = FCR_CE_HIGH;	// 0x1A
	while (!(FLASH_STATUS & 0x02));
//	ecc_status1 = FLASH_DATA;
	ecc_status |= FLASH_DATA&0xF;
	ecc_status |= FLASH_DATA&0xF;
	ecc_status |= FLASH_DATA&0xF;
	ecc_status |= FLASH_DATA&0xF;		
//	if(!ecc_status) return 0;

	FLASH_CONTROL = FCR_RESET;					// 0x00
	return 1;
}

static int FlashWriteECC(int page, unsigned char *ecc)
{
	FlashReset();

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
	FLASH_DATA = READ_SPARE;		
	FLASH_DATA = SEQ_DATA_INPUT;
	 
	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_ALE_HIGH; 	// 0x1E
	FLASH_DATA = 6;
	FLASH_DATA = page & 0xFF;
	FLASH_DATA = (page >> 8) & 0xFF;

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
	while (!(FLASH_STATUS & 0x02));
	FLASH_DATA = ecc[0];
	FLASH_DATA = ecc[1];
	FLASH_DATA = ecc[2];

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
	FLASH_DATA = PAGE_PROGRAM;

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
	while(!(FLASH_STATUS & 0x02));

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
	FLASH_DATA = READ_STATUS;

	FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
	if (FLASH_DATA & 0x01) return 0;							//invalid Block

	return 1;
}

int FlashReadPage(unsigned char *dst, int page, int count)
{
	int i;
	unsigned char compare1_data[3];
	unsigned char eccGen[3];
	unsigned short row_add_low, row_add_high;
	memset(pageData, 0, ffsize.byte_for_page);
	FlashReset();
	if(flash_id == FLASH_TYPE_1G)
	{
		row_add_low = (page>>11)&0xFF;
		row_add_high = (page>>19)&0xFF;	
		FLASH_CONTROL = FCR_CE_HIGH | FCR_CLE_HIGH; 	// 0x19
		FLASH_DATA = READ_1ST_HALF;
			
		FLASH_CONTROL = FCR_CE_HIGH | FCR_ALE_HIGH;		// 0x1A
		FLASH_DATA = 0;//col_add_low;
		FLASH_DATA = 0;//col_add_high;
		FLASH_DATA = row_add_low;
		FLASH_DATA = row_add_high;
		FLASH_CONTROL = FCR_CE_HIGH | FCR_CLE_HIGH; 	// 0x19
		FLASH_DATA= READ_2ND_HALF;	
		FLASH_CONTROL = FCR_CE_HIGH;					// 0x18
		while (!(FLASH_STATUS & 0x02));
		for (i = 0; i < count; i++)
		{
			pageData[i] = (unsigned char)(FLASH_DATA & 0xFF);
		}

		FLASH_CONTROL = FCR_RESET;	
		
		if(FlashReadECCStatus())
		{
			memcpy(dst, pageData, count);
		}
		else
		{
			ecc_error++;
			return 0;
		}
	}
	else
	{
		FLASH_CONTROL = FCR_CE_HIGH | FCR_CLE_HIGH; 	// 0x19
		FLASH_DATA = READ_1ST_HALF;
			
		FLASH_CONTROL = FCR_CE_HIGH | FCR_ALE_HIGH;		// 0x1A
		FLASH_DATA = 0;
		FLASH_DATA = page & 0xFF;
		FLASH_DATA = (page >> 8) & 0xFF;
		
		FLASH_CONTROL = FCR_CE_HIGH;					// 0x18
		while (!(FLASH_STATUS & 0x02));
		for (i = 0; i < count; i++)
		{
			pageData[i] = (unsigned char)(FLASH_DATA & 0xFF);
		}
		
		FLASH_CONTROL = FCR_RESET;						// 0x00
		
		FlashReadECC(page, compare1_data);

		FlashEccGen(pageData, eccGen);

		if ((compare1_data[0] != eccGen[0]) || (compare1_data[1] != eccGen[1]) || (compare1_data[2] != eccGen[2]))
		{
			if (FlashEccCheck(pageData, compare1_data, eccGen))
			{
				memcpy(dst, pageData, count);
			} 
			else
				return 0;
		} 
		else
		{
			memcpy(dst, pageData, count);
		}	
	} 
	return 1;		         					// valid Data인 경우
}

int FlashWritePage(unsigned char *Write_Data, int page, int count)
{
	int i;
	unsigned char eccGen[3];
 	static unsigned char buffer[BYTES_PER_PAGE_1G];
	unsigned short row_add_low, row_add_high;
	memset(pageData, 0, ffsize.byte_for_page);
	memcpy(pageData, Write_Data, count);
	if(flash_id == FLASH_TYPE_1G)
	{
		row_add_low = (page>>11)&0xFF;
		row_add_high = (page>>19)&0xFF;	
		FlashReset();
		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = READ_1ST_HALF;		
		FLASH_DATA = SEQ_DATA_INPUT;
	     
		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_ALE_HIGH; 	// 0x1E
		FLASH_DATA = 0;//col_add_low;
		FLASH_DATA = 0;//col_add_high;
		FLASH_DATA = row_add_low;
		FLASH_DATA = row_add_high;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
		while (!(FLASH_STATUS & 0x02));
		for (i = 0; i < count; i++)
		{
		   	FLASH_DATA = pageData[i];
		} 

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = PAGE_PROGRAM;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
		while(!(FLASH_STATUS & 0x02));

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = READ_STATUS;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
		if (FLASH_DATA & 0x01) 
		{
			return 0;
		}							//invalid Block
		if (!FlashReadPage(buffer, page, count)) return 0;

		for (i = 0; i < count; i++)
			if (Write_Data[i] != buffer[i]) 
				return 0;		        //invalid Block

	}
	else
	{	
		FlashEccGen(pageData, eccGen);
		FlashReset();
		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = READ_1ST_HALF;		
		FLASH_DATA = SEQ_DATA_INPUT;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_ALE_HIGH; 	// 0x1E
		FLASH_DATA = 0;
		FLASH_DATA = page & 0xFF;
		FLASH_DATA = (page >> 8) & 0xFF;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
		while (!(FLASH_STATUS & 0x02));
		for (i = 0; i < count; i++)
		{
			FLASH_DATA = pageData[i];
		} 

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = PAGE_PROGRAM;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
		while(!(FLASH_STATUS & 0x02));

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH | FCR_CLE_HIGH;	// 0x1D
		FLASH_DATA = READ_STATUS;

		FLASH_CONTROL = FCR_CE_HIGH | FCR_WP_HIGH; 					// 0x1C
		if (FLASH_DATA & 0x01) return 0;							//invalid Block

		if (!FlashWriteECC(page, eccGen)) return 0;

		if (!FlashReadPage(buffer, page, count)) return 0;

		for (i = 0; i < count; i++)
		if (Write_Data[i] != buffer[i]) 
			return 0;		        //invalid Block
	}
	return 1;
}
