/*********************************************************************
 *
 *               Data SPI EEPROM Access Routines
 *
 *********************************************************************
 * FileName:        SPIEEPROM.c
 * Dependencies:    None
 * Processor:       PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
 * Compiler:        Microchip C32 v1.05 or higher
 *					Microchip C30 v3.12 or higher
 *					Microchip C18 v3.30 or higher
 *					HI-TECH PICC-18 PRO 9.63PL2 or higher
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * Copyright (C) 2002-2009 Microchip Technology Inc.  All rights
 * reserved.
 *
 * Microchip licenses to you the right to use, modify, copy, and
 * distribute:
 * (i)  the Software when embedded on a Microchip microcontroller or
 *      digital signal controller product ("Device") which is
 *      integrated into Licensee's product; or
 * (ii) ONLY the Software driver source files ENC28J60.c, ENC28J60.h,
 *		ENCX24J600.c and ENCX24J600.h ported to a non-Microchip device
 *		used in conjunction with a Microchip ethernet controller for
 *		the sole purpose of interfacing with the ethernet controller.
 *
 * You should refer to the license agreement accompanying this
 * Software for additional information regarding your rights and
 * obligations.
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 *
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Nilesh Rajbharti     5/20/02     Original (Rev. 1.0)
 * Howard Schlunder     9/01/04     Rewritten for SPI EEPROMs
 * Howard Schlunder     8/10/06     Modified to control SPI module
 *                                  frequency whenever EEPROM accessed
 *                                  to allow bus sharing with different
 *                                  frequencies.
********************************************************************/

//#include "HardwareProfile.h"

// If the CS line is not defined, SPIEEPROM.c's content will not be compiled.  
// If you are using a serial EEPROM please define the CS pin as TRISDbits.TRISD9 
// in HardwareProfile.h

#include "TCPIP Stack/TCPIP.h"

#define XEE_SUCCESS FALSE

// IMPORTANT SPI NOTE: The code in this file expects that the SPI interrupt
//      flag (EEPROM_SPI_IF) be clear at all times.  If the SPI is shared with
//      other hardware, the other code should clear the EEPROM_SPI_IF when it is
//      done using the SPI.

// SPI Serial EEPROM buffer size.  To enhance performance while
// cooperatively sharing the SPI bus with other peripherals, bytes
// read and written to the memory are locally buffered. Legal
// sizes are 1 to the EEPROM page size.
#define EEPROM_BUFFER_SIZE              (16)

// Must be the EEPROM write page size, or any binary power of 2 divisor.  If 
// using a smaller number, make sure it is at least EEPROM_BUFFER_SIZE big for 
// max performance.  Microchip 25LC256 uses 64 byte page size, 25LC1024 uses 
// 256 byte page size, so 64 is compatible with both.
#define EEPROM_PAGE_SIZE				(16)

// EEPROM SPI opcodes
#define OPCODE_READ    0x03    // Read data from memory array beginning at selected address
#define OPCODE_WRITE   0x02    // Write data to memory array beginning at selected address
#define OPCODE_WRDI    0x04    // Reset the write enable latch (disable write operations)
#define OPCODE_WREN    0x06    // Set the write enable latch (enable write operations)
#define OPCODE_RDSR    0x05    // Read Status register
#define OPCODE_WRSR    0x01    // Write Status register

#define EEPROM_MAX_SPI_FREQ     (10000000ul)    // Hz

    #define ClearSPIDoneFlag()
    static inline __attribute__((__always_inline__)) void WaitForDataByte( void )
    {
        while (!SPI1STATbits.SPITBE || !SPI1STATbits.SPIRBF);
    }

    #define SPI_ON_BIT          (SPI1CONbits.ON)

static void DoWrite(void);

static BYTE EEPROMAddress;
static BYTE EEPROMBuffer[EEPROM_BUFFER_SIZE];
static BYTE vBytesInBuffer;

