diff --git a/arch/sparc/include/asm/clockevent.h b/arch/sparc/include/asm/clockevent.h
new file mode 100644
index 0000000000000000000000000000000000000000..faee3525b4d91c02d4000948dfd7ea5b32fa7272
--- /dev/null
+++ b/arch/sparc/include/asm/clockevent.h
@@ -0,0 +1,15 @@
+/**
+ * @file arch/sparc/include/asm/clockevent.h
+ */
+
+#ifndef _SPARC_CLOCKEVENT_H_
+#define _SPARC_CLOCKEVENT_H_
+
+
+#include <kernel/kernel.h>
+
+
+
+void sparc_clockevent_init(void);
+
+#endif /* _SPARC_CLOCKEVENT_H_ */
diff --git a/arch/sparc/include/asm/time.h b/arch/sparc/include/asm/time.h
index 87c1fc4af37002ce9d035f0e49403509001a7a80..611bd75614489c2e906b853d4a85cb6db7ca3a7d 100644
--- a/arch/sparc/include/asm/time.h
+++ b/arch/sparc/include/asm/time.h
@@ -21,9 +21,8 @@
 #endif
 
 
-#define GPTIMER_RELOAD		4
-#define GRTIMER_RELOAD		4	/* use 5 instead of 3 cycle minimum for
-					   round number of clock ticks */
+#define GPTIMER_RELOAD		7
+#define GRTIMER_RELOAD		4
 
 #define GPTIMER_TICKS_PER_SEC	((SPARC_CPU_CPS / (GPTIMER_RELOAD + 1)))
 #define GPTIMER_TICKS_PER_MSEC	(GPTIMER_TICKS_PER_SEC / 1000)
@@ -37,13 +36,12 @@
 
 #define GPTIMER_CYCLES_PER_SEC	SPARC_CPU_CPS
 #define GPTIMER_CYCLES_PER_MSEC	(GPTIMER_CYCLES_PER_SEC / 1000)
-#define GPTIMER_CYCLES_PER_USEC	(GPTIMER_CYCLESS_PER_SEC / 1000000)
+#define GPTIMER_CYCLES_PER_USEC	(GPTIMER_CYCLES_PER_SEC / 1000000)
 #define GPTIMER_USEC_PER_CYCLE	(1000000.0 / GPTIMER_CYCLES_PER_SEC)
 
 #define GRTIMER_CYCLES_PER_SEC	SPARC_CPU_CPS
 #define GRTIMER_CYCLES_PER_MSEC	(GRTIMER_CYCLES_PER_SEC / 1000)
-#define GRTIMER_CYCLES_PER_USEC	(GRTIMER_CYCLESS_PER_SEC / 1000000)
-#define GRTIMER_CYCLES_PER_NSEC	(GRTIMER_CYCLESS_PER_SEC / 1000000000)
+#define GRTIMER_CYCLES_PER_USEC	(GRTIMER_CYCLES_PER_SEC / 1000000)
 #define GRTIMER_SEC_PER_CYCLE	(      1.0 / GRTIMER_CYCLES_PER_SEC)
 #define GRTIMER_MSEC_PER_CYCLE	(   1000.0 / GRTIMER_CYCLES_PER_SEC)
 #define GRTIMER_USEC_PER_CYCLE	(1000000.0 / GRTIMER_CYCLES_PER_SEC)
@@ -53,7 +51,10 @@
  * note that the order is important, otherwise we may encounter integer
  * overflow on multiplication
  */
-#define CPU_CYCLES_TO_NS(x) (((x) / (SPARC_CPU_CPS / 1000000UL)) * 1000UL)
+#define CPU_CYCLES_TO_NS(x) (((x) >= 1000UL) \
+			     ? (((x) / (SPARC_CPU_CPS / 1000000UL)) * 1000UL) \
+			     : (((x) * 1000UL) / (SPARC_CPU_CPS / 1000000UL)))
+
 compile_time_assert((SPARC_CPU_CPS <= 1000000000UL),
 		    CPU_CYCLES_TO_NS_NEEDS_FIXUP);
 
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 623e0c294f018672e7870c2c8437b4468107832c..67a11861fa4da7915f174f4bedf6531125d5ea2e 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -28,6 +28,6 @@ obj-y += traps/data_access_exception_trap.o
 obj-y += traps/data_access_exception.o
 obj-y += irq.o
 obj-y += time.o
+obj-y += clockevent.o
 
-#libs-y                 += lib/
 
