/*
*************************************************************************************************
*	  					NAND FLASH driver for PIC24 (16-bit)using PMP Module 
*										All Rights Reserved
* File name		: HY27US08561A.c
* Programmer 	: John Leung, TechToys Co. Hong Kong
* Web presence  : www.TechToys.com.hk
* Language		: C30 complier version 3.01, MPLAB v7.60
* Hardware		: PIC24-Eval Rev C + TY280T-240320-BO Rev 1B
* Disclaimer	: THIS DRIVER IS PROVIDED "AS IS" WITH NO WARRANTY
*                 WHATSOEVER AND TECHTOYS COMPANY SPECIFICALLY DISCLAIMS ANY
*                 IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
*                 A PARTICULAR PURPOSE, OR AGAINST INFRINGEMENT.
*
*                 Copyright @2008 TECHTOYS COMPANY 
*                 All rights researved
*
* Rev			  Author	  		Date				Comment
* ---			  ------			----				-------
* 1.0a			 John Leung			22 Jan 2009			Initial Release
*************************************************************************************************
*										DESCRIPTION
*
* This module provides an interface to NAND Flash Hynix HY27US08561A for PIC24FJ128GA010 (PIM)
* Only basic I/O function is supported in this version. Therefore, no FAT is supported!
*
* Connect UART2 of the board to any standard RS232 monitoring program like hyperterminal
* Set the baud rate to be 19200bps, 8-data bit, 1-stop bit, no parity with no handshake.
* Follow the instruction on screen you will be able to write different content to the first
* block of the nand Flash at your wish.
*
* Remarks:  it is important to note that prior to writing to a block, 
*			block erase has to be performed; otherwise, inaccurate data would be written to the
*			same block. One may experiment with it with different procedures than 1-2-3-4.
*************************************************************************************************
*/

#include <p24Fxxxx.h>
#define  USE_AND_OR
#include "timer.h"
#include "HY27US08561A.h"

/*
*************************************************************************************************
*                         				NAND FLASH INITIALIZATION
*
* Description : This function initializes pin direction & PMP module (PIC24 series only)		
* Arguments   : none
*			
* Returns     : none
* Note		  : This function should be called once before any of the other functions
*				The WRITE PROTECT pin enabled in this function
*************************************************************************************************
*/
void nandInit(void)
{
	//set CE pin as output high to de-select NAND Flash
	NAND_CE_LAT = HIGH;
	NAND_CE_TRIS = OUTPUT;
	//set CLE output low to avoid command write by accident
	NAND_CLE_LAT = LOW;
	NAND_CLE_TRIS = OUTPUT;
	//set ALE output low to avoid address write by accident
	NAND_ALE_LAT = LOW;
	NAND_ALE_TRIS = OUTPUT;
	//write protect NAND Flash device
	NAND_WP_LAT = LOW;
	NAND_WP_TRIS = OUTPUT;
	//set READY_BUSY pin as an input, and wait until RB pin returns high for device ready signal
	NAND_RB_TRIS = INPUT;

/*
	// Set RD pin as output
	NAND_RD_TRIS_BIT = 0;
	NAND_RD_LAT_BIT = 1;

	// Set WR pin as output
	NAND_WR_TRIS_BIT = 0;
	NAND_WR_LAT_BIT = 1;
	
	NAND_DATA_TRIS = 0x0000;
*/
	//setup PMP module
    PMMODEbits.MODE  = 0b10;  	// Master 2
    PMMODEbits.WAITM = 0b0000;
	PMCONbits.WRSP   = 0;		//WR low active
	PMCONbits.RDSP	 = 0;		//RD low active
    PMCONbits.PTRDEN = 1;    
    PMCONbits.PTWREN = 1;
    PMCONbits.PMPEN  = 1; 	  	//enable PMP module

	//Fosc/2 = 16MHz, prescaled 1:256, it is 16us/tick
	OpenTimer1((T1_ON|T1_PS_1_256),0xFFFF);
}


