/****************************************************************************/
/* Copyright 2010 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
#include <p24fxxxx.h>
#include <stdio.h>
#include <string.h>
#include "cache.h"
#include "card.h"
#include "commands.h"
#include "errors.h"
#include "spi1.h"
#include "serial.h"
#include "sys_defs.h"
#include "misc.h"
#include "buffer.h"

#define SPI_SENTINEL 0x1FFF
#define SPI_DELAY 20

char spiOutBuff[SPI_OUT_BUFF_SIZE];
static unsigned int spiOutFlush = 0;

/* spi command buffer */
#define SPI_CMD_BUFF_SIZE   32
static WordBuffer spiCmdBuff;
static unsigned int spiCmdBuffData[SPI_CMD_BUFF_SIZE];
static unsigned int spiCmdOverFlow = 0;
static unsigned int spiCmdBuffCmd = 0;

/* spi state machine states */
#define SPI_STATE_RDY   0
#define SPI_STATE_INFO  1
#define SPI_STATE_READ  2
#define SPI_STATE_WRITE 3
#define SPI_STATE_FLUSH 4

static unsigned int spiState = SPI_STATE_RDY;
static unsigned int spiAddress = 0;

/* spi status counters */
static unsigned int spiIrqCnt = 0;
static unsigned int spiCmdCnt = 0;
static unsigned int spiReadCnt = 0;
static unsigned int spiInfoCnt = 0;
static unsigned int spiWriteCnt = 0;

void spiInit()
{
    /* init buffers */
    spiCmdBuff.data = spiCmdBuffData;
    spiCmdBuff.head = 0;
    spiCmdBuff.tail = 0;
    spiCmdBuff.ptr_mask = (SPI_CMD_BUFF_SIZE - 1); 

    /* setup SPI1 registers */
    SPI1STATbits.SISEL = 1;  /* Interruput whenever data is available */
    
    SPI1CON1bits.MODE16 = 1;    /* Use 16-bit SPI mode      */
    SPI1CON1bits.SSEN = 1;      /* Enable slave select pin  */
    SPI1CON1bits.MSTEN = 0;     /* Setup for slave mode     */

    SPI1CON1bits.SPRE = 0x06;   /* secondary prescale 2:1 */
    SPI1CON1bits.PPRE = 0x03;   /* pimary prescale 1:1    */

    SPI1CON2bits.SPIBEN = 0;    /* Leave in legacy buffer mode for now */

    /* clear SPI overflow */
    SPI1STATbits.SPIROV = 0;

    /* enable SPI module */
    SPI1STATbits.SPIEN = 1;

#define USE_CN_CODE 0
#if USE_CN_CODE
    /* setup SPI_CS to interrupt on change notification */
	//****SEE NOTE IN dig_io.c.**** --JP  

    CNEN1bits.CN13IE = 1;

    /* clear any change notification interrupts */
    _CNIF = 0;

    /* set it to a higher priority than SPI */
    _CNIP = 7;

    /* enable it */
    _CNIE = 1;
#endif    

    
    
    /* clear any pending interrupts just in case */
    _SPI1IF = 0;

#if USE_CN_CODE
    /* set interrupt priority level to higheset level */
    _SPI1IP = 6;
#else
    /* set interrupt priority level to higheset level */
    _SPI1IP = 7;
#endif    

    /* enable spi interrupt */
    _SPI1IE = 1;
}

int spiGetCmd(unsigned int* w)
{
    int stat;
 
    /* try to get a word from the buffer */
    EnterCriticalSection();
    stat = bufWordGet(w, &spiCmdBuff);
    ExitCriticalSection();

    return stat;
}

unsigned int spiGetIrqCnt()
{
    return spiIrqCnt;
}

unsigned int spiGetCmdCnt()
{
    return spiCmdCnt;
}


unsigned int spiGetInfoCnt()
{
    return spiInfoCnt;
}

unsigned int spiGetReadCnt()
{
    return spiReadCnt;
}

unsigned int spiGetWriteCnt()
{
    return spiWriteCnt;
}

unsigned int spiGetCmdOverFlow()
{
    return spiCmdOverFlow;
}

void spiClrIrqCnt()
{
    spiIrqCnt = 0;
}

void spiClrCmdCnt()
{
    spiCmdCnt = 0;
}

void spiClrInfoCnt()
{
    spiInfoCnt = 0;
}

void spiClrReadCnt()
{
    spiReadCnt = 0;
}

void spiClrWriteCnt()
{
    spiWriteCnt = 0;
}

