/****************************************************************************/
/* Copyright 2010 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
#include <p24fxxxx.h>
#include "spi1.h"
#include "misc.h"
#include "buffer.h"

/* 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;

/* spi info memory */
#define SPI_INFO_SIZE   8 /* should this be defined this in sys_defs? */
static unsigned int spiInfo[SPI_INFO_SIZE];

/* spi memory map */
#define SPI_MEM_MAP_SIZE    16 /* should this be defined this in sys_defs? */
static unsigned int spiMemMap[SPI_MEM_MAP_SIZE];

/* spi state machine states */
#define SPI_STATE_RDY   0
#define SPI_STATE_INFO  1
#define SPI_STATE_READ  2
#define SPI_STATE_WRITE 3

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);

    /* init spiInfo memory */
    spiInfo[0] = 0x0005;

    /* setup SPI1 registers */
    SPI1STATbits.SISEL = 0x01;  /* 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 */
    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;
}

void spiMemWrite(unsigned int address, unsigned int data)
{
    /* note: shouldn't need critical 
    section for unsigned int */
    
    /* write the reg if it's in range */
    if ( address < SPI_MEM_MAP_SIZE )
        spiMemMap[address] = data;

}

unsigned int spiMemRead(unsigned int address)
{
    /* note: shouldn't need critical 
    section for unsigned int */
    
    /* read the reg if it's in range */
    if ( address < SPI_MEM_MAP_SIZE )
        return spiMemMap[address];

    /* return zero if it's outta range */
    return 0;
}

int spiGetCmd(unsigned int* w)
{
    int stat;

    /* try to get a byte 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;
}


#if USE_CN_CODE
void __attribute__ ((interrupt,no_auto_psv)) _CNInterrupt(void)
{
LATBbits.LATB4 = 1;
    /* wheter you are entering or exiting a SPI 
    transaction the SPI state should be set to RDY */
    spiState = SPI_STATE_RDY;
LATBbits.LATB4 = 0;

    _CNIF = 0;
}
#endif

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;

    /* 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 < SPI_INFO_SIZE )
                                SPI1BUF = spiInfo[arg];
                            else
                                SPI1BUF = 0;
                            break;

            case READ_CMD:  spiState = SPI_STATE_READ;
                            if ( arg < SPI_MEM_MAP_SIZE )
                                SPI1BUF = spiMemMap[arg];
                            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 SPARE_3_CMD: ++spiCmdCnt;  /* spare 3 command here */break;
            case SPARE_4_CMD: ++spiCmdCnt;  /* spare 4 command here */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] = reg;
                            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 < SPI_MEM_MAP_SIZE )
            spiMemMap[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;
}








#if 0   /* old spi test code */

#define SPI_BUFF_SIZE       32

static WordBuffer spiBuff;
static unsigned int spiBuffData[SPI_BUFF_SIZE];
static int spiOverflow = 0;

/* Received data var */
static unsigned int spiTxCount = 0;

/********************************** from spiInit() */
    /* init buffers */
    spiBuff.data = spiBuffData;
    spiBuff.head = 0;
    spiBuff.tail = 0;
    spiBuff.ptr_mask = (SPI_BUFF_SIZE - 1);
/********************************** from spiInit() */

int spiGetWord(unsigned int* w)
{
    int stat;

    /* try to get a byte from the buffer */
    EnterCriticalSection();
    stat = bufWordGet(w, &spiBuff);
    ExitCriticalSection();

    return stat;
}

#endif


#if 0   /* body of old ISR */
    /* note: the standard buffer functions are not used here 
    as calling an external function from and interrupt service 
    routine increases interrupt overhead */
    /* if the buffer is not full, put in the byte */
    if ( ((spiBuff.head + 1) & spiBuff.ptr_mask) == spiBuff.tail )
    {
        /* set overflow flag and bail */
        spiOverflow = 1;
    }
    else
    {
        /* if there is room in the buffer put the byte in it */
        spiBuff.data[spiBuff.head] = (unsigned int)SPI1BUF;
        spiBuff.head = (spiBuff.head + 1) & spiBuff.ptr_mask;
/* send back a SPI count */
SPI1BUF = ++spiTxCount;
    }
#endif        

