/*
 * timer.c:  Architecture independent high resolution timer support.
 *
 * Copyright (C) 2000 TimeSys Corporation
 *
 * This is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * This file is derived from software distributed under the following terms:
 *
 * Real-Time and Multimedia Systems Laboratory
 * Copyright (c) 2000 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Real-Time and Multimedia Systems Laboratory
 *  Attn: Prof. Raj Rajkumar
 *  Electrical and Computer Engineering, and Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 *  or via email to raj@ece.cmu.edu
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
#include <rk/rk_linux.h>
#include <rk/rk_error.h>
#include <rk/rk.h>
#include <rk/posix_timers.h>
#include <rk/timespec.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <asm/semaphore.h>
#include <errno.h>
#include <asm/uaccess.h>

#ifdef	DEBUG_RK
 /**/
#define	DEBUG_RK_LINUX_TIMER
#undef	DEBUG_RK_LINUX_TIMER2
#define	DEBUG_RK_ENFORCE_TIMER
#define	DEBUG_RK_ENFORCE_TIMER_START
#undef	DEBUG_RK_ENFORCE_TIMER_ISR	/* rk_timer_isr */
#define  DEBUG_RK_ENFORCE if (0)
     /**/
#endif				/*DEBUG_RK */
#define	HIGH_RESOLUTION_TIMER	1	/* 1 = on, 0 = off */
    LIST_HEAD(rk_free_timer_root);
				/* root of free timers */
LIST_HEAD(rk_online_timer_root);	/* root of on-line timers */

cpu_tick_data_t rk_cpu_tick_at_boot;
cpu_tick_data_t rk_cpu_tick_at_last_jiffy;
volatile int rk_release_hw_timer_req = 0;
int rk_going_down = 0;		/* This is set when rk is being unloaded,
				   and the timer has been returned to
				   periodic mode.  Requests to add a timer
				   are ignored. */
extern int rk_has_timer;

int debug;

/* diagnostic function for timer debug */
void check_timer_list(void);

/*
 * Timer management functions:
 */
rk_timer_t rk_timer_create(void)
{
    rk_timer_t tmr;

    if (list_empty(&rk_free_timer_root)) {
	/* allocate a new one */
	tmr = malloc(sizeof(struct rk_timer));
	if (!tmr)
	    return NULL;
	bzero(tmr, sizeof(struct rk_timer));
    } else {
	unsigned long flags;
	rk_spin_lock(flags);
	{
	    /* there is one in free list */
	    tmr =
		list_entry(rk_free_timer_root.next, struct rk_timer,
			   tmr_tmr);
	    list_del(&tmr->tmr_tmr);
	    bzero(tmr, sizeof(struct rk_timer));
	}
	rk_spin_unlock(flags);
    }

    return tmr;
}

inline void rk_timer_destroy(rk_timer_t tmr)
{
    unsigned long flags;
    rk_spin_lock(flags);
    {
	bzero(tmr, sizeof(struct rk_timer));
	list_add(&tmr->tmr_tmr, &rk_free_timer_root);	/* return to free list */
    }
    rk_spin_unlock(flags);
}

/* Arms an already created timer; caller should hold the RK lock */
inline void rk_timer_add(rk_timer_t tmr)
{
    struct list_head *lh;
    rk_timer_t tmr1;
    int count = 0;

    if (!tmr) {
	printk("rk_timer_add: NULL timer.\n");
	return;
    }

    if (tmr->tmr_tmr.next || tmr->tmr_tmr.prev) {
	printk("rk_timer_add: duplicate timer (type %d).\n",
	       tmr->tmr_type);
	return;
    }

    if (rk_going_down)
	return;			/* Someone was stupid. */

    /* find a place */
    lh = rk_online_timer_root.next;
    while (lh != &rk_online_timer_root) {
	tmr1 = list_entry(lh, struct rk_timer, tmr_tmr);
	if (!tmr1)
	    panic("rk_timer_add: NULL timer in online list.\n");
	if (tmr->tmr_expire < tmr1->tmr_expire) {
	    break;
	}
	lh = lh->next;
	count++;
    }
    /* the same code can be used to add a timer at the end 
     * and to add a timer before `lh'. */
    list_add(&tmr->tmr_tmr, lh->prev);

    /* If the new timer is the first to expire, the hardware
       timer must be updated. */
    /* If it's the first timer, don't messup w/ first jiffy interrupt */
    if ((&tmr->tmr_tmr == rk_online_timer_root.next) && (rk_has_timer)) {
/*if (&tmr->tmr_tmr==rk_online_timer_root.next) { ASK APPLE!!!*/
	rk_update_hw_timer(tmr);
    }
}

