/****************************************************************************
 *  SPI flash driver for M25P32 
 ****************************************************************************/
#include "bfin_config.h"
#include <cdefBF537.h>
#include "stm_m25p32.h"

//Application definitions

#define	NUM_SECTORS 	64	/* number of sectors */
#define SECTOR_SIZE		0x10000
#define NOP_NUM		    1000
#define CONFIG_CCLK_HZ  MASTER_CLOCK
#define CONFIG_SPI_BAUD 2

#define COMMON_SPI_SETTINGS (SPE|MSTR|CPHA|CPOL) //Settings to the SPI_CTL
#define TIMOD01 (0x01)		//stes the SPI to work with core instructions

//Flash commands
#define SPI_WREN            (0x06)  //Set Write Enable Latch
#define SPI_WRDI            (0x04)  //Reset Write Enable Latch
#define SPI_RDSR            (0x05)  //Read Status Register
#define SPI_WRSR            (0x01)  //Write Status Register
#define SPI_READ            (0x03)  //Read data from memory
#define SPI_FAST_READ       (0x0B)  //Read data from memory
#define SPI_PP              (0x02)  //Program Data into memory
#define SPI_SE              (0xD8)  //Erase one sector in memory
#define SPI_BE              (0xC7)  //Erase all memory
#define WIP					(0x1)	//Check the write in progress bit of the SPI status register
#define WEL					(0x2)	//Check the write enable bit of the SPI status register

#define TIMEOUT 350000000

typedef enum
{
	NO_ERR,
	POLL_TIMEOUT,
	INVALID_SECTOR,
	INVALID_BLOCK,
}ERROR_CODE;
	

void spi_init_f (void);
void spi_init_r (void);

char ReadStatusRegister(void);
void Wait_For_SPIF(void);
void SetupSPI( const int spi_setting );
void SPI_OFF(void);
void SendSingleCommand( const int iCommand );
void udelay(unsigned long usec);

ERROR_CODE GetSectorNumber( unsigned long ulOffset, int *pnSector );
ERROR_CODE EraseBlock( int nBlock );
ERROR_CODE ReadData(  unsigned long ulStart, long lCount,int *pnData  );
ERROR_CODE WriteData( unsigned long ulStart, long lCount, int *pnData );
ERROR_CODE Wait_For_Status( char Statusbit );
ERROR_CODE Wait_For_WEL(void);

/* -------------------
 * Variables
 * ------------------- */

/* **************************************************************************
 *
 *  Function:    spi_init_f
 *
 *  Description: Init SPI-Controller (ROM part)
 *
 *  return:      ---
 *
 * *********************************************************************** */
void spi_init_f (void)
{
}

/* **************************************************************************
 *
 *  Function:    spi_init_r
 *
 *  Description: Init SPI-Controller (RAM part) -
 *		 The malloc engine is ready and we can move our buffers to
 *		 normal RAM
 *
 *  return:      ---
 *
 * *********************************************************************** */
void spi_init_r (void)
{
	return;
}

/****************************************************************************
 *  Function:    spi_write
 **************************************************************************** */
int spi_write (unsigned int offset, unsigned char *buffer, unsigned char *temp, int len)
{
	int	start_block,end_block;
	int start_byte,end_byte;
	ERROR_CODE result = NO_ERR;
	int i,num;
	
	//offset = addr[0]<<16 | addr[1] <<8 | addr[2];
	/* Get the start block number */
	result = GetSectorNumber(offset, &start_block);
	if(result == INVALID_SECTOR) {
		//printf("Invalid sector! ");
		return 0;
	}
	/* Get the end block number */
	result = GetSectorNumber(offset + len -1, &end_block);
	if(result == INVALID_SECTOR) {
		//printf("Invalid sector! ");
		return 0;
	}
	
	for(num = start_block;num <= end_block;num ++){
		ReadData(num*SECTOR_SIZE,SECTOR_SIZE,(int *)temp);
		start_byte = num*SECTOR_SIZE;
		end_byte   = (num+1) * SECTOR_SIZE -1;
		if(start_byte < offset) start_byte = offset;
		if( end_byte > (offset+len)) end_byte = (offset+len-1);
		for(i=start_byte;i<=end_byte;i++)
			temp[i-num*SECTOR_SIZE] = buffer[i - offset];
		EraseBlock(num);
		result = WriteData(num*SECTOR_SIZE, SECTOR_SIZE, (int *)temp);
		if(result != NO_ERR)
			return 0;
		//printf(".");
	}
	return len;
}