BOOL MACreadIsBusy(void);
BOOL MACEndWrite(void);
BOOL MACReadArray(BYTE address, BYTE *buffer, WORD length);

void	ReadMacFromEEPROM(unsigned char MACaddr[]);
void	MACreadInit(void);

/*********************************************************************
 * Function:        void XEEInit(unsigned char speed)
 *
 * PreCondition:    None
 *
 * Input:           speed - not used (included for compatibility only)
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Initialize SPI module to communicate to serial
 *                  EEPROM.
 *
 * Note:            Code sets SPI clock to Fosc/16.
 ********************************************************************/
    #define PROPER_SPICON1  (_SPI1CON_ON_MASK | _SPI1CON_CKE_MASK | _SPI1CON_MSTEN_MASK)

void MACreadInit(void)
{
    TRISBbits.TRISB14 = 0;     // Drive SPI EEPROM chip select pin
    PORTBbits.RB14 = 1;

//    TRISDbits.TRISD10 = 0;    // Set SCK pin as an output
//    TRISCbits.TRISC4 = 1;    // Make sure SDI pin is an input
//    TRISDbits.TRISD0 = 0;    // Set SDO pin as an output
//
//    ClearSPIDoneFlag();
//	SPI1BRG = (GetPeripheralClock()-1ul)/2ul/EEPROM_MAX_SPI_FREQ;
//    SPI1CON = PROPER_SPICON1;
}


/*********************************************************************
 * Function:        BOOL XEEBeginRead(DWORD address)
 *
 * PreCondition:    None
 *
 * Input:           address - Address at which read is to be performed.
 *
 * Output:          XEE_SUCCESS
 *
 * Side Effects:    None
 *
 * Overview:        Sets internal address counter to given address.
 *
 * Note:            None
 ********************************************************************/
BOOL MACBeginRead(BYTE address)
{
    // Save the address and emptry the contents of our local buffer
    EEPROMAddress = address;
    vBytesInBuffer = 0;
    return XEE_SUCCESS;
}


/*********************************************************************
 * Function:        BYTE XEERead(void)
 *
 * PreCondition:    XEEInit() && XEEBeginRead() are already called.
 *
 * Input:           None
 *
 * Output:          BYTE that was read
 *
 * Side Effects:    None
 *
 * Overview:        Reads next byte from EEPROM; internal address
 *                  is incremented by one.
 *
 * Note:            None
 ********************************************************************/
BYTE MACRead(void)
{
    // Check if no more bytes are left in our local buffer
    if(vBytesInBuffer == 0u)
    {
        // Get a new set of bytes
        MACReadArray(EEPROMAddress, EEPROMBuffer, EEPROM_BUFFER_SIZE);
        EEPROMAddress += EEPROM_BUFFER_SIZE;
        vBytesInBuffer = EEPROM_BUFFER_SIZE;
    }

    // Return a byte from our local buffer
    return EEPROMBuffer[EEPROM_BUFFER_SIZE - vBytesInBuffer--];
}

/*********************************************************************
 * Function:        BOOL XEEEndRead(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          XEE_SUCCESS
 *
 * Side Effects:    None
 *
 * Overview:        This function does nothing.
 *
 * Note:            Function is used for backwards compatability with
 *                  I2C EEPROM module.
 ********************************************************************/
BOOL MACreadEndRead(void)
{
    return XEE_SUCCESS;
}


/*********************************************************************
 * Function:        BOOL XEEReadArray(DWORD address,
 *                                          BYTE *buffer,
 *                                          WORD length)
 *
 * PreCondition:    XEEInit() is already called.
 *
 * Input:           address     - Address from where array is to be read
 *                  buffer      - Caller supplied buffer to hold the data
 *                  length      - Number of bytes to read.
 *
 * Output:          XEE_SUCCESS
 *
 * Side Effects:    None
 *
 * Overview:        Reads desired number of bytes in sequential mode.
 *                  This function performs all necessary steps
 *                  and releases the bus when finished.
 *
 * Note:            None
 ********************************************************************/
