ARM timekeeping functionality allows to register persistent/boot clock 
dynamically.
This code is arch-independent and can be useful on other plaforms as well.

As a byproduct of this change, tegra20_timer becomes ARM64 compatible.

Tested: backported the change to chromeos-3.14 kernel ran on tegra 64bit
board, made sure high-resolution clock works.

Signed-off-by: Anatol Pomozov <anatol.pomo...@gmail.com>
---
 arch/arm/include/asm/mach/time.h    |  5 -----
 arch/arm/kernel/time.c              | 36 -------------------------------
 arch/arm/plat-omap/counter_32k.c    |  9 +++++---
 drivers/clocksource/tegra20_timer.c | 10 +++++----
 include/linux/timekeeping.h         | 11 ++++++++++
 kernel/time/timekeeping.c           | 43 +++++++++++++++++++++++++++++++++----
 6 files changed, 62 insertions(+), 52 deletions(-)

diff --git a/arch/arm/include/asm/mach/time.h b/arch/arm/include/asm/mach/time.h
index 90c12e1..3cbcafc 100644
--- a/arch/arm/include/asm/mach/time.h
+++ b/arch/arm/include/asm/mach/time.h
@@ -12,9 +12,4 @@
 
 extern void timer_tick(void);
 
-struct timespec;
-typedef void (*clock_access_fn)(struct timespec *);
-extern int register_persistent_clock(clock_access_fn read_boot,
-                                    clock_access_fn read_persistent);
-
 #endif
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c
index 0cc7e58..0aa1dcd 100644
--- a/arch/arm/kernel/time.c
+++ b/arch/arm/kernel/time.c
@@ -76,42 +76,6 @@ void timer_tick(void)
 }
 #endif
 
