#include "archinc.h"
#include "cli.h"
#include "strutil.h"
#include "zarlink.h"

#ifdef CONFIG_INCLUDE_ZARLINK

#include <stdlib.h>

static void ssiDelay(void)
{
  volatile int i;
  lpc210x_reg dummy;

  for(i=0;i<128;i++) 
    dummy = CTIME0;
}

#ifdef CONFIG_TOOLCHAIN_IAR
#pragma inline
#endif
static void INLINE_KEYWORD ssiStrobeHi(void) { IOSET = (lpc210x_reg)0x0200; }

#ifdef CONFIG_TOOLCHAIN_IAR
#pragma inline
#endif
static void INLINE_KEYWORD ssiStrobeLo(void) { IOCLR = (lpc210x_reg)0x0200; }

#ifdef CONFIG_TOOLCHAIN_IAR
#pragma inline
#endif
static void INLINE_KEYWORD ssiDataOutHi(void) { IOSET = (lpc210x_reg)0x0800; }

#ifdef CONFIG_TOOLCHAIN_IAR
#pragma inline
#endif
static void INLINE_KEYWORD ssiDataOutLo(void) { IOCLR = (lpc210x_reg)0x0800; }

#ifdef CONFIG_TOOLCHAIN_IAR
#pragma inline
#endif
static int INLINE_KEYWORD ssiGetDataIn(void)
{
  lpc210x_reg pins = IOPIN;
  return ((pins & 0x0400) ? 1 : 0);
}

#ifdef CONFIG_TOOLCHAIN_IAR
#pragma inline
#endif
static void INLINE_KEYWORD ssiStart(void)
{
  ssiDataOutHi(); ssiDelay();
  ssiStrobeHi(); ssiDelay();
  ssiDataOutLo(); ssiDelay();
  ssiStrobeLo(); ssiDelay();
}

#ifdef CONFIG_TOOLCHAIN_IAR
#pragma inline
#endif
static void INLINE_KEYWORD ssiAbort(void)
{
  /* bring strobe then data high */
  ssiStrobeHi(); ssiDelay();
  ssiDataOutHi(); ssiDelay();

  ssiStrobeLo(); ssiDelay();
}

static void INLINE_KEYWORD ssiWriteBit(int bitVal)
{
  /* setup data */
  if (bitVal == 0)
    ssiDataOutLo();
  else 
    ssiDataOutHi();
  
   // setup delay for dataOut
   ssiDelay(); 
   
   // ensure clock is low
   ssiStrobeLo(); ssiDelay();

  /* strobe clock */
  ssiStrobeHi();  ssiDelay();
  ssiStrobeLo();  ssiDelay();

}

static INLINE_KEYWORD int ssiReadBit(void)
{
  int retVal;

  ssiStrobeLo();  ssiDelay();

  ssiStrobeHi();  ssiDelay();
  retVal = ssiGetDataIn();
  ssiStrobeLo();  ssiDelay();

  return retVal;
}

static void ssiWrite(unsigned char addr, unsigned short writeData, boolean twoClocks)
{
  int i;
  unsigned char deviceId = 0;
  unsigned char devAddr = (addr & 0x7);

  // send start condition
  ssiStart();

  // send device ID
  for(i=0;i<3;i++)
    ssiWriteBit((deviceId >> i) & 0x01);

  // send register index to write
  for(i=0;i<3;i++)
    ssiWriteBit((devAddr >> i) & 0x01);

  // send a '1' for write
  ssiWriteBit(1);

  // send the data
  for(i=0;i<16;i++)
    ssiWriteBit((writeData >> i) & 0x01);

  // leave dataOut hi as per timing diagram
  ssiDataOutHi();  ssiDelay();

  if (twoClocks == true) {
    // generate two trailing clock pulses
    ssiStrobeHi();  ssiDelay(); 
    ssiStrobeLo();  ssiDelay();
    ssiStrobeHi();  ssiDelay();
    ssiStrobeLo();  ssiDelay();  
    }
}

static unsigned short ssiRead(unsigned char addr)
{
  int i;
  unsigned char deviceId = 0;
  unsigned char devAddr = (addr & 0x7);
  unsigned short readData;

  // send start condition
  ssiStart();

  // send device ID
  for(i=0;i<3;i++)
    ssiWriteBit((deviceId >> i) & 0x01);

  // send register index to read
  for(i=0;i<3;i++)
    ssiWriteBit((devAddr >> i) & 0x01);

  // send a '0' for read
  ssiWriteBit(0);

  // leave dataOut high
  ssiDataOutHi(); 

  // get the data
  for(readData=0,i=0;i<16;i++)
    if (ssiReadBit() != 0)
      readData |= (unsigned short)(0x01 << i);

  return readData;
}

static void ssiIndexedWrite(unsigned short index, unsigned char data)
{
  ssiWrite(0x00, index, true);
  ssiWrite(0x02, ((unsigned short)data), true);
}

static unsigned char ssiIndexedRead(unsigned short index)
{
  ssiWrite(0x00, index, true);
  return ((unsigned char)(ssiRead(0x02) & 0x00ff));
}

#endif
