#include "SPI_Flash.h"
#include "SPI.h"
#include "BoardCfg.h"

unsigned char mSPIFlashBaudrate;
unsigned char mSPIFlashHighSpeedBaudrate;
unsigned char mFlashSectorBuffer[SPI_FLASH_SECTOR_SIZE];

int InitSPIFlash()
{
    FLASH_SS_PIN = 1;
    
    mSPIFlashBaudrate = SPICalculateBRG(PERIPHERAL_FREQ, 25000000);
//    mSPIFlashHighSpeedBaudrate = SPICalculateBRG(PERIPHERAL_FREQ, 50000000);
	mSPIFlashHighSpeedBaudrate = SPICalculateBRG(PERIPHERAL_FREQ, 35000000);
}

int SPIFlashCheckAndConfigure()
{
    
    if(SPIFlashCheckChipID() == RET_OK)
    {
        SPIFlashWriteEnable();
        
        FLASH_SS_PIN = 0;
        SPITransaction(SPI_FLASH_WRITE_STATUS_REG,mSPIFlashHighSpeedBaudrate); 
        SPITransaction(0x00,mSPIFlashHighSpeedBaudrate);        //Configure for write enable the whole memory
        FLASH_SS_PIN = 1;
        
        SPIFlashReadStatusReg(1);
        
      //  printf("SPI Flash configured\n");
		char mbuf[30];
		mbuf[0] = 'S';
		mbuf[1] = 'P';
		mbuf[2] = 'I';
		mbuf[3] = ' ';
		mbuf[4] = 'F';
		mbuf[5] = 'l';
		mbuf[6] = 'a';
		mbuf[7] = 's';
		mbuf[8] = 'h';
		mbuf[9] = ' ';
		mbuf[10] = 'C';
		mbuf[11] = 'o';
		mbuf[12] = 'n';
		mbuf[13] = 'f';
		mbuf[14] = 'i';
		mbuf[15] = 'g';
		mbuf[16] = 'u';
		mbuf[17] = 'r';
		mbuf[18] = 'e';
		mbuf[19] = 'd';
		mbuf[20] = '\n';
		mbuf[21] = '\r';
		SendInternalUartData(&mbuf[0],22);
		
        return RET_OK;
    }
    
   // printf("ERROR: SPI Flash not detected\n");
    return RET_ERROR;
    
}

int SPIFlashWriteEnable()
{
    FLASH_SS_PIN = 0;
    SPITransaction(SPI_FLASH_WRITE_ENABLE,mSPIFlashHighSpeedBaudrate);
    FLASH_SS_PIN = 1;
    
    return RET_OK;
}



unsigned char SPIFlashReadStatusReg(int print)
{
    unsigned char result;
    
    FLASH_SS_PIN = 0;
    result = SPITransaction(SPI_FLASH_READ_STATUS_REG,mSPIFlashBaudrate);
    result = SPITransaction(0x00,mSPIFlashBaudrate);    //get data
    FLASH_SS_PIN = 1;
    
    if(print)
    {
       // printf("Flash status register : 0x%x\n",result);
    }
    
    return result;
    
}

int SPIFlashCheckBusy()
{
    unsigned char status = SPIFlashReadStatusReg(0);
    if((status & SPI_FLASH_BUSY_MASK) != 0)
    {
        return 1;
    }
    
    return 0;
}


int SPIFlashCheckChipID()
{
    unsigned char VendorID, ChipID;
    
    FLASH_SS_PIN = 0;
    SPITransaction(SPI_FLASH_READ_ID,mSPIFlashBaudrate);
    SPITransaction(0x00,mSPIFlashBaudrate);
    SPITransaction(0x00,mSPIFlashBaudrate);
    SPITransaction(0x00,mSPIFlashBaudrate);    //Vendor address
    VendorID = SPITransaction(0x00,mSPIFlashBaudrate);    //Vendor ID, should be 0xBF
    ChipID = SPITransaction(0x00,mSPIFlashBaudrate);    //Device ID, should be 0x41
    FLASH_SS_PIN = 1;
    
    if(VendorID != SPI_FLASH_VENDOR_ID || ChipID != SPI_FLASH_CHIP_ID)
    {
       // printf("SPI Flash detection FAILED.  Vendor: 0x%x, Chip ID: 0x%x\n",VendorID,ChipID);
        return RET_ERROR;
    }
  //  printf("SPI Flash detected.  Vendor: 0x%x, Chip ID: 0x%x\n",VendorID,ChipID);
    return RET_OK;
    
}


