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

#ifdef CONFIG_INCLUDE_ZARLINK

#include <stdlib.h>


/*
 * SSI (clocked serial) interface
 */

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

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

/*
 *
 * Parallel (bus) Interface
 *
 */

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

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

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

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

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

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

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


// only drive the bus when writing to save power
static void busWrite(unsigned char writeAddr, unsigned char writeData)
{
  lpc210x_reg iopinMask;
  lpc210x_reg iodirSave;

  iopinMask  = (lpc210x_reg)((writeData << 16) & 0x00ff0000);
  iopinMask |= (lpc210x_reg)((writeAddr << 24) & 0x07000000);

  // drive the data and address bits
  iodirSave = IODIR;
  IODIR = (iodirSave | 0x07ff0000);
  IOSET = iopinMask;
  IOCLR = ((~iopinMask) & 0x07ff0000);

  ssiDelay(); // let the data settle
  ssiDelay(); // let the data settle


  // assert CS_N, then WE_N
  busCsAssert();
  ssiDelay();

  busWriteAssert();
  ssiDelay();
  ssiDelay();


  busWriteNegate();
  ssiDelay(); // let the data settle 
  
  busCsNegate();
  ssiDelay(); // let the data settle 


  // stop driving the data bits
  IODIR = iodirSave;
}

static unsigned char busRead(unsigned char readAddr)
{
  lpc210x_reg iopinMask;
  lpc210x_reg iopinState;
  lpc210x_reg iodirSave = IODIR;

  // drive address, not data
  IODIR = ((iodirSave | 0x07000000) & 0xff00ffff);

  iopinMask = (lpc210x_reg)((readAddr << 24) & 0x07000000);

  // drive the address bits
  IOSET = iopinMask;
  IOCLR = ((~iopinMask) & 0x07000000);

  ssiDelay();
  ssiDelay();

  // assert CS_N, then RD_N
  busCsAssert();
  ssiDelay();

  busReadAssert();
  ssiDelay();
  ssiDelay();

  // grab the data
  iopinState = IOPIN;

  // negate read
  busReadNegate();
  ssiDelay(); // let the data settle 
  busCsNegate();
  ssiDelay(); // let the data settle 

  // restore GPIO direction
  IODIR = iodirSave;

  return ((unsigned char)((iopinState >> 16) & 0xff));
}

void zarlinkIndexedWrite(unsigned short writeIndex, unsigned char writeData)
{
  busWrite(0x00, (unsigned char)(writeIndex & 0x00ff));
  busWrite(0x01, (unsigned char)((writeIndex >> 8) & 0x00ff));
  busWrite(0x02, writeData);
  return;
}

unsigned char zarlinkIndexedRead(unsigned short readIndex)
{
  busWrite(0x00, (unsigned char)(readIndex & 0x00ff));
  busWrite(0x01, (unsigned char)((readIndex >> 8) & 0x00ff));
  return busRead(0x02);
}

void zarlinkCommandWrite(unsigned char cmdData)
{
  busWrite(0x04, cmdData);
}

unsigned char zarlinkStatusRead(void)
{
  return busRead(0x04);
}

void zarlinkFrameWrite(unsigned char frameData)
{
  busWrite(0x03, frameData);
}

unsigned char zarlinkFrameRead(void)
{
  return busRead(0x03);
}

void zarlinkCtrlBuffer1Write(unsigned char ctrlData)
{
  busWrite(0x06, ctrlData);
}

unsigned char zarlinkCtrlBuffer1Read(void)
{
  return busRead(0x06);
}

unsigned char zarlinkCtrlBuffer2Read(void)
{
  return busRead(0x07);
}

void zarlinkInterruptWrite(unsigned char intData)
{
  busWrite(0x05, (intData & 0x07));
}

unsigned char zarlinkInterruptRead(void)
{
  return busRead(0x05);
}

#endif

