sched_clock: Make ARM's sched_clock generic for all architectures
authorStephen Boyd <sboyd@codeaurora.org>
Sun, 2 Jun 2013 06:39:40 +0000 (23:39 -0700)
committerJohn Stultz <john.stultz@linaro.org>
Wed, 12 Jun 2013 21:02:13 +0000 (14:02 -0700)
Nothing about the sched_clock implementation in the ARM port is
specific to the architecture. Generalize the code so that other
architectures can use it by selecting GENERIC_SCHED_CLOCK.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
[jstultz: Merge minor collisions with other patches in my tree]
Signed-off-by: John Stultz <john.stultz@linaro.org>
38 files changed:
arch/arm/Kconfig
arch/arm/common/timer-sp.c
arch/arm/include/asm/sched_clock.h [deleted file]
arch/arm/kernel/Makefile
arch/arm/kernel/arch_timer.c
arch/arm/kernel/sched_clock.c [deleted file]
arch/arm/kernel/time.c
arch/arm/mach-davinci/time.c
arch/arm/mach-imx/time.c
arch/arm/mach-integrator/integrator_ap.c
arch/arm/mach-ixp4xx/common.c
arch/arm/mach-mmp/time.c
arch/arm/mach-msm/timer.c
arch/arm/mach-omap1/time.c
arch/arm/mach-omap2/timer.c
arch/arm/mach-pxa/time.c
arch/arm/mach-sa1100/time.c
arch/arm/mach-u300/timer.c
arch/arm/plat-iop/time.c
arch/arm/plat-omap/counter_32k.c
arch/arm/plat-orion/time.c
arch/arm/plat-samsung/samsung-time.c
arch/arm/plat-versatile/sched-clock.c
drivers/clocksource/bcm2835_timer.c
drivers/clocksource/clksrc-dbx500-prcmu.c
drivers/clocksource/dw_apb_timer_of.c
drivers/clocksource/mxs_timer.c
drivers/clocksource/nomadik-mtu.c
drivers/clocksource/samsung_pwm_timer.c
drivers/clocksource/tegra20_timer.c
drivers/clocksource/time-armada-370-xp.c
drivers/clocksource/timer-marco.c
drivers/clocksource/timer-prima2.c
include/linux/sched_clock.h [new file with mode: 0644]
init/Kconfig
init/main.c
kernel/time/Makefile
kernel/time/sched_clock.c [new file with mode: 0644]

index 49d993c..53d3a35 100644 (file)
@@ -14,6 +14,7 @@ config ARM
        select GENERIC_IRQ_PROBE
        select GENERIC_IRQ_SHOW
        select GENERIC_PCI_IOMAP
+       select GENERIC_SCHED_CLOCK
        select GENERIC_SMP_IDLE_THREAD
        select GENERIC_IDLE_POLL_SETUP
        select GENERIC_STRNCPY_FROM_USER
index ddc7407..023ee63 100644 (file)
@@ -28,8 +28,8 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/sched_clock.h>
 
-#include <asm/sched_clock.h>
 #include <asm/hardware/arm_timer.h>
 #include <asm/hardware/timer-sp.h>
 
diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h
deleted file mode 100644 (file)
index 3d520dd..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * sched_clock.h: support for extending counters to full 64-bit ns counter
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#ifndef ASM_SCHED_CLOCK
-#define ASM_SCHED_CLOCK
-
-extern void sched_clock_postinit(void);
-extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate);
-
-extern unsigned long long (*sched_clock_func)(void);
-
-#endif
index 5f3338e..97cb057 100644 (file)
@@ -16,7 +16,7 @@ CFLAGS_REMOVE_return_address.o = -pg
 # Object file lists.
 
 obj-y          := elf.o entry-armv.o entry-common.o irq.o opcodes.o \
-                  process.o ptrace.o return_address.o sched_clock.o \
+                  process.o ptrace.o return_address.o \
                   setup.o signal.o stacktrace.o sys_arm.o time.o traps.o
 
 obj-$(CONFIG_ATAGS)            += atags_parse.o
