/**
 * \file
 *
 * \brief Simple timer able to perform tasks after a given delay
 *
 * This is a simple timer which can be used to execute tasks after
 * specific delays. Numerous tasks can be added and they will be
 * executed in the order of their time stamps.
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All devices
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 * Atmel AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
#include <debug.h>

#include <timer_tc.h>
#include <softirq.h>
#include <chip/clk.h>
#include <chip/irq-map.h>
#include <app/softirq.h>

#include "tc_regs.h"

/** Mask for the low part of the counter */
#define COUNTER_LOW_MASK   		((1UL << 16)-1)
/** Offset to the high part of the counter */
#define COUNTER_HIGH_OFFSET		16

static void timer_do_pending(void *timer);

counter_t timer_convert_us(struct timer *timer, uint32_t us)
{
	/*
	 * Avoid 64-bit division. This might need tweaking when running
	 * from a slow clock.
	 */
	if (us > 1000000) {
		us /= 1000;
		return (us * (timer_get_frequency(timer) / 1000));
	} else {
		return (us * (timer_get_frequency(timer) / 1000)) / 1000;
	}
}

/**
 * \brief Timer IRQ handler
 *
 * This handler will be executed whenever the timer either overflows
 * or hit the compare level. If it was an overflow the top part of the counter
 * is incremented. If the compare level is hit, that interrupt is disabled
 * (will be reenabled if a new alarm is set)
 *
 * The timer_do_pending() function will be called regardless of what caused
 * the interrupt. If an alarm did trigger, its callback will be called from
 * there, if we had an overflow, the first alarm in the next period will be
 * set up.
 *
 * \param  data  Pointer to timer control structure.
 */
static void timer_irq_handler(void *data)
{
	struct timer *timer = (struct timer *)data;
	unsigned long status = tc_read_reg(timer->port, SR);

	if (status & TC_SR_COVFS_MASK) {
		timer->counter_high++;
	}
	if (status & TC_SR_CPAS_MASK) {
		/* Disable compare interrupt, if necessary this will
		 * be renabled when the pending actions are done
		 */
		tc_write_reg(timer->port, IDR, TC_SR_CPAS_MASK);
	}

	/*
	 * Whenever we get an alarm or overflow, we need to set up the next
	 * alarm; this is handled by setting the pending action flag and
	 * calling the do_pending function.
	 */
	timer->action_pending = true;

	softirq_raise(SOFTIRQ_ID_TIMER);
}

DEFINE_IRQ_HANDLER(timer_tick, timer_irq_handler, 0);

static void timer_start(struct timer *timer)
{
	/* Reset and start timer */
	tc_write_reg(timer->port, IER, TC_SR_COVFS_MASK);
	tc_write_reg(timer->port, CCR, TC_CCR_CLKEN_MASK | TC_CCR_SWTRG_MASK);
	timer->is_running = true;
}

static void timer_stop(struct timer *timer)
{
	tc_write_reg(timer->port, CCR, TC_CCR_CLKDIS_MASK);
	tc_write_reg(timer->port, IDR, TC_SR_COVFS_MASK | TC_SR_CPAS_MASK);
	timer->is_running = false;

	/* Reset counter */
	timer->counter_high = 0;
}

static counter_t timer_get_time(struct timer *timer)
{
	uint16_t counter_low;
	counter_high_t counter_high_first, counter_high_second;

	/* A stopped timer will always restart from 0 */
	if (!timer->is_running)
		return 0;

	/* Read high and low counter; ensure consistence by re-reading if
	 * high value changes during operation. Since we can not stop the
	 * hardware timer, disabling interrupt here would not help us
	 */
	do {
		counter_high_first = timer->counter_high;
		counter_low = tc_read_reg(timer->port, CV);
		barrier();
		counter_high_second = timer->counter_high;
	} while (counter_high_second != counter_high_first);

	return (counter_t)counter_high_second << COUNTER_HIGH_OFFSET |
			counter_low;
}

/**
 * \internal
 * \brief Set alarm
 *
 * Set a alarm at the given time. This function will only set up alarms
 * in the near future (before next low overflow), if the alarm is after
 * the next low overflow, this function needs to be called again.
 *
 * If the time_stamp has already expired, the pending action flag will
 * be raised direcly, if the timer is stopped, it will be restarted.
 *
 * \param  timer       Pointer to timer control structure.
 * \param  time_stamp  Time of the alarm
 */
static void timer_set_alarm(struct timer *timer, counter_t time_stamp)
{
	uint16_t counter_low;

	/* Check if alarm time is before next overflow, if it is not
	 * we will not need to handle it yet
	 */
	if ((time_stamp >> COUNTER_HIGH_OFFSET) <
			(counter_t)(timer->counter_high + 1)) {
		counter_low = time_stamp & COUNTER_LOW_MASK;
		tc_write_reg(timer->port, RA, counter_low);
		tc_write_reg(timer->port, IER, TC_SR_CPAS_MASK);
	}

	if (!timer->is_running)
		timer_start(timer);
	/* Make sure we did not miss the deadline while setting alarm */
	else if (timer_get_time(timer) > time_stamp) {
		timer->action_pending = true;
		/* TODO: Utilize soft IRQ or workqueue here in order to avoid
		 * nested timer_do_pending() calls.
		 */
		timer_do_pending(timer);
	}
}