int SPIFlashReadBuffer(unsigned char *Buf, int Size, int StartAddress)
{
    if(StartAddress + Size - 1  > SPI_FLASH_MAX_ADDRESS)
    {
        return RET_ERROR;
    }
    
    FLASH_SS_PIN = 0;
    SPITransaction(SPI_FLASH_HI_SPEED_READ,mSPIFlashBaudrate);
	SPITransaction(((StartAddress & 0xFF0000) >> 16),mSPIFlashHighSpeedBaudrate);
	SPITransaction(((StartAddress & 0x00FF00) >> 8),mSPIFlashHighSpeedBaudrate);
	SPITransaction((StartAddress & 0x0000FF),mSPIFlashHighSpeedBaudrate);
    SPITransaction((0x00),mSPIFlashHighSpeedBaudrate);      //Chip requires a dummy read in high speed
    
    int i;
    for(i = 0; i < Size; i++)
    {
		unsigned char tmp;
		tmp =SPITransaction(0xDE,mSPIFlashHighSpeedBaudrate);
		*Buf++ = tmp;
 //       *Buf++ = SPITransaction(0xDE,mSPIFlashHighSpeedBaudrate);
    }
    
    FLASH_SS_PIN = 1;
    
    return RET_OK;
}

int SPIFlashEraseSector(int SectorAddress)
{
    if(SectorAddress % SPI_FLASH_SECTOR_SIZE != 0)  //Sectors are aligned on 0x1000
    {
        return RET_ERROR;      
    }
    
    SPIFlashWriteEnable();
    
    FLASH_SS_PIN = 0;
    SPITransaction(SPI_FLASH_4KB_SECOTR_ERASE,mSPIFlashHighSpeedBaudrate); 
    SPITransaction(((SectorAddress & 0xFF0000) >> 16),mSPIFlashHighSpeedBaudrate);
    SPITransaction(((SectorAddress & 0x00FF00) >> 8),mSPIFlashHighSpeedBaudrate);
    SPITransaction((SectorAddress & 0x0000FF),mSPIFlashHighSpeedBaudrate);
    FLASH_SS_PIN = 1;
    
    SectorAddress++;
    
    while( SPIFlashCheckBusy() == true);
	//SPIFlashWriteEnable();
    
    return RET_OK;
}

int SPIFlashErase64KSector(int SectorAddress, int Blocking)
{
	if(SectorAddress % SPI_FLASH_64K_SECTOR_SIZE != 0)  //Sectors are aligned on 0x1000
    {
        return RET_ERROR;      
    }
	if((SectorAddress + SPI_FLASH_64K_SECTOR_SIZE - 1) > SPI_FLASH_MAX_ADDRESS)
    {
        return RET_ERROR;      
    }
	
    SPIFlashWriteEnable();
    
    FLASH_SS_PIN = 0;
    SPITransaction(SPI_FLASH_64KB_BLOCK_ERASE,mSPIFlashHighSpeedBaudrate); 
    SPITransaction(((SectorAddress & 0xFF0000) >> 16),mSPIFlashHighSpeedBaudrate);
    SPITransaction(((SectorAddress & 0x00FF00) >> 8),mSPIFlashHighSpeedBaudrate);
    SPITransaction((SectorAddress & 0x0000FF),mSPIFlashHighSpeedBaudrate);
    FLASH_SS_PIN = 1;
    
    if(Blocking != 0)
	{
		while( SPIFlashCheckBusy() == true); 
	//	SPIFlashWriteEnable();
	}
    
    return RET_OK;
}



