diff --git a/include/odp.h b/include/odp.h
index 6a52346..9bb68a2 100644
--- a/include/odp.h
+++ b/include/odp.h
@@ -292,6 +292,7 @@ extern "C" {
 #include <odp_queue.h>
 #include <odp_ticketlock.h>
 #include <odp_time.h>
+#include <odp_timer.h>
 #include <odp_schedule.h>
 #include <odp_sync.h>
 #include <odp_packet.h>
diff --git a/include/odp_timer.h b/include/odp_timer.h
index ff54b8e..f051f71 100644
--- a/include/odp_timer.h
+++ b/include/odp_timer.h
@@ -35,7 +35,7 @@ typedef uint32_t odp_timer_t;
 /**
 * ODP timeout handle
 */
-typedef uint32_t odp_timer_tmo_t;
+typedef uint64_t odp_timer_tmo_t;
 
 #define ODP_TIMER_TMO_INVALID 0
 
diff --git a/platform/linux-generic/source/odp_timer.c b/platform/linux-generic/source/odp_timer.c
index b090257..fceb086 100644
--- a/platform/linux-generic/source/odp_timer.c
+++ b/platform/linux-generic/source/odp_timer.c
@@ -6,9 +6,300 @@
 
 #include <odp_timer.h>
 #include <odp_internal.h>
+#include <odp_atomic.h>
+#include <odp_spinlock.h>
+#include <odp_sync.h>
+#include <odp_debug.h>
+
+#include <signal.h>
+#include <time.h>
+
+#include <string.h>
+
+
+#define NUM_TIMERS    1
+#define MAX_TICKS     1024
+#define RESOLUTION_NS 1000000
+
+struct timeout_t;
+
+typedef struct timeout_t {
+	struct timeout_t *next;
+	int               timer_id;
+	int               tick;
+	uint64_t          tmo_tick;
+	odp_queue_t       queue;
+	odp_buffer_t      buf;
+	odp_buffer_t      tmo_buf;
+} timeout_t;
+
+typedef struct {
+	odp_spinlock_t lock;
+	timeout_t      *list;
+} tick_t;
+
+typedef struct {
+	volatile int      active;
+	volatile uint64_t cur_tick;
+	timer_t           timerid;
+	odp_buffer_pool_t pool;
+	uint64_t          resolution_ns;
+	uint64_t          max_ticks;
+	tick_t            tick[MAX_TICKS];
+
+} timer_ring_t;
+
+
+typedef struct {
+	timer_ring_t     timer[NUM_TIMERS];
+	odp_atomic_int_t num_timers;
+} timer_global_t;
+
+
+
+/* Global */
+timer_global_t odp_timer;
+
+
+static void add_tmo(tick_t *tick, timeout_t *tmo)
+{
+	odp_spinlock_lock(&tick->lock);
+
+	tmo->next  = tick->list;
+	tick->list = tmo;
+
+	odp_spinlock_unlock(&tick->lock);
+}
+
+
+static timeout_t *rem_tmo(tick_t *tick)
+{
+	timeout_t *tmo;
+
+	odp_spinlock_lock(&tick->lock);
+
+	tmo = tick->list;
+
+	if (tmo)
+		tick->list = tmo->next;
+
+	odp_spinlock_unlock(&tick->lock);
+
+	if (tmo)
+		tmo->next = NULL;
+
+	return tmo;
+}
+
+
+
+static void notify_function(union sigval sigval)
+{
+	(void) sigval;
+	uint64_t cur_tick;
+	timeout_t *tmo;
+	tick_t *tick;
+
+	if (odp_timer.timer[0].active == 0)
+		return;
+
+	/* ODP_DBG("Tick\n"); */
+
+	cur_tick = odp_timer.timer[0].cur_tick++;
+
+	tick = &odp_timer.timer[0].tick[cur_tick % MAX_TICKS];
+
+	while ((tmo = rem_tmo(tick)) != NULL) {
+		odp_queue_t  queue;
+		odp_buffer_t buf;
+
+		queue = tmo->queue;
+		buf   = tmo->buf;
+
+		if (buf != tmo->tmo_buf)
+			odp_buffer_free(tmo->tmo_buf);
+
+		odp_queue_enq(queue, buf);
+	}
+}
+
+
+static void timer_init(void)
+{
+	struct sigevent   sigev;
+	struct itimerspec ispec;
+
+	ODP_DBG("Timer thread starts\n");
+
+	memset(&sigev, 0, sizeof(sigev));
+	memset(&ispec, 0, sizeof(ispec));
+
+	sigev.sigev_notify          = SIGEV_THREAD;
+	sigev.sigev_notify_function = notify_function;
+
+	if (timer_create(CLOCK_MONOTONIC, &sigev,
+			 &odp_timer.timer[0].timerid)) {
+		ODP_DBG("Timer create failed\n");
+		return;
+	}
+
+	ispec.it_interval.tv_sec  = 0;
+	ispec.it_interval.tv_nsec = RESOLUTION_NS;
+	ispec.it_value.tv_sec     = 0;
+	ispec.it_value.tv_nsec    = RESOLUTION_NS;
+
+	if (timer_settime(odp_timer.timer[0].timerid, 0, &ispec, NULL)) {
+		ODP_DBG("Timer set failed\n");
+		return;
+	}
+
+	return;
+}
 
 
 int odp_timer_init_global(void)
 {
+	int i;
+
+	memset(&odp_timer, 0, sizeof(timer_global_t));
+
+	for (i = 0; i < MAX_TICKS; i++)
+		odp_spinlock_init(&odp_timer.timer[0].tick[i].lock);
+
+	timer_init();
+
+
 	return 0;
 }
+
+
+
+odp_timer_t odp_timer_create(const char *name, odp_buffer_pool_t pool,
+			     uint64_t resolution, uint64_t min_tmo,
+			     uint64_t max_tmo)
+{
+	int id;
+	(void) name; (void) resolution; (void) min_tmo; (void) max_tmo;
+
+	if (odp_timer.num_timers >= NUM_TIMERS)
+		return ODP_TIMER_INVALID;
+
+	id = odp_atomic_fetch_inc_int(&odp_timer.num_timers);
+
+	if (id >= NUM_TIMERS)
+		return ODP_TIMER_INVALID;
+
+	odp_timer.timer[id].pool          = pool;
+	odp_timer.timer[id].resolution_ns = RESOLUTION_NS;
+	odp_timer.timer[id].max_ticks     = MAX_TICKS;
+
+	odp_sync_stores();
+
+	odp_timer.timer[id].active = 1;
+
+	return id + 1;
+}
+
+
+odp_timer_tmo_t odp_timer_absolute_tmo(odp_timer_t timer, uint64_t tmo_tick,
+				       odp_queue_t queue, odp_buffer_t buf)
+{
+	int id;
+	uint64_t tick;
+	uint64_t cur_tick;
+	timeout_t *new_tmo;
+	odp_buffer_t tmo_buf;
+
+	id = timer - 1;
+
+	cur_tick = odp_timer.timer[id].cur_tick;
+
+	if (tmo_tick <= cur_tick) {
+		ODP_DBG("timeout too close\n");
+		return ODP_TIMER_TMO_INVALID;
+	}
+
+	tick = tmo_tick - cur_tick;
+
+	if (tick > MAX_TICKS) {
+		ODP_DBG("timeout too far\n");
+		return ODP_TIMER_TMO_INVALID;
+	}
+
+	tick = (cur_tick + tick) % MAX_TICKS;
+
+	tmo_buf = odp_buffer_alloc(odp_timer.timer[id].pool);
+
+	if (tmo_buf == ODP_BUFFER_INVALID) {
+		ODP_DBG("alloc failed\n");
+		return ODP_TIMER_TMO_INVALID;
+	}
+
+	new_tmo = (timeout_t *)odp_buffer_addr(tmo_buf);
+
+	new_tmo->timer_id = id;
+	new_tmo->tick     = (int)tick;
+	new_tmo->tmo_tick = tmo_tick;
+	new_tmo->queue    = queue;
+	new_tmo->tmo_buf  = tmo_buf;
+
+	if (buf != ODP_BUFFER_INVALID)
+		new_tmo->buf = buf;
+	else
+		new_tmo->buf = tmo_buf;
+
+	add_tmo(&odp_timer.timer[id].tick[tick], new_tmo);
+
+	return (odp_timer_tmo_t) new_tmo;
+}
+
+
+
+uint64_t odp_timer_tick_to_ns(odp_timer_t timer, uint64_t ticks)
+{
+	int id;
+
+	id = timer - 1;
+
+	return ticks * odp_timer.timer[id].resolution_ns;
+}
+
+
+uint64_t odp_timer_ns_to_tick(odp_timer_t timer, uint64_t ns)
+{
+	int id;
+
+	id = timer - 1;
+
+	return ns / odp_timer.timer[id].resolution_ns;
+}
+
+
+uint64_t odp_timer_resolution(odp_timer_t timer)
+{
+	int id;
+
+	id = timer - 1;
+
+	return odp_timer.timer[id].resolution_ns;
+}
+
+
+uint64_t odp_timer_maximum_tmo(odp_timer_t timer)
+{
+	int id;
+
+	id = timer - 1;
+
+	return odp_timer.timer[id].max_ticks;
+}
+
+
+uint64_t odp_timer_current_tick(odp_timer_t timer)
+{
+	int id;
+
+	id = timer - 1;
+
+	return odp_timer.timer[id].cur_tick;
+}
diff --git a/test/example/odp_example.c b/test/example/odp_example.c
index 0f421a3..4764657 100644
--- a/test/example/odp_example.c
+++ b/test/example/odp_example.c
@@ -53,6 +53,11 @@ typedef struct {
 
 static odp_barrier_t test_barrier;
 
+/* #define TEST_TIMEOUTS */
+#ifdef TEST_TIMEOUTS
+static odp_timer_t test_timer;
+#endif
+
 /*
  * Clear all scheduled queues. Retry to be sure that all
  * buffers have been scheduled.
@@ -71,6 +76,48 @@ static void clear_sched_queues(void)
 	}
 }
 
+#ifdef TEST_TIMEOUTS
+static void test_timeouts(int thr)
+{
+	uint64_t tick;
+	odp_queue_t queue;
+	odp_buffer_t buf;
+	int num = 10;
+
+	ODP_DBG("  [%i] test_timeouts\n", thr);
+
+	queue = odp_queue_lookup("timer_queue");
+
+	tick = odp_timer_current_tick(test_timer);
+
+	ODP_DBG("  [%i] current tick %"PRIu64"\n", thr, tick);
+
+	tick += 100;
+
+	odp_timer_absolute_tmo(test_timer, tick,
+			       queue, ODP_BUFFER_INVALID);
+
+
+	while (1) {
+		while ((buf = odp_queue_deq(queue) == ODP_BUFFER_INVALID))
+			;
+
+		/* ODP_DBG("  [%i] timeout\n", thr); */
+
+		odp_buffer_free(buf);
+
+		num--;
+
+		if (num == 0)
+			break;
+
+		tick = odp_timer_current_tick(test_timer) + 100;
+
+		odp_timer_absolute_tmo(test_timer, tick,
+				       queue, ODP_BUFFER_INVALID);
+	}
+}
+#endif
 
 /*
  * Test single buffer alloc and free
@@ -522,6 +569,12 @@ static void *run_thread(void *arg)
 				     ODP_SCHED_PRIO_LOWEST))
 		return NULL;
 
+#ifdef TEST_TIMEOUTS
+	odp_barrier_sync(&test_barrier);
+
+	test_timeouts(thr);
+#endif
+
 	printf("Thread %i exits\n", thr);
 	fflush(NULL);
 	return arg;
@@ -724,6 +777,21 @@ int main(int argc, char *argv[])
 	}
 
 
+#ifdef TEST_TIMEOUTS
+	/*
+	 * Create a queue for timer test
+	 */
+	queue = odp_queue_create("timer_queue", ODP_QUEUE_TYPE_SCHED, NULL);
+
+	if (queue == ODP_QUEUE_INVALID) {
+		ODP_ERR("Timer queue create failed.\n");
+		return -1;
+	}
+
+	test_timer = odp_timer_create("test_timer", pool,
+				      1000000, 1000000, 1000000000000);
+#endif
+
 	/*
 	 * Create queues for schedule test. QUEUES_PER_PRIO per priority.
 	 */