/*
*************************************************************************************************
*                         				READ DEVICE ID ROUTINE
*
* Description : Every NAND Flash contains a product identification.
*				This function reads the device ID by writing 0x90(NAND_READ_ID) to the command 
*				register, followed by an address input of 0x00. Two read cycles sequentially
*				output the manufacture code (0xAD) for Hynix, and the device code (0x75) resp 
*				after a waiting time of tAR defined as Nop() under HY27US08561A.h file		
* Arguments   : none
*			
* Returns     : Device ID of the NAND Flash
* Note		  : The manufacturer code varies from every semiconductor supplier. For Samsung, it 
*				is 0xEC instead of 0xAD
*				This function is a device specific function as PMP Read feature has been used.
*************************************************************************************************
*/
INT16U nandChkID(void)
{
	INT16U id;

	NAND_CE_LAT = LOW;	//select the NAND Flash here

	nandWrCmd(NAND_READ_ID);

	nandWrAddr(0x00);
	//wait tAR here for Nop()
	PMPDelay();

	id = NAND_DATA;				//dummy read for PMP module
	PMPDelay();
	id = NAND_DATA;
	PMPDelay();
	id = id<<8;
	id = id + NAND_DATA;

	NAND_CE_LAT	= HIGH;			//de-select the NAND Flash

	return id;
}

/*
*************************************************************************************************
*                         				READ STATUS FUNCTION
*
* Description : The NAND Flash contains a Status Register which may be read to find out whether
*				read, program or erase operation is completed, and whether the program or erase
*				operation has been completed successfully. 
*				After writing command NAND_READSTATUS(0x70) to the command register, a read cycle
*				outputs the content of the Status Register to the I/O pins on the falling edge
*				of CE or RE (PMRD) whichever occurs last.
*				  	
* Arguments   : none
*			
* Returns     : Result of an operation. Please refer to definition above for these
*				NAND_RS_SUCCESS (0)
*				NAND_RS_FAIL	(1)
* Note		  : 
*************************************************************************************************
*/
INT8U	nandRdStatus(void)
{
	INT8U status;

	nandWrCmd(NAND_READSTATUS);

	PMPDelay();

	status = NAND_DATA;	//dummy read
	PMPDelay();
	status = NAND_DATA&0x01;

	return status;
	
}

/*
*************************************************************************************************
*                         				BLOCK ERASE FUNCTION
*
* Description : This erase operation is done on a block (16k byte) basis! 
*				A page is (512+16spare)bytes, and 32 pages comprises a Block.
*				This function writes NAND_BLOCKERASE1 (0x60) being the Block Erase Command 1st 
*				cycle then the block address; finally the NAND_BLOCKERASE2 (0xD0) to initiate
*				the erase process. This function erases (returns all bytes to 0xFF) for the whole
*				block
* Arguments   : 'INT16U' block denotes the block address to erase
*			
* Returns     : Result of operation
*				NAND_RS_SUCCESS
*				NAND_RS_FAIL
*				NAND_RB_TIMEOUT
*
* Note		  : It is a MUST to unlock the WRITE PROTECT prior to block erase.
*				This function lock the WRITE PROTECT upon exit
*************************************************************************************************
*/
INT8U 	nandEraseBlk(INT16U block)
{
	INT16U blockNum;
	INT8U status;

	blockNum = block<<5;	//5 bit shift for valid address from A14-A24, address A9:A13 ignored
							//Why? Because 2^11 = 2,048. Bits A14:24 totally 11 bits, accounts
							//for altogether 2,048 blocks in 32MByte NAND Flash.
	NAND_WP_LAT = HIGH;		//release the write protect
	NAND_CE_LAT = LOW;		//select the NAND Flash here

	nandWrCmd(NAND_BLOCKERASE_C1);
	nandWrAddr((INT8U)(blockNum&0xff));				//write lower byte first
	nandWrAddr((INT8U)((blockNum>>8)&0xff));		//write higher byte
	nandWrCmd(NAND_BLOCKERASE_C2);					//confirm the erase operation here
	
	//wait for tWB for a max 100ns
	PMPDelay();
	PMPDelay();
	//wait for tBERS typical 2ms, 3ms maximum
	TMR1 = 0;
	while(TMR1<188);								//188*16us = 3ms	
	//wait for RB returns high, if NAND_RB_PORT still low, a timeout error occurred.
	if (NAND_RB_PORT==LOW) 
		status = NAND_RB_TIMEOUT;
	else
		status = nandRdStatus();

	NAND_CE_LAT = HIGH;	//de-select the NAND Flash here	
	NAND_WP_LAT = LOW;	//write protect the device

	return status;
}

