// CrossWorks Tasking Library.
//
// Copyright (c) 2004 Rowley Associates Limited.
//
// This file may be distributed under the terms of the License Agreement
// provided with this software.
//
// THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "ctl_api.h"
#include <targets/AT91SAM7.h>

#define INTERRUPT_SOURCE_COUNT 32

static unsigned int currentVector;

void irq_handler(void) __attribute__((naked));

#define AIC_SMR(n) (*(&AIC_SMR0 + n))
#define AIC_SVR(n) (*(&AIC_SVR0 + n))

void
irq_handler(void)
{
  asm("stmfd sp!, {r0-r12, lr}"); 
  asm("mrs r0, spsr");
  asm("stmfd sp!, {r0}");
  ctl_interrupt_count++;
  /* Call ISR */
  ((CTL_ISR_FN_t)AIC_IVR)();
  /* Signal EOI */
  AIC_EOICR = 0;
  asm("mov r0, sp");
  asm("ldr r1, =ctl_exit_isr");
  asm("bx r1");
}

int
ctl_set_isr(unsigned int vector, unsigned int priority, CTL_ISR_TRIGGER_t trigger, CTL_ISR_FN_t isr, CTL_ISR_FN_t *oldisr)
{
  if (vector >= 0 && vector < INTERRUPT_SOURCE_COUNT)
    {
      if (oldisr)
        *oldisr = (CTL_ISR_FN_t)AIC_SVR(vector);
      if (isr)
        {
          /* Installing an ISR */
          switch (trigger)
            {
              case CTL_ISR_TRIGGER_NEGATIVE_EDGE:
                priority = (priority & 0xFFFFFF9F) | 0x00000020;
                break;
              case CTL_ISR_TRIGGER_HIGH_LEVEL:
                priority = (priority & 0xFFFFFF9F) | 0x00000040;
                break;
              case CTL_ISR_TRIGGER_POSITIVE_EDGE:
                priority |= 0x00000060;
                break;
            }
          AIC_SMR(vector) = priority;
          AIC_SVR(vector) = (unsigned int)isr;
        }
      else
        {
          /* Removing an ISR */
          AIC_SMR(vector) = 0;
          AIC_SVR(vector) = 0;
        }
      return 1;
    }
  else if (vector == 0xFFFFFFFF)
    {
      /* Installing a spurious interrupt handler */
      if (oldisr)
        *oldisr = (CTL_ISR_FN_t)AIC_SPU;
      AIC_SPU = (unsigned int)isr;
      return 1;
    }
  return 0;
}

int
ctl_unmask_isr(unsigned int vector)
{
  AIC_IECR = 1 << vector;
  return 1;
}

int
ctl_mask_isr(unsigned int vector)
{
  AIC_IDCR = 1 << vector;
  return 1;
}
