/*
 *  interrupt.c
 *
 *  This is an example of a simple program that uses an interrupt
 *  handler written entirely in C. This is achieved using the 
 *  __attribute__ ((interrupt)) directive.
 *
 *  The program configures timer1 to time out every 100ms
 *  (less for >25MHz bus clocks). This is counted in the timer_isr()
 *  function. The main program outputs the number of timer ticks and
 *  exits after 10 seconds approximately.
 *
 *  Author:
 *    David Fiddes, D.J@fiddes.net
 *    http://fiddes.net/coldfire/
 *
 *  This code has no copyright and may freely be used in whatever 
 *  manner you see fit.
 *
 *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY 
 *  OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT    
 *  LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR    
 *  FITNESS FOR A PARTICULAR PURPOSE.                              
 */
#include <stdio.h>

/*
 * Figure out which processor we are using and setup it's SIM
 */
#ifdef MCF5307
  #include "sim5307.h"

  volatile SIM5307 *sim = (SIM5307 *)0x10000000;
#else
  #include "sim5206.h"

  volatile SIM5206 *sim = (SIM5206 *)0x10000000;
#endif


/*
 * Carry out some XP voodoo so that this runs on lots of different
 * boards.
 */

#define AUTOVEC_VECTOR          24

#if defined(VARIANT_sbc5206)
  #define VBR                     (0x00000000)

  #define TIMER1_IRQ_LEVEL        5
  #define TIMER1_IRQ_PRIORITY     3
  #define TIMER1_IRQ_VECTOR       (AUTOVEC_VECTOR + TIMER1_IRQ_LEVEL)
  #define TIMER1_IRQ_MASK         MCF5206_SIM_IMR_T1
  #define TIMER1_IRQ_CONFIG       sim->icr9 = MCF5206_SIM_ICR_AVEC                   | \
                                              MCF5206_SIM_ICR_IL(TIMER1_IRQ_LEVEL)   | \
                                              MCF5206_SIM_ICR_IP(TIMER1_IRQ_PRIORITY) 

#elif defined(VARIANT_sbc5307)
  #define VBR                     (0x00000000)

  #define TIMER1_IRQ_LEVEL        5
  #define TIMER1_IRQ_PRIORITY     3
  #define TIMER1_IRQ_VECTOR       (AUTOVEC_VECTOR + TIMER1_IRQ_LEVEL)
  #define TIMER1_IRQ_MASK         MCF5307_SIM_IMR_TIMER1
  #define TIMER1_IRQ_CONFIG       sim->icr1 = MCF5307_SIM_ICR_AVEC                   | \
                                              MCF5307_SIM_ICR_IL(TIMER1_IRQ_LEVEL)   | \
                                              MCF5307_SIM_ICR_IP(TIMER1_IRQ_PRIORITY)

#elif defined(VARIANT_sbc5206e)
  #define VBR                     (0x00000000)

  #define TIMER1_IRQ_LEVEL        5
  #define TIMER1_IRQ_PRIORITY     3
  #define TIMER1_IRQ_VECTOR       (AUTOVEC_VECTOR + TIMER1_IRQ_LEVEL)
  #define TIMER1_IRQ_MASK         MCF5206_SIM_IMR_T1
  #define TIMER1_IRQ_CONFIG       sim->icr9 = MCF5206_SIM_ICR_AVEC                   | \
                                              MCF5206_SIM_ICR_IL(TIMER1_IRQ_LEVEL)   | \
                                              MCF5206_SIM_ICR_IP(TIMER1_IRQ_PRIORITY) 

#elif defined(VARIANT_sbc5206e)
  #define VBR                     (0x00000000)

  #define TIMER1_IRQ_LEVEL        5
  #define TIMER1_IRQ_PRIORITY     3
  #define TIMER1_IRQ_VECTOR       (AUTOVEC_VECTOR + TIMER1_IRQ_LEVEL)
  #define TIMER1_IRQ_MASK         MCF5206_SIM_IMR_T1
  #define TIMER1_IRQ_CONFIG       sim->icr9 = MCF5206_SIM_ICR_AVEC                   | \
                                              MCF5206_SIM_ICR_IL(TIMER1_IRQ_LEVEL)   | \
                                              MCF5206_SIM_ICR_IP(TIMER1_IRQ_PRIORITY) 