/*
*************************************************************************************************
*                         				PAGE WRITE FUNCTION
*
* Description : This function writes a NAND Flash page.
*
* Arguments   : block		- the block to program
*				page 		- programming starts from this page address. There are 64k pages in our 
*							  NAND Flash model
*				col  		- programming starts from this column address. There are 512+16 columns
*				* buffer	- data buffer with data to be programmed to NAND Flash
*
* Returns     : Result of operation
*				NAND_RS_SUCCESS
*				NAND_RS_FAIL
*				NAND_RB_TIMEOUT
*
* Note		  : It is a MUST to unlock the WRITE PROTECT prior to block erase.
*				Prior to program a particular page, it is required to perform a block erase first
*				This function lock the WRITE PROTECT upon exit
*************************************************************************************************
*/
INT8U 	nandWrPage(INT16U block, INT16U page, INT16U col, INT8U* buffer)
{
	INT16U blockPage, i;
	INT8U status;

	blockPage = (block<<5) + page;

	NAND_WP_LAT = HIGH;							//release the write protect
	NAND_CE_LAT = LOW;							//select the NAND Flash here

	nandWrCmd(NAND_PAGE_PROG_C1);				//write the 1st cycle of command for page program
	nandWrAddr(col);							//write the column address
	nandWrAddr((INT8U)(blockPage&0xff));		//write the lower page address
	nandWrAddr((INT8U)((blockPage>>8)&0xff));	//write the higher page address

	for(i=0;i<NAND_PAGE_SIZE_BYTE;i++) 
		nandWrDat(*buffer++);

	nandWrCmd(NAND_PAGE_PROG_C2);		//confirm NAND Flash program by writing the 2nd command cycle

	//wait for tWB for a max 100ns
	PMPDelay();
	PMPDelay();

	//wait for tPROG here typically 200us and max. 500us
	TMR1 = 0;
	while(TMR1<32);						//32*16us = 512us

	//wait for RB returns high, if NAND_RB_PORT still low, a timeout error occurred.
	if (NAND_RB_PORT==LOW)
		status = NAND_RB_TIMEOUT;
	else
		status = nandRdStatus();

	NAND_WP_LAT = LOW;					//write protect the device
	NAND_CE_LAT = HIGH;					//de-select the NAND Flash here	

	return status;
	
}

/*
*************************************************************************************************
*                         				PAGE READ FUNCTION
*
* Description : This function reads from a NAND Flash page.
*
* Arguments   : block		- the block to read from
*				page 		- programming starts from this page address. There are 64k pages in our 
*							  NAND Flash model
*				col  		- programming starts from this column address. There are 512+16 columns
*				* buffer	- data buffer for data read from NAND ARRAYs
*
* Returns     : None
*************************************************************************************************
*/

void nandRdPage(INT16U block, INT16U page, INT16U col, INT8U* buffer)
{
	INT16U blockPage, i;

	blockPage = (block<<5) + page;

	NAND_CE_LAT = LOW;					//select the NAND Flash here

	nandWrCmd(NAND_READ0);				//write 0x00 command
	nandWrAddr(col);					//write the column address
	nandWrAddr((INT8U)(blockPage&0xff));		//write the lower page address
	nandWrAddr((INT8U)((blockPage>>8)&0xff));//write the higher page address

	//wait for tR for a max 12us
	TMR1 = 0;
	while(TMR1<1);						//1*16us tick = 16us

	//NAND_DATA_TRIS = 0xFFFF;			//set port an input

	i = NAND_DATA;						//dummy read

	for(i=0;i<NAND_PAGE_SIZE_BYTE;i++) 	
	{
		buffer[i] = NAND_DATA;
	}

	NAND_CE_LAT = HIGH;					//de-select the NAND Flash here	
	//NAND_DATA_TRIS = 0x0000;			//reset port to be an output
}