BOOL MACReadArray(BYTE address,
                        BYTE *buffer,
                        WORD length)
{
    volatile BYTE Dummy;
    BYTE vSPIONSave;
    DWORD SPICON1Save;

    // Save SPI state (clock speed)
    SPICON1Save = SPI1CON;
    vSPIONSave = SPI_ON_BIT;

    // Configure SPI
    SPI_ON_BIT = 0;
    SPI1CON = PROPER_SPICON1;
    SPI_ON_BIT = 1;

    PORTBbits.RB14 = 0;

    // Send READ opcode
    SPI1BUF = OPCODE_READ;
    WaitForDataByte();
    Dummy = SPI1BUF;

    // Send address
    SPI1BUF = address;
    WaitForDataByte();
    Dummy = SPI1BUF;

    while(length--)
    {
        SPI1BUF = 0;
        WaitForDataByte();
        Dummy = SPI1BUF;
        if(buffer != NULL)
            *buffer++ = Dummy;
    };

    PORTBbits.RB14 = 1;

    // Restore SPI state
    SPI_ON_BIT = 0;
    SPI1CON = SPICON1Save;
    SPI_ON_BIT = vSPIONSave;


    return XEE_SUCCESS;
}


/*********************************************************************
 * Function:        BOOL XEEBeginWrite(DWORD address)
 *
 * PreCondition:    None
 *
 * Input:           address     - address to be set for writing
 *
 * Output:          XEE_SUCCESS
 *
 * Side Effects:    None
 *
 * Overview:        Modifies internal address counter of EEPROM.
 *
 * Note:            Unlike XEESetAddr() in xeeprom.c for I2C EEPROM
 *                  memories, this function is used only for writing
 *                  to the EEPROM.  Reads must use XEEBeginRead(),
 *                  XEERead(), and XEEEndRead().
 *                  This function does not use the SPI bus.
 ********************************************************************/
BOOL MACBeginWrite(BYTE address)
{
	vBytesInBuffer = 0;
    EEPROMAddress = address;
    return XEE_SUCCESS;
}


/*********************************************************************
 * Function:        BOOL XEEWrite(BYTE val)
 *
 * PreCondition:    XEEInit() && XEEBeginWrite() are already called.
 *
 * Input:           val - Byte to be written
 *
 * Output:          XEE_SUCCESS
 *
 * Side Effects:    None
 *
 * Overview:        Writes a byte to the write cache, and if full, 
 *					commits the write.  Also, if a write boundary is 
 *					reached the write is committed.  When finished 
 *					writing, XEEEndWrite() must be called to commit 
 *					any unwritten bytes from the write cache.
 *
 * Note:            None
 ********************************************************************/
BOOL MACWrite(BYTE val)
{
	EEPROMBuffer[vBytesInBuffer++] = val;
	if(vBytesInBuffer >= sizeof(EEPROMBuffer))
		DoWrite();
	else if((((BYTE)EEPROMAddress + vBytesInBuffer) & (EEPROM_PAGE_SIZE-1)) == 0u)
		DoWrite();

    return XEE_SUCCESS;
}


/*****************************************************************************
  Function:
    BOOL XEEWriteArray(BYTE *val, WORD wLen)

  Summary:
    Writes an array of bytes to the EEPROM part.

  Description:
    This function writes an array of bytes to the EEPROM at the address 
    specified when XEEBeginWrite() was called.  Page boundary crossing is 
    handled internally.
    
  Precondition:
    XEEInit() was called once and XEEBeginWrite() was called.

  Parameters:
    vData - The array to write to the next memory location
    wLen - The length of the data to be written

  Returns:
    None

  Remarks:
    The internal write cache is flushed at completion, so it is unnecessary 
    to call XEEEndWrite() after calling this function.  However, if you do 
    so, no harm will be done.
  ***************************************************************************/