diff --git a/arch/sparc/kernel/clockevent.c b/arch/sparc/kernel/clockevent.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e7080637127efce1e25e507c0c642cc348d4973
--- /dev/null
+++ b/arch/sparc/kernel/clockevent.c
@@ -0,0 +1,261 @@
+/**
+ * @file arch/sparc/kernel/clockevent.c
+ */
+
+#include <kernel/kernel.h>
+#include <kernel/clockevent.h>
+#include <kernel/kmem.h>
+#include <kernel/string.h>
+#include <kernel/irq.h>
+#include <errno.h>
+
+#ifdef CONFIG_LEON3
+#include <gptimer.h>
+#include <asm/time.h>
+
+
+
+/* XXX: want AMBA PNP autodetect later...) */
+
+
+#define LEON3_GPTIMERS	4
+#define GPTIMER_0_IRQ	8
+
+static struct gpclkdevs {
+	struct gptimer_unit *gptu;
+	struct clock_event_device dev[LEON3_GPTIMERS];
+} _gp_clk_ev = {
+	.gptu =  (struct gptimer_unit *)  LEON3_BASE_ADDRESS_GPTIMER
+};
+
+
+
+/**
+ * @brief the clock device event handler
+ */
+
+static irqreturn_t gp_clk_dev_irq_handler(unsigned int irq, void *userdata)
+{
+	struct clock_event_device *ce = (struct clock_event_device *) userdata;
+
+	if (ce)
+		if (ce->event_handler)
+			ce->event_handler(ce);
+
+	return 0;
+}
+
+
+/**
+ * @brief clock device suspend call
+ */
+
+static void gp_clk_dev_suspend(struct clock_event_device *ce)
+{
+	gptimer_clear_enabled(_gp_clk_ev.gptu, ce->irq - GPTIMER_0_IRQ);
+
+}
+
+
+/**
+ * @brief clock device resume call
+ */
+
+static void gp_clk_dev_resume(struct clock_event_device *ce)
+{
+	gptimer_set_enabled(_gp_clk_ev.gptu, ce->irq - GPTIMER_0_IRQ);
+}
+
+
+/**
+ * @brief clock device set_state call
+ */
+
+static void gp_clk_dev_set_state(enum clock_event_state state,
+				struct clock_event_device *ce)
+{
+	int timer;
+
+
+	/* derive the timer index from its IRL */
+	timer = ce->irq - GPTIMER_0_IRQ;
+
+
+	switch (state) {
+		case CLOCK_EVT_STATE_PERIODIC:
+			gptimer_set_restart(_gp_clk_ev.gptu, timer);
+			gp_clk_dev_resume(ce);
+			break;
+		case CLOCK_EVT_STATE_ONESHOT:
+			gptimer_clear_restart(_gp_clk_ev.gptu, timer);
+			gp_clk_dev_resume(ce);
+			break;
+		case CLOCK_EVT_STATE_SHUTDOWN:
+			gp_clk_dev_suspend(ce);
+			break;
+		case CLOCK_EVT_STATE_UNUSED:
+			gp_clk_dev_suspend(ce);
+			break;
+		default:
+			break;
+	}
+}
+
+
+/**
+ * @brief program the gptimer to underflow at a given event timeout
+ * @param evt the number of clock source ticks until the event
+ *
+ * @note the expiration time will be forcibly clamped to the valid range of
+ *	 clock device
+ */
+
+static int gp_clk_dev_set_next_event(unsigned long evt,
+				     struct clock_event_device *ce)
+{
+	int timer;
+
+
+	/* derive the timer index from its IRL */
+	timer = ce->irq - GPTIMER_0_IRQ;
+
+	switch(ce->state) {
+		case CLOCK_EVT_STATE_PERIODIC:
+			gptimer_start_cyclical(_gp_clk_ev.gptu, timer, evt);
+			break;
+		case CLOCK_EVT_STATE_ONESHOT:
+			gptimer_start(_gp_clk_ev.gptu, timer, evt);
+			break;
+		default:
+			break; /* not sure what you want? */
+	};
+
+
+	return 0;
+}
+
+
+/**
+ * @brief program the gptimer to underflow at a given absolute kernel time
+ *
+ * @param ktime the kernel time at which the timer will underflow
+ *
+ *
+ * @returns 0 on success, -ETIME if time is in the past
+ *
+ * @note the expiration time will be forcibly clamped to the valid range of
+ *	 clock device
+ *
+ */
+
+static int gp_clk_dev_set_next_ktime(struct timespec expires,
+				     struct clock_event_device *ce)
+{
+	uint32_t evt;
+
+	double delta;
+
+
+	delta = difftime_ns(expires, get_ktime());
+
+	if (delta < 0)
+		return -ETIME;
+
+	/* clamp to valid range */
+	evt  = clamp((typeof(ce->max_delta_ns)) delta,
+		     ce->min_delta_ns, ce->max_delta_ns);
+
+	/* adjust ns delta to actual clock tick value*/
+	evt = evt / ce->mult;
+
+	return gp_clk_dev_set_next_event((unsigned long) evt, ce);
+}
+
+
+/**
+ * @brief register the 4 general purpose timers (of the GR712)
+ */
+
+static void leon_setup_clockdevs(void)
+{
+	int i;
+	char *buf;
+
+	struct clock_event_device *ce;
+
+
+	/* the prescaler is the same for all timers */
+	gptimer_set_scaler_reload(_gp_clk_ev.gptu, GPTIMER_RELOAD);
+
+	for (i = 0; i < LEON3_GPTIMERS; i++) {
+
+
+		ce = &_gp_clk_ev.dev[i];
+
+		ce->mult = CPU_CYCLES_TO_NS(GPTIMER_RELOAD + 1);
+
+		/* we can only fit so many nanoseconds into a 32 bit number
+		 * note that we set the timer value in "ticks", where each tick
+		 * corresponds to GPTMER_RELOAD + 1 cpu cycles, so our actual
+		 * possible timeout in nanoseconds is typically much higher
+		 * than what we can fit in here
+		 */
+
+		if (sizeof(typeof(ce->max_delta_ns)) > 4)
+			ce->max_delta_ns = 0xFFFFFFFFUL * ce->mult;
+		else
+			ce->max_delta_ns = 0xFFFFFFFFUL;
+
+		/* We cannot be better than the system clock overhead,
+		 * as long as we support CLOCK_EVT_FEAT_KTIME
+		 * To be safe, we should at least use twice that value.
+		 */
+		ce->min_delta_ns = 2 * ktime_get_readout_overhead();
+
+		BUG_ON(!ce->mult);
+
+		ce->set_next_event = &gp_clk_dev_set_next_event;
+		ce->set_next_ktime = &gp_clk_dev_set_next_ktime;
+		ce->set_state      = &gp_clk_dev_set_state;
+		ce->suspend        = &gp_clk_dev_suspend;
+		ce->resume         = &gp_clk_dev_resume;
+
+
+		buf = kmalloc(16);
+		BUG_ON(!buf);
+		snprintf(buf, 16, "GPTIMER%1d", i);
+		ce->name = buf;
+
+
+		/* we rate our timers by nanosecond resoltion, so we'll just
+		 * the multiplier as our rating
+		 */
+		ce->rating = ce->mult;
+
+		ce->state = CLOCK_EVT_STATE_UNUSED;
+
+		ce->features = (CLOCK_EVT_FEAT_PERIODIC |
+				CLOCK_EVT_FEAT_ONESHOT  |
+				CLOCK_EVT_FEAT_KTIME);
+
+
+		ce->irq  = GPTIMER_0_IRQ + i;
+
+		gptimer_clear_enabled(_gp_clk_ev.gptu, i);
+
+		BUG_ON(irq_request(ce->irq, ISR_PRIORITY_NOW,
+				   &gp_clk_dev_irq_handler,
+				   &_gp_clk_ev.dev[i]));
+
+		clockevents_register_device(ce);
+	}
+}
+#endif /* CONFIG_LEON3 */
+
+
+void sparc_clockevent_init(void)
+{
+#ifdef CONFIG_LEON3
+	leon_setup_clockdevs();
+#endif /* CONFIG_LEON3 */
+}
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
index 84d8e7d5cf7b591acf5e870556755aae9d84a766..115a9b595d3a082bef6f0c0ec70d642916744217 100644
--- a/arch/sparc/kernel/setup.c
+++ b/arch/sparc/kernel/setup.c
@@ -13,6 +13,7 @@
 #include <mm.h>
 #include <asm/irq.h>
 #include <asm/time.h>
