// 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 AT91SAM7A1-EK Timer Interrupt Example
//
// Description
// -----------
// This example demonstrates a simple interrupt handler and setting up a timer.
//
////////////////////////////////////////////////////////////////////////////////

#include <targets/AT91SAM7A1.h>

#define CORECLK_FREQUENCY 30000000

#define GIC_SMR(n) (*(&GIC_SMR0 + n))
#define GIC_SVR(n) (*(&GIC_SVR0 + n))

#define GIC_PRIOR                       0x07    /* Priority */
#define GIC_SRCTYPE                     0x60    /* Source Type Definition */

#define GIC_SRCTYPE_INT_LEVEL_SENSITIVE 0x00    /* Level Sensitive */
#define GIC_SRCTYPE_INT_EDGE_TRIGGERED  0x20    /* Edge Triggered */
#define GIC_SRCTYPE_EXT_LOW_LEVEL       0x00    /* Low Level */
#define GIC_SRCTYPE_EXT_NEGATIVE_EDGE   0x20    /* Negative Edge */
#define GIC_SRCTYPE_EXT_HIGH_LEVEL      0x40    /* High Level */
#define GIC_SRCTYPE_EXT_POSITIVE_EDGE   0x60    /* Positive Edge */

#define ST0_INT 24

static int count;

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

void
timer_irq_handler(void)
{
  if (++count & 1)
    GPT0C0_CODR = 0x00020000;
  else
    GPT0C0_SODR = 0x00020000;
  ST0_CSR = 1 << 0; // Clear timer interrupt
  GIC_EOICR = 0; // Signal end of interrupt
}

void
ledInit()
{
  GPT0C0_ECR = 0x00000001;
  GPT0C0_PER = 0x00020000;
  GPT0C0_OER = 0x00020000;
  GPT0C0_CODR = 0x00020000;
}

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

  ledInit();

  // Set up timer interrupt
  __ARMLIB_enableIRQ();
  GIC_IDCR = 1 << ST0_INT;
  GIC_SVR(ST0_INT) = (unsigned)timer_irq_handler;
  GIC_SMR(ST0_INT) = GIC_SRCTYPE_INT_LEVEL_SENSITIVE;
  GIC_ICCR = 1 << ST0_INT;
  GIC_IECR = 1 << ST0_INT;

  // Set up timer
  ST0_ECR = 1 << 1; // Enable simple timer clock
  ST0_CR = 1 << 2; // Disable simple timer channel 0

  ST0_PR0 = 0x00000020 | (((CORECLK_FREQUENCY / 2000000) - 1) << 8); // Setup prescaler for 1Mhz timer frequency
  ST0_CT0 = 999; // Generate a 1Khz timer interrupt

  ST0_IER |= 1 << 0; // Generate interrupt when timer reaches zero
  ST0_CR = 1 << 1; // Enable simple timer channel 0

  while (count < 5);

  return 0; 
}