int SPIFlashWriteSectorWorkingBuffer(int SectorAddress, int Erase)
{
    if(SectorAddress % SPI_FLASH_SECTOR_SIZE != 0)  //Sectors are aligned on 0x1000
    {
        return RET_ERROR;      
    }

    if(Erase == 1)
    {
        SPIFlashEraseSector(SectorAddress);
    }
    
    unsigned char *DataPtr = &mFlashSectorBuffer[0];
    int j;
    for(j = 0; j < SPI_FLASH_SECTOR_SIZE; j++)
    {
		unsigned char curbyte;
		curbyte = *DataPtr;
        SPIFlashWriteEnable();
		

		
		char Add1, Add2, Add3;
		Add1 = (unsigned char)((SectorAddress & 0xFF0000) >> 16);
		Add2 = ((unsigned char)((SectorAddress & 0x00FF00) >> 8));
		Add3 = ((unsigned char)(SectorAddress & 0x0000FF));
		
		int t;
		t = 0;	
        
        FLASH_SS_PIN = 0;
        SPITransaction(SPI_FLASH_BYTE_PROGRAM,mSPIFlashHighSpeedBaudrate); 
//        SPITransaction((unsigned char)((SectorAddress & 0xFF0000) >> 16),mSPIFlashHighSpeedBaudrate);
//        SPITransaction((unsigned char)((SectorAddress & 0x00FF00) >> 8),mSPIFlashHighSpeedBaudrate);
//        SPITransaction((unsigned char)(SectorAddress & 0x0000FF),mSPIFlashHighSpeedBaudrate);
		SPITransaction(Add1,mSPIFlashHighSpeedBaudrate);
        SPITransaction(Add2,mSPIFlashHighSpeedBaudrate);
        SPITransaction(Add3,mSPIFlashHighSpeedBaudrate);

		SPITransaction(curbyte,mSPIFlashHighSpeedBaudrate);
//        SPITransaction(*DataPtr,mSPIFlashHighSpeedBaudrate);
        FLASH_SS_PIN = 1;
        
		DataPtr++;
        SectorAddress++;
        
        while( SPIFlashCheckBusy() == true);
    }
    
     return RET_OK;
}

int SPIFlashWriteByte(unsigned int ByteAddress, char byte, int blocking)
{
	if(ByteAddress > SPI_FLASH_MAX_ADDRESS)
	{
		return RET_ERROR;
	}
	
	SPIFlashWriteEnable();
        
    FLASH_SS_PIN = 0;
    SPITransaction(SPI_FLASH_BYTE_PROGRAM,mSPIFlashHighSpeedBaudrate); 
    SPITransaction(((ByteAddress & 0xFF0000) >> 16),mSPIFlashHighSpeedBaudrate);
    SPITransaction(((ByteAddress & 0x00FF00) >> 8),mSPIFlashHighSpeedBaudrate);
    SPITransaction((ByteAddress & 0x0000FF),mSPIFlashHighSpeedBaudrate);
    SPITransaction(byte,mSPIFlashHighSpeedBaudrate);
    FLASH_SS_PIN = 1;
        
    if(blocking)
	{        
		while( SPIFlashCheckBusy() == true);
	}
	
	
	return RET_OK;
	
}



//int SPIFlashEraseSectorForWrite(unsigned char StartAddress, int Size)
//{
//    //First, let's determine which sector to erase.
//    
//    int NbSectors = 1;
//    int FirstSector = StartAddress / 0x1000;
//    int LastSector = (StartAddress + Size) / 0x1000;
//    
//    if(LastSector > FirstSector)
//    {
//        NbSectors = LastSector - FirstSector;
//    }
//    
//    int i;
//    for(i = FirstSector; i < LastSector; i++ ) //Erase each sector one by one
//    {
//        
//    }
//    
//}

