#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/semaphore.h>

MODULE_AUTHOR("Michał Wróbel <michal.wrobel@flytronic.pl>");
MODULE_DESCRIPTION("Test 'ixp4xx: clockevent set_next_event' bug.");
MODULE_LICENSE("GPL");

static struct semaphore sem;
static struct hrtimer t;
static ktime_t should_expire, expired;
static int samples = 10000, delay = 1000000;

module_param(samples, int, 0644);
MODULE_PARM_DESC(samples, "number of measurements");

module_param(delay, int, 0644);
MODULE_PARM_DESC(delay, "delay of single measurement");

static enum hrtimer_restart timeout(struct hrtimer *t)
{
	expired = ktime_get();
	up(&sem);
	return HRTIMER_NORESTART;
}

static int __init m_init(void)
{
	int i, ret;
	s64 delta, delta_min, delta_max, delta_sum = 0;

	init_MUTEX_LOCKED(&sem);
	hrtimer_init(&t, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
	t.function = timeout;

	printk("starting measurements: %d samples * %d ns\n", samples, delay);

	for (i=0; i<samples; i++) {
		should_expire = ktime_add_ns(ktime_get(), delay);
		hrtimer_start(&t, should_expire, HRTIMER_MODE_ABS);
		ret = down_interruptible(&sem);
		if (ret) {
			hrtimer_cancel(&t);
			return ret;
		}
		delta = ktime_to_ns(ktime_sub(expired, should_expire));
		if (delta < delta_min || i == 0)
			delta_min = delta;
		if (delta > delta_max || i == 0)
			delta_max = delta;
		delta_sum += delta;
	}

	do_div(delta_sum, samples);
	printk("measurements finished\n");
	printk("min: %lld ns\n", delta_min);
	printk("avg: %lld ns\n", delta_sum);
	printk("max: %lld ns\n", delta_max);

	return 0;
}

static void __exit m_exit(void)
{
	hrtimer_cancel(&t);
}

module_init(m_init);
module_exit(m_exit);




