/***************************************************************************/
/*  Author           : P.H. Seerden                                        */
/*  Description      : Driver for the SSP hardware interface on            */
/*                     the NXP LPC13xx / LPC11xx microcontrollers.         */
/*                                                                         */
/*                  (C) Copyright 2012 NXP Semiconductors                  */
/*                                                                         */
/***************************************************************************/

#include <LPC11Uxx.h>                              // LPC11Uxx definitions
#include "ssp0.h"

static   SSP_MSG *msg;                             // ptr to active message block
volatile BYTE    rxcnt;
volatile BYTE    txcnt;
static   BYTE    state;                            // driver state

void SSP0_IRQHandler(void) 
{
  LONG regValue;

    regValue = LPC_SSP0->MIS;
    if (regValue & 1)                              // receive overrun interrupt?
    {
        state = SSP_ROVR;
        LPC_SSP0->ICR = 1;                         // clear all flags
    }

    if (regValue & 2)                              // receive timeout interrupt?
    {
        while ((LPC_SSP0->SR & 4) && rxcnt)
        {
            *msg->rxbuf++ = LPC_SSP0->DR;          // read data
            rxcnt --;
        }
        LPC_SSP0->ICR = 2;                         // clear RTIC
    }

    if (regValue & 4)                              // Rx at least half full
    {
        *msg->rxbuf++ = LPC_SSP0->DR;              // save 4 frames
        *msg->rxbuf++ = LPC_SSP0->DR;
        *msg->rxbuf++ = LPC_SSP0->DR;
        *msg->rxbuf++ = LPC_SSP0->DR;

        rxcnt -= 4;                                // counter - 4
    }

    if (regValue & 8)                              // Tx FIFO half empty interrupt?
    {
        if (txcnt)
        {
            while ((LPC_SSP0->SR & 2) && txcnt)    // write 8 or less frames to TxFIFO
            {
                LPC_SSP0->DR = *msg->txbuf ++;
                txcnt --;
            }
        }
        else                                       // no more messages
        {
            LPC_SSP0->IMSC &= ~(1 << 3);           // End of transfer, no more TX interrupts
        }
    }

    if (rxcnt == 0)
    {
        switch (msg->ssel & 0xF0)                  // SSEL high
        {
          case 0x00: LPC_GPIO->SET[0] = (1<<(msg->ssel & 0x0F)); break;
          case 0x10: LPC_GPIO->SET[1] = (1<<(msg->ssel & 0x0F)); break;
        }
        state = SSP_OK;
    }
}

BYTE SSP0_Transfer(SSP_MSG *p)
{
    msg   = p;
    rxcnt = p->len;
    txcnt = p->len;
    state = SSP_BUSY;

    switch (p->ssel & 0xF0)                        // SSEL low
    {
      case 0x00: LPC_GPIO->CLR[0] = (1<<(p->ssel & 0x0F)); break;
      case 0x10: LPC_GPIO->CLR[1] = (1<<(p->ssel & 0x0F)); break;
      default:   return SSP_SSEL;
    }

    LPC_SSP0->IMSC = 0x0F;                         // TX FIFO, Error (Overrrun), Timeout, RX FIFO

    while (state == SSP_BUSY)
    {
//        if (++timeOut > 40000)
//            state = SSP_TIME_OUT;
    }

    return state;
}

void SSP0_Init(void)
{
    LPC_SYSCON->PRESETCTRL    |= (1<<0);           // de-assert SSP0 reset
    LPC_SYSCON->SYSAHBCLKCTRL |= (1<<11);          // SSP0 clock enable
    LPC_SYSCON->SSP0CLKDIV     = 1;                // Divided by 1

    LPC_IOCON->PIO0_8  = 0x00000081;               // P0.8  = SSP0 MISO
    LPC_IOCON->PIO0_9  = 0x00000081;               // P0.9  = SSP0 MOSI
    LPC_IOCON->PIO1_29 = 0x00000081;               // P1.29 = SSP0 SCK

    LPC_SSP0->CR0  = 0x0007;                       // 8-bit SPI, CPOL = 0, CPHA = 0 and SCR = 0
    LPC_SSP0->CPSR = SystemCoreClock / 1000000;    // master mode, minimum divisor is 2

    NVIC_EnableIRQ(SSP0_IRQn);                     // Enable the SSP0 Interrupt
	
    LPC_SSP0->CR1 = (1 << 1);                      // Device select as master, SSP0 Enabled
}