index 59dcdce..221f07b 100644 (file)
@@ -11,9 +11,9 @@
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/sched_clock.h>
 
 #include <asm/delay.h>
-#include <asm/sched_clock.h>
 
 #include <clocksource/arm_arch_timer.h>
 
diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c
deleted file mode 100644 (file)
index a781c59..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * sched_clock.c: support for extending counters to full 64-bit ns counter
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/clocksource.h>
-#include <linux/init.h>
-#include <linux/jiffies.h>
-#include <linux/kernel.h>
-#include <linux/moduleparam.h>
-#include <linux/sched.h>
-#include <linux/syscore_ops.h>
-#include <linux/timer.h>
-
-#include <asm/sched_clock.h>
-
-struct clock_data {
-       u64 epoch_ns;
-       u32 epoch_cyc;
-       u32 epoch_cyc_copy;
-       unsigned long rate;
-       u32 mult;
-       u32 shift;
-       bool suspended;
-};
-
-static void sched_clock_poll(unsigned long wrap_ticks);
-static DEFINE_TIMER(sched_clock_timer, sched_clock_poll, 0, 0);
-static int irqtime = -1;
-
-core_param(irqtime, irqtime, int, 0400);
-
-static struct clock_data cd = {
-       .mult   = NSEC_PER_SEC / HZ,
-};
-
-static u32 __read_mostly sched_clock_mask = 0xffffffff;
-
-static u32 notrace jiffy_sched_clock_read(void)
-{
-       return (u32)(jiffies - INITIAL_JIFFIES);
-}
-
-static u32 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read;
-
-static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift)
-{
-       return (cyc * mult) >> shift;
-}
-
-static unsigned long long notrace cyc_to_sched_clock(u32 cyc, u32 mask)
-{
-       u64 epoch_ns;
-       u32 epoch_cyc;
-
-       /*
-        * Load the epoch_cyc and epoch_ns atomically.  We do this by
-        * ensuring that we always write epoch_cyc, epoch_ns and
-        * epoch_cyc_copy in strict order, and read them in strict order.
-        * If epoch_cyc and epoch_cyc_copy are not equal, then we're in
-        * the middle of an update, and we should repeat the load.
-        */
-       do {
-               epoch_cyc = cd.epoch_cyc;
-               smp_rmb();
-               epoch_ns = cd.epoch_ns;
-               smp_rmb();
-       } while (epoch_cyc != cd.epoch_cyc_copy);
-
-       return epoch_ns + cyc_to_ns((cyc - epoch_cyc) & mask, cd.mult, cd.shift);
-}
-
-/*
- * Atomically update the sched_clock epoch.
- */
-static void notrace update_sched_clock(void)
-{
-       unsigned long flags;
-       u32 cyc;
-       u64 ns;
-
-       cyc = read_sched_clock();
-       ns = cd.epoch_ns +
-               cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
-                         cd.mult, cd.shift);
-       /*
-        * Write epoch_cyc and epoch_ns in a way that the update is
-        * detectable in cyc_to_fixed_sched_clock().
-        */
-       raw_local_irq_save(flags);
-       cd.epoch_cyc_copy = cyc;
-       smp_wmb();
-       cd.epoch_ns = ns;
-       smp_wmb();
-       cd.epoch_cyc = cyc;
-       raw_local_irq_restore(flags);
-}
-
-static void sched_clock_poll(unsigned long wrap_ticks)
-{
-       mod_timer(&sched_clock_timer, round_jiffies(jiffies + wrap_ticks));
-       update_sched_clock();
-}
-
-void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
-{
-       unsigned long r, w;
-       u64 res, wrap;
-       char r_unit;
-
-       if (cd.rate > rate)
-               return;
-
-       BUG_ON(bits > 32);
-       WARN_ON(!irqs_disabled());
-       read_sched_clock = read;
-       sched_clock_mask = (1 << bits) - 1;
-       cd.rate = rate;
-
-       /* calculate the mult/shift to convert counter ticks to ns. */
-       clocks_calc_mult_shift(&cd.mult, &cd.shift, rate, NSEC_PER_SEC, 0);
-
-       r = rate;
-       if (r >= 4000000) {
-               r /= 1000000;
-               r_unit = 'M';
-       } else if (r >= 1000) {
-               r /= 1000;
-               r_unit = 'k';
-       } else
-               r_unit = ' ';
-
-       /* calculate how many ns until we wrap */
-       wrap = cyc_to_ns((1ULL << bits) - 1, cd.mult, cd.shift);
-       do_div(wrap, NSEC_PER_MSEC);
-       w = wrap;
-
-       /* calculate the ns resolution of this counter */
-       res = cyc_to_ns(1ULL, cd.mult, cd.shift);
-       pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lums\n",
-               bits, r, r_unit, res, w);
-
-       /*
-        * Start the timer to keep sched_clock() properly updated and
-        * sets the initial epoch.
-        */
-       sched_clock_timer.data = msecs_to_jiffies(w - (w / 10));
-       update_sched_clock();
-
-       /*
-        * Ensure that sched_clock() starts off at 0ns
-        */
-       cd.epoch_ns = 0;
-
-       /* Enable IRQ time accounting if we have a fast enough sched_clock */
-       if (irqtime > 0 || (irqtime == -1 && rate >= 1000000))
-               enable_sched_clock_irqtime();
-
-       pr_debug("Registered %pF as sched_clock source\n", read);
-}
-
-static unsigned long long notrace sched_clock_32(void)
-{
-       u32 cyc = read_sched_clock();
-       return cyc_to_sched_clock(cyc, sched_clock_mask);
-}
-
-unsigned long long __read_mostly (*sched_clock_func)(void) = sched_clock_32;
-
-unsigned long long notrace sched_clock(void)
-{
-       if (cd.suspended)
-               return cd.epoch_ns;
-
-       return sched_clock_func();
-}
-
-void __init sched_clock_postinit(void)
-{
-       /*
-        * If no sched_clock function has been provided at that point,
-        * make it the final one one.
-        */
-       if (read_sched_clock == jiffy_sched_clock_read)
-               setup_sched_clock(jiffy_sched_clock_read, 32, HZ);
-
-       sched_clock_poll(sched_clock_timer.data);
-}
-
-static int sched_clock_suspend(void)
-{
-       sched_clock_poll(sched_clock_timer.data);
-       cd.suspended = true;
-       return 0;
-}
-
-static void sched_clock_resume(void)
-{
-       cd.epoch_cyc = read_sched_clock();
-       cd.epoch_cyc_copy = cd.epoch_cyc;
-       cd.suspended = false;
-}
-
-static struct syscore_ops sched_clock_ops = {
-       .suspend = sched_clock_suspend,
-       .resume = sched_clock_resume,
-};
-
-static int __init sched_clock_syscore_init(void)
-{
-       register_syscore_ops(&sched_clock_ops);
-       return 0;
-}
-device_initcall(sched_clock_syscore_init);
index abff4e9..98aee32 100644 (file)
@@ -24,9 +24,9 @@
 #include <linux/timer.h>
 #include <linux/clocksource.h>
 #include <linux/irq.h>
