#include "archinc.h"
#include "cli.h"
#include "eventq.h"
#include "strutil.h"
#include "zarlink.h"
#include "zlregs.h"
#include "zl_event.h"
#include "zl_bus.h"

#include <stdio.h>

#ifdef CONFIG_INCLUDE_ZARLINK

#include <stdlib.h>

static unsigned int eventCount = 0;
static unsigned int frameRxEventCount = 0;
static unsigned int ctrl1RxEventCount = 0;
static unsigned int ctrl2RxEventCount = 0;
static unsigned int resetEventCount = 0;

static int eventCmdHandler(int argc, char *argv[], int flags);
static int clearintCmdHandler(int argc, char *argv[], int flags);
static int pollCmdHandler(int argc, char *argv[], int flags);

/*
 * this module takes care of interrupt handling and dispatches interrupts
 * as events for deferred handling. On the Medusa rev 1 board, the Zarlink
 * interrupt is connected to the LPC2106 EINT2 external input pin
 */

DECLARE_ISR(zarlinkIsr);

int zarlinkEventInit(boolean connectIrq)
{
  int retStat = -1;
  int i;
  unsigned long dummy;
  int irqVecNum;

#ifdef CONFIG_BOARD_MEDUSA
  irqVecNum = LPC21XX_IRQVEC_EINT2;
#else
  #ifdef CONFIG_BOARD_MEDUSA_REV2
    irqVecNum = LPC21XX_IRQVEC_EINT0;
  #else
    #error "Board not defined"
  #endif /* CONFIG_BOARD_MEDUSA_REV2 */
#endif /* CONFIG_BOARD_MEDUSA */

  // low true interrupt assertion at ZL50409
  zarlinkIndexedWrite(DEVICE, 0x00);

  if (connectIrq == true ) {
    // delay for #INT to deassert
    for(i=0;i<10;i++)
      dummy = CTIME0;

    // clear the EINT2 latch at the LPC2106
#ifdef CONFIG_BOARD_MEDUSA
    EXTINT = (unsigned long)0x04;
#else
  #ifdef CONFIG_BOARD_MEDUSA_REV2
    EXTINT   = (unsigned long)0x01;
    EXTMODE  = (unsigned long)0x01;
    EXTPOLAR = (unsigned long)0x00;
  #else
    #error "board not defined"
  #endif /* CONFIG_BOARD_MEDUSA_REV2 */
#endif /* CONFIG_BOARD_MEDUSA */

    // connect the interrupt vector
    if ((retStat = armvicIntConnect(8, irqVecNum, zarlinkIsr)) != 0)
      return retStat;

    // enable the interrupt at the VIC
    if ((retStat = armvicIntEnable(irqVecNum)) != 0)
      return retStat;
    } /* if (connectIrq == true ) */

  cliRegisterCommand("poll", pollCmdHandler, "poll the switch for events");
  cliRegisterCommand("event", eventCmdHandler, "show info on events received from switch");
  cliRegisterCommand("clearint", clearintCmdHandler, "clear the interrupt at the LPC2106");

  return 0;
}

static int pollCmdHandler(int argc, char *argv[], int flags)
{
  unsigned char intAckMask = 0;
  unsigned char pollIrqReg;

  // handle the event
  pollIrqReg = zarlinkInterruptRead();

  // frame received (bit 0)
  if ((pollIrqReg & INT_CPU_FRAME) != 0) {
    frameRxEventCount++;
    eventqPut(SWITCH_FRAME_RX);
    intAckMask |= INT_CPU_FRAME;
    }

  if ((pollIrqReg & INT_CONTROL_FRAME1) != 0) {
    ctrl1RxEventCount++;
    eventqPut(SWITCH_CTRL1_RX);
    intAckMask |= INT_CONTROL_FRAME1;
    }

  if ((pollIrqReg & INT_CONTROL_FRAME2) != 0) {
    ctrl2RxEventCount++;
    eventqPut(SWITCH_CTRL2_RX);
    intAckMask |= INT_CONTROL_FRAME2;
    }

  if ((pollIrqReg & INT_CHIP_RESET) != 0) {
    // eeek! the switch reset on us: reinitialize it
    resetEventCount++;
    eventqPut(SWITCH_RESET);
    intAckMask |= INT_CHIP_RESET;
    }

  // mask interrupts while they are handled
  zarlinkEventMaskIrq(intAckMask);

  return 0;
}