//int SPIFlashWriteBuffer(unsigned char *Buf, int Size, int StartAddress)
//{
//    //First, we need to determine if the data overlaps or uses more than one sector
//    //First, let's determine which sector to erase.
//    int EndAddress = StartAddress + Size - 1;
//    if(EndAddress > SPI_FLASH_MAX_ADDRESS)
//    {
//        return RET_ERROR;
//    }
//    
//    int NbSectors = 1;
//    int FirstSector = StartAddress / SPI_FLASH_SECTOR_SIZE;
//    int LastSector = EndAddress / SPI_FLASH_SECTOR_SIZE;
//    
//    if(LastSector > FirstSector)
//    {
//        NbSectors = LastSector - FirstSector + 1;
//    }
//    
//    int i;
//    int FlashAddress = StartAddress;
//    int CurSector = FirstSector; 
//    for(i = 0; i < NbSectors; i++ ) //Read, erase and write each sector one by one
//    {
//        //first we need to backup the data outside our buffer.  
//        //TODO: optimize
//        int SectorStartAddress = CurSector++ * SPI_FLASH_SECTOR_SIZE;
//        int SectorEndAddress = SectorStartAddress + SPI_FLASH_SECTOR_SIZE - 1;
//        if(SectorEndAddress > EndAddress)
//        {
//            SectorEndAddress = EndAddress;
//        }
//        
//        SPIFlashReadBuffer(mFlashSectorBuffer,SPI_FLASH_SECTOR_SIZE,SectorStartAddress);    //Get local RAM buffer of the sector
//        
//        //Update the data to write.
//        int RAMAddress = FlashAddress - SectorStartAddress;
//        int RAMSectorSize = SectorEndAddress - FlashAddress + 1;
//        FlashAddress += RAMSectorSize;
//        
//        unsigned char* RAMPtr = &mFlashSectorBuffer[RAMAddress];
//        int j;
//        for(j = 0; j < RAMSectorSize; j++)
//        {
//            *RAMPtr++ = *Buf++;
//        }
//        
//        int SectorAddress = SectorStartAddress;
//        RAMPtr = mFlashSectorBuffer;
//        
//        for(j = 0; j < SPI_FLASH_SECTOR_SIZE; j++)
//        {
//            SPIFlashWriteEnable();
//            
//            FLASH_SS_PIN = 0;
//            SPITransaction(SPI_FLASH_BYTE_PROGRAM,mSPIFlashHighSpeedBaudrate); 
//            SPITransaction(((SectorAddress & 0xFF0000) >> 16),mSPIFlashBaudrate);
//            SPITransaction(((SectorAddress & 0x00FF00) >> 8),mSPIFlashBaudrate);
//            SPITransaction((SectorAddress & 0x0000FF),mSPIFlashBaudrate);
//            SPITransaction(*RAMPtr++,mSPIFlashBaudrate);
//            FLASH_SS_PIN = 1;
//            
//            SectorAddress++;
//            
//            while( SPIFlashCheckBusy() == true);
//        }
//    }
//}

int SPIFlashWriteBuffer(unsigned char *Buf, int Size, int StartAddress)
{
    int CurDataFlashAddress, DataFlashEndAddress;
    char *DataBufPtr, *WorkPagePtr;
    int WriteFinished;
    
    
    //Init stuff
    WriteFinished = 0;
    DataFlashEndAddress = StartAddress + Size;      //Start + size means the data at "DataFlashEndAddress" should not be written.
    CurDataFlashAddress = StartAddress;
    DataBufPtr = Buf;
    
    if(DataFlashEndAddress > SPI_FLASH_MAX_ADDRESS)
    {
        return RET_ERROR;
    }
    
    while(WriteFinished == 0)
    {
        //Determine the current sector start address.
        int SectorStartAddress;
        SectorStartAddress = ((CurDataFlashAddress / SPI_FLASH_SECTOR_SIZE) * SPI_FLASH_SECTOR_SIZE);      //Weird but it works
        
        //Load the sector in RAM working buffer
        if(SPIFlashReadBuffer(mFlashSectorBuffer,SPI_FLASH_SECTOR_SIZE,SectorStartAddress) != RET_OK)
        {
            return RET_ERROR;
        }
        
        //Set the working buffer pointer to the right value.
        WorkPagePtr = &mFlashSectorBuffer[CurDataFlashAddress - SectorStartAddress];
        
        //Modify the working buffer with data to write.
        int SectorFinished = 0;
        while(SectorFinished == 0)
        {
            *WorkPagePtr++ = *DataBufPtr++;
            CurDataFlashAddress++;
            
            //Are we at the end of the buffer to write?
            if(CurDataFlashAddress == DataFlashEndAddress)
            {
                SectorFinished = 1;
                WriteFinished = 1;
//                SPIFlashEraseSector(SectorStartAddress);
                SPIFlashWriteSectorWorkingBuffer(SectorStartAddress,1);
                
                break;
            }
            else if(CurDataFlashAddress % SPI_FLASH_SECTOR_SIZE == 0)   //Are we at the beginning of the next sector?
            {
                SectorFinished = 1;
//                SPIFlashEraseSector(SectorStartAddress);
                SPIFlashWriteSectorWorkingBuffer(SectorStartAddress,1);
                
                break;
            }
        }
        
    }
    
    return 1;
}