+#include <linux/sched_clock.h>
 
 #include <asm/thread_info.h>
-#include <asm/sched_clock.h>
 #include <asm/stacktrace.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/time.h>
@@ -120,6 +120,4 @@ void __init time_init(void)
                machine_desc->init_time();
        else
                clocksource_of_init();
-
-       sched_clock_postinit();
 }
index bad361e..7a55b5c 100644 (file)
@@ -18,8 +18,8 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
+#include <linux/sched_clock.h>
 
-#include <asm/sched_clock.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/time.h>
 
index fea9131..cd46529 100644 (file)
@@ -26,8 +26,8 @@
 #include <linux/clockchips.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/sched_clock.h>
 
-#include <asm/sched_clock.h>
 #include <asm/mach/time.h>
 
 #include "common.h"
index b23c8e4..aa43462 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/stat.h>
 #include <linux/sys_soc.h>
 #include <linux/termios.h>
+#include <linux/sched_clock.h>
 #include <video/vga.h>
 
 #include <mach/hardware.h>
@@ -49,7 +50,6 @@
 #include <asm/setup.h>
 #include <asm/param.h>         /* HZ */
 #include <asm/mach-types.h>
-#include <asm/sched_clock.h>
 
 #include <mach/lm.h>
 #include <mach/irqs.h>
