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 49d993cee51232874a81814fe39ca99e09b88bad..53d3a356f61f877acebc12a22fc7b3d884a7cfc8 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 ddc740769601a58ecc4f1c430fd20cf3a45ac794..023ee63827a2baad853768e740759c9551b28cfa 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 5f3338eacad21024a75d7b1e7480f3fc58e804b8..97cb0576d07c3977adf4b4e94807a34e6477d03f 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 59dcdced6e30df8e3603ae64c3df076859a0ce82..221f07b11ccb0ad4c79cdcbb65072ffb2b822b0a 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 abff4e9aaee07c518f1101469d3032626334c695..98aee3258398663b1147e38372215c8fbfe8267e 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 bad361ec1666defa463d0c60205818bb3c875f81..7a55b5c9597124a23345066771b9bb88a5893478 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 fea91313678b25efb17e7342afb45013c2fa156f..cd46529e9eaa1a8925129370fb25476a861908e8 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 b23c8e4f28e859180b90d04fb1a7c8b79e619b69..aa4346227c416df8cd9a7d4a3f9df09e6b80a03e 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 6600cff6bd922b88984e549fce3d2c406c4939a2..58307cff1f18824ca5aadb8d28357f084ea93e84 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 86a18b3d252ea9e286277bd9b03beca8925538ce..7ac41e83cfefd42b0e219a2f6cfc173f0624ab2f 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 284313f3e02cb1edd56cfd25a425efe40a5207a1..b6418fd5fe0dc49eb6c6fbfef7d30b918ba2e193 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 726ec23d29c71abc2224ee8b9957483833cf6639..80603d2fef77035a8fe54f9fe99e5ef63fcfdcbf 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 f8b23b8040d9aa61a1593d30a55c5ed951cc5f6a..4c069b0cab21253cd8395e55caf1515e981afd41 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 8f1ee92aea306eb2c46ad8a552990945d34c65ef..9aa852a8fab9f2c7784003764086cd9d1af8dc2e 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 a59a13a665a65e992e45d81a09ecffb8387bac86..713c86cd3d640ab758b723da8e4abd4ee6bf85c6 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 d9e73209c9b8d7aa0d26b9957ed7401f9b53b61d..af771b76fe1caf266ee8a0eb5e10bc5e77891936 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 837a2d52e9db342f0c958ddf2128bb6f2bce0718..29606bd75f3f19b265e7bfaf2cb11c0156afbcb7 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 5b0b86bb34bb8d8ab22466e9f7a54c4937fddd5e..d9bc98eb2a6b6b80559faa096f47cda066060e00 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 5d5ac0f05422a45f49c09fb4c3f35e01b947d859..9d2b2ac74938da9b52f2ee1f629fdb6ca3b602b7 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 f899cbc9b28832cfdba7d4740713728b25562947..2957075ca836f6beacfa62df244e4b7cd5e9aa6f 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 b33b74c8723268fae41224b5a8f13ae06cbfa3ad..51b109e3b6c38d4d265dc53f581203ecef34ab34 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 766611d299455efa3699fe49d5aa9a42812bd226..07ea7ce900dc19a10491732927c162ae8212cda2 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 54f3d119d99c7b495008ee219538f488106b07d8..0a7fb2440e2909490769a3613d2878411f420068 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 af13b8559b613134b99ca04a0aca61caea37e560..a97b4065dacfb17d0434ea52f9941b842389fbad 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 02af4204af867e8f1b0796dc205df8fd4c204953..0f5e65f74dc3ee5f9749c6c782628016bb884f97 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 e405531e1cc5ceb620818b10b63e42e31d1f2acc..8864c17841c86e17b067f43176ebec959b3b3707 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 0234c8d2c8f26ccefc187be2385f103e8197f48c..584b5472eea3e4f593c50dbea18cd5a78fe9d826 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 ae877b021b54449219cef0d1f3e43fe442c54bca..93961703b887e9a82ccde7d42bd981ce5b7a83aa 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 47a673070d70edbf13d6ecaf7b60d04e5b5ed3c5..efdca3263afef24fc557862dc7f0e622effdb72f 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 97738dbf3e3bc18ddc97d4d5f36f257089dfbb47..e5dc9129ca263f0a9cba409f7d866e1dc73b086c 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 760882665d7a44f90de0ce522a18a5d53b45e03e..ef3cfb269d8bce34091e1fde228da32821d41925 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 9d3a7887a6d3e0b7a8323865b658e1d088dcea85..1a3f93329a67b285410d5bdb4516594323bb0f89 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 9484f4ba88d05aa589bb737d96836a1a01db55ce..bef4a6ac7c76dd62ad6339fd5a11b81b87c79009 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 d52ac8bf0006e62eb25dc590898943834df2f089..9250130646f510796fb2811ad7fb11483abc0040 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);