/******************************************************************************
*                                                                             *
* License Agreement                                                           *
*                                                                             *
* Copyright (c) 2004 Altera Corporation, San Jose, California, USA.           *
* All rights reserved.                                                        *
*                                                                             *
* Permission is hereby granted, free of charge, to any person obtaining a     *
* copy of this software and associated documentation files (the "Software"),  *
* to deal in the Software without restriction, including without limitation   *
* the rights to use, copy, modify, merge, publish, distribute, sublicense,    *
* and/or sell copies of the Software, and to permit persons to whom the       *
* Software is furnished to do so, subject to the following conditions:        *
*                                                                             *
* The above copyright notice and this permission notice shall be included in  *
* all copies or substantial portions of the Software.                         *
*                                                                             *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,    *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING     *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER         *
* DEALINGS IN THE SOFTWARE.                                                   *
*                                                                             *
* This agreement shall be governed in all respects by the laws of the State   *
* of California and by the laws of the United States of America.              *
*                                                                             *
* Altera does not recommend, suggest or require that this reference design    *
* file be used in conjunction or combination with any other product.          *
******************************************************************************/

#include "sys/alt_irq.h"
#include "sys/alt_alarm.h"
#include "os/alt_hooks.h"
#include "alt_types.h"

/*
 * "_alt_tick_rate" is used to store the value of the system clock frequency
 * in ticks per second. It is initialised to zero, which corresponds to there
 * being no system clock facility available. 
 */

alt_u32 _alt_tick_rate = 0;

/*
 * "_alt_nticks" is the number of system clock ticks that have elapsed since
 * reset. 
 */

volatile alt_u32 _alt_nticks = 0;

/*
 * "alt_alarm_list" is the head of a linked list of registered alarms. This is
 * initialised to be an empty list.
 */

ALT_LLIST_HEAD(alt_alarm_list);

/*
 * alt_alarm_stop() is called to remove an alarm from the list of registered 
 * alarms. Alternatively an alarm can unregister itself by returning zero when 
 * the alarm executes.
 */

void alt_alarm_stop (alt_alarm* alarm)
{
  alt_irq_context irq_context;

  irq_context = alt_irq_disable_all();
  alt_llist_remove (&alarm->llist);
  alt_irq_enable_all (irq_context);
}

/*
 * alt_tick() is periodically called by the system clock driver in order to
 * process the registered list of alarms. Each alarm is registed with a
 * callback interval, and a callback function, "callback". 
 *
 * The return value of the callback function indicates how many ticks are to
 * elapse until the next callback. A return value of zero indicates that the
 * alarm should be deactivated. 
 * 
 * alt_tick() is expected to run at interrupt level.
 */

void alt_tick (void)
{
  alt_alarm* next;
  alt_alarm* alarm = (alt_alarm*) alt_alarm_list.next;

  alt_u32    next_callback;

  /* update the tick counter */

  _alt_nticks++;

  /* process the registered callbacks */

  while (alarm != (alt_alarm*) &alt_alarm_list)
  {
    next = (alt_alarm*) alarm->llist.next;

    /* 
     * Upon the tick-counter rolling over it is safe to clear the 
     * roll-over flag; once the flag is cleared this (or subsequnt)
     * tick events are enabled to generate an alarm event. 
     */
    if ((alarm->rollover) && (_alt_nticks == 0))
    {
      alarm->rollover = 0;
    }
    
    /* if the alarm period has expired, make the callback */    
    if ((alarm->time <= _alt_nticks) && (alarm->rollover == 0))
    {
      next_callback = alarm->callback (alarm->context);

      /* deactivate the alarm if the return value is zero */

      if (next_callback == 0)
      {
        alt_alarm_stop (alarm);
      }
      else
      {
        alarm->time += next_callback;
        
        /* 
         * If the desired alarm time causes a roll-over, set the rollover
         * flag. This will prevent the subsequent tick event from causing
         * an alarm too early.
         */
        if(alarm->time < _alt_nticks)
        {
          alarm->rollover = 1;
        }
      }
    }
    alarm = next;
  }

  /* 
   * Update the operating system specific timer facilities.
   */

  ALT_OS_TIME_TICK();
}

