/* ---------------------------------------------------------------------------
 * eeporm.c - v0.1                                     (c) 2007 Micro-key bv
 * ---------------------------------------------------------------------------
 * Micro-key bv
 * Industrieweg 28, 9804 TG Noordhorn
 * Postbus 92, 9800 AB Zuidhorn
 * The Netherlands
 * Tel: +31 594 503020
 * Fax: +31 594 505825
 * Email: support@microkey.nl  
 * Web: www.microkey.nl
 * ---------------------------------------------------------------------------
 * Description: EEPROM-driver
 * ---------------------------------------------------------------------------
 * Version(s):  0.1, 28-11-2007, fvds.
 *                   Creation.
 * ---------------------------------------------------------------------------
 */

/* ---------------------------------------------------------------------------
 * System include files
 * ---------------------------------------------------------------------------
 */

/* ---------------------------------------------------------------------------
 * Application include files
 * ---------------------------------------------------------------------------
 */
#include "LPC23xx.h"
#include "types.h"
#include "sys_config.h"
#include "ssp1.h"
#include "eeprom.h"
#include "armVIC.h"

/* FreeRTOS includes */
#include "FreeRTOS.h"
#include "task.h"

/* ---------------------------------------------------------------------------
 * Local constant and macro definitions
 * ---------------------------------------------------------------------------
 */
#define EEPROM_CS_DIR_REG   FIO0DIR
#define EEPROM_CS_SET_REG   FIO0SET
#define EEPROM_CS_CLR_REG   FIO0CLR
#define EEPROM_CS_BIT       BIT( 6 )
#define EEPROM_CS_DISABLE   (EEPROM_CS_SET_REG = EEPROM_CS_BIT)
#define EEPROM_CS_ENABLE    (EEPROM_CS_CLR_REG = EEPROM_CS_BIT)
#define EEPROM_PAGE_SIZE    (128)

#define SPICMD_WRITE_STATUS     0x01
#define SPICMD_WRITE            0x02
#define SPICMD_READ             0x03
#define SPICMD_WRITE_DISABLE    0x04
#define SPICMD_READ_STATUS      0x05
#define SPICMD_WRITE_ENABLE     0x06

#define SPICMD_STATUS_WIP       0x01
#define SPICMD_STATUS_WEL       0x02
#define SPICMD_STATUS_BP0       0x04
#define SPICMD_STATUS_BP1       0x08
#define SPICMD_STATUS_WPEN      0x80

#define spiWrite                ssp1Write
#define spiRead                 ssp1Read
#define spiWriteBuffer          ssp1WriteBuffer
#define spiReadBuffer           ssp1ReadBuffer
#define spiTakeBus              ssp1TakeBus
#define spiReleaseBus           ssp1ReleaseBus

/* ---------------------------------------------------------------------------
 * Global variable definitions
 * ---------------------------------------------------------------------------
 */
//extern UINT16 adres_var[]; 
UINT16 adres_var[MAX_VARSINEEPROM]; // t.b.v. aantal variabelen in EEPROM JHi 12-03-2008

/* ---------------------------------------------------------------------------
 * Local variable definitions
 * ---------------------------------------------------------------------------
 */

/* ---------------------------------------------------------------------------
 * Local function definitions
 * ---------------------------------------------------------------------------
 */
static void DisableWriteProtection(void);
static void WaitForEndOfWrite(void);
static void WaitForEndOfWrite(void);
static void WriteDataByte(UINT16 address, UINT8 dataOut);
static void WriteDataArray(UINT16 address, UINT8 *buffer, UINT16 size);
static void ReadDataByte(UINT16 address, UINT8 *dataOut);
static void ReadDataArray(UINT16 address, UINT8 *buffer, UINT16 size);
static void WriteEnable(void);
static UINT8 GetStatus(void);


void eepromInit()
{
    // Setup ChipSelect line
    SCS |= (1UL<<0); // set GPIOM in SCS for fast IO    
    EEPROM_CS_DIR_REG |= EEPROM_CS_BIT;
    EEPROM_CS_DISABLE;
}

RESULT eepromRead(UINT16 address, UINT8 *byte)
{
    WaitForEndOfWrite();
    
    ReadDataByte(address, byte);
    
    return OK;
}

RESULT eepromWrite(UINT16 address, UINT8 byte)
{
    WaitForEndOfWrite();
    
    WriteEnable();
    
    //EEPROM_DELAY;
    
    WriteDataByte( address, byte );
    
    //EEPROM_WRITE_DELAY;
    //EEPROM_Status |= C25LCXX_STATUS_WIP;    // indicate we started a write action
    
    return OK;

}

RESULT eepromWriteBuffer(UINT16 address, UINT8 *buffer, UINT16 bufferSize)
{
    // Split up in page boundaries, if necessary
	while (bufferSize > 0)
	{
		UINT16 length;
		UINT16 endAddress;
		
		// Calculate length
		if (bufferSize >= EEPROM_PAGE_SIZE)
		{
			endAddress = address + EEPROM_PAGE_SIZE;
		}
		else
		{
			endAddress = address + bufferSize;
		}
		
        // Check if buffer overflows a page
		if (((endAddress - 1) / EEPROM_PAGE_SIZE) == (address / EEPROM_PAGE_SIZE))
		{
			// buffer doesn't exceed page
			length = endAddress - address;
		}
		else
		{
			// buffer exceeds page
			length = endAddress - (endAddress % EEPROM_PAGE_SIZE) - address; 
		}
		
		WaitForEndOfWrite();
        WriteEnable();
        WriteDataArray( address, buffer, length );

		bufferSize = bufferSize - length;
	    address = address + length;
	    buffer = buffer + length;
	}
	
    return OK; 
}

