From 995cf4dba0b01af66ed94fff0b485a75fdeb9c16 Mon Sep 17 00:00:00 2001
From: Alessandro Rubini <rubini@gnudd.com>
Date: Wed, 3 Dec 2014 15:47:19 +0100
Subject: [PATCH] kernel: add a clocksource module for WR time

Signed-off-by: Alessandro Rubini <rubini@gnudd.com>
---
 kernel/Makefile                        |   2 +-
 kernel/wr_clocksource/Makefile         |  25 ++++
 kernel/wr_clocksource/wr_clocksource.c | 173 +++++++++++++++++++++++++
 3 files changed, 199 insertions(+), 1 deletion(-)
 create mode 100644 kernel/wr_clocksource/Makefile
 create mode 100644 kernel/wr_clocksource/wr_clocksource.c

diff --git a/kernel/Makefile b/kernel/Makefile
index 7534f5d89..b04218a4e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -1,5 +1,5 @@
 
-DIRS = wr_vic wr_nic wr_rtu at91_softpwm wr_pstats
+DIRS = wr_vic wr_nic wr_rtu at91_softpwm wr_pstats wr_clocksource
 
 # We may "LINUX ?= /usr/src/linux-wrswitch", but it's better to leave it empty
 
diff --git a/kernel/wr_clocksource/Makefile b/kernel/wr_clocksource/Makefile
new file mode 100644
index 000000000..f96375737
--- /dev/null
+++ b/kernel/wr_clocksource/Makefile
@@ -0,0 +1,25 @@
+obj-m := wr_clocksource.o
+
+# accept WRN_DEBUG from the environment. It turns pr_debug() into printk.
+ifdef WRN_DEBUG
+	ccflags-y += -DDEBUG
+endif
+
+# What follows is standard stuff
+export ARCH ?= arm
+export CROSS_COMPILE ?= $(CROSS_COMPILE_ARM)
+
+all modules:
+	$(MAKE) CONFIG_DEBUG_SECTION_MISMATCH=y \
+		-C $(LINUX) SUBDIRS=$(shell /bin/pwd) modules
+
+# looking at preprocessed output is helpful for bug hunting
+preprocess:
+	$(MAKE) CONFIG_DEBUG_SECTION_MISMATCH=y \
+		-C $(LINUX) SUBDIRS=$(shell /bin/pwd) $(wr-nic-objs:.o=.i)
+
+# We might "$(MAKE) -C $(LINUX)" but "make clean" with no LINUX defined
+# is sometimes useful to have
+clean:
+	rm -f *.mod.c *.o *.ko *.i .*cmd Module.symvers modules.order *~
+	rm -rf .tmp_versions
diff --git a/kernel/wr_clocksource/wr_clocksource.c b/kernel/wr_clocksource/wr_clocksource.c
new file mode 100644
index 000000000..9a2a1508c
--- /dev/null
+++ b/kernel/wr_clocksource/wr_clocksource.c
@@ -0,0 +1,173 @@
+/* Alessandro Rubini for CERN 2014, GPLv2 or later */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/vmalloc.h>
+#include <linux/clocksource.h>
+
+/* We need these two defined in order to include nic-hardware.h */
+#define WR_IS_NODE 0
+#define WR_IS_SWITCH 1
+
+/* We have no centralized defines yet: pick frequency and registers base */
+#include "../wr_nic/nic-hardware.h"
+#define WRCS_FREQUENCY		REFCLK_FREQ
+#define WRCS_TICK_NS		(NSEC_PER_SEC / WRCS_FREQUENCY)
+
+static int wrcs_stats;
+module_param(wrcs_stats, int, 0644);
+MODULE_PARM_DESC(wrcs_stats, "Count how often the clocksource is being read");
+
+static __iomem struct PPSG_WB *wrcs_ppsg;
+
+static int wrcs_is_registered; /* no need for atomic_t or whatever */
+
+/* If so requested, print statistics once per second */
+static inline void wrcs_do_stats(void)
+{
+	static unsigned long nextp;
+	static int ncalls;
+
+	if (!wrcs_stats)
+		return;
+
+	if (!nextp)
+		nextp = jiffies + HZ;
+
+	/* This, when enabled, shows around 400 calls per second */
+	if (time_after_eq(jiffies, nextp)) {
+		pr_info("%s: called %i times\n", __func__,
+			ncalls);
+		ncalls = 0;
+		nextp += HZ;
+	}
+	ncalls++;
+}
+
+static cycle_t wrcs_read(struct clocksource *cs)
+{
+	static uint32_t offset, last, this;
+
+	wrcs_do_stats();
+
+	/* FIXME: identify a time jump by monitoring the tick counter */
+
+	/*
+	 * Turn the counter into a 32-bit one (see cs->mask below).
+	 * We reset at 0x3b9aca0, so without this we should use mask = 0x1f
+	 * and mac_idle = 32 ticks = 512ns. Unaffordable.
+	 */
+	this = readl(&wrcs_ppsg->CNTR_NSEC);
+	if (this < last)
+		offset += WRCS_FREQUENCY;
+	last = this;
+	return offset + this;
+}
+
+
+static struct clocksource wrcs_cs = {
+	.name = "white-rabbit",
+	.rating = 450,		/* perfect... */
+	.read = wrcs_read,
+	/* no enable/disable */
+	.mask = 0xffffffff, /* We fake a 32-bit thing */
+	.max_idle_ns = 900 * 1000 * 1000, /* well, 1s... */
+	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+/*
+ * The timer is used to check when does WR synchronize.  When that
+ * happens, we set time of day and register our clocksource. Time
+ * jumps after synchronization are not well supported.
+ */
+static void wrcs_timer_fn(unsigned long unused);
+static DEFINE_TIMER(wrcs_timer, wrcs_timer_fn, 0, 0);
+
+static void wrcs_timer_fn(unsigned long unused)
+{
+	uint32_t ticks, tai_l, tai_h;
+	int64_t tai;
+
+	/* Read ppsg, all fields consistently se we can use the value */
+	do {
+		tai_l = readl(&wrcs_ppsg->CNTR_UTCLO);
+		tai_h = readl(&wrcs_ppsg->CNTR_UTCHI);
+		ticks = readl(&wrcs_ppsg->CNTR_NSEC);
+	} while (readl(&wrcs_ppsg->CNTR_UTCLO) != tai_l);
+	tai = (typeof(tai))tai_h << 32 | tai_l;
+
+	/* If we are before 2010 (date +%s --date=2010-01-01), try again */
+	if (tai < 1262300400LL) {
+		mod_timer(&wrcs_timer, jiffies + HZ);
+		return;
+	}
+
+	clocksource_register(&wrcs_cs);
+	wrcs_is_registered = 1;
+	/* And don't restart the timer */
+}
+
+static int wrcs_init(void)
+{
+	wrcs_ppsg = ioremap(FPGA_BASE_PPSG, FPGA_SIZE_PPSG);
+	if (!wrcs_ppsg) {
+		pr_err("WR Clocksource: can't remap PPS registers\n");
+		return -EIO;
+	}
+
+	clocksource_calc_mult_shift(&wrcs_cs, WRCS_FREQUENCY, 1);
+
+	/* Fire the timer */
+	mod_timer(&wrcs_timer, jiffies + HZ);
+	return 0;
+}
+
+static void wrcs_exit(void)
+{
+	del_timer_sync(&wrcs_timer);
+	if (wrcs_is_registered)
+		clocksource_unregister(&wrcs_cs);
+	iounmap(wrcs_ppsg);
+}
+
+
+module_init(wrcs_init);
+module_exit(wrcs_exit);
+
+MODULE_LICENSE("GPL");
+
+
+/* Hack: this is not exported by current kernel. Define a local copy */
+void
+clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec)
+{
+	u64 tmp;
+	u32 sft, sftacc= 32;
+
+	/*
+	 * Calculate the shift factor which is limiting the conversion
+	 * range:
+	 */
+	tmp = ((u64)maxsec * from) >> 32;
+	while (tmp) {
+		tmp >>=1;
+		sftacc--;
+	}
+
+	/*
+	 * Find the conversion shift/mult pair which has the best
+	 * accuracy and fits the maxsec conversion range:
+	 */
+	for (sft = 32; sft > 0; sft--) {
+		tmp = (u64) to << sft;
+		tmp += from / 2;
+		do_div(tmp, from);
+		if ((tmp >> sftacc) == 0)
+			break;
+	}
+	*mult = tmp;
+	*shift = sft;
+}
+
-- 
GitLab