/* Disarms a potentially armed timer; call this before destroying
	unless you know that the timer is not armed. */
inline void rk_timer_remove(rk_timer_t tmr)
{
    unsigned long flags;
    int is_next = 0;

    rk_spin_lock(flags);

    if (!tmr->tmr_tmr.next || !tmr->tmr_tmr.prev) {
	rk_spin_unlock(flags);
	return;			/* not armed */
    }

    if (tmr ==
	list_entry(rk_online_timer_root.next, struct rk_timer,
		   tmr_tmr)) is_next = 1;

    list_del(&tmr->tmr_tmr);

    if (is_next) {
	/* This is the next timer to expire,
	   so reprogram the hw timer for the 
	   next timer to expire */

	rk_update_hw_timer(list_entry
			   (tmr->tmr_tmr.next, struct rk_timer, tmr_tmr));
    }

    tmr->tmr_tmr.next = tmr->tmr_tmr.prev = NULL;

    rk_spin_unlock(flags);
}

rk_timer_t rk_enforce_tmr, rk_jiffy_tmr;

void rk_timer_init(void)
{
    rk_enforce_tmr = rk_timer_create();
    rk_jiffy_tmr = rk_timer_create();
    rk_enforce_tmr->tmr_type = TMR_ENFORCE;
    rk_jiffy_tmr->tmr_type = TMR_JIFFY;
}

void rk_release_hw_timer(void)
{
    rk_release_hw_timer_req = 1;
    while (rk_release_hw_timer_req);	/* Wait for next jiffy */
}

/*
 * rk_replenish_reserve(rk_timer_t tmr), called with lock
 */
void rk_replenish_reserve(rk_timer_t tmr)
{
    rk_reserve_t rsv = tmr->tmr_rsv;
    cpu_tick_data_t period;

#ifdef	DEBUG_RK
    printk("rk_replenish_reserve: rsv(0x%x) tmr(0x%x) expired(%lu usec)\n",
	   (int) rsv, (int) tmr, TICK2USEC(&tmr->tmr_expire));
#endif

    if (tmr->tmr_type != TMR_REPLENISH_RSV) {
	/* something wrong */
	rk_timer_destroy(tmr);
    }

    /* replenish it, and set timer expiration `ticks' later */
    (rsv->rsv_ops->replenish) (rsv, &period, &tmr->tmr_expire);
    tmr->tmr_expire += period;

#ifdef	DEBUG_RK
    printk("rk_replenish_reserve: next expire(%lu usec)\n",
	   TICK2USEC(&tmr->tmr_expire));
#endif
    /* set next timer */
    /*tmr->tmr_handler = rk_replenish_reserve; */
    rk_timer_add(tmr);
}

/*
 * rk_start_reserve(rk_timer_t tmr), called with lock
 *
 * This function is needed for the following reason.
 * Suppose that the user creates a resource set, and attaches a process
 * to the resource set BEFORE creating the CPU reserve inside the
 * resource set. In that case, we need to start running the process 
 * as soon as the reserve is eligible to execute.
 *
 * But this also means that the reserve will not be attached to the
 * CPU resource set until later.
 */