+#include <asm/clockevent.h>
 #include <compiler.h>
 
 #include <page.h>
@@ -104,4 +105,6 @@ void setup_arch(void)
 	leon_irq_init();
 
 	sparc_uptime_init();
+
+	sparc_clockevent_init();
 }
diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile
index ef73f273abd175153959cb6f0828f93cfd65a220..4a30bf1ca0e60fac92ec216d2f3fcd92028b1599 100644
--- a/arch/sparc/lib/Makefile
+++ b/arch/sparc/lib/Makefile
@@ -1,5 +1,6 @@
 
 CHECKFLAGS     += -D__sparc__
 
-obj-y += grtimer.o
-obj-y += grtimer_longcount.o
+lib-y += gptimer.o
+lib-y += grtimer.o
+lib-y += grtimer_longcount.o
diff --git a/arch/sparc/lib/gptimer.c b/arch/sparc/lib/gptimer.c
index 9ae67688ca355653da3267123e30ba1e1d671402..d07ebfecae101ae4f70b75e91b41bbb63290b798 100644
--- a/arch/sparc/lib/gptimer.c
+++ b/arch/sparc/lib/gptimer.c
@@ -333,6 +333,9 @@ uint32_t gptimer_get_reload(struct gptimer_unit *ptu, uint32_t timer)
 
 void gptimer_start(struct gptimer_unit *ptu, uint32_t timer, uint32_t value)
 {
+	gptimer_clear_enabled(ptu, timer);
+	gptimer_clear_restart(ptu, timer);
+
 	gptimer_set_value(ptu, timer, value);
 	gptimer_set_reload(ptu, timer, value);
 
@@ -352,10 +355,12 @@ void gptimer_start(struct gptimer_unit *ptu, uint32_t timer, uint32_t value)
 void gptimer_start_cyclical(struct gptimer_unit *ptu,
 			    uint32_t timer, uint32_t value)
 {
+	gptimer_clear_enabled(ptu, timer);
+	gptimer_clear_restart(ptu, timer);
+
 	gptimer_set_value(ptu, timer, value);
 	gptimer_set_reload(ptu, timer, value);
 
-
 	gptimer_set_interrupt_enabled(ptu, timer);
 	gptimer_set_load(ptu, timer);
 	gptimer_set_restart(ptu, timer);
diff --git a/include/kernel/clockevent.h b/include/kernel/clockevent.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e1599544ec82d18c907f3e3876d0abf03a49a01
--- /dev/null
+++ b/include/kernel/clockevent.h
@@ -0,0 +1,113 @@
+/**
+ * @file    include/kernel/clockevent.h
+ * @author  Armin Luntzer (armin.luntzer@univie.ac.at)
+ *
+ * @ingroup time
+ *
+ * @copyright GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _KERNEL_CLOCKEVENT_H_
+#define _KERNEL_CLOCKEVENT_H_
+
+#include <list.h>
+#include <kernel/types.h>
+#include <kernel/time.h>
+
+
+/* clock event states */
+enum clock_event_state {
+	CLOCK_EVT_STATE_UNUSED,
+	CLOCK_EVT_STATE_SHUTDOWN,
+	CLOCK_EVT_STATE_PERIODIC,
+	CLOCK_EVT_STATE_ONESHOT
+};
+
+
+/* feature set of a particular clock device */
+
+#define CLOCK_EVT_FEAT_PERIODIC	0x000001
+#define CLOCK_EVT_FEAT_ONESHOT	0x000002
+#define CLOCK_EVT_FEAT_KTIME	0x000004
+
+
+/**
+ * event_handler:	callback function executed as the event occurs
+ *
+ * set_next_event:	set next event function using a clock source delta
+ * set_next_ktime:	set next event function using a direct ktime value
+ *
+ * max_delta_ns:	maximum programmable delta value in nanoseconds
+ * min_delta_ns:	minimum programmable delta value in nanoseconds
+ * mult:		device ticks to nanoseconds multiplier
+ * state:		timer operating state
+ * features:		timer event features
+ * set_state:		set state function
+ * rating:		quality rating of the device, less is better (more
+ *			resolution, e.g nanosecond-resolution)
+ * name:		clock event name
+ * irq:			IRQ number (-1 if device without IRL)
+ */
+
+struct clock_event_device {
+	void			(*event_handler)(struct clock_event_device *);
+	int			(*set_next_event)(unsigned long evt,
+						  struct clock_event_device *);
+	int			(*set_next_ktime)(struct timespec expires,
+						  struct clock_event_device *);
+	uint32_t		max_delta_ns;
+	uint32_t		min_delta_ns;
+	uint32_t		mult;
+
+
+	enum clock_event_state	state;
+	unsigned int		features;
+
+	void			(*set_state)(enum clock_event_state state,
+					    struct clock_event_device *);
+	void			(*suspend)(struct clock_event_device *);
+	void			(*resume)(struct clock_event_device *);
+
+	unsigned int		rating;
+	const char		*name;
+	int			irq;
+
+	struct list_head	node;
+};
+
+
+bool clockevents_timout_in_range(struct clock_event_device *dev,
+				 unsigned long nanoseconds);
+
+bool clockevents_feature_periodic(struct clock_event_device *dev);
+bool clockevents_feature_oneshot(struct clock_event_device *dev);
+
+void clockevents_set_state(struct clock_event_device *dev,
+			   enum clock_event_state state);
+
+void clockevents_set_handler(struct clock_event_device *dev,
+			     void (*event_handler)(struct clock_event_device *));
+
+void clockevents_register_device(struct clock_event_device *dev);
+
+void clockevents_exchange_device(struct clock_event_device *old,
+				 struct clock_event_device *new);
+
+int clockevents_program_event(struct clock_event_device *dev,
+			      struct timespec expires);
+
+int clockevents_program_timeout_ns(struct clock_event_device *dev,
+				   unsigned long nanoseconds);
+
+
+#endif /* _KERNEL_CLOCKEVENT_H_ */
diff --git a/include/kernel/kernel.h b/include/kernel/kernel.h
index 536b84f6c591cc2d0b5c645182959e5769ce62dc..3e7433569941a62d63f43991b7fb53a4c4dd6493 100644
--- a/include/kernel/kernel.h
+++ b/include/kernel/kernel.h
@@ -53,4 +53,32 @@ __extension__
 
 
 
+/* min()/max()/clamp() macros with strict type checking
+ * (ripped off from linux/kernel.h)
+ */
+
+#define min(x, y) ({				\
+	typeof(x) _min1 = (x);			\
+	typeof(y) _min2 = (y);			\
+	(void) (&_min1 == &_min2);		\
+	_min1 < _min2 ? _min1 : _min2; })
+
+#define max(x, y) ({				\
+	typeof(x) _max1 = (x);			\
+	typeof(y) _max2 = (y);			\
+	(void) (&_max1 == &_max2);		\
+	_max1 > _max2 ? _max1 : _max2; })
+
+
+#define clamp(val, min, max) ({			\
+	typeof(val) __val = (val);		\
+	typeof(min) __min = (min);		\
+	typeof(max) __max = (max);		\
+	(void) (&__val == &__min);		\
+	(void) (&__val == &__max);		\
+	__val = __val < __min ? __min: __val;	\
+	__val > __max ? __max: __val; })
+
+
+
 #endif /* _KERNEL_H_ */
