/*
 * rtl_time.c
 *
 * architecture-dependent clock support
 *
 * Written by Michael Barabanov
 * Copyright  Finite State Machine Labs Inc. 1998-1999
 * Released under the terms of the GPL Version 2
 *
 * StandAlone RTLinux integration 
 * written by Vicente Esteve LLoret <viesllo@inf.upv.es> 
 * Copyright (C) May, 2003 OCERA Consortium.
 *
 *
 * 
 */
#include <rtl_conf.h>
#include <arch/hw_irq.h>
#include <arch/timer.h>
#include <arch/rtl_io.h>
#include <arch/mprot.h>

#include <rtl_core.h>
#include <rtl_debug.h>
#include <rtl_sync.h>
#include <rtl_time.h>

#include <arch/os.h>
#include <arch/hypervisor.h>
#include <arch/time.h>
//#include <lib.h>
#include <arch/events.h>

static void _xeno_uninit(clockid_t clock); 
static int  _xeno_init(clockid_t clock); 
static hrtime_t  _xeno_gettime(struct rtl_clock *); 
static int _xeno_settimer(struct rtl_clock *,hrtime_t interval);
static int _xeno_settimermode(struct rtl_clock *,int mode);

/************************************************************************
 * Time functions
 *************************************************************************/

static unsigned int rdtsc_bitshift;
static u32 st_scale_f; /* convert ticks -> usecs */
static u32 st_scale_i; /* convert ticks -> usecs */

/* These are peridically updated in shared_info, and then copied here. */
static u32 shadow_tsc_stamp;
static s64 shadow_system_time;
static u32 shadow_time_version;
static struct timeval shadow_tv;


struct rtl_clock xeno_clock;

#define TIME_VALUES_UP_TO_DATE \
    (shadow_time_version == HYPERVISOR_shared_info->time_version2)

#define HANDLE_USEC_OVERFLOW(_tv)          \
    do {                                   \
        while ( (_tv).tv_usec >= 1000000 ) \
        {                                  \
            (_tv).tv_usec -= 1000000;      \
            (_tv).tv_sec++;                \
        }                                  \
    } while ( 0 )



static void timer_handler(int ev, struct pt_regs *regs)
{
 static int i;
 i++;
 if (i >= 10) {
  xeno_clock.handler(regs);
 };
}
#ifndef rmb
#define rmb()  __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
#endif


static void get_time_values_from_xen(void)
{
    do {
        shadow_time_version = HYPERVISOR_shared_info->time_version2;
        rmb();
        shadow_tv.tv_sec    = HYPERVISOR_shared_info->wc_sec;
        shadow_tv.tv_usec   = HYPERVISOR_shared_info->wc_usec;
        shadow_tsc_stamp    = HYPERVISOR_shared_info->tsc_timestamp;
        shadow_system_time  = HYPERVISOR_shared_info->system_time;
        rmb();
    }
    while ( shadow_time_version != HYPERVISOR_shared_info->time_version1 );
}


static inline unsigned long get_time_delta_usecs(void)
{
    s32      delta_tsc;
    u32      low;
    u64      delta, tsc;

    rdtscll(tsc);
    low = (u32)(tsc >> rdtsc_bitshift);
    delta_tsc = (s32)(low - shadow_tsc_stamp);
    if ( unlikely(delta_tsc < 0) ) delta_tsc = 0;
    delta = ((u64)delta_tsc * st_scale_f);
    delta >>= 32;
    delta += ((u64)delta_tsc * st_scale_i);

    return (unsigned long)delta;
}


void gettimeofday(struct timeval *tv)
{
    struct timeval _tv;

    do {
        get_time_values_from_xen();
        _tv.tv_usec = get_time_delta_usecs();
        _tv.tv_sec   = shadow_tv.tv_sec;
        _tv.tv_usec += shadow_tv.tv_usec;
    }
    while ( unlikely(!TIME_VALUES_UP_TO_DATE) );

    HANDLE_USEC_OVERFLOW(_tv);
    *tv = _tv;
}



void init_time(void)
{
    u64         __cpu_khz, cpu_freq, scale;
    unsigned long cpu_khz;

    __cpu_khz = HYPERVISOR_shared_info->cpu_freq;
    cpu_khz = (u32) (__cpu_khz/1000);

    rdtsc_bitshift = HYPERVISOR_shared_info->rdtsc_bitshift;
    cpu_freq       = HYPERVISOR_shared_info->cpu_freq;

    scale = 1000000LL << (32 + rdtsc_bitshift);
    scale /= cpu_freq;

    st_scale_f = scale & 0xffffffff;
    st_scale_i = scale >> 32;

    printf("Xen reported: %lu.%03lu MHz processor.\n", 
           cpu_khz / 1000, cpu_khz % 1000);

    add_ev_action(EV_TIMER, &timer_handler);
    enable_ev_action(EV_TIMER);
    enable_hypervisor_event(EV_TIMER);

}



static int  _xeno_init(clockid_t clock) {
  
};



hrtime_t gethrtime(void)
{ 
  hrtime_t aux;
  struct timeval tv;
  gettimeofday(&tv);
  aux=((((hrtime_t) tv.tv_sec)*1000000000) + (((hrtime_t) tv.tv_usec)*1000000));
  return aux;
}


hrtime_t gethrtimeres(void)
{
  return 0;
}

clockid_t rtl_getbestclock (unsigned int cpu)
{
  return &xeno_clock;
}



int init_clocks (void)
{
  xeno_clock = RTL_CLOCK_DEFAULTS;
  xeno_clock.init = _xeno_init;
//  xeno_clock.uninit = _xeno_uninit;
  xeno_clock.handler = timer_handler;
  xeno_clock.mode = RTL_CLOCK_MODE_PERIODIC;
  xeno_clock.gethrtime = gethrtime;
  xeno_clock.value = gethrtime;
  init_time();
  return 0;
}