index 6600cff..58307cf 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/export.h>
 #include <linux/gpio.h>
 #include <linux/cpu.h>
+#include <linux/sched_clock.h>
 
 #include <mach/udc.h>
 #include <mach/hardware.h>
@@ -38,7 +39,6 @@
 #include <asm/pgtable.h>
 #include <asm/page.h>
 #include <asm/irq.h>
-#include <asm/sched_clock.h>
 #include <asm/system_misc.h>
 
 #include <asm/mach/map.h>
index 86a18b3..7ac41e8 100644 (file)
@@ -28,8 +28,8 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/sched_clock.h>
 
-#include <asm/sched_clock.h>
 #include <mach/addr-map.h>
 #include <mach/regs-timers.h>
 #include <mach/regs-apbc.h>
index 284313f..b6418fd 100644 (file)
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/sched_clock.h>
 
 #include <asm/mach/time.h>
 #include <asm/localtimer.h>
-#include <asm/sched_clock.h>
 
 #include "common.h"
 
index 726ec23..80603d2 100644 (file)
@@ -43,9 +43,9 @@
 #include <linux/clocksource.h>
 #include <linux/clockchips.h>
 #include <linux/io.h>
+#include <linux/sched_clock.h>
 
 #include <asm/irq.h>
-#include <asm/sched_clock.h>
 
 #include <mach/hardware.h>
 #include <asm/mach/irq.h>
index f8b23b8..4c069b0 100644 (file)
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/dmtimer-omap.h>
+#include <linux/sched_clock.h>
 
 #include <asm/mach/time.h>
 #include <asm/smp_twd.h>
-#include <asm/sched_clock.h>
 
 #include "omap_hwmod.h"
 #include "omap_device.h"
index 8f1ee92..9aa852a 100644 (file)
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/clockchips.h>
+#include <linux/sched_clock.h>
 
 #include <asm/div64.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/time.h>
-#include <asm/sched_clock.h>
 #include <mach/regs-ost.h>
 #include <mach/irqs.h>
 
index a59a13a..713c86c 100644 (file)
@@ -14,9 +14,9 @@
 #include <linux/irq.h>
 #include <linux/timex.h>
 #include <linux/clockchips.h>
+#include <linux/sched_clock.h>
 
 #include <asm/mach/time.h>
-#include <asm/sched_clock.h>
 #include <mach/hardware.h>
 #include <mach/irqs.h>
 
index d9e7320..af771b7 100644 (file)
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/irq.h>
+#include <linux/sched_clock.h>
 
 #include <mach/hardware.h>
 #include <mach/irqs.h>
 
 /* Generic stuff */
-#include <asm/sched_clock.h>
 #include <asm/mach/map.h>
 #include <asm/mach/time.h>
 
index 837a2d5..29606bd 100644 (file)
@@ -22,9 +22,9 @@
 #include <linux/clocksource.h>
 #include <linux/clockchips.h>
 #include <linux/export.h>
+#include <linux/sched_clock.h>
 #include <mach/hardware.h>
 #include <asm/irq.h>
-#include <asm/sched_clock.h>
 #include <asm/uaccess.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/time.h>
index 5b0b86b..d9bc98e 100644 (file)
@@ -18,9 +18,9 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/clocksource.h>
+#include <linux/sched_clock.h>
 
 #include <asm/mach/time.h>
-#include <asm/sched_clock.h>
 
 #include <plat/counter-32k.h>
 
index 5d5ac0f..9d2b2ac 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
-#include <asm/sched_clock.h>
+#include <linux/sched_clock.h>
 
 /*
  * MBus bridge block registers.
index f899cbc..2957075 100644 (file)
 #include <linux/clk.h>
 #include <linux/clockchips.h>
 #include <linux/platform_device.h>
+#include <linux/sched_clock.h>
 
 #include <asm/smp_twd.h>
 #include <asm/mach/time.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
-#include <asm/sched_clock.h>
 
 #include <mach/map.h>
 #include <plat/devs.h>
index b33b74c..51b109e 100644 (file)
@@ -20,8 +20,8 @@
  */
 #include <linux/kernel.h>
 #include <linux/io.h>
