// Copyright (c) 2001-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.
//
////////////////////////////////////////////////////////////////////////////////
//
//                 ATMEL EB40A Timer Interrupt Example
//
// Description
// -----------
// This example demonstrates a simple interrupt handler and setting up a timer.
//
////////////////////////////////////////////////////////////////////////////////

#include <targets/AT91x408xx.h>

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

#define AIC_PRIOR                       0x07    /* Priority */
#define AIC_SRCTYPE                     0x60    /* Source Type Definition */

#define AIC_SRCTYPE_INT_LEVEL_SENSITIVE 0x00    /* Level Sensitive */
#define AIC_SRCTYPE_INT_EDGE_TRIGGERED  0x20    /* Edge Triggered */
#define AIC_SRCTYPE_EXT_LOW_LEVEL       0x00    /* Low Level */
#define AIC_SRCTYPE_EXT_NEGATIVE_EDGE   0x20    /* Negative Edge */
#define AIC_SRCTYPE_EXT_HIGH_LEVEL      0x40    /* High Level */
#define AIC_SRCTYPE_EXT_POSITIVE_EDGE   0x60    /* Positive Edge */

// Peripheral interrupts
#define FIQ_ID          0       /* Fast Interrupt */
#define SWIRQ_ID        1       /* Soft Interrupt (generated by the AIC) */
#define US0_ID          2       /* USART Channel 0 interrupt */
#define US1_ID          3       /* USART Channel 1 interrupt */
#define TC0_ID          4       /* Timer Channel 0 interrupt */
#define TC1_ID          5       /* Timer Channel 1 interrupt */
#define TC2_ID          6       /* Timer Channel 2 interrupt */
#define WDI_ID          7       /* Watchdog interrupt */
#define PIO_ID          8       /* Parallel I/O Controller A interrupt */

#define IRQ0_ID         16      /* External interrupt 0 */
#define IRQ1_ID         17      /* External interrupt 1 */
#define IRQ2_ID         18      /* External interrupt 2 */

// Timer definitions
#define TC_CLKEN            0x1
#define TC_CLKDIS           0x2
#define TC_SWTRG            0x4

#define TC_CLKS                  0x7
#define TC_CLKS_MCK2             0x0
#define TC_CLKS_MCK8             0x1
#define TC_CLKS_MCK32            0x2
#define TC_CLKS_MCK128           0x3
#define TC_CLKS_MCK1024          0x4

#define TC_CLKS_SLCK             0x4

#define TC_CLKS_XC0              0x5
#define TC_CLKS_XC1              0x6
#define TC_CLKS_XC2              0x7

#define TC_COVFS            0x1         /* Counter Overflow Status */
#define TC_LOVRS            0x2         /* Load Overrun Status */
#define TC_CPAS             0x4         /* RA Compare Status */
#define TC_CPBS             0x8         /* RB Compare Status */
#define TC_CPCS             0x10        /* RC Compare Status */
#define TC_LDRAS            0x20        /* RA Loading Status */
#define TC_LDRBS            0x40        /* RB Loading Status */
#define TC_ETRGS            0x80        /* External Trigger Status */
#define TC_CLKSTA           0x10000     /* Clock Status */
#define TC_MTIOA            0x20000     /* TIOA Mirror */
#define TC_MTIOB            0x40000     /* TIOB Status */

static int count;

void timer_irq_handler(void) __attribute__ ((interrupt ("IRQ")));

void
timer_irq_handler(void)
{
  TC0_SR;
  ++count;
  // Signal end of interrupt
  AIC_EOICR = 0;
}

int
main(void)
{
  // Put LDR PC, [PC, #-0xF20] instruction into IRQ vector in order to use 
  // hardware interrupt vectoring.
  *(volatile unsigned int *)0x00000018 = 0xE51FFF20;

  // Set up timer interrupt
  __ARMLIB_enableIRQ();
  AIC_IDCR = 1 << TC0_ID;
  AIC_SVR(TC0_ID) = (unsigned)timer_irq_handler;
  AIC_SMR(TC0_ID) = AIC_SRCTYPE_INT_LEVEL_SENSITIVE;
  AIC_ICCR = 1 << TC0_ID;
  AIC_IECR = 1 << TC0_ID;

  // Set up timer
  TC0_CCR = TC_CLKDIS;
  TC0_IDR = 0xFFFFFFFF;
  TC0_CMR = TC_CLKS_MCK1024;
  TC0_IER = TC_COVFS;
  TC0_CCR = TC_CLKEN | TC_SWTRG;

  while (count < 5);

  return 0; 
}