/**
 * \brief Initialize a timer
 *
 * This function will initialize the timer. It will leave it stopped with
 * interrupts disabled. The timer will automatically start when an task
 * is added. Global interrupts need to be enabled elsewhere.
 *
 * This function will set the timer resolution/frequency as close to the
 * requested resolution as possible; the real frequency can be read with
 * the timer_get_frequency() function.
 *
 * \param  timer       Pointer to timer control structure.
 * \param  port        Pointer to TC register block
 * \param  resolution  Requested resolution of the timer
 *                               (given in ticks pr. sec)
 */
void timer_init(struct timer *timer, void *port, uint32_t resolution)
{
	unsigned long clock_rate = get_pba_clock_rate();
	uint8_t tcclks = 0;

	build_assert(sizeof(counter_t) ==
			(sizeof(uint16_t) + sizeof(counter_high_t)));
	assert(timer);
	assert(port);

	timer->port = port;
	timer->counter_high = 0;
	timer->action_pending = false;
	timer->first_task = NULL;
	timer->is_running = false;

	setup_irq_handler(TC0_IRQ, timer_tick, 0, timer);
	softirq_set_handler(SOFTIRQ_ID_TIMER, timer_do_pending, timer);

	if (clock_rate >= resolution * 128) {
		tcclks = 4;
		timer->frequency = clock_rate / 128;
	} else if (clock_rate >= resolution * 32) {
		tcclks = 3;
		timer->frequency = clock_rate / 32;
	} else if (clock_rate >= resolution * 8) {
		tcclks = 2;
		timer->frequency = clock_rate / 8;
	} else {
		tcclks = 1;
		timer->frequency = clock_rate / 2;
	}

	tc_write_reg(timer->port, CMR, TC_CMR_WAVE_MASK | tcclks);
}

/**
 * \brief Run pending tasks
 *
 * This function will check the task list of the timer for pending tasks
 * and execute them. When all pending tasks have been executed, the
 * alarm will be set on the timestamp of the next task in the list.
 *
 * \param  context      Pointer to timer control structure.
 */
static void timer_do_pending(void *context)
{
	struct timer *timer = context;
	counter_t current_time;
	struct timer_task *task;
	unsigned long iflags;

	assert(timer);

	while (timer->action_pending)
	{
		/* Save time by only reading out this once, if we get another
		 * timeout while executing tasks, the pending action flag will
		 * be raised again.
		 */
		current_time = timer_get_time(timer);
		while (timer->first_task)
		{
			if (timer->first_task->time_stamp <= current_time) {
				iflags = cpu_irq_save();
				task = timer->first_task;
				timer->first_task = timer->first_task->next;
				cpu_irq_restore(iflags);

				task->next = NULL;
				/* Run task handler with interrupts enabled */
				task->callback(timer_task_get_data(task));
			} else {
				break;
			}
		}

		iflags = cpu_irq_save();
		timer->action_pending = false;

		/* Stop timer if queue is empty */
		if (timer->first_task == NULL) {
			timer_stop(timer);
			cpu_irq_restore(iflags);
		} else {
			cpu_irq_restore(iflags);
			/* Set next alarm, this will set pending action flag if
			 * timestamp is exceeded.
			 */
			timer_set_alarm(timer, timer->first_task->time_stamp);
		}
	}
}

/**
 * \brief Add new task with delay given in timer ticks
 *
 * Add a new task to the be executed after a given delay (given in timer ticks).
 *
 * A task must always be completed before resubmitting it to the timer, but
 * as tasks are considered completed when the callback is called, they can
 * be resubmitted from their own callback.
 *
 * \param  timer       Pointer to timer control structure
 * \param  task        Pointer to task structure.
 * \param  delay       Delay given in timer ticks
 */
void timer_add_task_ticks(
		struct timer *timer,
		struct timer_task *task,
		counter_t delay)
{
	struct timer_task *temp_task, *prev_task;

	assert(timer);
	assert(task);
	assert(task->callback);
	assert(delay < TIMER_MAX_DELAY);

	/* A initialized task should have cleared next pointer */
	assert(task->next == NULL);

	task->timer = timer;
	/* Calculate the absolute time_stamp of the task */
	task->time_stamp = timer_get_time(timer) + delay;

	/* Special handling if list is empty */
	if (timer->first_task == NULL) {
		timer->first_task = task;
		task->next = NULL;
		goto out;
	}

	/* Same for lists with only one entry
	 * (this should be optimized when the list is replaced)
	 */
	if (task->time_stamp < timer->first_task->time_stamp) {
		task->next = timer->first_task;
		timer->first_task = task;
		goto out;
	}

	/* Search through list and insert task */
	/* TODO: Optimize this */
	prev_task = timer->first_task;
	temp_task = prev_task->next;
	while (temp_task != NULL) {
		if (task->time_stamp < temp_task->time_stamp)
			break;
		prev_task = temp_task;
		temp_task = temp_task->next;
	}
	task->next = temp_task;
	prev_task->next = task;

out:
	/* Set alarm -- will also handle already expired tasks */
	timer_set_alarm(timer, task->time_stamp);
}