void spiClrCmdOverFlow()
{
    spiCmdOverFlow = 0;
}

unsigned int tIn;

void spiUpdate()
{       
    if (spiOutFlush)
    {
        unsigned int word;
        unsigned int i;
        char* cmd_ptr;
        char strbuf[SPI_OUT_BUFF_SIZE] = { 0 };
        
        for (i = 0; bufWordGet(&word, &spiCmdBuff); i++)
        {
            strbuf[i] = word;
        }
        
        bufWordClear(&spiCmdBuff);
        
        memset(spiOutBuff, 0, SPI_OUT_BUFF_SIZE);
        /* Skip white space here */
        cmd_ptr = prsSkip(strbuf, "     ");
        
        if ( *cmd_ptr != '\0' )
            prsParse(cmd_ptr, cmdsRoot, ERR_NO_COMMAND);
        
        spiCmdOverFlow = 0;
        spiOutFlush = 0;
    }
}

void __attribute__ ((interrupt,no_auto_psv)) _SPI1Interrupt(void)
{
    unsigned int reg;
    unsigned int cmd;
    unsigned int arg;

    /* temporary SPI IRQ debug bit */
    LATBbits.LATB4 = 1;
    
    /* read SPI data ASAP */
    reg = SPI1BUF;
    tIn = reg;

    /* increment the spi IRQ counter everytime */
    ++spiIrqCnt;
    
    /* if the state is SPI_STATE_RDY it's a memory transaction of command */ 
    if ( spiState == SPI_STATE_RDY )
    {
        cmd = (reg >> 10);
        arg = (ARG_MASK & reg);
        spiAddress = arg;

        switch ( cmd )
        {
            case INFO_CMD:  spiState = SPI_STATE_INFO;
                            if ( arg < sizeof(CardInfo) )
                                SPI1BUF = (INFO_CMD << 10) + 
                                    *((unsigned char*)&cardInfo + spiAddress);
                            else
                                SPI1BUF = 0;
                            break;

            case READ_CMD:  spiState = SPI_STATE_READ;
                            if ( arg < CACHE_VALLEN )
                                SPI1BUF = cacheValues[spiAddress];
                            else
                                SPI1BUF = 0;
                            break;

            case WRITE_CMD: spiState = SPI_STATE_WRITE; break;
            case SPARE_1_CMD: ++spiCmdCnt;  /* spare 1 command here */break;
            case SPARE_2_CMD: ++spiCmdCnt;  /* spare 2 command here */break;
            case BUFF_RET_CMD:
                            if ( !spiOutFlush && spiAddress < SPI_OUT_BUFF_SIZE )
                                SPI1BUF = spiOutBuff[spiAddress];
                            else
                                SPI1BUF = 0;
                            break;
            case BUFF_CMD:
                if (arg == ARG_MASK)
                {
                    spiOutFlush = 1;
                    break;
                }
            default:
                ++spiCmdCnt;
                /* put all unprocessed word in the cmd buffer */
                if ( ((spiCmdBuff.head + 1) & spiCmdBuff.ptr_mask) == 
                       spiCmdBuff.tail )
                {
                    /* set overflow flag and bail */
                    spiCmdOverFlow = 1;
                }
                else
                {
                    /* put the command in the buffer */
                    spiCmdBuff.data[spiCmdBuff.head] = arg;
                    spiCmdBuff.head = (spiCmdBuff.head + 1) & 
                                      spiCmdBuff.ptr_mask;
                }
        }
    }
    else if ( spiState == SPI_STATE_INFO )
    {
        /* increment the reg read counter */
        ++spiInfoCnt;

        /* return to SPI ready state */
        spiState = SPI_STATE_RDY;
    }
    else if ( spiState == SPI_STATE_READ )
    {
        /* increment the reg read counter */
        ++spiReadCnt;

        /* return to SPI ready state */
        spiState = SPI_STATE_RDY;
    }
    else if ( spiState == SPI_STATE_WRITE )
    {
        /* write the byte if in range */
        if ( spiAddress < CACHE_VALLEN )
            cacheValues[spiAddress] = reg;
        
        /* increment the reg write counter */
        ++spiWriteCnt;

        /* return to SPI ready state */
        spiState = SPI_STATE_RDY;
    }
    else
    {
        /* if you're in an unknown state, get back to ready */
        spiState = SPI_STATE_RDY;
    }

    /* temporary SPI IRQ debug bit */
    LATBbits.LATB4 = 0;
    
    /* clear the interrupt and return */
    _SPI1IF = 0;
    return;
}

