/****************************************************************************/
/* Copyright 2010 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
#include <p24fxxxx.h>

#include "spi.h"
#include "tasks.h"
#include "serial.h"
#include "sys_timer.h"
#include "sys_defs.h"

static int firstFlashWrite = TRUE;
static unsigned long writeAddressPtr = 0;

#define READ_ARRAY      0x03
#define READ_STATUS     0x05
#define WRITE_STATUS    0x01
#define WRITE_DISABLE   0x04
#define WRITE_ENABLE    0x06
#define CHIP_ERASE      0x60
#define SEQ_PROG_MODE   0xAD
#define BYTE_PROG_MODE  0x02

/* status register bits */
#define SR_SPRL 0x80
#define SR_SPM  0x40
#define SR_EPE  0x20
#define SR_WPP  0x10
#define SR_SWP2 0x08
#define SR_SWP1 0x04
#define SR_WEL  0x02
#define SR_BUSY 0x01

#define MEM_SIZE    0x000FFFFF

void sendCommand(unsigned int cmd);
unsigned int readStatus();
void addressToBytes(unsigned long address, unsigned char* bytes);


void dmInit()
{
    /* send write disable command to escape from sequential 
    program mode if this was a soft reset */
    sendCommand(WRITE_DISABLE);
    
    /* send write enable */
    sendCommand(WRITE_ENABLE);
    
    /* perform global sector unprotect */

    /* assert chip select */
    spiCS(1);

    /* send the command unprotect command */
    spiReadWrite(WRITE_STATUS);
    spiReadWrite(0x00);
    
    /* deassert chip select */
    spiCS(0);

    /* find fisrt blank byte (e.g. reads as 0xFF) and set addess */
    /* assert chip select */
    spiCS(1);

    /* write read array command */
    spiReadWrite(READ_ARRAY);
    
    /* write adress bits */
    spiReadWrite(0x00);
    spiReadWrite(0x00);
    spiReadWrite(0x00);
    
    /* read back the data bytes until you get an 0xFF */
    while ( spiReadWrite(0x00) != 0xFF )
    {
        /* if you overrun the MEM_SIZE, bail out */
        if ( ++writeAddressPtr > MEM_SIZE)
            break;
    }

    /* deassert chip select */
    spiCS(0);

    return;
}

int dmErase()
{
    unsigned int status;
   
    sendCommand(WRITE_DISABLE);
    /* set WEL bit */
    sendCommand(WRITE_ENABLE);
    
    /* erase chip */
    sendCommand(CHIP_ERASE);

    serPutString(SER_CONSOLE, "ERASING NVM");
    
    status = readStatus();
    while ( status & SR_BUSY )
    {
        tmrDelayMs(250L);
        status = readStatus();
        serPutString(SER_CONSOLE, ".");
        /* make sure you ping all the tasks */
        taskExecute(TMR_TASK | START_TASK);
    }

    serPutString(SER_CONSOLE, "\r\nERASE COMPLETE\r\n");
    
    /* reset write pointer */
    writeAddressPtr = 0;

    return 0;
}

int dmRead(unsigned long address, char* data, int size)
{
    int i;
    unsigned char bytes[4];

    /* send write disable command just in case you're in 
    sequential program mode */
    sendCommand(WRITE_DISABLE);

    while ( readStatus() & SR_BUSY )
        ; /* make sure the device is not busy */

    /* assert chip select */
    spiCS(1);

    /* write read array command */
    spiReadWrite(READ_ARRAY);
    
    /* write adress bits */
    addressToBytes(address, bytes);

    spiReadWrite(bytes[2]);
    spiReadWrite(bytes[1]);
    spiReadWrite(bytes[0]);
    
    /* read back the data bytes */
    for (i = 0; i < size; ++i)
        data[i] = spiReadWrite(0x00);

    /* deassert chip select */
    spiCS(0);

    return 0;
}


/* this will be fashioned much like serPutByte(...) */
int dmWrite(char data)
{
    unsigned char bytes[4];
    
    if ( writeAddressPtr > MEM_SIZE)
        return -1;
    
    /* clear the first bit of the byte if it's set */
    data &= 0x7F;

    while ( readStatus() & SR_BUSY )
        ; /* make sure the device is not busy */
    
    /* set WEL bit */
    sendCommand(WRITE_ENABLE);

    spiCS(1);

    /* send the command unprotect command */
    spiReadWrite(BYTE_PROG_MODE);

    /* write address */
    addressToBytes(writeAddressPtr, bytes);
    spiReadWrite(bytes[2]);
    spiReadWrite(bytes[1]);
    spiReadWrite(bytes[0]);

    /* write the data */
    spiReadWrite(data);

    /* deassert chip select */
    spiCS(0);

    ++writeAddressPtr;

/* Enter critical section */
    /* check the status and if the device is busy return 0 */
/* Exit critical section */

/* Enter critical section */
    /* write a byte */
/* Exit critical section */

    return 0;
}

int dmPutString(char* buff)
{
    int i = 0;

    while ( buff[i] != '\0' )
    {
        if ( dmWrite(buff[i++]) < 0 )
            return -1;
    }

    return 0;
}

void sendCommand(unsigned int cmd)
{
    /* assert chip select */
    spiCS(1);

    /* send the command */
    spiReadWrite(cmd);
    
    /* deassert chip select */
    spiCS(0);
}

unsigned int readStatus()
{
    unsigned int status;

    /* assert chip select */
    spiCS(1);

    /* send the read status command */
    spiReadWrite(READ_STATUS);
    
    /* read back the status */
    status = spiReadWrite(0x00);

    /* deassert chip select */
    spiCS(0);

    return status;

}

void addressToBytes(unsigned long address, unsigned char* bytes)
{
    int i;

    for (i = 0; i < 4; ++i)
    {
        bytes[i] = (address & 0x000000FF);
        address >>= 8;
    }
}