/****************************************************************************
 *  Function:    spi_read
 **************************************************************************** */
int spi_read (unsigned int offset, unsigned char *buffer, int len)
{
	ReadData ( offset, len, (int *)buffer );
	return len;
}

void SendSingleCommand( const int iCommand )
{
	unsigned short dummy;

	/*turns on the SPI in single write mode*/
	SetupSPI( (COMMON_SPI_SETTINGS|TIMOD01) );

	/*sends the actual command to the SPI TX register*/
	*pSPI_TDBR = iCommand;
	 __builtin_bfin_ssync();

	/*The SPI status register will be polled to check the SPIF bit*/
	Wait_For_SPIF();
	
	dummy = *pSPI_RDBR;

	/*The SPI will be turned off*/
	SPI_OFF();

}

void SetupSPI( const int spi_setting )
{
	
	/*if(icache_status()||dcache_status())
                udelay(CONFIG_CCLK_HZ/50000000);*/

	/*sets up the PF10 to be the slave select of the SPI */
	*pPORTF_FER |= ( PF10 | PF11| PF12 | PF13);
	*pSPI_FLG = 0xFD02;
	*pSPI_BAUD = CONFIG_SPI_BAUD;
	*pSPI_CTL = spi_setting;
	 __builtin_bfin_ssync();
}

void SPI_OFF(void)
{
	
	*pSPI_CTL = 0x0400;	/* disable SPI*/
	*pSPI_FLG = 0;
	*pSPI_BAUD = 0;
	 __builtin_bfin_ssync();
	udelay(CONFIG_CCLK_HZ/50000000);
	
}

void Wait_For_SPIF(void)
{
	unsigned short dummyread;
	while( (*pSPI_STAT&TXS));
	while(!(*pSPI_STAT&SPIF));
	while(!(*pSPI_STAT&RXS));
	dummyread = *pSPI_RDBR;			// Read dummy to empty the receive register	
	
}

ERROR_CODE Wait_For_WEL(void)
{
	int i;
	char status_register = 0;
	ERROR_CODE ErrorCode = NO_ERR;	/* tells us if there was an error erasing flash*/
	
		for(i = 0; i < TIMEOUT; i++)
		{
			status_register = ReadStatusRegister();
			if( (status_register & WEL) )
			{
				ErrorCode = NO_ERR;	/* tells us if there was an error erasing flash*/
				break;
			}
			ErrorCode = POLL_TIMEOUT;	/* Time out error*/
		};
		
	return ErrorCode;
}

ERROR_CODE Wait_For_Status( char Statusbit )
{
	int i;
	char status_register = 0xFF;
	ERROR_CODE ErrorCode = NO_ERR;	/* tells us if there was an error erasing flash */
	
		for(i = 0; i < TIMEOUT; i++)
		{
			status_register = ReadStatusRegister();
			if( !(status_register & Statusbit) )
			{
				ErrorCode = NO_ERR;	/* tells us if there was an error erasing flash */
				break;
			}
			ErrorCode = POLL_TIMEOUT;	/* Time out error */
		};
		
	
	return ErrorCode;
}


char ReadStatusRegister(void)
{
	char status_register = 0;

	SetupSPI( (COMMON_SPI_SETTINGS|TIMOD01) ); /* Turn on the SPI */

	*pSPI_TDBR = SPI_RDSR;	/* send instruction to read status register */
	 __builtin_bfin_ssync();
	Wait_For_SPIF();		/*wait until the instruction has been sent*/
	*pSPI_TDBR = 0;			/*send dummy to receive the status register*/
	 __builtin_bfin_ssync();
	Wait_For_SPIF();		/*wait until the data has been sent*/
	status_register = *pSPI_RDBR;	/*read the status register*/
	
	SPI_OFF();		/* Turn off the SPI */

	return status_register;
}

ERROR_CODE GetSectorNumber( unsigned long ulOffset, int *pnSector )
{
	int nSector = 0;
	ERROR_CODE ErrorCode = NO_ERR;
	
	if(ulOffset > (NUM_SECTORS*0x10000 -1)){
		ErrorCode = INVALID_SECTOR;
		return ErrorCode;
	}
			
	nSector = (int)ulOffset/0x10000;
	*pnSector = nSector;
	
	// ok
	return ErrorCode;
}