RESULT eepromReadBuffer(UINT16 address, UINT8 *buffer, UINT16 bufferSize)
{
    WaitForEndOfWrite();
    
    while (bufferSize > 0)
    {
    	int length = EEPROM_PAGE_SIZE;
    	if (bufferSize < EEPROM_PAGE_SIZE) 
    	{
    		length = bufferSize;
    	}
    	
    	ReadDataArray( address, buffer, length );
    	
    	bufferSize -= length;
    	address = address + length;
    	buffer = buffer + length;
    	
    	if (bufferSize > 0) vTaskDelay(3);
    }

    return OK; 
}

RESULT eepromWriteVar(int variableId, void *parameter )
{
	UINT16 address = adres_var[variableId];
	UINT16 length = adres_var[variableId + 1] - address;
	
	return eepromWriteBuffer( address, parameter, length );
}


RESULT eepromReadVar(int variableId, void *parameter )
{
	UINT16 address = adres_var[variableId];
	UINT16 length = adres_var[variableId + 1] - address;
	
	return eepromReadBuffer( address, parameter, length );
}

void DisableWriteProtection()
{
    WriteEnable();
}

UINT8
GetStatus() 
{
    UINT8 spiCommand[2];
    static UINT8 status;
    
    spiCommand[0] = SPICMD_READ_STATUS;
    spiCommand[1] = 0x00;
    
    spiTakeBus();
    {
        EEPROM_CS_ENABLE;
        
        spiWriteBuffer( spiCommand, 2 );
        spiReadBuffer( spiCommand, 2 );
        
        EEPROM_CS_DISABLE;
    }
    spiReleaseBus();
    
    status = spiCommand[1];
    
    return spiCommand[1];    // lsb of word
}

void WaitForEndOfWrite(void)
{
    UINT8 eepromStatus = GetStatus();

    while ((eepromStatus & SPICMD_STATUS_WIP) != 0x00)
        {
        eepromStatus = GetStatus();
        }
}

void WriteEnable()
{
    UINT8 spiCommand[1];
    
    spiCommand[0] = SPICMD_WRITE_ENABLE;
    
    spiTakeBus();
    {
        EEPROM_CS_ENABLE;
        
        spiWriteBuffer( spiCommand, 1 );
        spiReadBuffer( spiCommand, 1 );
        
        EEPROM_CS_DISABLE;
    }
    spiReleaseBus();
}


void WriteDataByte(UINT16 address, UINT8 dataOut)
{
    UINT8 spiCommand[4];
    UINT8 spiResponse[4];
    
    spiCommand[0] = SPICMD_WRITE;
    spiCommand[1] = ((address >> 8) & 0xFF);
    spiCommand[2] = address & 0xFF;
    spiCommand[3] = dataOut;
    
    spiTakeBus();
    {
        EEPROM_CS_ENABLE;
        
        spiWriteBuffer( spiCommand, 4 );
        spiReadBuffer( spiResponse, 4 );
    
        EEPROM_CS_DISABLE;
    }
    spiReleaseBus();
        
}

void ReadDataByte(UINT16 address, UINT8 *byte)
{
    UINT8 spiCommand[4];
    UINT8 spiResponse[4];
    
    spiCommand[0] = SPICMD_READ;
    spiCommand[1] = ((address >> 8) & 0x00FF);
    spiCommand[2] = (address & 0x00FF);
    spiCommand[3] = 0x00;
    
    spiTakeBus();
    {
        EEPROM_CS_ENABLE;
        
        spiWriteBuffer( spiCommand, 4 );
        spiReadBuffer( spiResponse, 4 );
        
        EEPROM_CS_DISABLE;
    }
    spiReleaseBus();
    
    *byte = (UINT8)spiResponse[3];
}

void WriteDataArray(UINT16 address, UINT8 *buffer, UINT16 size)
{
    UINT8 spiCommand[3];
    UINT8 spiResponse[3];
    UINT8 dummyResponse;
    UINT16 index;

    spiTakeBus();
    {
        EEPROM_CS_ENABLE;
        
        spiCommand[0] = SPICMD_WRITE;
        spiCommand[1] = ((address >> 8) & 0xFF);
        spiCommand[2] = address & 0xFF;
    
        // Write command + address
        spiWriteBuffer( spiCommand, 3 );
        spiWriteBuffer( buffer, size );
        spiReadBuffer( spiResponse, 3 );
        for (index = 0; index < size; index++)
            {
            spiRead( &dummyResponse );
            }
       
    
        EEPROM_CS_DISABLE;
    }
    spiReleaseBus();
}

void ReadDataArray(UINT16 address, UINT8 *buffer, UINT16 size)
{
    UINT8 spiCommand[3];
    UINT8 spiResponse[3];
    UINT16 index;

    spiCommand[0] = SPICMD_READ;
    spiCommand[1] = ((address >> 8) & 0x00FF);
    spiCommand[2] = (address & 0x00FF);
    
    // Fill buffer with zero's
    for (index = 0; index < size; index++)
        {
        buffer[index] = 0x00;
        }

    spiTakeBus();
    {
        EEPROM_CS_ENABLE;
    
        spiWriteBuffer( spiCommand, 3 );
        spiWriteBuffer( buffer, size );
        spiReadBuffer( spiResponse, 3 );
        
        for (index = 0; index < size; index++)
            {
            spiRead( buffer );
            buffer++;
            }
    
        EEPROM_CS_DISABLE;
    }
    spiReleaseBus();
}