-static void dummy_clock_access(struct timespec *ts)
-{
-       ts->tv_sec = 0;
-       ts->tv_nsec = 0;
-}
-
-static clock_access_fn __read_persistent_clock = dummy_clock_access;
-static clock_access_fn __read_boot_clock = dummy_clock_access;;
-
-void read_persistent_clock(struct timespec *ts)
-{
-       __read_persistent_clock(ts);
-}
-
-void read_boot_clock(struct timespec *ts)
-{
-       __read_boot_clock(ts);
-}
-
-int __init register_persistent_clock(clock_access_fn read_boot,
-                                    clock_access_fn read_persistent)
-{
-       /* Only allow the clockaccess functions to be registered once */
-       if (__read_persistent_clock == dummy_clock_access &&
-           __read_boot_clock == dummy_clock_access) {
-               if (read_boot)
-                       __read_boot_clock = read_boot;
-               if (read_persistent)
-                       __read_persistent_clock = read_persistent;
-
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
 void __init time_init(void)
 {
        if (machine_desc->init_time) {
diff --git a/arch/arm/plat-omap/counter_32k.c b/arch/arm/plat-omap/counter_32k.c
index 61b4d70..0dbfffd 100644
--- a/arch/arm/plat-omap/counter_32k.c
+++ b/arch/arm/plat-omap/counter_32k.c
@@ -18,10 +18,9 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/clocksource.h>
+#include <linux/time.h>
 #include <linux/sched_clock.h>
 
-#include <asm/mach/time.h>
-
 #include <plat/counter-32k.h>
 
 /* OMAP2_32KSYNCNT_CR_OFF: offset of 32ksync counter register */
@@ -76,6 +75,10 @@ static void omap_read_persistent_clock(struct timespec *ts)
        spin_unlock_irqrestore(&read_persistent_clock_lock, flags);
 }
 
+static const struct persistent_clock_ops omap_persistent_clock = {
+       .read = omap_read_persistent_clock
+};
+
 /**
  * omap_init_clocksource_32k - setup and register counter 32k as a
  * kernel clocksource
@@ -116,7 +119,7 @@ int __init omap_init_clocksource_32k(void __iomem *vbase)
        }
 
        sched_clock_register(omap_32k_read_sched_clock, 32, 32768);
-       register_persistent_clock(NULL, omap_read_persistent_clock);
+       register_persistent_clock(&omap_persistent_clock);
        pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
 
        return 0;
diff --git a/drivers/clocksource/tegra20_timer.c 
b/drivers/clocksource/tegra20_timer.c
index d2616ef..210130e 100644
--- a/drivers/clocksource/tegra20_timer.c
+++ b/drivers/clocksource/tegra20_timer.c
@@ -28,9 +28,7 @@
 #include <linux/of_irq.h>
 #include <linux/sched_clock.h>
 #include <linux/delay.h>
-
-#include <asm/mach/time.h>
-#include <asm/smp_twd.h>
+#include <linux/timekeeping.h>
 
 #define RTC_SECONDS            0x08
 #define RTC_SHADOW_SECONDS     0x0c
@@ -142,6 +140,10 @@ static void tegra_read_persistent_clock(struct timespec 
*ts)
        *ts = *tsp;
 }
 
+static const struct persistent_clock_ops tegra_persistent_clock = {
+       .read = tegra_read_persistent_clock
+};
+
 static unsigned long tegra_delay_timer_read_counter_long(void)
 {
        return readl(timer_reg_base + TIMERUS_CNTR_1US);
@@ -252,7 +254,7 @@ static void __init tegra20_init_rtc(struct device_node *np)
        else
                clk_prepare_enable(clk);
 
-       register_persistent_clock(NULL, tegra_read_persistent_clock);
+       register_persistent_clock(&tegra_persistent_clock);
 }
 CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
 
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index 1caa6b0..02023f7 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -201,6 +201,17 @@ static inline bool has_persistent_clock(void)
        return persistent_clock_exist;
 }
 
+struct persistent_clock_ops {
+       void (*read)(struct timespec *ts);
+       int (*update)(const struct timespec ts);
+};
+
+struct boot_clock_ops {
+       void (*read)(struct timespec *ts);
+};
+
+extern void register_persistent_clock(const struct persistent_clock_ops 
*clock);
+extern void register_boot_clock(const struct boot_clock_ops *clock);
 extern void read_persistent_clock(struct timespec *ts);
 extern void read_boot_clock(struct timespec *ts);
 extern int update_persistent_clock(struct timespec now);
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index fb4a9c2..414c172 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -66,6 +66,9 @@ int __read_mostly timekeeping_suspended;
 /* Flag for if there is a persistent clock on this platform */
 bool __read_mostly persistent_clock_exist = false;
 
+const struct persistent_clock_ops __read_mostly *persistent_clock = NULL;
+const struct boot_clock_ops __read_mostly *boot_clock = NULL;
+
 static inline void tk_normalize_xtime(struct timekeeper *tk)
 {
        while (tk->tkr.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr.shift)) {
@@ -956,6 +959,30 @@ u64 timekeeping_max_deferment(void)
        return ret;
 }
 
+extern void register_persistent_clock(const struct persistent_clock_ops *clock)
+{
+       BUG_ON(!clock);
+       BUG_ON(!clock->read);
+
+       if (persistent_clock) {
+               pr_warn("Ignore extra persistent clock registration");
+               return;
+       }
+       persistent_clock = clock;
+}
+
+extern void register_boot_clock(const struct boot_clock_ops *clock)
+{
+       BUG_ON(!clock);
+       BUG_ON(!clock->read);
+
+       if (boot_clock) {
+               pr_warn("Ignore extra boot clock registration");
+               return;
+       }
+       boot_clock = clock;
+}
+
 /**
  * read_persistent_clock -  Return time from the persistent clock.
  *
@@ -967,8 +994,12 @@ u64 timekeeping_max_deferment(void)
  */
 void __weak read_persistent_clock(struct timespec *ts)
 {
-       ts->tv_sec = 0;
-       ts->tv_nsec = 0;
+       if (persistent_clock) {
+               persistent_clock->read(ts);
+       } else {
+               ts->tv_sec = 0;
+               ts->tv_nsec = 0;
+       }
 }
 
 /**
@@ -982,8 +1013,12 @@ void __weak read_persistent_clock(struct timespec *ts)
  */
 void __weak read_boot_clock(struct timespec *ts)
 {
-       ts->tv_sec = 0;
-       ts->tv_nsec = 0;
+       if (boot_clock) {
+               boot_clock->read(ts);
+       } else {
+               ts->tv_sec = 0;
+               ts->tv_nsec = 0;
+       }
 }
 
 /*
-- 
2.1.0.rc2.206.gedb03e5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to