void rk_start_reserve(rk_timer_t tmr)
{
    rk_reserve_t rsv = tmr->tmr_rsv;
    cpu_tick_data_t period;
    rk_resource_set_t rs = rsv->rsv_rs;

    if (!rk_valid_rset(rs)) {
      /* destroy itself */
	rsv->rsv_ops->destroy(rsv);
	return;
    }

    if (tmr->tmr_type != TMR_REPLENISH_RSV) {
	/* something wrong */
	rk_timer_destroy(tmr);
    }

    /* enable cpu reserve  */
    if (rsv->rsv_type == RSV_CPU) {
        cpu_reserve_start(rsv, &tmr->tmr_expire);
    }

    /* attach reserve to resource set */
    if (rk_resource_set_attach_reserve(rs, rsv) != RK_SUCCESS) {
	/* another reserve attached in the meantime? Delete reserve */
	cpu_reserve_delete(rs);

	/* timer essentially commits suicide */
	rk_timer_destroy(tmr);
	return;
    }

    /* replenish it, and set timer expiration "ticks" later */
    (rsv->rsv_ops->replenish) (rsv, &period, &tmr->tmr_expire);
    tmr->tmr_expire += period;

#ifdef DEBUG_RK
    printk("rk_start_reserve: next expire(%lu usec)\n",
	   TICK2USEC(&tmr->tmr_expire));
#endif				/* DEBUG_RK */

    /* set next timer: this IS needed */
    tmr->tmr_handler = rk_replenish_reserve;

    /* add timer to queue */
    rk_timer_add(tmr);
}

/*
 * rk_replenish_timer_create():
 * 	Create a timer to replenish a reserve, add it to the queue.
 * 	Set a linux timer if necessary.
 */
void rk_replenish_timer_create(rk_reserve_t rsv, struct timespec start)
{
    rk_timer_t tmr;
    unsigned long flags;
    cpu_tick_data_t nanos, now;
    struct timespec cur_time;

    /* create a timer for a reserve and add it to queue */
    tmr = rk_timer_create();
    rsv->rsv_replenish_tmr = tmr;
    tmr->tmr_rsv = rsv;
    tmr->tmr_type = TMR_REPLENISH_RSV;

    tmr->tmr_handler = rk_start_reserve;
    rk_spin_lock(flags);
    {
	/* get relative delay for when to start reservation */
	rk_cur_time(&cur_time);
	rk_rdtsc(&now);
	timespec_sub(start, cur_time);

	if (start.tv_sec < 0) {
	    /* we are already past the start time: start 10 us later */
	    start.tv_sec = 0;
	    start.tv_nsec = 10000;
	}

	/* set up timer expiry time (now + relative delay in start) */
	nanos = timespec2nano(start);
	nanosec2tick(&nanos, &tmr->tmr_expire);
	tmr->tmr_expire += now;

	rk_timer_add(tmr);
    }
    rk_spin_unlock(flags);

#ifdef	DEBUG_RK
    printk("rk_replenish_timer_create: rsv(0x%x) replenish(0x%x)\n",
	   (int) rsv, (int) rsv->rsv_replenish_tmr);
#endif
}
#if 0
extern void netdev_replenish_bw(rk_timer_t tmr);
/****************************Sourav*****************************/
void rk_netdev_timer_create(rk_timer_t dev_timer)
{
    rk_timer_t tmr;
    unsigned long flags;
    cpu_tick_data_t nanos, now;
    struct timespec period;

    /* create a timer for a reserve and add it to queue */
    tmr = rk_timer_create();
    
    tmr->tmr_rsv = NULL;
    tmr->tmr_type = TMR_REPLENISH_RSV;

    tmr->tmr_handler = netdev_replenish_bw;

    dev_timer = tmr;

    period.tv_sec = 5;
    period.tv_nsec = 0;
    rk_spin_lock(flags);
    {
	/* get relative delay for when to start reservation */
      //rk_cur_time(&cur_time);
	rk_rdtsc(&now);
	//timespec_sub(start, cur_time);

	
	/* set up timer expiry time (now + relative delay in start) */
	nanos = timespec2nano(period);
	nanosec2tick(&nanos, &tmr->tmr_expire);
	tmr->tmr_expire += now;

	rk_timer_add(tmr);
    }
    rk_spin_unlock(flags);

#ifdef	DEBUG_RK
    printk("rk_replenish_timer_create: rsv(0x%x) replenish(0x%x)\n",
	   (int) rsv, (int) rsv->rsv_replenish_tmr);
#endif
}
#endif
/*****************sourav****************************/
void rk_replenish_timer_destroy(rk_reserve_t rsv)
{
    rk_timer_t replenish_tmr;

#ifdef	DEBUG_RK
    printk("rk_replenish_timer_destory: rsv(0x%x) replenish(0x%x)\n",
	   (int) rsv, (int) rsv->rsv_replenish_tmr);
#endif
    replenish_tmr = rsv->rsv_replenish_tmr;

    rk_timer_remove(replenish_tmr);
    rk_timer_destroy(replenish_tmr);
}


