#include "archinc.h"
#include "armvic.h"
#include "cli.h"
#include "clichar.h"

#include <stdio.h>
#include <stdlib.h>

/*
 * Needed to replace macros defined in lpc210x.h
 */
#define VICVectAddrN(n) (*(volatile unsigned long *)(0xfffff100 + ((n)*4)))
#define VICVectCtrlN(n) (*(volatile unsigned long *)(0xfffff200 + ((n)*4)))

static unsigned int unassignedVectorIrqs = 0;

DECLARE_ISR(unassignedVectorIsr);

ISR_FUNCTION(unassignedVectorIsr)
{
  ISR_ENTER();

  unassignedVectorIrqs++;

  // acknowledge the interrupt at the VIC
  VICIrqDone;

  ISR_LEAVE();
}

static int vicstatHandler(int argc, char *argv[], int flags)
{
  int bufLen;
  char * pOutBuf;
  unsigned char putData;

  pOutBuf = clicharGetOutputBuffer(&bufLen);
  sprintf(pOutBuf, "%s: unassigned Vector IRQs[%u]\r\n",argv[0], unassignedVectorIrqs);
  clicharSendOutputBuffer(pOutBuf, bufLen);
  return 0;
}

int armvicInit(void)
{

  /* Clear all interrupt enables */
  VICIntEnClr = (unsigned long)0xffffffff;

  /* Initialize all interrupts as IRQs (no FIQs, please) */
  VICIntSelect = (unsigned long)0;

  /* Setup the 'default' (unassigned vector/spurious) vector */
  VICDefVectAddr = (unsigned long)unassignedVectorIsr;

  return 0;
}

/* second initialization after CLI is up */

int armvicInit2(void)
{
  cliRegisterCommand("vicstat", vicstatHandler, "dump info on VIC");
}

int armvicIntPrioLevelSet(int vecNum, int priorityLevel)
{
  /* Check vector number */
  if ((vecNum < LPC21XX_IRQVEC_WATCHDOG) || (vecNum > LPC21XX_IRQVEC_EINT2))
    return -1;

  VICVectCtrlN(vecNum) = (unsigned long)((priorityLevel & 0x1F) | 0x20);

  return 0;

}

int armvicIntConnect(int vecNum, armNestedIsr_t isr)
{
  /* Check vector number */
  if ((vecNum < LPC21XX_IRQVEC_FIRST) || (vecNum > LPC21XX_IRQVEC_LAST))
    return -1;

  /* Set the IRQ/FIQ Vector */
  VICVectAddrN(vecNum) = (unsigned long)isr;

  /* Set the initial IRQ/FIQ priority to the vector number */
  VICVectCtrlN(vecNum) = (unsigned long)((vecNum & 0x1F) | 0x20);

  return 0;
}

int armvicIntDisconnect(int vecNum)
{
  /* Check vector number */
  if ((vecNum < LPC21XX_IRQVEC_FIRST) || (vecNum > LPC21XX_IRQVEC_LAST))
    return -1;

  VICVectCtrlN(vecNum) = 0;

  // TODO: should probably connect to a default unknown vector handler
  VICVectAddrN(vecNum) = (unsigned long)&unassignedVectorIsr;

  return 0;
}

int armvicSoftIntAssert(int vecNum)
{
  // TODO: not implemented
  return -1;
}

int armvicSoftIntNegate(int vecNum)
{
  // TODO: not implemented
  return -1;
}

int armvicSoftIntNegateAll(void)
{
  // TODO: not implemented
  return -1;
}

int armvicIntEnable(int vecNum)
{
  if ((vecNum < LPC21XX_IRQVEC_FIRST) || (vecNum > LPC21XX_IRQVEC_LAST)) {
    return -1;
    }

  VICIntEnable = (unsigned long)VECNUM_TO_MASK(vecNum);

  return 0;
}

int armvicIntDisable(int vecNum)
{
  if ((vecNum < LPC21XX_IRQVEC_FIRST) || (vecNum > LPC21XX_IRQVEC_LAST)) {
    return -1;
    }

  VICIntEnClr = (unsigned long)VECNUM_TO_MASK(vecNum);

  return 0;
}

