/*
 Interrupt handling and vector setup. We only use USB interrupt in this
 code.
 
 Copyright (c) 2008 Dimtel, Inc., All Rights Reserved

 $Id: interrupts.c,v 1.2 2013/06/06 04:35:11 dim Exp $
 $Date: 2013/06/06 04:35:11 $
 $Author: dim $
 $Revision: 1.2 $
 $Log: interrupts.c,v $
 Revision 1.2  2013/06/06 04:35:11  dim
 Added EP8 OUT to interface 0 for fast JTAG block loading

 Revision 1.1  2012/11/11 01:24:32  dim
 Moved from BxB, adapted to compile for both LLRF4 and IGP12

 Revision 1.1  2011/12/27 19:31:46  dim
 Updated to remove deprecated constructs (at, sfr, xdata, sbit)


*/

#include "fx2regs.h"
#include "common.h"
#include "interrupts.h"
#include "usb.h"

volatile __bit SUDAV_service;

extern char __xdata *debug_ptr;

extern __xdata uchar _vector_table[];
extern __xdata uchar _usb_autovector_table[];

void usb_setup_strings(void);

static void isr_SUDAV(void) __interrupt
{
  EXIF &= ~bmEXIF_USBINT;
  INT2CLR = 0;
  SUDAV_service = 1;
}

static void isr_RESET(void) __interrupt
{
  EXIF &= ~bmEXIF_USBINT;
  INT2CLR = 0;
  usb_update_descriptors();
}

static void isr_HS(void) __interrupt
{
  EXIF &= ~bmEXIF_USBINT;
  INT2CLR = 0;
  usb_update_descriptors();
}

void setup_interrupts(void)
{
  /* Global interrupt disable */
  EA = 0;

  /* Disable USB interrupt */
  EIUSB = 0;
  
  /* Turn off FIFO/GPIF interrupt */
  EIEX4 = 0;
  
  /* Turn off all FIFO interrupt enables */
  EP2FIFOIE = 0; SYNCDELAY;
  EP4FIFOIE = 0; SYNCDELAY;
  EP6FIFOIE = 0; SYNCDELAY;
  EP8FIFOIE = 0; SYNCDELAY;

  /* Clear pending IRQs */
  EP2FIFOIRQ = 0xff; SYNCDELAY;
  EP4FIFOIRQ = 0xff; SYNCDELAY;
  EP6FIFOIRQ = 0xff; SYNCDELAY;
  EP8FIFOIRQ = 0xff; SYNCDELAY;

  /* IN-BULK-NAK interrupt */
  IBNIE  = 0; IBNIRQ = 0xff;

  /* PING interrupt */
  NAKIE  = 0; NAKIRQ = 0xff;

  /* SUTOK, SUDAV, EP0-ACK, SOF interrupts */
  USBIE  = 0; USBIRQ = 0xff;

  /* Endpoint interrupts */
  EPIE   = 0; EPIRQ  = 0xff;

  /* GPIF interrupt */
  SYNCDELAY; GPIFIE = 0; SYNCDELAY; GPIFIRQ = 0xff;

  /* USB error limit interrupt */
  USBERRIE = 0; USBERRIRQ = 0xff;
  
  /* Reset USB error counter - any value works */
  CLRERRCNT = 1;
  
  /* Enable USB interrupt autovectoring */
  INTSETUP = bmAV2EN;
  
  /* Clear the USB IRQ */
  EXIF &= ~bmEXIF_USBINT;
  
  /* Point USB interrupt to autovector table */
  _vector_table[USB_INTERRUPT  ] = LJMP_OPC;
  _vector_table[USB_INTERRUPT+1] = (ushort)_usb_autovector_table >> 8;
  _vector_table[USB_INTERRUPT+2] = (ushort)_usb_autovector_table & 0xff;

  /* Set up USB interrupt vectors */
  _usb_autovector_table[VEC_SUDAV  ] = LJMP_OPC;
  _usb_autovector_table[VEC_SUDAV+1] = (ushort)isr_SUDAV >> 8;
  _usb_autovector_table[VEC_SUDAV+2] = (ushort)isr_SUDAV & 0xff;
  _usb_autovector_table[VEC_RESET  ] = LJMP_OPC;
  _usb_autovector_table[VEC_RESET+1] = (ushort)isr_RESET >> 8;
  _usb_autovector_table[VEC_RESET+2] = (ushort)isr_RESET & 0xff;
  _usb_autovector_table[VEC_HS  ]    = LJMP_OPC;
  _usb_autovector_table[VEC_HS+1]    = (ushort)isr_HS >> 8;
  _usb_autovector_table[VEC_HS+2]    = (ushort)isr_HS & 0xff;

  /* Get string descriptors configured */
  usb_setup_strings();

  /* Configure USB descriptors */
  usb_update_descriptors();

  /* Unmask the above vectors */
  USBIE = bmSUDAV | bmURES | bmHSGRANT; SYNCDELAY;
  
  /* Enable USB interrupts */
  EIUSB = 1;
  
  /* Turn interrupts back on */
  EA = 1;
}