void MACWriteArray(BYTE address, BYTE *val, WORD wLen)
{
	vBytesInBuffer = 0;
    EEPROMAddress = address;

	while(wLen--)
		MACWrite(*val++);
	
	MACEndWrite();
}


/*********************************************************************
 * Function:        BOOL XEEEndWrite(void)
 *
 * PreCondition:    XEEInit() && XEEBeginWrite() are already called.
 *
 * Input:           None
 *
 * Output:          XEE_SUCCESS
 *
 * Side Effects:    None
 *
 * Overview:        Commits any last uncommitted bytes in cache to 
 *					physical storage.
 *
 * Note:            Call this function when you no longer need to 
 *					write any more bytes at the selected address.
 ********************************************************************/
BOOL MACEndWrite(void)
{
	if(vBytesInBuffer)
		DoWrite();

    return XEE_SUCCESS;
}

static void DoWrite(void)
{
    BYTE i;
    volatile BYTE vDummy;
    BYTE vSPIONSave;
    DWORD SPICON1Save;

    // Save SPI state
    SPICON1Save = SPI1CON;
    vSPIONSave = SPI_ON_BIT;

    // Configure SPI
    SPI_ON_BIT = 0;
    SPI1CON = PROPER_SPICON1;
    SPI_ON_BIT = 1;

    // Set the Write Enable latch
    PORTBbits.RB14 = 0;
    SPI1BUF = OPCODE_WREN;
    WaitForDataByte();
    vDummy = SPI1BUF;
    PORTBbits.RB14 = 1;

    // Send WRITE opcode
    PORTBbits.RB14 = 0;
    SPI1BUF = OPCODE_WRITE;
    WaitForDataByte();
    vDummy = SPI1BUF;

    // Send address

    SPI1BUF = EEPROMAddress;
    WaitForDataByte();
    vDummy = SPI1BUF;

    for(i = 0; i < vBytesInBuffer; i++)
    {
        // Send the byte to write
        SPI1BUF = EEPROMBuffer[i];
        WaitForDataByte();
        vDummy = SPI1BUF;
    }

    // Begin the write
    PORTBbits.RB14 = 1;

	// Update write address and clear write cache
    EEPROMAddress += vBytesInBuffer;
    vBytesInBuffer = 0;

    // Restore SPI State
    SPI_ON_BIT = 0;
    SPI1CON = SPICON1Save;
    SPI_ON_BIT = vSPIONSave;


    // Wait for write to complete
    while( MACreadIsBusy() );
}


/*********************************************************************
 * Function:        BOOL XEEIsBusy(void)
 *
 * PreCondition:    XEEInit() is already called.
 *
 * Input:           None
 *
 * Output:          FALSE if EEPROM is not busy
 *                  TRUE if EEPROM is busy
 *
 * Side Effects:    None
 *
 * Overview:        Reads the status register
 *
 * Note:            None
 ********************************************************************/
BOOL MACreadIsBusy(void)
{
    volatile BYTE_VAL result;
    BYTE vSPIONSave;
    DWORD SPICON1Save;

    // Save SPI state
    SPICON1Save = SPI1CON;
    vSPIONSave = SPI_ON_BIT;

    // Configure SPI
    SPI_ON_BIT = 0;
    SPI1CON = PROPER_SPICON1;
    SPI_ON_BIT = 1;

    PORTBbits.RB14 = 0;
    // Send RDSR - Read Status Register opcode
    SPI1BUF = OPCODE_RDSR;
    WaitForDataByte();
    result.Val = SPI1BUF;

    // Get register contents
    SPI1BUF = 0;
    WaitForDataByte();
    result.Val = SPI1BUF;
    PORTBbits.RB14 = 1;

    // Restore SPI State
    SPI_ON_BIT = 0;
    SPI1CON = SPICON1Save;
    SPI_ON_BIT = vSPIONSave;

    return result.bits.b0;
}


void	ReadMacFromEEPROM (unsigned char MACaddr[])
{

	MACReadArray(0xfa, MACaddr, 6);
}