/*
 * Enforcement timer management
 *
 * cpu_reserve_start_account(rsv)
 *	 -> rk_timer_set_enforce_timer(rsv, ticks_expired);
 *
 * cpu_reserve_stop_account(rsv)
 *	 -> rk_timer_cancel_enforce_timer(rsv);
 *
 * "rk_current_resource_set != 0" --> accounting is on
 * "rk_current_resource_set == 0" --> accounting is off
 *
 */
#include <linux/timex.h>
#include <asm/io.h>

//static        cpu_tick_data_t rk_enforcement_expire;
static cpu_tick_data_t rk_cpu_tick_at_next_jiffy;

/*
 *
 */
static inline void
rk_enforce_timer_set(cpu_tick_t how_long, cpu_tick_t start_time)
{
    cpu_tick_data_t now;
    unsigned long flags;

    rk_spin_lock(flags);

    rk_rdtsc(&now);
    rk_enforce_tmr->tmr_expire = now + *how_long;
    rk_timer_add(rk_enforce_tmr);

    *start_time = now;

    rk_spin_unlock(flags);
    return;
}

/*
 * Cancel enforce_timer and set timer to next jiffy
 */
void rk_enforce_timer_cancel(void)
{
    rk_timer_remove(rk_enforce_tmr);
}

/* 
 * rk_timer_isr():
 * 1) determines if this IRQ0 interrupts needs to be propagated to Linux.
 *    i.e. check if this is for a jiffy or not.
 *	2) For a jiffy timer, set the next jiffy.
 * 3) For an enforcement timer, call rk_resource_set_do_enforcement.
 * 4) For any other timer, call tmr_handler.  This is done with the
 *		RK lock in place.
 * 5) Set the hardware timer to interrupt on the next timer to expire.
 */
int rk_timer_isr(void)
{
    cpu_tick_data_t now;
    int no_IRQ = 0;
    int ret = 0;
    rk_timer_t tmr;
    unsigned long flags;
    struct list_head *timers;

    if (rk_going_down)		/* We gave up the timer but we haven't yet
				   removed our interrupt hook */
	return 1;

    rk_spin_lock(flags);
    rk_rdtsc(&now);

    timers = rk_online_timer_root.next;

    if (!rk_has_timer) {	/* First RK timer interrupt */
	rk_cpu_tick_at_last_jiffy = now;
	rk_cpu_tick_at_next_jiffy = now + rk_cpu_ticks_per_jiffy;
	rk_jiffy_tmr->tmr_expire = rk_cpu_tick_at_next_jiffy;
	rk_timer_add(rk_jiffy_tmr);
	rk_has_timer = 1;
	rk_spin_unlock(flags);
	return 1;
    }

    if (rk_release_hw_timer_req) {	/* Last RK timer interrupt */
	rk_going_down = 1;
	rk_hw_timer_set_periodic();
	rk_release_hw_timer_req = 0;
	rk_spin_unlock(flags);
	return 1;
    }

    /* Find the timer(s) that expired */
    while (timers != &rk_online_timer_root) {
	struct list_head *next_tmr;

	tmr = list_entry(timers, struct rk_timer, tmr_tmr);
	next_tmr = timers->next;

	if (now >= tmr->tmr_expire) {	/* timer has expired */
	    list_del(timers);
	    timers->next = timers->prev = NULL;

	    switch (tmr->tmr_type) {
	    case TMR_JIFFY:

//                                      printk("jiffy at %d %d, last at %d %d\n",
//                                              (int)(now>>32),(int)now,(int)(rk_cpu_tick_at_last_jiffy>>32),(int)rk_cpu_tick_at_last_jiffy);

		ret = 1;	/* Interrupt gets passed to Linux */
		rk_cpu_tick_at_last_jiffy += rk_cpu_ticks_per_jiffy;
		rk_cpu_tick_at_next_jiffy =
		    rk_cpu_tick_at_last_jiffy + rk_cpu_ticks_per_jiffy;

		if (now >= rk_cpu_tick_at_next_jiffy) {
		    no_IRQ = now - rk_cpu_tick_at_last_jiffy;
		    /* this must not happen.
		     * So, I don't know what should be here */
		    rk_cpu_tick_at_last_jiffy = now;
		    rk_cpu_tick_at_next_jiffy
			=
			rk_cpu_tick_at_last_jiffy + rk_cpu_ticks_per_jiffy;
		}

		/* Add a timer for the next jiffy */
		tmr->tmr_expire = rk_cpu_tick_at_next_jiffy;
		if (debug)
		    printk("adding jiffy\n");
		rk_timer_add(tmr);
		break;

	    case TMR_ENFORCE:
		if (rk_current_resource_set)
		    rk_resource_set_do_enforcement
			(rk_current_resource_set);
		else
		    printk
			("enforcement timer expired with no current resource set!\n");
		break;

	    default:
		tmr->tmr_handler(tmr);
	    }
	} else
	    break;		/* No more expired timers */

	timers = next_tmr;
    }

    rk_update_hw_timer(list_entry
		       (rk_online_timer_root.next, struct rk_timer,
			tmr_tmr));

    if (no_IRQ)
	printk("rk_timer_isr: long no IRQ0 (%d ticks too long)\n", no_IRQ);

    rk_spin_unlock(flags);
    return ret;
}