diff --git a/include/kernel/tick.h b/include/kernel/tick.h
new file mode 100644
index 0000000000000000000000000000000000000000..e80edc0494ba4fbb15053b7605ce00d9211526e4
--- /dev/null
+++ b/include/kernel/tick.h
@@ -0,0 +1,40 @@
+/**
+ * @file    include/kernel/time.h
+ * @author  Armin Luntzer (armin.luntzer@univie.ac.at)
+ *
+ * @ingroup time
+ *
+ * @copyright GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _KERNEL_TICK_H_
+#define _KERNEL_TICK_H_
+
+#include <kernel/types.h>
+#include <kernel/kernel.h>
+#include <kernel/clockevent.h>
+
+
+/* tick modes */
+enum tick_mode {
+	TICK_MODE_PERIODIC,
+	TICK_MODE_ONESHOT
+};
+
+
+void tick_check_device(struct clock_event_device *dev);
+int tick_set_mode(enum tick_mode mode);
+int tick_set_next_ns(unsigned long nanoseconds);
+int tick_set_next_ktime(struct timespec expires);
+
+#endif /* _KERNEL_TICK_H_ */
diff --git a/include/kernel/time.h b/include/kernel/time.h
index ec981ae6504dadfb7292a216dbd506500ca11e98..3e59149e7655b45f22d5ef094795e387c88dd07c 100644
--- a/include/kernel/time.h
+++ b/include/kernel/time.h
@@ -1,5 +1,5 @@
 /**
- * @file    include/kernel/time.h
+ * @file    include/kernel/ktime.h
  * @author  Armin Luntzer (armin.luntzer@univie.ac.at)
  *
  * @ingroup time
@@ -17,8 +17,8 @@
  *
  */
 
-#ifndef _KERNEL_TIME_H_
-#define _KERNEL_TIME_H_
+#ifndef _KERNEL_KTIME_H_
+#define _KERNEL_KTIME_H_
 
 #include <kernel/types.h>
 #include <kernel/kernel.h>