+#include <linux/sched_clock.h>
 
-#include <asm/sched_clock.h>
 #include <plat/sched_clock.h>
 
 static void __iomem *ctr;
index 766611d..07ea7ce 100644 (file)
@@ -28,8 +28,8 @@
 #include <linux/of_platform.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/sched_clock.h>
 
-#include <asm/sched_clock.h>
 #include <asm/irq.h>
 
 #define REG_CONTROL    0x00
index 54f3d11..0a7fb24 100644 (file)
@@ -14,8 +14,7 @@
  */
 #include <linux/clockchips.h>
 #include <linux/clksrc-dbx500-prcmu.h>
-
-#include <asm/sched_clock.h>
+#include <linux/sched_clock.h>
 
 #define RATE_32K               32768
 
index af13b85..a97b406 100644 (file)
@@ -20,8 +20,7 @@
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
-
-#include <asm/sched_clock.h>
+#include <linux/sched_clock.h>
 
 static void timer_get_base_and_rate(struct device_node *np,
                                    void __iomem **base, u32 *rate)
index 02af420..0f5e65f 100644 (file)
@@ -29,9 +29,9 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/stmp_device.h>
+#include <linux/sched_clock.h>
 
 #include <asm/mach/time.h>
-#include <asm/sched_clock.h>
 
 /*
  * There are 2 versions of the timrot on Freescale MXS-based SoCs.
index e405531..8864c17 100644 (file)
@@ -18,8 +18,8 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/platform_data/clocksource-nomadik-mtu.h>
+#include <linux/sched_clock.h>
 #include <asm/mach/time.h>
-#include <asm/sched_clock.h>
 
 /*
  * The MTU device hosts four different counters, with 4 set of
index 0234c8d..584b547 100644 (file)
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/sched_clock.h>
 
 #include <clocksource/samsung_pwm.h>
 
-#include <asm/sched_clock.h>
 
 /*
  * Clocksource driver
index ae877b0..9396170 100644 (file)
 #include <linux/io.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/sched_clock.h>
 
 #include <asm/mach/time.h>
 #include <asm/smp_twd.h>
-#include <asm/sched_clock.h>
 
 #define RTC_SECONDS            0x08
 #define RTC_SHADOW_SECONDS     0x0c
index 47a6730..efdca32 100644 (file)
@@ -27,8 +27,8 @@
 #include <linux/of_address.h>
 #include <linux/irq.h>
 #include <linux/module.h>
+#include <linux/sched_clock.h>
 
-#include <asm/sched_clock.h>
 #include <asm/localtimer.h>
 #include <linux/percpu.h>
 /*
index 97738db..e5dc912 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_address.h>
-#include <asm/sched_clock.h>
+#include <linux/sched_clock.h>
 #include <asm/localtimer.h>
 #include <asm/mach/time.h>
 
index 7608826..ef3cfb2 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_address.h>
-#include <asm/sched_clock.h>
+#include <linux/sched_clock.h>
 #include <asm/mach/time.h>
 
 #define SIRFSOC_TIMER_COUNTER_LO       0x0000
diff --git a/include/linux/sched_clock.h b/include/linux/sched_clock.h
new file mode 100644 (file)
index 0000000..fa7922c
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * sched_clock.h: support for extending counters to full 64-bit ns counter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef LINUX_SCHED_CLOCK
+#define LINUX_SCHED_CLOCK
+
+#ifdef CONFIG_GENERIC_SCHED_CLOCK
+extern void sched_clock_postinit(void);
+#else
+static inline void sched_clock_postinit(void) { }
+#endif
+
+extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate);
+
+extern unsigned long long (*sched_clock_func)(void);
+
+#endif
index 9d3a788..1a3f933 100644 (file)
@@ -757,6 +757,9 @@ config LOG_BUF_SHIFT
 config HAVE_UNSTABLE_SCHED_CLOCK
        bool
 
+config GENERIC_SCHED_CLOCK
+       bool
+
 #
 # For architectures that want to enable the support for NUMA-affine scheduler
 # balancing logic:
index 9484f4b..bef4a6a 100644 (file)
@@ -74,6 +74,7 @@
 #include <linux/ptrace.h>
 #include <linux/blkdev.h>
 #include <linux/elevator.h>
+#include <linux/sched_clock.h>
 
 #include <asm/io.h>
 #include <asm/bugs.h>
@@ -555,6 +556,7 @@ asmlinkage void __init start_kernel(void)
        softirq_init();
        timekeeping_init();
        time_init();
+       sched_clock_postinit();
        profile_init();
        call_function_init();
        WARN(!irqs_disabled(), "Interrupts were enabled early\n");
index d52ac8b..9250130 100644 (file)
@@ -4,6 +4,7 @@ obj-y += timeconv.o posix-clock.o alarmtimer.o
 obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD)                += clockevents.o
 obj-$(CONFIG_GENERIC_CLOCKEVENTS)              += tick-common.o
 obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)    += tick-broadcast.o
+obj-$(CONFIG_GENERIC_SCHED_CLOCK)              += sched_clock.o
 obj-$(CONFIG_TICK_ONESHOT)                     += tick-oneshot.o
 obj-$(CONFIG_TICK_ONESHOT)                     += tick-sched.o
 obj-$(CONFIG_TIMER_STATS)                      += timer_stats.o
diff --git a/kernel/time/sched_clock.c b/kernel/time/sched_clock.c
new file mode 100644 (file)
index 0000000..aad1ae6
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * sched_clock.c: support for extending counters to full 64-bit ns counter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/syscore_ops.h>
+#include <linux/timer.h>
+#include <linux/sched_clock.h>
+
+struct clock_data {
+       u64 epoch_ns;
+       u32 epoch_cyc;
+       u32 epoch_cyc_copy;
+       unsigned long rate;
+       u32 mult;
+       u32 shift;
+       bool suspended;
+};
+
+static void sched_clock_poll(unsigned long wrap_ticks);
+static DEFINE_TIMER(sched_clock_timer, sched_clock_poll, 0, 0);
+static int irqtime = -1;
+
+core_param(irqtime, irqtime, int, 0400);
+
+static struct clock_data cd = {
+       .mult   = NSEC_PER_SEC / HZ,
+};
+
+static u32 __read_mostly sched_clock_mask = 0xffffffff;
+
+static u32 notrace jiffy_sched_clock_read(void)
+{
+       return (u32)(jiffies - INITIAL_JIFFIES);
+}
+
+static u32 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read;
+
+static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift)
+{
+       return (cyc * mult) >> shift;
+}
+
+static unsigned long long notrace cyc_to_sched_clock(u32 cyc, u32 mask)
+{
+       u64 epoch_ns;
+       u32 epoch_cyc;
+
+       /*
+        * Load the epoch_cyc and epoch_ns atomically.  We do this by
+        * ensuring that we always write epoch_cyc, epoch_ns and
+        * epoch_cyc_copy in strict order, and read them in strict order.
+        * If epoch_cyc and epoch_cyc_copy are not equal, then we're in
+        * the middle of an update, and we should repeat the load.
+        */
+       do {
+               epoch_cyc = cd.epoch_cyc;
+               smp_rmb();
+               epoch_ns = cd.epoch_ns;
+               smp_rmb();
+       } while (epoch_cyc != cd.epoch_cyc_copy);
+
+       return epoch_ns + cyc_to_ns((cyc - epoch_cyc) & mask, cd.mult, cd.shift);
+}
+
+/*
+ * Atomically update the sched_clock epoch.
+ */
+static void notrace update_sched_clock(void)
+{
+       unsigned long flags;
+       u32 cyc;
+       u64 ns;
+
+       cyc = read_sched_clock();
+       ns = cd.epoch_ns +
+               cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
+                         cd.mult, cd.shift);
+       /*
+        * Write epoch_cyc and epoch_ns in a way that the update is
+        * detectable in cyc_to_fixed_sched_clock().
+        */
+       raw_local_irq_save(flags);
+       cd.epoch_cyc_copy = cyc;
+       smp_wmb();
+       cd.epoch_ns = ns;
+       smp_wmb();
+       cd.epoch_cyc = cyc;
+       raw_local_irq_restore(flags);
+}
+
+static void sched_clock_poll(unsigned long wrap_ticks)
+{
+       mod_timer(&sched_clock_timer, round_jiffies(jiffies + wrap_ticks));
+       update_sched_clock();
+}
+
+void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
+{
+       unsigned long r, w;
+       u64 res, wrap;
+       char r_unit;
+
+       if (cd.rate > rate)
+               return;
+
+       BUG_ON(bits > 32);
+       WARN_ON(!irqs_disabled());
+       read_sched_clock = read;
+       sched_clock_mask = (1 << bits) - 1;
+       cd.rate = rate;
+
+       /* calculate the mult/shift to convert counter ticks to ns. */
+       clocks_calc_mult_shift(&cd.mult, &cd.shift, rate, NSEC_PER_SEC, 0);
+
+       r = rate;
+       if (r >= 4000000) {
+               r /= 1000000;
+               r_unit = 'M';
+       } else if (r >= 1000) {
+               r /= 1000;
+               r_unit = 'k';
+       } else
+               r_unit = ' ';
+
+       /* calculate how many ns until we wrap */
+       wrap = cyc_to_ns((1ULL << bits) - 1, cd.mult, cd.shift);
+       do_div(wrap, NSEC_PER_MSEC);
+       w = wrap;
+
+       /* calculate the ns resolution of this counter */
+       res = cyc_to_ns(1ULL, cd.mult, cd.shift);
+       pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lums\n",
+               bits, r, r_unit, res, w);
+
+       /*
+        * Start the timer to keep sched_clock() properly updated and
+        * sets the initial epoch.
+        */
+       sched_clock_timer.data = msecs_to_jiffies(w - (w / 10));
+       update_sched_clock();
+
+       /*
+        * Ensure that sched_clock() starts off at 0ns
+        */
+       cd.epoch_ns = 0;
+
+       /* Enable IRQ time accounting if we have a fast enough sched_clock */
+       if (irqtime > 0 || (irqtime == -1 && rate >= 1000000))
+               enable_sched_clock_irqtime();
+
+       pr_debug("Registered %pF as sched_clock source\n", read);
+}
+
+static unsigned long long notrace sched_clock_32(void)
+{
+       u32 cyc = read_sched_clock();
+       return cyc_to_sched_clock(cyc, sched_clock_mask);
+}
+
+unsigned long long __read_mostly (*sched_clock_func)(void) = sched_clock_32;
+
+unsigned long long notrace sched_clock(void)
+{
+       if (cd.suspended)
+               return cd.epoch_ns;
+
+       return sched_clock_func();
+}
+
+void __init sched_clock_postinit(void)
+{
+       /*
+        * If no sched_clock function has been provided at that point,
+        * make it the final one one.
+        */
+       if (read_sched_clock == jiffy_sched_clock_read)
+               setup_sched_clock(jiffy_sched_clock_read, 32, HZ);
+
+       sched_clock_poll(sched_clock_timer.data);
+}
+
+static int sched_clock_suspend(void)
+{
+       sched_clock_poll(sched_clock_timer.data);
+       cd.suspended = true;
+       return 0;
+}
+
+static void sched_clock_resume(void)
+{
+       cd.epoch_cyc = read_sched_clock();
+       cd.epoch_cyc_copy = cd.epoch_cyc;
+       cd.suspended = false;
+}
+
+static struct syscore_ops sched_clock_ops = {
+       .suspend = sched_clock_suspend,
+       .resume = sched_clock_resume,
+};
+
+static int __init sched_clock_syscore_init(void)
+{
+       register_syscore_ops(&sched_clock_ops);
+       return 0;
+}
+device_initcall(sched_clock_syscore_init);