/* called from "rsv->rsv_ops->start_account()" */
void
rk_enforce_timer_start(rk_reserve_t rsv, cpu_tick_t next_available_ticks,
		       cpu_tick_t start)
{
#ifdef	DEBUG_RK_ENFORCE_TIMER_START
#ifdef	DEBUG_RK_ENFORCE
    DEBUG_RK_ENFORCE
#endif
    {
	printk
	    ("rk_enforce_timer_start: rsv(0x%x) next_available(%lu usec)\n",
	     (int) rsv, TICK2USEC(next_available_ticks));
    }
#endif
    rk_enforce_timer_set(next_available_ticks, start);
}

extern rwlock_t xtime_lock;

/* Called with lock */
asmlinkage void rk_posix_timer_handler(rk_timer_t rkt)
{
    siginfo_t si;

    /* Re-arm the timer if necessary */
    if (rkt->pt->its.it_interval.tv_sec
	|| rkt->pt->its.it_interval.tv_nsec) {
	cpu_tick_data_t ticks =
	    rkt->pt->its.it_interval.tv_sec * 1000000000 +
	    rkt->pt->its.it_interval.tv_nsec;
	rkt->pt->its.it_value = rkt->pt->its.it_interval;
	nanosec2tick(&ticks, &ticks);
	rkt->tmr_expire += rkt->period;
	rk_timer_add(rkt);
    }

    /* Deliver the signal */
    switch (rkt->pt->ev.sigev_notify) {
    case SIGEV_SIGNAL:
	si.si_signo = rkt->pt->ev.sigev_signo;
	si.si_code = SI_TIMER;
	si.si_value = rkt->pt->ev.sigev_value;
	si.si_timer = rkt->pt;
	rkt->pt->pending++;

	send_sig_info(si.si_signo, &si, rkt->pt->p);

	break;

	/* TODO: SIGEV_THREAD */
    }
}

/* The posix_timers list is unique to each thread and not touched
	by interrupts, and as such does not need a lock. */
