/**
 * @file
 * @brief PowerPC Microprocessor Family clock device driver
 *
 * @date 06.11.12
 * @author Ilia Vaprol
 */

#include <asm/psr.h>
#include <asm/regs.h>
#include <embox/unit.h>
#include <hal/clock.h>
#include <hal/system.h>
#include <kernel/irq.h>
#include <kernel/time/clock_source.h>
#include <stdint.h>

/** Test for different frequancy (300K ticks -- i.e. 5min)
 * 300MHz is 00:04:10.356 (300K)
 * 333MHz is 00:04:37.672 (300K)
 * 350MHz is 00:05:22.747 (300K)
 * 366MHz is 00:05:39.987 (300K)
 * 375MHz is 00:06:27.322 (343K)
 * We will use 333MHz as default settings
 */
#define PPCCLK_IRQ  10
#define PPCCLK_FREQ SYS_CLOCK
#define PPCCLK_DECR (PPCCLK_FREQ / 1000) /* precision for ms */

static irq_return_t clock_handler(unsigned int irq_nr, void *data) {
	__set_tsr(__get_tsr() & ~TSR_DIS);
	clock_tick_handler( data);
	return IRQ_HANDLED;
}

static int ppc_clk_set_periodic(struct clock_source *cs) {
	__set_dec(PPCCLK_DECR);
	__set_decar(PPCCLK_DECR);
    __set_tcr(TCR_DIE | TCR_ARE);
	return 0;
}

static cycle_t ppc_clk_read(struct clock_source *cs) {
	uint32_t l, u, tmp;
	tmp = __get_tbu();
	do {
		u = tmp;
		l = __get_tbl();
		tmp = __get_tbu();
	} while (tmp != u);
	return ((uint64_t)u << 32) | l;
}

static struct time_event_device ppc_clk_event = {
	.set_periodic = ppc_clk_set_periodic,
	.irq_nr = PPCCLK_IRQ,
};

static struct time_counter_device ppc_clk_counter = {
	.read = ppc_clk_read,
	.cycle_hz = PPCCLK_FREQ,
};

static int ppc_clk_init(struct clock_source *cs) {
	return irq_attach(PPCCLK_IRQ, clock_handler, 0, cs, "ppc_clk");
}

CLOCK_SOURCE_DEF(ppc_clk, ppc_clk_init, NULL,
	&ppc_clk_event, &ppc_clk_counter);
