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.pomozov@xxxxxxxxx> --- 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-tegra" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html