asmlinkage int sys_timer_create(clockid_t clock_id, struct sigevent *evp,
				timer_t * timerid)
{
    rk_timer_t rkt = NULL;
    posix_timer *pt = NULL;
    timer_t tid = -1;
    if (clock_id != CLOCK_REALTIME)
	return -EINVAL;

    rk_enable();
    rkt = rk_timer_create();
    pt = malloc(sizeof(posix_timer));
    if (!rkt || !pt)
	goto no_mem;
    bzero(pt, sizeof(*pt));

    if (evp) {
	if (copy_from_user(&pt->ev, evp, sizeof(pt->ev))) {
	    rk_timer_destroy(rkt);
	    free(pt);
	    rk_disable();
	    return -EFAULT;
	}
	if (pt->ev.sigev_notify != SIGEV_SIGNAL
	    && pt->ev.sigev_notify != SIGEV_NONE) return -EINVAL;
    }

    if (!current->posix_timers) {
	current->posix_timers =
	    malloc(sizeof(posix_timer *) * MAXPOSIXTIMERS);
	if (!current->posix_timers)
	    goto no_mem;
	bzero(current->posix_timers,
	      sizeof(posix_timer *) * MAXPOSIXTIMERS);
	tid = 0;
    } else {
	for (tid = 0; tid < MAXPOSIXTIMERS; tid++)
	    if (!current->posix_timers[tid])
		break;
	if (tid == MAXPOSIXTIMERS) {
	    rk_timer_destroy(rkt);
	    free(pt);
	    rk_disable();
	    return -EAGAIN;
	}
    }

    if (!evp) {
	pt->ev.sigev_notify = SIGEV_SIGNAL;
	pt->ev.sigev_signo = SIGRTMIN;
	pt->ev.sigev_value.sival_int = tid;
    }

    pt->rkt = rkt;
    pt->p = current;
    current->posix_timers[tid] = pt;

    /* For some reason, neither verify_area nor put_user seem to be
       working properly. */
    if (copy_to_user(timerid, &tid, sizeof(timer_t))) {
	free(pt);
	rk_timer_destroy(rkt);
	current->posix_timers[tid] = NULL;
	if (!current->num_posix_timers) {
	    free(current->posix_timers);
	    current->posix_timers = NULL;
	}
	rk_disable();
	return -EFAULT;
    }
    current->num_posix_timers++;
    rkt->tmr_type = TMR_POSIX;
    rkt->tmr_handler = rk_posix_timer_handler;
    rkt->pt = pt;
    return 0;

  no_mem:
    if (rkt)
	rk_timer_destroy(rkt);
    if (pt)
	free(pt);
    rk_disable();
    return -ENOMEM;
}

asmlinkage int sys_timer_delete(timer_t timerid)
{
    posix_timer *pt;
    if (!current->posix_timers || timerid >= MAXPOSIXTIMERS ||
	timerid < 0 || !current->posix_timers[timerid])
	return -EINVAL;
    pt = current->posix_timers[timerid];
    current->posix_timers[timerid] = NULL;
    if (!--current->num_posix_timers) {
	free(current->posix_timers);
	current->posix_timers = NULL;
    }
    rk_timer_destroy(pt->rkt);
    rk_disable();
    free(pt);
    return 0;
}

void rk_sub_time(struct timespec *ts, struct timespec *ts2)
{
    ts->tv_sec -= ts2->tv_sec;
    ts->tv_nsec -= ts2->tv_nsec;
    if (ts->tv_nsec < 0) {
	ts->tv_nsec += 1000000000;
	ts->tv_sec--;
    }
}

struct timespec *rk_cur_time(struct timespec *ts)
{
    cpu_tick_data_t now;
    unsigned long flags;

    save_flags(flags);
    cli();
    rk_rdtsc(&now);
    now -= rk_cpu_tick_at_last_jiffy;
    tick2nanosec(&now, &now);

//      read_lock_irqsave(&xtime_lock,flags);
    ts->tv_sec = xtime.tv_sec;
    ts->tv_nsec = xtime.tv_usec * 1000 + now;
//      read_unlock_irqrestore(&xtime_lock,flags);
    restore_flags(flags);

    if (ts->tv_nsec > 1000000000) {
	ts->tv_nsec -= 1000000000;
	ts->tv_sec++;
    }
    return ts;
}

asmlinkage int sys_timer_settime(timer_t timerid, int flags,
				 const struct itimerspec *value,
				 struct itimerspec *ovalue)
{
    posix_timer *pt;
    struct itimerspec its;
    cpu_tick_data_t ticks, now;
    unsigned long lock_flags;

    if (!current->posix_timers || timerid >= MAXPOSIXTIMERS ||
	timerid < 0 || !current->posix_timers[timerid])
	return -EINVAL;
    pt = current->posix_timers[timerid];