#elif defined(VARIANT_sbc5206elite)
  #define VBR                     (0x30000000)

  #define TIMER1_IRQ_LEVEL        5
  #define TIMER1_IRQ_PRIORITY     3
  #define TIMER1_IRQ_VECTOR       (AUTOVEC_VECTOR + TIMER1_IRQ_LEVEL)
  #define TIMER1_IRQ_MASK         MCF5206_SIM_IMR_T1
  #define TIMER1_IRQ_CONFIG       sim->icr9 = MCF5206_SIM_ICR_AVEC                   | \
                                              MCF5206_SIM_ICR_IL(TIMER1_IRQ_LEVEL)   | \
                                              MCF5206_SIM_ICR_IP(TIMER1_IRQ_PRIORITY) 
#else
  #error "Invalid board variant!";
#endif


/*
 * Vector table structure and VBR setting macro
 */

typedef struct
{
  unsigned long table[255];
} vectors;

volatile vectors *vect = (vectors *)VBR;

#define m68k_set_vbr( _vbr ) \
    asm volatile ("move.l  %%a7,%%d1 \n\t" \
	          "move.l  %0,%%a7\n\t"    \
	          "movec   %%a7,%%vbr\n\t" \
                  "move.l  %%d1,%%a7\n\t"  \
		  : : "d" (_vbr) : "d1" );
  



/*
 * Timer Interrupt service routine and tick count
 * Note the use of __attribute__ ((interrupt))
 */
volatile int timer_ticks = 0;

static void __attribute__ ((interrupt))
timer_isr (void)
{
  /* Clear pending interrupt... */
  sim->timer1.ter = MCF5XXX_TIMER_TER_REF | MCF5XXX_TIMER_TER_CAP;

  /* Announce the clock tick */
  timer_ticks++;
}


/*
 * Configures Timer1 appropriately and installs the interrupt
 * service routine.
 */
static void
install_timer (void)
{
    /* Configure timer1 interrupts */
    TIMER1_IRQ_CONFIG;

    /* Enable timer 1 interrupts */
    sim->imr = sim->imr & (~TIMER1_IRQ_MASK);  

    /* Register the interrupt handler */
    /* AutoVector 5 - Timer tick */
    vect->table[TIMER1_IRQ_VECTOR] = (long)&timer_isr; 
  
    /* Reset timer1 */
    sim->timer1.tmr = MCF5XXX_TIMER_TMR_RST;    
    sim->timer1.tmr = MCF5XXX_TIMER_TMR_CLK_STOP;   
    sim->timer1.tmr = MCF5XXX_TIMER_TMR_RST;    
    sim->timer1.tcn = 0; /* zero count */
    sim->timer1.ter = MCF5XXX_TIMER_TER_REF | MCF5XXX_TIMER_TER_CAP; /* clear pending */
    
          
    /*
     * Prescale Timer1 by 1/250 
     */
    sim->timer1.tmr = MCF5XXX_TIMER_TMR_PS( 250 )  |
                      MCF5XXX_TIMER_TMR_CE_NONE |
                      MCF5XXX_TIMER_TMR_ORI     |
                      MCF5XXX_TIMER_TMR_FRR     |
                      MCF5XXX_TIMER_TMR_RST;

    /* Set the timer timeout value to be 100 ms */      
    sim->timer1.trr = 10000 - 1;
    
    /* Kick it into life! */
    sim->timer1.tmr = sim->timer1.tmr | MCF5XXX_TIMER_TMR_CLK_MSCLK;
}    


/*
 * Uninstalls the timer ISR and stops the timer.
 */
static void
uninstall_timer (void)
{
    /* disable all timer1 interrupts */
    sim->imr = sim->imr | TIMER1_IRQ_MASK;
 
    /* reset timer1... */
    sim->timer1.tmr = MCF5XXX_TIMER_TMR_CLK_STOP;

    /* clear pending */
    sim->timer1.ter = MCF5XXX_TIMER_TER_REF | MCF5XXX_TIMER_TER_CAP; 
}


/*
 * The main program just starts things up, outputs the number of 100ms
 * blocks that have elapsed then cleans up.
 */
int main()
{
  int last_second = 0;

  m68k_set_vbr( VBR );
  install_timer();

  /* enable interrupts */
  asm(" move #0x2000,%sr ");
  
  while(1)
  {
    /* count approximately 1 second then output */
    if (timer_ticks >= last_second + 10)
    {
      last_second = timer_ticks;
      printf("%i\n",timer_ticks);
    }

    /* wait for 10 seconds(approx) before quitting */
    if (timer_ticks > 100)
      break;
  }
  
  /* clean everything up */
  uninstall_timer();

  return 0;
}