ERROR_CODE EraseBlock( int nBlock )
{
	unsigned long ulSectorOff = 0x0, ShiftValue;
	ERROR_CODE ErrorCode = NO_ERR;	


	// if the block is invalid just return
	if ( (nBlock < 0) || (nBlock > NUM_SECTORS) )
	{
		ErrorCode = INVALID_BLOCK;	// tells us if there was an error erasing flash
		return ErrorCode;
	}
	
	// figure out the offset of the block in flash
	if ( (nBlock >= 0) && (nBlock < NUM_SECTORS) )
	{
		ulSectorOff = (nBlock * SECTOR_SIZE);
		
	}
	else
	{
		ErrorCode = INVALID_BLOCK;	// tells us if there was an error erasing flash
		return ErrorCode;
	}

	// A write enable instruction must previously have been executed
	SendSingleCommand(SPI_WREN);
	
	//The status register will be polled to check the write enable latch "WREN"
	ErrorCode = Wait_For_WEL();
	
	if( POLL_TIMEOUT == ErrorCode )
	{
		//printf("SPI Erase block error\n");
		return ErrorCode;
	}
	else

	
	//Turn on the SPI to send single commands
	SetupSPI( (COMMON_SPI_SETTINGS|TIMOD01) );

	// Send the erase block command to the flash followed by the 24 address 
	// to point to the start of a sector.
	*pSPI_TDBR = SPI_SE;
	 __builtin_bfin_ssync();
	Wait_For_SPIF();			
	ShiftValue = (ulSectorOff >> 16);	// Send the highest byte of the 24 bit address at first
	*pSPI_TDBR = ShiftValue;			
	 __builtin_bfin_ssync();
	Wait_For_SPIF();					// Wait until the instruction has been sent
	ShiftValue = (ulSectorOff >> 8);	// Send the middle byte of the 24 bit address  at second
	*pSPI_TDBR = ShiftValue;
	 __builtin_bfin_ssync();
	Wait_For_SPIF();					// Wait until the instruction has been sent
	*pSPI_TDBR = ulSectorOff;			// Send the lowest byte of the 24 bit address finally
	 __builtin_bfin_ssync();
	Wait_For_SPIF();					// Wait until the instruction has been sent
	
	//Turns off the SPI
	SPI_OFF();

	// Poll the status register to check the Write in Progress bit
	// Sector erase takes time
	ErrorCode = Wait_For_Status(WIP);
	
	// block erase should be complete
	return ErrorCode;
}

/*****************************************************************************
* ERROR_CODE ReadData()
*
* Read a value from flash for verify purpose
*
* Inputs:	unsigned long ulStart - holds the SPI start address
*			int pnData - pointer to store value read from flash
*			long lCount - number of elements to read
***************************************************************************** */
ERROR_CODE ReadData(  unsigned long ulStart, long lCount,int *pnData  )
{
	unsigned long ShiftValue;
	char *cnData;
	int i;

	cnData = (char *)pnData; /* Pointer cast to be able to increment byte wise */

	// Start SPI interface	
	SetupSPI( (COMMON_SPI_SETTINGS|TIMOD01) );

#ifdef CONFIG_SPI_FLASH_FAST_READ
	*pSPI_TDBR = SPI_FAST_READ;			// Send the read command to SPI device
#else
	*pSPI_TDBR = SPI_READ;			// Send the read command to SPI device
#endif
	 __builtin_bfin_ssync();
	Wait_For_SPIF();				// Wait until the instruction has been sent
	ShiftValue = (ulStart >> 16);	// Send the highest byte of the 24 bit address at first
	*pSPI_TDBR = ShiftValue;		// Send the byte to the SPI device
	 __builtin_bfin_ssync();
	Wait_For_SPIF();				// Wait until the instruction has been sent
	ShiftValue = (ulStart >> 8);	// Send the middle byte of the 24 bit address  at second
	*pSPI_TDBR = ShiftValue;		// Send the byte to the SPI device
	 __builtin_bfin_ssync();
	Wait_For_SPIF();				// Wait until the instruction has been sent
	*pSPI_TDBR = ulStart;			// Send the lowest byte of the 24 bit address finally
	 __builtin_bfin_ssync();
	Wait_For_SPIF();				// Wait until the instruction has been sent

#ifdef CONFIG_SPI_FLASH_FAST_READ
	*pSPI_TDBR = 0;			// Send dummy for FAST_READ
	 __builtin_bfin_ssync();
	Wait_For_SPIF();				// Wait until the instruction has been sent
#endif

	// After the SPI device address has been placed on the MOSI pin the data can be
	// received on the MISO pin.
	for (i=0; i<lCount; i++)
	{
		*pSPI_TDBR = 0;			//send dummy
		 __builtin_bfin_ssync();
		while(!(*pSPI_STAT&RXS));
		*cnData++  = *pSPI_RDBR;	//read 
		
		if((i>=SECTOR_SIZE)&&(i%SECTOR_SIZE == 0))
			//printf(".");
            continue;
	}
	
	SPI_OFF();					// Turn off the SPI
	
	return NO_ERR;
}