    if (ovalue) {
	struct itimerspec oits;
	rk_rdtsc(&now);
	if (now >= pt->rkt->tmr_expire)
	    now = 0;
	else
	    now = pt->rkt->tmr_expire - now;
	tick2nanosec(&now, &now);
	nano2timespec(oits.it_value, now);
	oits.it_interval = pt->its.it_interval;
	copy_to_user_ret(ovalue, &oits, sizeof(pt->its), -EFAULT);
    }

    copy_from_user_ret(&its, value, sizeof(its), -EFAULT);
    if (its.it_interval.tv_nsec < 0 || its.it_interval.tv_nsec > 1000000000
	|| its.it_value.tv_nsec < 0 || its.it_value.tv_nsec > 1000000000)
	return -EINVAL;
    pt->its = its;

    if (flags & TIMER_ABSTIME) {
	struct timespec ts;
	rk_cur_time(&ts);
	timespec_sub(pt->its.it_value, ts);
    }

    ticks = (cpu_tick_data_t) pt->its.it_interval.tv_sec * 1000000000 +
	pt->its.it_interval.tv_nsec;
    nanosec2tick(&ticks, &pt->rkt->period);

    ticks = (cpu_tick_data_t) pt->its.it_value.tv_sec * 1000000000 +
	pt->its.it_value.tv_nsec;
    nanosec2tick(&ticks, &ticks);

    rk_timer_remove(pt->rkt);
    if (ticks) {
	/* arm the timer */
	rk_rdtsc(&now);
	pt->rkt->tmr_expire = ticks + now;

	rk_spin_lock(lock_flags);
	rk_timer_add(pt->rkt);
	rk_spin_unlock(lock_flags);
    }

    return 0;
}

asmlinkage int sys_timer_gettime(timer_t timerid, struct itimerspec *value)
{
    cpu_tick_data_t now;
    struct itimerspec its;
    posix_timer *pt;

    if (!current->posix_timers || timerid >= MAXPOSIXTIMERS ||
	timerid < 0 || !current->posix_timers[timerid])
	return -EINVAL;
    pt = current->posix_timers[timerid];

    rk_rdtsc(&now);
    if (now >= pt->rkt->tmr_expire)
	now = 0;
    else
	now = pt->rkt->tmr_expire - now;
    tick2nanosec(&now, &now);
    nano2timespec(its.it_value, now);
    its.it_interval = pt->its.it_interval;
    copy_to_user_ret(value, &its, sizeof(pt->its), -EFAULT);
    return 0;
}

asmlinkage int sys_timer_getoverrun(timer_t timerid)
{
    posix_timer *pt;

    if (!current->posix_timers || timerid >= MAXPOSIXTIMERS ||
	timerid < 0 || !current->posix_timers[timerid])
	return -EINVAL;
    pt = current->posix_timers[timerid];
    return pt->pending;
}

void check_timer_list(void)
{
    struct list_head *rtimer, *r2;
    int jfound = 0;
    rk_timer_t t1, t2;
    cpu_tick_data_t now;

    rk_rdtsc(&now);
    printk("ttypes on tmr-q at 0x%Lx: ", now);
    rtimer = rk_online_timer_root.next;
    while (rtimer != &rk_online_timer_root) {
	t1 = list_entry(rtimer, struct rk_timer, tmr_tmr);
	if (t1->tmr_type == TMR_JIFFY) {
	    jfound = 1;
	}
	printk("%d(0x%Lx,", t1->tmr_type, t1->tmr_expire);
	printk("0x%Lx)",
	       ((t1->tmr_expire - now) * CLOCK_TICK_RATE) /
	       rk_cpu_ticks_per_second);
	r2 = rtimer->next;
	if (r2 != &rk_online_timer_root) {
	    t2 = list_entry(r2, struct rk_timer, tmr_tmr);
	    if (t1->tmr_expire > t2->tmr_expire) {
		printk("(e>%d,%d)", t1->tmr_type, t2->tmr_type);
	    }
	}
	rtimer = r2;
    }
    if (!jfound)
	printk("check_timer_queue: No jiffy timer in queue!\n");
}