static unsigned char irqReg;
static unsigned long dummy;

ISR_FUNCTION(zarlinkIsr)
{
  unsigned char intMask;
  unsigned char currentIntMask;

  ISR_ENTER()  

  intMask = 0;
  eventCount++;

  // handle the event
  irqReg = zarlinkInterruptRead();

  // frame received (bit 0)
  if ((irqReg & INT_CPU_FRAME) != 0) {
    frameRxEventCount++;
    eventqPut(SWITCH_FRAME_RX);
    intMask |= INT_CPU_FRAME;
    }

  if ((irqReg & INT_CONTROL_FRAME1) != 0) {
    ctrl1RxEventCount++;
    eventqPut(SWITCH_CTRL1_RX);
    intMask |= INT_CONTROL_FRAME1;
    }

  if ((irqReg & INT_CONTROL_FRAME2) != 0) {
    ctrl2RxEventCount++;
    eventqPut(SWITCH_CTRL2_RX);
    intMask |= INT_CONTROL_FRAME2;
    }

  if ((irqReg & INT_CHIP_RESET) != 0) {
    // eeek! the switch reset on us: reinitialize it
    resetEventCount++;
    eventqUrgentPut(SWITCH_RESET);
    intMask |= INT_CHIP_RESET;
    }

  // mask interrupts while they are handled
  currentIntMask = zarlinkIndexedRead(INT_MASK0);
  zarlinkIndexedWrite(INT_MASK0, (intMask | currentIntMask));

  // clear the EINT2 latch at the LPC2106
#ifdef CONFIG_BOARD_MEDUSA
  EXTINT = (unsigned long)0x04; // EINT2
  dummy = EXTINT;
#endif

#ifdef CONFIG_BOARD_MEDUSA_REV2
  EXTINT = (unsigned long)0x01; // EINT0
  dummy = EXTINT;
#endif

  // acknowledge the interrupt at the VIC
  VICIrqDone;

  ISR_LEAVE()
}

void zarlinkEventMaskIrq(unsigned char intMask)
{
  unsigned char currentIntMask;

  // mask interrupts while they are handled
  currentIntMask = zarlinkIndexedRead(INT_MASK0);
  zarlinkIndexedWrite(INT_MASK0, (intMask | currentIntMask));
}

void zarlinkEventUnmaskIrq(unsigned char intMask)
{
  unsigned char currentIntMask;

  // mask interrupts while they are handled
  currentIntMask = zarlinkIndexedRead(INT_MASK0);
  zarlinkIndexedWrite(INT_MASK0, (~intMask & currentIntMask));
}

void zarlinkEventMaskPorts(unsigned char portMask)
{
  zarlinkIndexedWrite(INTP_MASK0, portMask);
  zarlinkIndexedWrite(INTP_MASK1, portMask);
  zarlinkIndexedWrite(INTP_MASK2, portMask);
  zarlinkIndexedWrite(INTP_MASK3, portMask);
  zarlinkIndexedWrite(INTP_MASK4, portMask);
}

static int clearintCmdHandler(int argc, char *argv[], int flags)
{
  // clear the EINT2 latch at the LPC2106

#ifdef CONFIG_BOARD_MEDUSA
  EXTINT = (unsigned long)0x04; // EINT2
#else 
  #ifdef CONFIG_BOARD_MEDUSA_REV2
  EXTINT = (unsigned long)0x01; // EINT0
  #else
    #error "board not defined"
  #endif
#endif

  return 0;
}

static int eventCmdHandler(int argc, char *argv[], int flags)
{
  int bufLen;
  char * pOutBuf;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  sprintf(pOutBuf, "%s: events[%u] rxFrame[%u] ctrl1[%u] ctrl2[%u] reset[%u]\r\n",
    argv[0],eventCount, frameRxEventCount, ctrl1RxEventCount, ctrl2RxEventCount,
    resetEventCount);
  clicharSendOutputBuffer(pOutBuf, bufLen);    

  return 0;
}

#endif