ERROR_CODE WriteFlash ( unsigned long ulStartAddr, long lTransferCount, int *iDataSource, long *lWriteCount )
{

	unsigned long ulWAddr;
	long lWTransferCount = 0;
	int i;
	char iData;
	char *temp = (char *)iDataSource;
	ERROR_CODE ErrorCode = NO_ERR;	// tells us if there was an error erasing flash

	// First, a Write Enable Command must be sent to the SPI.
	SendSingleCommand(SPI_WREN);
	
	// Second, the SPI Status Register will be tested whether the 
	//         Write Enable Bit has been set. 
	ErrorCode = Wait_For_WEL();
	if( POLL_TIMEOUT == ErrorCode )
		{
		//printf("SPI Write Time Out\n");
		return ErrorCode;
		}
	else
	// Third, the 24 bit address will be shifted out the SPI MOSI bytewise.
	SetupSPI( (COMMON_SPI_SETTINGS|TIMOD01) ); // Turns the SPI on
	*pSPI_TDBR = SPI_PP;
	 __builtin_bfin_ssync();
	Wait_For_SPIF();		//wait until the instruction has been sent
	ulWAddr = (ulStartAddr >> 16);
	*pSPI_TDBR = ulWAddr;
	 __builtin_bfin_ssync();
	Wait_For_SPIF();		//wait until the instruction has been sent
	ulWAddr = (ulStartAddr >> 8);
	*pSPI_TDBR = ulWAddr;
	 __builtin_bfin_ssync();
	Wait_For_SPIF();		//wait until the instruction has been sent
	ulWAddr = ulStartAddr;
	*pSPI_TDBR = ulWAddr;
	 __builtin_bfin_ssync();
	Wait_For_SPIF();		//wait until the instruction has been sent
	// Fourth, maximum number of 256 bytes will be taken from the Buffer
	// and sent to the SPI device.
	for (i=0; (i < lTransferCount) && (i < 256); i++, lWTransferCount++)
	{
		iData = *temp;
		*pSPI_TDBR = iData;
		__builtin_bfin_ssync();
		Wait_For_SPIF();		//wait until the instruction has been sent
		temp++;
	}
		
	SPI_OFF(); // Turns the SPI off

	// Sixth, the SPI Write in Progress Bit must be toggled to ensure the 
	// programming is done before start of next transfer.
	ErrorCode = Wait_For_Status(WIP);
	
	if( POLL_TIMEOUT == ErrorCode )
		{
		//printf("SPI Program Time out!\n");
		return ErrorCode;
		}
	else
	
	*lWriteCount = lWTransferCount;
	
	return ErrorCode;
}


ERROR_CODE WriteData( unsigned long ulStart, long lCount, int *pnData )
{

	unsigned long ulWStart = ulStart; 
	long lWCount = lCount, lWriteCount;
	long *pnWriteCount = &lWriteCount;
	
	ERROR_CODE ErrorCode = NO_ERR;

	while (lWCount != 0)
	{
		ErrorCode = WriteFlash(ulWStart, lWCount, pnData, pnWriteCount);
		
		// After each function call of WriteFlash the counter must be adjusted
		lWCount -= *pnWriteCount;
		
		// Also, both address pointers must be recalculated.
		ulWStart += *pnWriteCount;
		pnData += *pnWriteCount/4;
	}

	// return the appropriate error code
	return ErrorCode;
}

void udelay(unsigned long usec)
{
	unsigned long delay, start, stop;
	unsigned long cclk;
	cclk = (CONFIG_CCLK_HZ);

	while ( usec > 1 ) {
	       /*
		* how many clock ticks to delay?
		*  - request(in useconds) * clock_ticks(Hz) / useconds/second
		*/
		if (usec < 1000) {
			delay = (usec * (cclk/244)) >> 12 ;
			usec = 0;
		} else {
			delay = (1000 * (cclk/244)) >> 12 ;
			usec -= 1000;
		}

		asm volatile (" %0 = CYCLES;": "=r"(start));
		do {
			asm volatile (" %0 = CYCLES; ": "=r"(stop));
		} while (stop - start < delay);
	}

	return;
}