@@ -43,11 +43,20 @@ compile_time_assert((member_size(struct timespec, tv_nsec) == sizeof(uint32_t)),
 
 struct timekeeper {
 	struct clocksource *clock;
+	uint32_t readout_ns;	/* readout time overhead in ns */
 };
 
 
 
-void time_get_uptime(struct timespec *ts);
+struct timespec get_uptime(void);
+struct timespec get_ktime(void);
+
+uint32_t ktime_get_readout_overhead(void);
+
+
+double difftime(const struct timespec time1, const struct timespec time0);
+double difftime_ns(const struct timespec time1, const struct timespec time0);
+
 void time_init(struct clocksource *clock);
 
-#endif /* _KERNEL_TIME_H_ */
+#endif /* _KERNEL_KTIME_H_ */
diff --git a/init/main.c b/init/main.c
index 346056782f5ad39a42cdd34cf5d9e7f8f8db681e..a3985180bbff8e9c31d1ee535f0c64049cfefaa8 100644
--- a/init/main.c
+++ b/init/main.c
@@ -18,12 +18,18 @@
 #include <kernel/printk.h>
 #include <kernel/kernel.h>
 #include <kernel/kthread.h>
+#include <kernel/time.h>
 #include <modules-image.h>
 
+#include <kernel/string.h>
+#include <kernel/kmem.h>
+
 #include <asm/processor.h>
 
 /* for our demo */
 #include "xentium_demo.h"
+/* arch irq disable/enable */
+#include <asm/irqflags.h>
 
 
 #define MSG "MAIN: "
@@ -67,7 +73,11 @@ static void twiddle(void)
 
 #define TREADY 4
 
+#if 1
+static volatile int *console = (int *)0x80100100;
+#else
 static volatile int *console = (int *)0x80000100;
+#endif
 
 static int putchar(int c)
 {
@@ -85,8 +95,6 @@ static int putchar(int c)
 
 
 extern struct task_struct *kernel;
-struct task_struct *tsk1;
-struct task_struct *tsk2;
 
 int threadx(void *data)
 {
@@ -102,9 +110,10 @@ int threadx(void *data)
 			b++;
 		}
 		putchar('\n');
-
-		if (b > 3 * (int)c)
+#if 1
+		if (b > (int) c * (int)c)
 			break;
+#endif
 	//	schedule();
 		//twiddle();
 		//cpu_relax();
@@ -112,50 +121,6 @@ int threadx(void *data)
 	return 0;
 }
 
-int thread1(void *data)
-{
-	int b = 0;
-
-	while(1) {
-		//printk(".");
-		int i;
-		for (i = 0; i < 20; i++)
-			putchar('.');
-		putchar('\n');
-
-		if (b++ > 20)
-			break;
-
-		schedule();
-		//twiddle();
-		cpu_relax();
-	}
-	return 0;
-}
-
-int thread2(void *data)
-{
-	int b = 0;
-
-	while (1) {
-		int i;
-		for (i = 0; i < 20; i++) {
-			putchar('o');
-			b++;
-		}
-
-		putchar('\n');
-		if (b > 200)
-			break;
-		schedule();
-
-	}
-		//schedule();
-		//cpu_relax();
-		printk("Actually, I left...\n");
-		return 0xdeadbeef;
-}
-
 /**
  * @brief kernel initialisation routines
  */
@@ -176,13 +141,32 @@ static int kernel_init(void)
 arch_initcall(kernel_init);
 
 
+#include <kernel/clockevent.h>
+void clk_event_handler(struct clock_event_device *ce)
+{
+
+	struct timespec expires;
+	struct timespec now;
+
+//	printk("DIIIING-DOOONG\n");
+	get_ktime(&now);
+
+	expires = now;
+
+	expires.tv_sec += 1;
+
+	clockevents_program_event(&clk_event_handler, expires, now, CLOCK_EVT_MODE_ONESHOT);
+}
+
 
 /**
  * @brief kernel main function
  */
-
+#define MAX_TASKS 800
 int kernel_main(void)
 {
+	struct task_struct *tasks[MAX_TASKS];
+	int tcnt = 0;
 #if 0
 	void *addr;
 	struct elf_module m;
@@ -236,54 +220,81 @@ int kernel_main(void)
 	printk(MSG "Boot complete, spinning idly.\n");
 
 
-#define GR712_IRL1_GPTIMER_2    10
 
-#define LEON3_TIMER_EN 0x00000001       /* enable counting */
-#define LEON3_TIMER_RL 0x00000002       /* reload at 0     */
-#define LEON3_TIMER_LD 0x00000004       /* load counter    */
-#define LEON3_TIMER_IE 0x00000008       /* irq enable      */
 	{
-	struct gptimer_unit *mtu = (struct gptimer_unit *) 0x80000300;
+	struct timespec expires;
+	struct timespec now;
+	get_ktime(&now);
 
-	printk("%s() entered\n", __func__);
+	expires = now;
+	expires.tv_nsec += 18000;
 
-	irq_request(8,  ISR_PRIORITY_NOW, dummy, NULL);
+	BUG_ON(clockevents_program_event(NULL, expires, now, CLOCK_EVT_MODE_PERIODIC));
 
-	mtu->scaler_reload = 5;
-	/* abs min: 270 / (5+1) (sched printing) */
-	/* abs min: 800 / (5+1) (threads printing) */
-	mtu->timer[0].reload = 2000 / (mtu->scaler_reload + 1);
-	mtu->timer[0].value = mtu->timer[0].reload;
-	mtu->timer[0].ctrl = LEON3_TIMER_LD | LEON3_TIMER_EN
-		| LEON3_TIMER_RL | LEON3_TIMER_IE;
 	}
 
 
 	kernel = kthread_init_main();
 
-	tsk1 = kthread_create(thread1, NULL, KTHREAD_CPU_AFFINITY_NONE, "Thread1");
-	tsk2 = kthread_create(thread2, NULL, KTHREAD_CPU_AFFINITY_NONE, "Thread2");
-	//kthread_wake_up(tsk2);
-	//	kthread_wake_up(tsk2);
-	//
+
+
+
 	{
 	static char zzz[] = {':', '/', '\\', '~', '|'};
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(zzz); i++) 
+	for (i = 0; i < ARRAY_SIZE(zzz); i++)
 		kthread_create(threadx, &zzz[i], KTHREAD_CPU_AFFINITY_NONE, "Thread2");
 	}
 
-	while(1) {
-		//printk("-");
-#if 0
+	{
+		static char zzz[] = {':', '/', '\\', '~', '|'};
+		static int z;
+		char *buf = NULL;
 		int i;
-		for (i = 0; i < 20; i++)
-			putchar('-');
-		putchar('\n');
-#endif
+		struct timespec ts;
+		get_uptime(&ts);
+		printk("creating tasks at %d s %d ns (%g)\n", ts.tv_sec, ts.tv_nsec, (double) ts.tv_sec + (double) ts.tv_nsec / 1e9);
+
+
+
+		for (i = 0; i < MAX_TASKS; i++) {
+		//	buf = kmalloc(30);
+		//	BUG_ON(!buf);
+
+		//	sprintf(buf, "Thread %d", z);
+			z++;
+
+			tasks[tcnt++] = kthread_create(threadx, &zzz[i], KTHREAD_CPU_AFFINITY_NONE, buf);
+		//	kfree(buf);
+		}
+
+	}
+
+
+	{
+		int i;
+
+		struct timespec ts;
+		get_uptime(&ts);
+		printk("total %d after %d s %d ns (%g)\n", tcnt, ts.tv_sec, ts.tv_nsec, (double) ts.tv_sec + (double) ts.tv_nsec / 1e9);
+		BUG_ON(tcnt > MAX_TASKS);
+
+		for (i = 0; i < tcnt; i++)
+			kthread_wake_up(tasks[i]);
+
+		arch_local_irq_disable();
+		get_uptime(&ts);
+		printk("all awake after %d s %d ns (%g)\n", ts.tv_sec, ts.tv_nsec, (double) ts.tv_sec + (double) ts.tv_nsec / 1e9);
+		arch_local_irq_enable();
+	}
+
+
+	while(1) {
+		twiddle();
 		cpu_relax();
 	}
+
 	/* never reached */
 	BUG();
 
diff --git a/kernel/Makefile b/kernel/Makefile
index 3334a1dc0916e180c8ec44ea6f47120c0466b418..e6bb7a3e0ee3d1fc9843ec26863bf7a4b8634a8c 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -8,3 +8,5 @@ obj-y += irq.o
 obj-$(CONFIG_XENTIUM) += xentium.o
 obj-y += kthread.o
 obj-y += time.o
+obj-y += clockevent.o
+obj-y += tick.o
diff --git a/kernel/clockevent.c b/kernel/clockevent.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2f73c970e23cedb9e262fed60b2adc08c13d551
--- /dev/null
+++ b/kernel/clockevent.c
@@ -0,0 +1,325 @@
+/**
+ * @file kernel/clockevent.c
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ *
+ *
+ * @ingroup time
+ *
+ * @note This roughly follows the concept found in linux clockevents
+ *	 All glory to the Hypnotoad!
+ */
+
+
+#include <asm-generic/spinlock.h>
+#include <asm-generic/irqflags.h>
+#include <kernel/clockevent.h>
+#include <kernel/export.h>
+#include <kernel/tick.h>
+#include <errno.h>
+
+
+
+static LIST_HEAD(clockevent_devices);
+
+static struct spinlock clockevents_spinlock;
+
+
+/**
+ * @brief convert nanoseconds delta to device ticks
+ *
+ * @note this implicitly clamps the delta to the valid range of the device
+ */
+
+static
+unsigned long clockevents_delta2ticks(unsigned long delta,
+				      struct clock_event_device *dev)
+{
+	delta = (unsigned long) clamp((typeof(dev->max_delta_ns)) delta,
+				      dev->min_delta_ns, dev->max_delta_ns);
+
+	return delta / dev->mult;
+}
+
+
+/**
+ * @brief check if a timeout is in the legal range for the device
+ *
+ * @returns true if in range, false otherwise
+ */
+
+bool clockevents_timout_in_range(struct clock_event_device *dev,
+				 unsigned long nanoseconds)
+{
+	unsigned long cl;
+
+	cl = (unsigned long) clamp((typeof(dev->max_delta_ns)) nanoseconds,
+				    dev->min_delta_ns, dev->max_delta_ns);
+
+	return cl == nanoseconds;
+}
+
+
+/**
+ * @brief check if a device supports periodic ticks
+ *
+ * @returns true if the feature is supported
+ */
+
+bool clockevents_feature_periodic(struct clock_event_device *dev)
+{
+	if (dev->features & CLOCK_EVT_FEAT_PERIODIC)
+		return true;
+
+	return false;
+}
+
+
+/**
+ * @brief check if a device supports oneshot ticks
+ *
+ * @returns true if the feature is supported
+ */
+
+bool clockevents_feature_oneshot(struct clock_event_device *dev)
+{
+	if (dev->features & CLOCK_EVT_FEAT_ONESHOT)
+		return true;
+
+	return false;
+}
+
+
+/**
+ * @brief check if a device supports a given state
+ *
+ * @returns true if a feature is supported
+ *
+ * @note only operative modes (periodic, oneshot are considered)
+ */
+
+bool clockevents_state_supported(struct clock_event_device *dev,
+				 enum clock_event_state state)
+{
+	switch (state)
+	{
+	case CLOCK_EVT_STATE_PERIODIC:
+		return clockevents_feature_periodic(dev);
+	case CLOCK_EVT_STATE_ONESHOT:
+		return clockevents_feature_oneshot(dev);
+	default:
+		break;
+
+	}
+
+	return true;
+}
+
+
+/**
+ * @brief set the operating state of a clock event device
+ * @param dev the device to modify
+ * @param state the new state
+ *
+ * @note if a state is not supported according to the device features, calling
+ *	 this function will have no effect
+ */
+
+void clockevents_set_state(struct clock_event_device *dev,
+			   enum clock_event_state state)
+{
+	if (!dev) {
+		pr_warn("CLOCKEVENT: NULL pointer argument to %s in call from "
+			"%p\n", __func__, __caller(0));
+		return;
+	}
+
+
+	if (!clockevents_state_supported(dev, state)) {
+		pr_warn("CLOCKEVENT: selected state %d not supported by device "
+			"%s\n", state, dev->name);
+		return;
+	}
+
+
+	if (dev->state != state) {
+
+
+		dev->set_state(state, dev);
+		dev->state = state;
+	}
+}
+
+
+/**
+ * @brief set the event handler for a clock event device
+ */
+
+void clockevents_set_handler(struct clock_event_device *dev,
+			     void (*event_handler)(struct clock_event_device *))
+{
+	dev->event_handler = event_handler;
+}
+
+
+/**
+ * @brief suspend all clock devices
+ */
+
+void clockevents_suspend(void)
+{
+	struct clock_event_device *dev;
+
+	list_for_each_entry_rev(dev, &clockevent_devices, node)
+		if (dev->suspend)
+			dev->suspend(dev);
+}
+
+
+/**
+ *  @brief resume all clock devices
+ */
+void clockevents_resume(void)
+{
+	struct clock_event_device *dev;
+
+	list_for_each_entry(dev, &clockevent_devices, node)
+		if (dev->resume)
+			dev->resume(dev);
+}
+
+
+/**
+ * @brief release a clock event device in exchange for another one
+ *
+ * @param old the device to be released (may be NULL)
+ * @param new the device to be acquired (may be NULL)
+ */
+
+void clockevents_exchange_device(struct clock_event_device *old,
+				 struct clock_event_device *new)
+{
+
+	if (old)
+		clockevents_set_state(old, CLOCK_EVT_STATE_UNUSED);
+
+
+	if (new) {
+		BUG_ON(new->state != CLOCK_EVT_STATE_UNUSED);
+		clockevents_set_state(new, CLOCK_EVT_STATE_SHUTDOWN);
+	}
+}
+
+
+/**
+ * @brief register a clock event device
+ */
+
+void clockevents_register_device(struct clock_event_device *dev)
+{
+
+	BUG_ON(!dev);
+
+	if (!dev->set_next_event) {
+		pr_crit("set_next_event() not set for clock %p\n", dev);
+		return;
+	}
+
+	if (dev->features & CLOCK_EVT_FEAT_KTIME) {
+		if (!dev->set_next_ktime) {
+			pr_crit("set_next_ktime() not set for clock %p\n", dev);
+			return;
+		}
+	}
+
+	if (!dev->set_state) {
+		pr_crit("set_state() not set for clock %p\n", dev);
+		return;
+	}
+
+	if (!dev->suspend)
+		pr_err("suspend() not set for clock %p\n", dev);
+
+	spin_lock(&clockevents_spinlock);
+	list_add_tail(&dev->node, &clockevent_devices);
+
+	arch_local_irq_disable();
+	tick_check_device(dev);
+	arch_local_irq_enable();
+
+	spin_unlock(&clockevents_spinlock);
+}
+EXPORT_SYMBOL(clockevents_register_device);
+
+
+/**
+ * @brief program a clock event
+ *
+ * returns 0 on success, -ETIME if expiration time is in the past
+ *
+ * @warn if the timeout exceeds the bounds of the programmable range
+ *	 for the device, it is forcibly clamped without warning
+ *
+ * @note if the clock event device is in periodic mode, the delta between
+ *	 expiration time and current time will be the new period
+ */
+
+int clockevents_program_event(struct clock_event_device *dev,
+			      struct timespec expires)
+{
+	unsigned long evt;
+
+	double delta;
+
+
+	if (dev->state == CLOCK_EVT_STATE_SHUTDOWN)
+		return 0;
+
+
+	/* if the set_nex_ktime handler was configured for this device */
+	if (dev->features & CLOCK_EVT_FEAT_KTIME)
+		return dev->set_next_ktime(expires, dev);
+
+
+	/* otherwise we'll do it ourselves */
+	delta = difftime_ns(expires, get_ktime());
+
+	if (delta < 0)
+		return -ETIME;
+
+	/* clamp, adjust to clock tick period and set event */
+	evt = clockevents_delta2ticks((unsigned long) delta, dev);
+
+	dev->set_next_event(evt, dev);
+
+	return 0;
+}
+
+
+/**
+ * @brief program a clockevent timeout in nanoseconds
+ *
+ * returns 0 on success, 1 if range was clamped
+ *
+ * @warn if the timeout exceeds the bounds of the programmable range
+ *	 for the device, it is forcibly clamped
+ */
+
+int clockevents_program_timeout_ns(struct clock_event_device *dev,
+				   unsigned long nanoseconds)
+{
+	unsigned long evt;
+
+
+	if (dev->state == CLOCK_EVT_STATE_SHUTDOWN)
+		return 0;
+
+	/* clamp and adjust to clock tick period */
+	evt = clockevents_delta2ticks(nanoseconds, dev);
+
+	dev->set_next_event(evt, dev);
+
+	return evt != nanoseconds;
+}
+
+
+
diff --git a/kernel/tick.c b/kernel/tick.c
new file mode 100644
index 0000000000000000000000000000000000000000..cd0ba943e1a957d4ca02eaed1831b55a63260c66
--- /dev/null
+++ b/kernel/tick.c
@@ -0,0 +1,214 @@
+/**
+ * @file kernel/tick.c
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ *
+ *
+ * @ingroup time
+ *
+ * @note this roughly follows the concept found in linux ticks
+ */
+
+
+
+#include <errno.h>
+#include <kernel/tick.h>
+#include <kernel/time.h>
+#include <kernel/export.h>
+#include <kernel/clockevent.h>
+#include <kernel/kthread.h>
+
+#define MSG "TICK: "
+
+static struct clock_event_device *tick_device;
+
+
+
+
+static void tick_event_handler(struct clock_event_device *dev)
+{
+	/* does nothing, schedule later */
+}
+
+
+struct clock_event_device *tick_get_device(__attribute__((unused)) int cpu)
+{
+	return tick_device;
+}
+
+void tick_set_device(struct clock_event_device *dev,
+					   __attribute__((unused)) int cpu)
+{
+	tick_device = dev;
+}
+
+/**
+ * @brief tick device selection check
+ *
+ * @note placeholder, does not do much right now
+ */
+
+static bool tick_check_preferred(struct clock_event_device *cur,
+				 struct clock_event_device *new)
+{
+
+	/* XXX: need that until we have internal mode tracking for the
+	 * ticker, after wich we can reprogram the the oneshot
+	 * timer after each event to emulate periodicity
+	 */
+	if (!clockevents_feature_periodic(new))
+		return false;
+
+
+	/* If we have nothing, we'll take what we can get */
+	if (!cur)
+		return true;
+
+	return false;
+}
+
+
+/**
+ * @brief configure for periodic mode if available
+ *
+ * @returns -EINVAL if mode is not supported by underlying clock event device
+ */
+
+static int tick_set_mode_periodic(struct clock_event_device *dev)
+{
+	if (!clockevents_feature_periodic(dev))
+		return -EINVAL;
+
+	clockevents_set_state(dev, CLOCK_EVT_STATE_PERIODIC);
+
+	return 0;
+}
+
+
+/**
+ * @brief configure for oneshot mode if available
+ *
+ * @returns -EINVAL if mode is not supported by underlying clock event device
+ */
+
+static int tick_set_mode_oneshot(struct clock_event_device *dev)
+{
+	if (!clockevents_feature_oneshot(dev))
+		return -EINVAL;
+
+	clockevents_set_state(dev, CLOCK_EVT_STATE_ONESHOT);
+
+	return 0;
+}
+
+
+/**
+ * @brief configure the tick device
+ */
+
+static void tick_setup_device(struct clock_event_device *dev)
+{
+#define RANDOM_TICK_RATE_NS	18000
+	clockevents_set_handler(dev, tick_event_handler);
+
+	/* FIXME: assume blindly for the moment */
+	tick_set_mode_periodic(dev);
+
+	clockevents_program_timeout_ns(dev, RANDOM_TICK_RATE_NS);
+}
+
+
+/**
+ * @brief offer a new clock event device to the ticker
+ */
+
+void tick_check_device(struct clock_event_device *dev)
+{
+	struct clock_event_device *cur;
+
+
+	if (!dev)
+		return;
+
+	/* XXX need per-cpu selection later */
+	cur = tick_get_device(0);
+
+	if (!tick_check_preferred(cur, dev))
+		return;
+
+	clockevents_exchange_device(cur, dev);
+
+	/* XXX as above */
+	tick_set_device(dev, 0);
+
+	tick_setup_device(dev);
+}
+
+
+/**
+ * @brief configure the mode of the ticker
+ *
+ * @returns 0 on success, -EINVAL if mode not available
+ */
+
+int tick_set_mode(enum tick_mode mode)
+{
+	struct clock_event_device *dev;
+
+
+	/* XXX need per-cpu selection later */
+	dev = tick_get_device(0);
+
+	switch(mode) {
+		case TICK_MODE_PERIODIC:
+			return tick_set_mode_periodic(dev);
+		case TICK_MODE_ONESHOT:
+			return tick_set_mode_oneshot(dev);
+		default:
+			break;
+	}
+
+	return -EINVAL;
+}
+
+
+/**
+ * @brief configure next tick period in nanoseconds
+ *
+ * returns 0 on success, 1 if nanoseconds range was clamped to clock range
+ */
+
+
+int tick_set_next_ns(unsigned long nanoseconds)
+{
+	struct clock_event_device *dev;
+
+
+	/* XXX need per-cpu selection later */
+	dev = tick_get_device(0);
+
+	return clockevents_program_timeout_ns(dev, nanoseconds);
+}
+
+
+/**
+ * @brief configure next tick period in ktime
+ *
+ * returns 0 on success, -ETIME if expiration time is in the past
+ *
+ * @warn if the timeout exceeds the bounds of the programmable range
+ *	 for the device, it is forcibly clamped without warning
+ *
+ * @note if the clock event device is in periodic mode, the delta between
+ *	 expiration time and current time will be the new period
+ */
+
+int tick_set_next_ktime(struct timespec expires)
+{
+	struct clock_event_device *dev;
+
+
+	/* XXX need per-cpu selection later */
+	dev = tick_get_device(0);
+
+	return clockevents_program_event(dev, expires);
+}
diff --git a/kernel/time.c b/kernel/time.c
index 344cc7e5fdd154cc5ca54aafbedf9a3dd1562b66..dd297b1565f26bca4177cf12a0b18456e2da0413 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -1,5 +1,5 @@
 /**
- * @file kernel/time.c
+ * @file kernel/ktime.c
  * @author Armin Luntzer (armin.luntzer@univie.ac.at)
  *
  *
@@ -13,28 +13,43 @@
 #include <kernel/time.h>
 #include <kernel/export.h>
 
+#define MSG "KTIME: "
+
 static struct timekeeper tk;
 
 
+/**
+ * @brief returns the readout overhead of the uptime/ktime clock
+ *	  in nanoseconds
+ *
+ * @note this is a self-calibrated value
+ */
+
+uint32_t ktime_get_readout_overhead(void)
+{
+	return tk.readout_ns;
+}
+EXPORT_SYMBOL(ktime_get_readout_overhead);
 
 
 /**
  * @brief get the time elapsed since boot
  *
- * @param[out] ts a struct timespec
+ * @return struct timespec
  *
- * @note if no uptime clock was configured, the result
- *	 will be undefined
+ * @note if no uptime clock was configured, the result will be 0
  */
 
-void time_get_uptime(struct timespec *ts)
+struct timespec get_uptime(void)
 {
 	uint32_t sec;
 	uint32_t nsec;
 
+	struct timespec ts = {0};
+
 
 	if (!tk.clock)
-		return;
+		return ts;
 
 
 	tk.clock->read(&sec, &nsec);
@@ -44,16 +59,86 @@ void time_get_uptime(struct timespec *ts)
 	 * (see also kernel/time.h)
 	 */
 
-	ts->tv_sec  = (typeof(ts->tv_sec)) sec;
-	ts->tv_nsec = (typeof(ts->tv_sec)) nsec;
+	ts.tv_sec  = (typeof(ts.tv_sec)) sec;
+	ts.tv_nsec = (typeof(ts.tv_sec)) nsec;
+
+	return ts;
+}
+EXPORT_SYMBOL(get_uptime);
+
 
+/**
+ * @brief get the current kernel time
+ * @note for now, this is just an alias of get_uptime
+ */
+
+struct timespec get_ktime(void) __attribute__((alias("get_uptime")));
+EXPORT_SYMBOL(get_ktime);
 
 
+
+/**
+ * @brief returns the number of seconds elapsed between time1 and time0
+ *
+ * @param ts1 a struct timespec
+ * @param ts2 a struct timespec
+ *
+ * @returns the time delta in seconds, represented as double
+ */
+
+double difftime(const struct timespec time1, const struct timespec time0)
+{
+	double t0, t1;
+
+	t0 = (double) time0.tv_sec + (double) time0.tv_nsec * 1e-9;
+	t1 = (double) time1.tv_sec + (double) time1.tv_nsec * 1e-9;
+
+	return t1 - t0;
 }
-EXPORT_SYMBOL(time_get_uptime);
+EXPORT_SYMBOL(difftime);
+
+
+/**
+ * @brief returns the number of nanoseconds elapsed between time1 and time0
+ *
+ * @param ts1 a struct timespec
+ * @param ts2 a struct timespec
+ *
+ * @returns the time delta in nanoseconds, represented as double
+ */
+
+double difftime_ns(const struct timespec time1, const struct timespec time0)
+{
+	return difftime(time1, time0) * 1e9;
+}
+EXPORT_SYMBOL(difftime_ns);
+
 
 
 
+static void time_init_overhead_calibrate(void)
+{
+#define CALIBRATE_LOOPS 100
+	int i;
+
+	double delta = 0.0;
+
+	struct timespec t0;
+
+
+	for (i = 0; i < CALIBRATE_LOOPS; i++) {
+		t0 = get_ktime();
+		delta += difftime_ns(get_ktime(), t0);
+	}
+
+	/* overhead is readout delta / 2 */
+	tk.readout_ns = (typeof(tk.readout_ns)) (0.5 * delta / (double) i);
+
+	printk(MSG "calibrated main uptime clock readout overhead to %d ns\n",
+	           tk.readout_ns);
+}
+
+
 /**
  * @brief initialise the timing system
  */
@@ -61,4 +146,5 @@ EXPORT_SYMBOL(time_get_uptime);
 void time_init(struct clocksource *clock)
 {
 	tk.clock = clock;
+	time_init_overhead_calibrate();
 }