* [PATCH 0/13] 64-bit sched_clock
@ 2010-12-16 9:25 Russell King - ARM Linux
2010-12-16 9:27 ` [PATCH 01/13] ARM: sched_clock: provide common infrastructure for sched_clock() Russell King - ARM Linux
` (13 more replies)
0 siblings, 14 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:25 UTC (permalink / raw)
To: linux-arm-kernel
This patch series replaces existing sched_clock() implementations
with a version which gives a full 64-bit nanosecond time from counters
with less than 33 bits.
This series depends on the prevously posted clocksource patch series,
and the notrace sched_clock patch, and the ftrace tree previously
merged into my devel-stable branch. It also depends on a fix to
the clocksource code to achieve the accuracy quoted below - though
it'll build and work without.
We achieve the 64-bit nanosecond time by maintaining a pair of
epochs - one for the counter and a corresponding one for the ns value.
This allows us to calculate how many counter ticks have happened since
the ns epoch was last updated, and therefore how much time has passed.
We use a lockless method to read and update the epoch values,
eliminating any locking overhead. The epoch update happens at about
90% of the wrap period, resulting in less maintainence overhead.
Lastly, we allow platforms to provide constants for the counter->ns
conversion.
The PXA sched_clock implementation using cnt32_to_63() builds to 72
bytes of code and 12 bytes of data, and provides 54 bits of
nanoseconds, with an error of 0.00008%.
The PXA sched_clock implementation using this infrastructure builds
to 84 bytes of code and 8 bytes of data, and provides 64 bits of
nanoseconds, with an error of 0.00000002%.
The epoch updates in action on Versatile Express (24MHz 32-bit counter):
sched_clock: update 68391420 -> 50e0a246 0ns -> 162637292211ns
sched_clock: update 50e0a246 -> 372c24b7 162637292211ns -> 323625136880ns
sched_clock: update 372c24b7 -> 1d35c46a 323625136880ns -> 484433069633ns
sched_clock: update 1d35c46a -> 0367a736 484433069633ns -> 645350944761ns
...
sched_clock: update f6f7fd55 -> dd2d882e 32669955162290ns -> 32830883021959ns
sched_clock: update dd2d882e -> c36312cd 32830883021959ns -> 32991810879212ns
sched_clock: update c36312cd -> a9989d41 32991810879212ns -> 33152738734673ns
sched_clock: update a9989d41 -> 8fce2781 33152738734673ns -> 33313666587967ns
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 01/13] ARM: sched_clock: provide common infrastructure for sched_clock()
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
@ 2010-12-16 9:27 ` Russell King - ARM Linux
2010-12-16 12:52 ` Russell King - ARM Linux
2010-12-16 9:28 ` [PATCH 02/13] ARM: ixp4xx: convert sched_clock() to use new infrastructure Russell King - ARM Linux
` (12 subsequent siblings)
13 siblings, 1 reply; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:27 UTC (permalink / raw)
To: linux-arm-kernel
Provide common sched_clock() infrastructure for platforms to use to
create a 64-bit ns based sched_clock() implementation from a counter
running at a non-variable clock rate.
This implementation is based upon maintaining an epoch for the counter
and an epoch for the nanosecond time. When we desire a sched_clock()
time, we calculate the number of counter ticks since the last epoch
update, convert this to nanoseconds and add to the epoch nanoseconds.
We regularly refresh these epochs within the counter wrap interval.
We perform a similar calculation as above, and store the new epochs.
We read and write the epochs in such a way that sched_clock() can easily
(and locklessly) detect when an update is in progress, and repeat the
loading of these constants when they're known not to be stable. The
one caveat is that sched_clock() is not called in the middle of an
update.
Finally, if the clock rate is known at compile time, the counter to ns
conversion factors can be specified, allowing sched_clock() to be tightly
optimized. We ensure that these factors are correct by providing an
initialization function which performs a run-time check.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 3 +
arch/arm/include/asm/sched_clock.h | 116 ++++++++++++++++++++++++++++++++++++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/sched_clock.c | 69 +++++++++++++++++++++
4 files changed, 189 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/include/asm/sched_clock.h
create mode 100644 arch/arm/kernel/sched_clock.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 49778bb..ed7a0a7 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -38,6 +38,9 @@ config HAVE_PWM
config SYS_SUPPORTS_APM_EMULATION
bool
+config HAVE_SCHED_CLOCK
+ bool
+
config GENERIC_GPIO
bool
diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h
new file mode 100644
index 0000000..f4480aa
--- /dev/null
+++ b/arch/arm/include/asm/sched_clock.h
@@ -0,0 +1,116 @@
+/*
+ * 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
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+struct clock_data {
+ u64 epoch_ns;
+ u32 epoch_cyc;
+ u32 epoch_cyc_copy;
+ u32 mult;
+ u32 shift;
+};
+
+#define DEFINE_CLOCK_DATA(name) struct clock_data name
+
+static inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift)
+{
+ return (cyc * mult) >> shift;
+}
+
+/*
+ * Atomically update the sched_clock epoch.
+ */
+static inline void update_sched_clock(struct clock_data *cd, u32 cyc, u32 mask)
+{
+ u64 ns = cd->epoch_ns +
+ cyc_to_ns((cyc - cd->epoch_cyc) & 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().
+ */
+ cd->epoch_cyc = cyc;
+ smp_wmb();
+ cd->epoch_ns = ns;
+ smp_wmb();
+ cd->epoch_cyc_copy = cyc;
+}
+
+/*
+ * If your clock rate is known at compile time, using this will allow
+ * you to optimize the mult/shift loads away. Use init_fixed_sched_clock()
+ * with this to ensure that your mult/shift are correct.
+ *
+ * NB: you must provide locking around this function, which must include
+ * reading the cycle counter.
+ */
+static inline unsigned long long cyc_to_fixed_sched_clock(struct clock_data *cd,
+ u32 cyc, u32 mask, u32 mult, u32 shift)
+{
+ 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, mult, shift);
+}
+
+/*
+ * Otherwise, you need to use this, which will obtain the mult/shift
+ * from the clock_data structure. Use init_sched_clock() with this.
+ *
+ * NB: you must provide locking around this function, which must include
+ * reading the cycle counter.
+ */
+static inline unsigned long long cyc_to_sched_clock(struct clock_data *cd,
+ u32 cyc, u32 mask)
+{
+ return cyc_to_fixed_sched_clock(cd, cyc, mask, cd->mult, cd->shift);
+}
+
+/*
+ * Initialize the clock data - calculate the appropriate multiplier
+ * and shift. Also setup a timer to ensure that the epoch is refreshed
+ * at the appropriate time interval.
+ */
+void init_sched_clock(struct clock_data *, void (*)(void),
+ unsigned int, unsigned long);
+
+/*
+ * Use this initialization function if you're using cyc_to_fixed_sched_clock,
+ * which will confirm that your constants are correct.
+ */
+static inline void init_fixed_sched_clock(struct clock_data *cd,
+ void (*update)(void), unsigned int bits, unsigned long rate,
+ u32 mult, u32 shift)
+{
+ init_sched_clock(cd, update, bits, rate);
+ if (cd->mult != mult || cd->shift != shift) {
+ pr_crit("sched_clock: wrong multiply/shift: %u>>%u vs calculated %u>>%u\n"
+ "sched_clock: fix multiply/shift to avoid scheduler hiccups\n",
+ mult, shift, cd->mult, cd->shift);
+ }
+}
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 679851a..fd3ec49 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_MODULES) += armksyms.o module.o
obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o isa.o
+obj-$(CONFIG_HAVE_SCHED_CLOCK) += sched_clock.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c
new file mode 100644
index 0000000..b10b02b
--- /dev/null
+++ b/arch/arm/kernel/sched_clock.c
@@ -0,0 +1,69 @@
+/*
+ * 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/sched.h>
+#include <linux/timer.h>
+
+#include <asm/sched_clock.h>
+
+static void sched_clock_poll(unsigned long wrap_ticks);
+static DEFINE_TIMER(sched_clock_timer, sched_clock_poll, 0, 0);
+static void (*sched_clock_update_fn)(void);
+
+static void sched_clock_poll(unsigned long wrap_ticks)
+{
+ mod_timer(&sched_clock_timer, round_jiffies(jiffies + wrap_ticks));
+ sched_clock_update_fn();
+}
+
+void __init init_sched_clock(struct clock_data *cd, void (*update)(void),
+ unsigned int clock_bits, unsigned long rate)
+{
+ unsigned long r, w;
+ u64 res, wrap;
+ char r_unit;
+
+ sched_clock_update_fn = update;
+
+ /* calculate the mult/shift to convert counter ticks to ns. */
+ clocks_calc_mult_shift(&cd->mult, &cd->shift, rate, NSEC_PER_SEC, 60);
+
+ r = rate;
+ if (r >= 4000000) {
+ r /= 1000000;
+ r_unit = 'M';
+ } else {
+ r /= 1000;
+ r_unit = 'k';
+ }
+
+ /* calculate how many ns until we wrap */
+ wrap = cyc_to_ns((1ULL << clock_bits) - 1, cd->mult, cd->shift);
+ do_div(wrap, 1000000);
+ 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",
+ clock_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));
+ sched_clock_poll(sched_clock_timer.data);
+
+ /*
+ * Ensure that sched_clock() starts off at 0ns
+ */
+ cd->epoch_ns = 0;
+}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 02/13] ARM: ixp4xx: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
2010-12-16 9:27 ` [PATCH 01/13] ARM: sched_clock: provide common infrastructure for sched_clock() Russell King - ARM Linux
@ 2010-12-16 9:28 ` Russell King - ARM Linux
2010-12-16 9:28 ` [PATCH 03/13] ARM: mmp: " Russell King - ARM Linux
` (11 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:28 UTC (permalink / raw)
To: linux-arm-kernel
Convert ixp4xx to use the new sched_clock() infrastructure for
extending 32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-ixp4xx/common.c | 31 ++++++++++++++++++++-----------
2 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ed7a0a7..6f58bce 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -437,6 +437,7 @@ config ARCH_IXP4XX
select CPU_XSCALE
select GENERIC_GPIO
select GENERIC_CLOCKEVENTS
+ select HAVE_SCHED_CLOCK
select DMABOUNCE if PCI
help
Support for Intel's IXP4XX (XScale) family of processors.
diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c
index e0b91d8..4dbfcbb 100644
--- a/arch/arm/mach-ixp4xx/common.c
+++ b/arch/arm/mach-ixp4xx/common.c
@@ -35,6 +35,7 @@
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/irq.h>
+#include <asm/sched_clock.h>
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
@@ -399,6 +400,23 @@ void __init ixp4xx_sys_init(void)
}
/*
+ * sched_clock()
+ */
+static DEFINE_CLOCK_DATA(cd);
+
+unsigned long long notrace sched_clock(void)
+{
+ u32 cyc = *IXP4XX_OSTS;
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
+
+static void notrace ixp4xx_update_sched_clock(void)
+{
+ u32 cyc = *IXP4XX_OSTS;
+ update_sched_clock(&cd, cyc, (u32)~0);
+}
+
+/*
* clocksource
*/
static cycle_t ixp4xx_get_cycles(struct clocksource *cs)
@@ -418,18 +436,9 @@ unsigned long ixp4xx_timer_freq = FREQ;
EXPORT_SYMBOL(ixp4xx_timer_freq);
static void __init ixp4xx_clocksource_init(void)
{
- clocksource_register_hz(&clocksource_ixp4xx, ixp4xx_timer_freq);
-}
-
-/*
- * sched_clock()
- */
-unsigned long long notrace sched_clock(void)
-{
- cycle_t cyc = ixp4xx_get_cycles(NULL);
- struct clocksource *cs = &clocksource_ixp4xx;
+ init_sched_clock(&cd, ixp4xx_update_sched_clock, 32, ixp4xx_timer_freq);
- return clocksource_cyc2ns(cyc, cs->mult, cs->shift);
+ clocksource_register_hz(&clocksource_ixp4xx, ixp4xx_timer_freq);
}
/*
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 03/13] ARM: mmp: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
2010-12-16 9:27 ` [PATCH 01/13] ARM: sched_clock: provide common infrastructure for sched_clock() Russell King - ARM Linux
2010-12-16 9:28 ` [PATCH 02/13] ARM: ixp4xx: convert sched_clock() to use new infrastructure Russell King - ARM Linux
@ 2010-12-16 9:28 ` Russell King - ARM Linux
2010-12-16 9:28 ` [PATCH 04/13] ARM: pxa: " Russell King - ARM Linux
` (10 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:28 UTC (permalink / raw)
To: linux-arm-kernel
Convert mmp to use the new sched_clock() infrastructure for extending
32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-mmp/time.c | 32 +++++++++++---------------------
2 files changed, 12 insertions(+), 21 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 6f58bce..0208e1e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -513,6 +513,7 @@ config ARCH_MMP
select ARCH_REQUIRE_GPIOLIB
select COMMON_CLKDEV
select GENERIC_CLOCKEVENTS
+ select HAVE_SCHED_CLOCK
select TICK_ONESHOT
select PLAT_PXA
select SPARSE_IRQ
diff --git a/arch/arm/mach-mmp/time.c b/arch/arm/mach-mmp/time.c
index a2ea33d..aeb9ae2 100644
--- a/arch/arm/mach-mmp/time.c
+++ b/arch/arm/mach-mmp/time.c
@@ -26,8 +26,8 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/sched.h>
-#include <linux/cnt32_to_63.h>
+#include <asm/sched_clock.h>
#include <mach/addr-map.h>
#include <mach/regs-timers.h>
#include <mach/regs-apbc.h>
@@ -42,23 +42,7 @@
#define MAX_DELTA (0xfffffffe)
#define MIN_DELTA (16)
-#define TCR2NS_SCALE_FACTOR 10
-
-static unsigned long tcr2ns_scale;
-
-static void __init set_tcr2ns_scale(unsigned long tcr_rate)
-{
- unsigned long long v = 1000000000ULL << TCR2NS_SCALE_FACTOR;
- do_div(v, tcr_rate);
- tcr2ns_scale = v;
- /*
- * We want an even value to automatically clear the top bit
- * returned by cnt32_to_63() without an additional run time
- * instruction. So if the LSB is 1 then round it up.
- */
- if (tcr2ns_scale & 1)
- tcr2ns_scale++;
-}
+static DEFINE_CLOCK_DATA(cd);
/*
* FIXME: the timer needs some delay to stablize the counter capture
@@ -77,8 +61,14 @@ static inline uint32_t timer_read(void)
unsigned long long notrace sched_clock(void)
{
- unsigned long long v = cnt32_to_63(timer_read());
- return (v * tcr2ns_scale) >> TCR2NS_SCALE_FACTOR;
+ u32 cyc = timer_read();
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
+
+static void notrace mmp_update_sched_clock(void)
+{
+ u32 cyc = timer_read();
+ update_sched_clock(&cd, cyc, (u32)~0);
}
static irqreturn_t timer_interrupt(int irq, void *dev_id)
@@ -185,7 +175,7 @@ void __init timer_init(int irq)
{
timer_config();
- set_tcr2ns_scale(CLOCK_TICK_RATE);
+ init_sched_clock(&cd, mmp_update_sched_clock, 32, CLOCK_TICK_RATE);
ckevt.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, ckevt.shift);
ckevt.max_delta_ns = clockevent_delta2ns(MAX_DELTA, &ckevt);
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 04/13] ARM: pxa: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (2 preceding siblings ...)
2010-12-16 9:28 ` [PATCH 03/13] ARM: mmp: " Russell King - ARM Linux
@ 2010-12-16 9:28 ` Russell King - ARM Linux
2010-12-16 10:05 ` Uwe Kleine-König
2010-12-16 9:29 ` [PATCH 05/13] ARM: sa1100: " Russell King - ARM Linux
` (9 subsequent siblings)
13 siblings, 1 reply; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:28 UTC (permalink / raw)
To: linux-arm-kernel
Convert pxa to use the new sched_clock() infrastructure for extending
32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-pxa/time.c | 29 +++++++++--------------------
2 files changed, 10 insertions(+), 20 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 0208e1e..23035d6 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -593,6 +593,7 @@ config ARCH_PXA
select COMMON_CLKDEV
select ARCH_REQUIRE_GPIOLIB
select GENERIC_CLOCKEVENTS
+ select HAVE_SCHED_CLOCK
select TICK_ONESHOT
select PLAT_PXA
select SPARSE_IRQ
diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c
index b8d9dff..e7f64d9 100644
--- a/arch/arm/mach-pxa/time.c
+++ b/arch/arm/mach-pxa/time.c
@@ -17,11 +17,11 @@
#include <linux/interrupt.h>
#include <linux/clockchips.h>
#include <linux/sched.h>
-#include <linux/cnt32_to_63.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>
/*
@@ -32,29 +32,18 @@
* long as there is always less than 582 seconds between successive
* calls to sched_clock() which should always be the case in practice.
*/
+static DEFINE_CLOCK_DATA(cd);
-#define OSCR2NS_SCALE_FACTOR 10
-
-static unsigned long oscr2ns_scale;
-
-static void __init set_oscr2ns_scale(unsigned long oscr_rate)
+unsigned long long notrace sched_clock(void)
{
- unsigned long long v = 1000000000ULL << OSCR2NS_SCALE_FACTOR;
- do_div(v, oscr_rate);
- oscr2ns_scale = v;
- /*
- * We want an even value to automatically clear the top bit
- * returned by cnt32_to_63() without an additional run time
- * instruction. So if the LSB is 1 then round it up.
- */
- if (oscr2ns_scale & 1)
- oscr2ns_scale++;
+ u32 cyc = OSCR;
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
}
-unsigned long long notrace sched_clock(void)
+static void notrace pxa_update_sched_clock(void)
{
- unsigned long long v = cnt32_to_63(OSCR);
- return (v * oscr2ns_scale) >> OSCR2NS_SCALE_FACTOR;
+ u32 cyc = OSCR;
+ update_sched_clock(&cd, cyc, (u32)~0);
}
@@ -144,7 +133,7 @@ static void __init pxa_timer_init(void)
OIER = 0;
OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;
- set_oscr2ns_scale(clock_tick_rate);
+ init_sched_clock(&cd, pxa_update_sched_clock, 32, clock_tick_rate);
ckevt_pxa_osmr0.mult =
div_sc(clock_tick_rate, NSEC_PER_SEC, ckevt_pxa_osmr0.shift);
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 05/13] ARM: sa1100: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (3 preceding siblings ...)
2010-12-16 9:28 ` [PATCH 04/13] ARM: pxa: " Russell King - ARM Linux
@ 2010-12-16 9:29 ` Russell King - ARM Linux
2010-12-16 9:29 ` [PATCH 06/13] ARM: tegra: " Russell King - ARM Linux
` (8 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:29 UTC (permalink / raw)
To: linux-arm-kernel
Convert sa1100 to use the new sched_clock() infrastructure for extending
32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-sa1100/generic.c | 23 -----------------------
arch/arm/mach-sa1100/time.c | 30 ++++++++++++++++++++++++++++++
3 files changed, 31 insertions(+), 23 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 23035d6..0e1a966 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SA1100
select CPU_FREQ
select GENERIC_CLOCKEVENTS
select HAVE_CLK
+ select HAVE_SCHED_CLOCK
select TICK_ONESHOT
select ARCH_REQUIRE_GPIOLIB
help
diff --git a/arch/arm/mach-sa1100/generic.c b/arch/arm/mach-sa1100/generic.c
index 33b4969..fbc224d 100644
--- a/arch/arm/mach-sa1100/generic.c
+++ b/arch/arm/mach-sa1100/generic.c
@@ -16,9 +16,7 @@
#include <linux/pm.h>
#include <linux/cpufreq.h>
#include <linux/ioport.h>
-#include <linux/sched.h> /* just for sched_clock() - funny that */
#include <linux/platform_device.h>
-#include <linux/cnt32_to_63.h>
#include <asm/div64.h>
#include <mach/hardware.h>
@@ -110,27 +108,6 @@ unsigned int sa11x0_getspeed(unsigned int cpu)
}
/*
- * This is the SA11x0 sched_clock implementation. This has
- * a resolution of 271ns, and a maximum value of 32025597s (370 days).
- *
- * The return value is guaranteed to be monotonic in that range as
- * long as there is always less than 582 seconds between successive
- * calls to this function.
- *
- * ( * 1E9 / 3686400 => * 78125 / 288)
- */
-unsigned long long notrace sched_clock(void)
-{
- unsigned long long v = cnt32_to_63(OSCR);
-
- /* the <<1 gets rid of the cnt_32_to_63 top bit saving on a bic insn */
- v *= 78125<<1;
- do_div(v, 288<<1);
-
- return v;
-}
-
-/*
* Default power-off for SA1100
*/
static void sa1100_power_off(void)
diff --git a/arch/arm/mach-sa1100/time.c b/arch/arm/mach-sa1100/time.c
index 96154f5..ae4f3d8 100644
--- a/arch/arm/mach-sa1100/time.c
+++ b/arch/arm/mach-sa1100/time.c
@@ -12,12 +12,39 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/sched.h> /* just for sched_clock() - funny that */
#include <linux/timex.h>
#include <linux/clockchips.h>
#include <asm/mach/time.h>
+#include <asm/sched_clock.h>
#include <mach/hardware.h>
+/*
+ * This is the SA11x0 sched_clock implementation.
+ */
+static DEFINE_CLOCK_DATA(cd);
+
+/*
+ * Constants generated by clocks_calc_mult_shift(m, s, 3.6864MHz,
+ * NSEC_PER_SEC, 60).
+ * This gives a resolution of about 271ns and a wrap period of about 19min.
+ */
+#define SC_MULT 2275555556u
+#define SC_SHIFT 23
+
+unsigned long long notrace sched_clock(void)
+{
+ u32 cyc = OSCR;
+ return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT);
+}
+
+static void notrace sa1100_update_sched_clock(void)
+{
+ u32 cyc = OSCR;
+ update_sched_clock(&cd, cyc, (u32)~0);
+}
+
#define MIN_OSCR_DELTA 2
static irqreturn_t sa1100_ost0_interrupt(int irq, void *dev_id)
@@ -96,6 +123,9 @@ static void __init sa1100_timer_init(void)
OIER = 0; /* disable any timer interrupts */
OSSR = 0xf; /* clear status on all timers */
+ init_fixed_sched_clock(&cd, sa1100_update_sched_clock, 32,
+ 3686400, SC_MULT, SC_SHIFT);
+
ckevt_sa1100_osmr0.mult =
div_sc(3686400, NSEC_PER_SEC, ckevt_sa1100_osmr0.shift);
ckevt_sa1100_osmr0.max_delta_ns =
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 06/13] ARM: tegra: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (4 preceding siblings ...)
2010-12-16 9:29 ` [PATCH 05/13] ARM: sa1100: " Russell King - ARM Linux
@ 2010-12-16 9:29 ` Russell King - ARM Linux
2010-12-16 9:29 ` [PATCH 07/13] ARM: u300: " Russell King - ARM Linux
` (7 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:29 UTC (permalink / raw)
To: linux-arm-kernel
Convert tegra to use the new sched_clock() infrastructure for extending
32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-tegra/timer.c | 22 ++++++++++++++++++++--
2 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 0e1a966..ec7b027 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -570,6 +570,7 @@ config ARCH_TEGRA
select GENERIC_CLOCKEVENTS
select GENERIC_GPIO
select HAVE_CLK
+ select HAVE_SCHED_CLOCK
select COMMON_CLKDEV
select ARCH_HAS_BARRIERS if CACHE_L2X0
select ARCH_HAS_CPUFREQ
diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 19a6f7e..cbd1082 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -25,7 +25,6 @@
#include <linux/clocksource.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/cnt32_to_63.h>
#include <asm/mach/time.h>
#include <asm/localtimer.h>
@@ -110,9 +109,25 @@ static struct clocksource tegra_clocksource = {
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
+static DEFINE_CLOCK_DATA(cd);
+
+/*
+ * Constants generated by clocks_calc_mult_shift(m, s, 1MHz, NSEC_PER_SEC, 60).
+ * This gives a resolution of about 1us and a wrap period of about 1h11min.
+ */
+#define SC_MULT 4194304000u
+#define SC_SHIFT 22
+
unsigned long long notrace sched_clock(void)
{
- return cnt32_to_63(timer_readl(TIMERUS_CNTR_1US)) * 1000;
+ u32 cyc = timer_readl(TIMERUS_CNTR_1US);
+ return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT);
+}
+
+static void notrace tegra_update_sched_clock(void)
+{
+ u32 cyc = timer_readl(TIMERUS_CNTR_1US);
+ update_sched_clock(&cd, cyc, (u32)~0);
}
static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id)
@@ -157,6 +172,9 @@ static void __init tegra_init_timer(void)
WARN(1, "Unknown clock rate");
}
+ init_fixed_sched_clock(&cd, tegra_update_sched_clock, 32,
+ 1000000, SC_MULT, SC_SHIFT);
+
if (clocksource_register_hz(&tegra_clocksource, 1000000)) {
printk(KERN_ERR "Failed to register clocksource\n");
BUG();
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 07/13] ARM: u300: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (5 preceding siblings ...)
2010-12-16 9:29 ` [PATCH 06/13] ARM: tegra: " Russell King - ARM Linux
@ 2010-12-16 9:29 ` Russell King - ARM Linux
2010-12-16 9:30 ` [PATCH 08/13] ARM: iop: " Russell King - ARM Linux
` (6 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:29 UTC (permalink / raw)
To: linux-arm-kernel
Convert u300 to use the new sched_clock() infrastructure for extending
32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-u300/timer.c | 17 +++++++++++++----
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ec7b027..d5bd266 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -790,6 +790,7 @@ config ARCH_U300
bool "ST-Ericsson U300 Series"
depends on MMU
select CPU_ARM926T
+ select HAVE_SCHED_CLOCK
select HAVE_TCM
select ARM_AMBA
select ARM_VIC
diff --git a/arch/arm/mach-u300/timer.c b/arch/arm/mach-u300/timer.c
index 377ff7f..42b9f5c 100644
--- a/arch/arm/mach-u300/timer.c
+++ b/arch/arm/mach-u300/timer.c
@@ -21,6 +21,7 @@
#include <mach/hardware.h>
/* Generic stuff */
+#include <asm/sched_clock.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/mach/irq.h>
@@ -352,12 +353,18 @@ static struct clocksource clocksource_u300_1mhz = {
* this wraps around for now, since it is just a relative time
* stamp. (Inspired by OMAP implementation.)
*/
+static DEFINE_CLOCK_DATA(cd);
+
unsigned long long notrace sched_clock(void)
{
- return clocksource_cyc2ns(clocksource_u300_1mhz.read(
- &clocksource_u300_1mhz),
- clocksource_u300_1mhz.mult,
- clocksource_u300_1mhz.shift);
+ u32 cyc = readl(U300_TIMER_APP_VBASE + U300_TIMER_APP_GPT2CC);
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
+
+static void notrace u300_update_sched_clock(void)
+{
+ u32 cyc = readl(U300_TIMER_APP_VBASE + U300_TIMER_APP_GPT2CC);
+ update_sched_clock(&cd, cyc, (u32)~0);
}
@@ -375,6 +382,8 @@ static void __init u300_timer_init(void)
clk_enable(clk);
rate = clk_get_rate(clk);
+ init_sched_clock(&cd, u300_update_sched_clock, 32, rate);
+
/*
* Disable the "OS" and "DD" timers - these are designed for Symbian!
* Example usage in cnh1601578 cpu subsystem pd_timer_app.c
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 08/13] ARM: iop: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (6 preceding siblings ...)
2010-12-16 9:29 ` [PATCH 07/13] ARM: u300: " Russell King - ARM Linux
@ 2010-12-16 9:30 ` Russell King - ARM Linux
2010-12-16 9:30 ` [PATCH 09/13] ARM: nomadik: " Russell King - ARM Linux
` (5 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:30 UTC (permalink / raw)
To: linux-arm-kernel
Convert iop platforms to use the new sched_clock() infrastructure for
extending 32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/plat-iop/time.c | 14 +++++++++++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index d5bd266..58e2fe3 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -992,6 +992,7 @@ config ARCH_ACORN
config PLAT_IOP
bool
select GENERIC_CLOCKEVENTS
+ select HAVE_SCHED_CLOCK
config PLAT_ORION
bool
diff --git a/arch/arm/plat-iop/time.c b/arch/arm/plat-iop/time.c
index 2bb7db4..a3babae 100644
--- a/arch/arm/plat-iop/time.c
+++ b/arch/arm/plat-iop/time.c
@@ -49,15 +49,21 @@ static struct clocksource iop_clocksource = {
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
+static DEFINE_CLOCK_DATA(cd);
+
/*
* IOP sched_clock() implementation via its clocksource.
*/
unsigned long long notrace sched_clock(void)
{
- cycle_t cyc = iop_clocksource_read(NULL);
- struct clocksource *cs = &iop_clocksource;
+ u32 cyc = 0xffffffffu - read_tcr1();
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
- return clocksource_cyc2ns(cyc, cs->mult, cs->shift);
+static void notrace iop_update_sched_clock(void)
+{
+ u32 cyc = 0xffffffffu - read_tcr1();
+ update_sched_clock(&cd, cyc, (u32)~0);
}
/*
@@ -142,6 +148,8 @@ void __init iop_init_time(unsigned long tick_rate)
{
u32 timer_ctl;
+ init_sched_clock(&cd, iop_update_sched_clock, 32, tick_rate);
+
ticks_per_jiffy = DIV_ROUND_CLOSEST(tick_rate, HZ);
iop_tick_rate = tick_rate;
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 09/13] ARM: nomadik: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (7 preceding siblings ...)
2010-12-16 9:30 ` [PATCH 08/13] ARM: iop: " Russell King - ARM Linux
@ 2010-12-16 9:30 ` Russell King - ARM Linux
2010-12-16 9:31 ` [PATCH 10/13] ARM: omap: " Russell King - ARM Linux
` (4 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:30 UTC (permalink / raw)
To: linux-arm-kernel
Convert nomadik platforms to use the new sched_clock() infrastructure
for extending 32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/plat-nomadik/Kconfig | 1 +
arch/arm/plat-nomadik/timer.c | 76 +++++------------------------------------
2 files changed, 10 insertions(+), 67 deletions(-)
diff --git a/arch/arm/plat-nomadik/Kconfig b/arch/arm/plat-nomadik/Kconfig
index 5da3f97..187f4e8 100644
--- a/arch/arm/plat-nomadik/Kconfig
+++ b/arch/arm/plat-nomadik/Kconfig
@@ -14,6 +14,7 @@ if PLAT_NOMADIK
config HAS_MTU
bool
+ select HAVE_SCHED_CLOCK
help
Support for Multi Timer Unit. MTU provides access
to multiple interrupt generating programmable
diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c
index b0bd6df..1d81891 100644
--- a/arch/arm/plat-nomadik/timer.c
+++ b/arch/arm/plat-nomadik/timer.c
@@ -17,9 +17,8 @@
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/err.h>
-#include <linux/cnt32_to_63.h>
-#include <linux/timer.h>
#include <asm/mach/time.h>
+#include <asm/sched_clock.h>
#include <plat/mtu.h>
@@ -52,81 +51,24 @@ static struct clocksource nmdk_clksrc = {
* Override the global weak sched_clock symbol with this
* local implementation which uses the clocksource to get some
* better resolution when scheduling the kernel.
- *
- * Because the hardware timer period may be quite short
- * (32.3 secs on the 133 MHz MTU timer selection on ux500)
- * and because cnt32_to_63() needs to be called at least once per
- * half period to work properly, a kernel keepwarm() timer is set up
- * to ensure this requirement is always met.
- *
- * Also the sched_clock timer will wrap around at some point,
- * here we set it to run continously for a year.
*/
-#define SCHED_CLOCK_MIN_WRAP 3600*24*365
-static struct timer_list cnt32_to_63_keepwarm_timer;
-static u32 sched_mult;
-static u32 sched_shift;
+static DEFINE_CLOCK_DATA(cd);
unsigned long long notrace sched_clock(void)
{
- u64 cycles;
+ u32 cyc;
if (unlikely(!mtu_base))
return 0;
- cycles = cnt32_to_63(-readl(mtu_base + MTU_VAL(0)));
- /*
- * sched_mult is guaranteed to be even so will
- * shift out bit 63
- */
- return (cycles * sched_mult) >> sched_shift;
+ cyc = -readl(mtu_base + MTU_VAL(0));
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
}
-/* Just kick sched_clock every so often */
-static void cnt32_to_63_keepwarm(unsigned long data)
+static void notrace nomadik_update_sched_clock(void)
{
- mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));
- (void) sched_clock();
-}
-
-/*
- * Set up a timer to keep sched_clock():s 32_to_63 algorithm warm
- * once in half a 32bit timer wrap interval.
- */
-static void __init nmdk_sched_clock_init(unsigned long rate)
-{
- u32 v;
- unsigned long delta;
- u64 days;
-
- /* Find the apropriate mult and shift factors */
- clocks_calc_mult_shift(&sched_mult, &sched_shift,
- rate, NSEC_PER_SEC, SCHED_CLOCK_MIN_WRAP);
- /* We need to multiply by an even number to get rid of bit 63 */
- if (sched_mult & 1)
- sched_mult++;
-
- /* Let's see what we get, take max counter and scale it */
- days = (0xFFFFFFFFFFFFFFFFLLU * sched_mult) >> sched_shift;
- do_div(days, NSEC_PER_SEC);
- do_div(days, (3600*24));
-
- pr_info("sched_clock: using %d bits @ %lu Hz wrap in %lu days\n",
- (64 - sched_shift), rate, (unsigned long) days);
-
- /*
- * Program a timer to kick us at half 32bit wraparound
- * Formula: seconds per wrap = (2^32) / f
- */
- v = 0xFFFFFFFFUL / rate;
- /* We want half of the wrap time to keep cnt32_to_63 warm */
- v /= 2;
- pr_debug("sched_clock: prescaled timer rate: %lu Hz, "
- "initialize keepwarm timer every %d seconds\n", rate, v);
- /* Convert seconds to jiffies */
- delta = msecs_to_jiffies(v*1000);
- setup_timer(&cnt32_to_63_keepwarm_timer, cnt32_to_63_keepwarm, delta);
- mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + delta));
+ u32 cyc = -readl(mtu_base + MTU_VAL(0));
+ update_sched_clock(&cd, cyc, (u32)~0);
}
/* Clockevent device: use one-shot mode */
@@ -236,7 +178,7 @@ void __init nmdk_timer_init(void)
pr_err("timer: failed to initialize clock source %s\n",
nmdk_clksrc.name);
- nmdk_sched_clock_init(rate);
+ init_sched_clock(&cd, nomadik_update_sched_clock, 32, rate);
/* Timer 1 is used for events */
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 10/13] ARM: omap: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (8 preceding siblings ...)
2010-12-16 9:30 ` [PATCH 09/13] ARM: nomadik: " Russell King - ARM Linux
@ 2010-12-16 9:31 ` Russell King - ARM Linux
2010-12-16 9:31 ` [PATCH 11/13] ARM: orion: " Russell King - ARM Linux
` (3 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:31 UTC (permalink / raw)
To: linux-arm-kernel
Convert omap to use the new sched_clock() infrastructure for extending
32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/plat-omap/counter_32k.c | 24 ++++++++++++++++++++++--
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 58e2fe3..726279f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -839,6 +839,7 @@ config ARCH_OMAP
select ARCH_REQUIRE_GPIOLIB
select ARCH_HAS_CPUFREQ
select GENERIC_CLOCKEVENTS
+ select HAVE_SCHED_CLOCK
select ARCH_HAS_HOLES_MEMORYMODEL
help
Support for TI's OMAP platform (OMAP1/2/3/4).
diff --git a/arch/arm/plat-omap/counter_32k.c b/arch/arm/plat-omap/counter_32k.c
index 0de0583..65bde61 100644
--- a/arch/arm/plat-omap/counter_32k.c
+++ b/arch/arm/plat-omap/counter_32k.c
@@ -17,6 +17,8 @@
#include <linux/clk.h>
#include <linux/io.h>
+#include <asm/sched_clock.h>
+
#include <plat/common.h>
#include <plat/board.h>
@@ -109,10 +111,25 @@ static struct clocksource clocksource_32k = {
* Returns current time from boot in nsecs. It's OK for this to wrap
* around for now, as it's just a relative time stamp.
*/
+static DEFINE_CLOCK_DATA(cd);
+
+/*
+ * Constants generated by clocks_calc_mult_shift(m, s, 32768, NSEC_PER_SEC, 60).
+ * This gives a resolution of about 30us and a wrap period of about 36hrs.
+ */
+#define SC_MULT 4000000000u
+#define SC_SHIFT 17
+
unsigned long long notrace sched_clock(void)
{
- return clocksource_cyc2ns(clocksource_32k.read(&clocksource_32k),
- clocksource_32k.mult, clocksource_32k.shift);
+ u32 cyc = clocksource_32k.read(&clocksource_32k);
+ return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT);
+}
+
+static void notrace omap_update_sched_clock(void)
+{
+ u32 cyc = clocksource_32k.read(&clocksource_32k);
+ update_sched_clock(&cd, cyc, (u32)~0);
}
/**
@@ -170,6 +187,9 @@ static int __init omap_init_clocksource_32k(void)
if (clocksource_register_hz(&clocksource_32k, 32768))
printk(err, clocksource_32k.name);
+
+ init_fixed_sched_clock(&cd, omap_update_sched_clock, 32,
+ 32768, SC_MULT, SC_SHIFT);
}
return 0;
}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 11/13] ARM: orion: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (9 preceding siblings ...)
2010-12-16 9:31 ` [PATCH 10/13] ARM: omap: " Russell King - ARM Linux
@ 2010-12-16 9:31 ` Russell King - ARM Linux
2010-12-16 9:31 ` [PATCH 12/13] ARM: versatile: " Russell King - ARM Linux
` (2 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:31 UTC (permalink / raw)
To: linux-arm-kernel
Convert orion platforms to use the new sched_clock() infrastructure for
extending 32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/plat-orion/time.c | 44 +++++++++-----------------------------------
2 files changed, 10 insertions(+), 35 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 726279f..92e8c01 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -997,6 +997,7 @@ config PLAT_IOP
config PLAT_ORION
bool
+ select HAVE_SCHED_CLOCK
config PLAT_PXA
bool
diff --git a/arch/arm/plat-orion/time.c b/arch/arm/plat-orion/time.c
index 123f96f..c3da247 100644
--- a/arch/arm/plat-orion/time.c
+++ b/arch/arm/plat-orion/time.c
@@ -13,11 +13,11 @@
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/cnt32_to_63.h>
#include <linux/timer.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <asm/sched_clock.h>
#include <asm/mach/time.h>
#include <mach/bridge-regs.h>
#include <mach/hardware.h>
@@ -44,52 +44,26 @@ static u32 ticks_per_jiffy;
/*
* Orion's sched_clock implementation. It has a resolution of
- * at least 7.5ns (133MHz TCLK) and a maximum value of 834 days.
- *
- * Because the hardware timer period is quite short (21 secs if
- * 200MHz TCLK) and because cnt32_to_63() needs to be called at
- * least once per half period to work properly, a kernel timer is
- * set up to ensure this requirement is always met.
+ * at least 7.5ns (133MHz TCLK).
*/
-#define TCLK2NS_SCALE_FACTOR 8
-
-static unsigned long tclk2ns_scale;
+static DEFINE_CLOCK_DATA(cd);
unsigned long long notrace sched_clock(void)
{
- unsigned long long v = cnt32_to_63(0xffffffff - readl(TIMER0_VAL));
- return (v * tclk2ns_scale) >> TCLK2NS_SCALE_FACTOR;
+ u32 cyc = 0xffffffff - readl(TIMER0_VAL);
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
}
-static struct timer_list cnt32_to_63_keepwarm_timer;
-static void cnt32_to_63_keepwarm(unsigned long data)
+static void notrace orion_update_sched_clock(void)
{
- mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));
- (void) sched_clock();
+ u32 cyc = 0xffffffff - readl(TIMER0_VAL);
+ update_sched_clock(&cd, cyc, (u32)~0);
}
static void __init setup_sched_clock(unsigned long tclk)
{
- unsigned long long v;
- unsigned long data;
-
- v = NSEC_PER_SEC;
- v <<= TCLK2NS_SCALE_FACTOR;
- v += tclk/2;
- do_div(v, tclk);
- /*
- * We want an even value to automatically clear the top bit
- * returned by cnt32_to_63() without an additional run time
- * instruction. So if the LSB is 1 then round it up.
- */
- if (v & 1)
- v++;
- tclk2ns_scale = v;
-
- data = (0xffffffffUL / tclk / 2 - 2) * HZ;
- setup_timer(&cnt32_to_63_keepwarm_timer, cnt32_to_63_keepwarm, data);
- mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));
+ init_sched_clock(&cd, orion_update_sched_clock, 32, tclk);
}
/*
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 12/13] ARM: versatile: convert sched_clock() to use new infrastructure
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (10 preceding siblings ...)
2010-12-16 9:31 ` [PATCH 11/13] ARM: orion: " Russell King - ARM Linux
@ 2010-12-16 9:31 ` Russell King - ARM Linux
2010-12-16 9:32 ` [PATCH 13/13] ARM: vexpress: add sched_clock() for Versatile Express Russell King - ARM Linux
2010-12-16 13:04 ` [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:31 UTC (permalink / raw)
To: linux-arm-kernel
Convert versatile platforms to use the new sched_clock() infrastructure
for extending 32bit counters to full 64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 2 +
arch/arm/mach-realview/core.c | 10 ++++
arch/arm/mach-versatile/core.c | 10 ++++
arch/arm/plat-versatile/include/plat/sched_clock.h | 6 +++
arch/arm/plat-versatile/sched-clock.c | 48 +++++++++++---------
5 files changed, 54 insertions(+), 22 deletions(-)
create mode 100644 arch/arm/plat-versatile/include/plat/sched_clock.h
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 92e8c01..34311f4 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -236,6 +236,7 @@ config ARCH_REALVIEW
bool "ARM Ltd. RealView family"
select ARM_AMBA
select COMMON_CLKDEV
+ select HAVE_SCHED_CLOCK
select ICST
select GENERIC_CLOCKEVENTS
select ARCH_WANT_OPTIONAL_GPIOLIB
@@ -250,6 +251,7 @@ config ARCH_VERSATILE
select ARM_AMBA
select ARM_VIC
select COMMON_CLKDEV
+ select HAVE_SCHED_CLOCK
select ICST
select GENERIC_CLOCKEVENTS
select ARCH_WANT_OPTIONAL_GPIOLIB
diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c
index 07c0815..3d653e0 100644
--- a/arch/arm/mach-realview/core.c
+++ b/arch/arm/mach-realview/core.c
@@ -52,6 +52,8 @@
#include <mach/irqs.h>
#include <plat/timer-sp.h>
+#include <plat/sched_clock.h>
+
#include "core.h"
/* used by entry-macro.S and platsmp.c */
@@ -658,6 +660,12 @@ void realview_leds_event(led_event_t ledevt)
#endif /* CONFIG_LEDS */
/*
+ * The sched_clock counter
+ */
+#define REFCOUNTER (__io_address(REALVIEW_SYS_BASE) + \
+ REALVIEW_SYS_24MHz_OFFSET)
+
+/*
* Where is the timer (VA)?
*/
void __iomem *timer0_va_base;
@@ -672,6 +680,8 @@ void __init realview_timer_init(unsigned int timer_irq)
{
u32 val;
+ versatile_sched_clock_init(REFCOUNTER, 24000000);
+
/*
* set clock frequency:
* REALVIEW_REFCLK is 32KHz
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index e38acb0..56cdc22 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -51,6 +51,8 @@
#include <mach/platform.h>
#include <plat/timer-sp.h>
+#include <plat/sched_clock.h>
+
#include "core.h"
/*
@@ -886,6 +888,12 @@ void __init versatile_init(void)
}
/*
+ * The sched_clock counter
+ */
+#define REFCOUNTER (__io_address(VERSATILE_SYS_BASE) + \
+ VERSATILE_SYS_24MHz_OFFSET)
+
+/*
* Where is the timer (VA)?
*/
#define TIMER0_VA_BASE __io_address(VERSATILE_TIMER0_1_BASE)
@@ -900,6 +908,8 @@ static void __init versatile_timer_init(void)
{
u32 val;
+ versatile_sched_clock_init(REFCOUNTER, 24000000);
+
/*
* set clock frequency:
* VERSATILE_REFCLK is 32KHz
diff --git a/arch/arm/plat-versatile/include/plat/sched_clock.h b/arch/arm/plat-versatile/include/plat/sched_clock.h
new file mode 100644
index 0000000..5c3e4fc
--- /dev/null
+++ b/arch/arm/plat-versatile/include/plat/sched_clock.h
@@ -0,0 +1,6 @@
+#ifndef ARM_PLAT_SCHED_CLOCK_H
+#define ARM_PLAT_SCHED_CLOCK_H
+
+void versatile_sched_clock_init(void __iomem *, unsigned long);
+
+#endif
diff --git a/arch/arm/plat-versatile/sched-clock.c b/arch/arm/plat-versatile/sched-clock.c
index 42efd14..3d6a4c2 100644
--- a/arch/arm/plat-versatile/sched-clock.c
+++ b/arch/arm/plat-versatile/sched-clock.c
@@ -18,37 +18,41 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/cnt32_to_63.h>
#include <linux/io.h>
#include <linux/sched.h>
-#include <asm/div64.h>
-#include <mach/hardware.h>
-#include <mach/platform.h>
+#include <asm/sched_clock.h>
+#include <plat/sched_clock.h>
-#ifdef VERSATILE_SYS_BASE
-#define REFCOUNTER (__io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_24MHz_OFFSET)
-#endif
-
-#ifdef REALVIEW_SYS_BASE
-#define REFCOUNTER (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_24MHz_OFFSET)
-#endif
+static DEFINE_CLOCK_DATA(cd);
+static void __iomem *ctr;
/*
- * This is the Realview and Versatile sched_clock implementation. This
- * has a resolution of 41.7ns, and a maximum value of about 35583 days.
- *
- * The return value is guaranteed to be monotonic in that range as
- * long as there is always less than 89 seconds between successive
- * calls to this function.
+ * Constants generated by clocks_calc_mult_shift(m, s, 24MHz, NSEC_PER_SEC, 60).
+ * This gives a resolution of about 41ns and a wrap period of about 178s.
*/
+#define SC_MULT 2796202667u
+#define SC_SHIFT 26
+
unsigned long long notrace sched_clock(void)
{
- unsigned long long v = cnt32_to_63(readl(REFCOUNTER));
+ if (ctr) {
+ u32 cyc = readl(ctr);
+ return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0,
+ SC_MULT, SC_SHIFT);
+ } else
+ return 0;
+}
- /* the <<1 gets rid of the cnt_32_to_63 top bit saving on a bic insn */
- v *= 125<<1;
- do_div(v, 3<<1);
+static void notrace versatile_update_sched_clock(void)
+{
+ u32 cyc = readl(ctr);
+ update_sched_clock(&cd, cyc, (u32)~0);
+}
- return v;
+void __init versatile_sched_clock_init(void __iomem *reg, unsigned long rate)
+{
+ ctr = reg;
+ init_fixed_sched_clock(&cd, versatile_update_sched_clock,
+ 32, rate, SC_MULT, SC_SHIFT);
}
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 13/13] ARM: vexpress: add sched_clock() for Versatile Express
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (11 preceding siblings ...)
2010-12-16 9:31 ` [PATCH 12/13] ARM: versatile: " Russell King - ARM Linux
@ 2010-12-16 9:32 ` Russell King - ARM Linux
2010-12-16 13:04 ` [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
13 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 9:32 UTC (permalink / raw)
To: linux-arm-kernel
Add a sched_clock() implementation to Versatile Express using the new
sched_clock() infrastructure for extending 32bit counters to full
64-bit nanoseconds.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/Kconfig | 1 +
arch/arm/mach-vexpress/v2m.c | 4 ++++
arch/arm/plat-versatile/Makefile | 4 +---
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 34311f4..17b5ff0 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -268,6 +268,7 @@ config ARCH_VEXPRESS
select COMMON_CLKDEV
select GENERIC_CLOCKEVENTS
select HAVE_CLK
+ select HAVE_SCHED_CLOCK
select ICST
select PLAT_VERSATILE
help
diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c
index 7eaa232..8c28310 100644
--- a/arch/arm/mach-vexpress/v2m.c
+++ b/arch/arm/mach-vexpress/v2m.c
@@ -22,6 +22,8 @@
#include <mach/clkdev.h>
#include <mach/motherboard.h>
+#include <plat/sched_clock.h>
+
#include <plat/timer-sp.h>
#include "core.h"
@@ -50,6 +52,8 @@ void __init v2m_map_io(struct map_desc *tile, size_t num)
static void __init v2m_timer_init(void)
{
+ versatile_sched_clock_init(MMIO_P2V(V2M_SYS_24MHZ), 24000000);
+
writel(0, MMIO_P2V(V2M_TIMER0) + TIMER_CTRL);
writel(0, MMIO_P2V(V2M_TIMER1) + TIMER_CTRL);
diff --git a/arch/arm/plat-versatile/Makefile b/arch/arm/plat-versatile/Makefile
index 5cf88e8..230eb37 100644
--- a/arch/arm/plat-versatile/Makefile
+++ b/arch/arm/plat-versatile/Makefile
@@ -1,7 +1,5 @@
-obj-y := clock.o
+obj-y := clock.o sched-clock.o
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
-obj-$(CONFIG_ARCH_REALVIEW) += sched-clock.o
-obj-$(CONFIG_ARCH_VERSATILE) += sched-clock.o
ifeq ($(CONFIG_LEDS_CLASS),y)
obj-$(CONFIG_ARCH_REALVIEW) += leds.o
obj-$(CONFIG_ARCH_VERSATILE) += leds.o
--
1.6.2.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 04/13] ARM: pxa: convert sched_clock() to use new infrastructure
2010-12-16 9:28 ` [PATCH 04/13] ARM: pxa: " Russell King - ARM Linux
@ 2010-12-16 10:05 ` Uwe Kleine-König
2010-12-16 10:14 ` Russell King - ARM Linux
0 siblings, 1 reply; 20+ messages in thread
From: Uwe Kleine-König @ 2010-12-16 10:05 UTC (permalink / raw)
To: linux-arm-kernel
Hello Russell,
a very minor nit ...
On Thu, Dec 16, 2010 at 09:28:48AM +0000, Russell King - ARM Linux wrote:
> + return cyc_to_sched_clock(&cd, cyc, (u32)~0);
The value of ~0 is compiler implementation defined, so in general
~((u32)0) is saver. (Though I admit that on arm ~0 will probably always
be -1, still I think using the save construct doesn't hurt here.)
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 04/13] ARM: pxa: convert sched_clock() to use new infrastructure
2010-12-16 10:05 ` Uwe Kleine-König
@ 2010-12-16 10:14 ` Russell King - ARM Linux
0 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 10:14 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 16, 2010 at 11:05:23AM +0100, Uwe Kleine-K?nig wrote:
> Hello Russell,
>
> a very minor nit ...
>
> On Thu, Dec 16, 2010 at 09:28:48AM +0000, Russell King - ARM Linux wrote:
> > + return cyc_to_sched_clock(&cd, cyc, (u32)~0);
> The value of ~0 is compiler implementation defined, so in general
> ~((u32)0) is saver. (Though I admit that on arm ~0 will probably always
> be -1, still I think using the save construct doesn't hurt here.)
I've been debating about using something like CLOCKSOURCE_MASK() here,
but haven't yet decided it to be worth it. I also toyed with the idea
of storing the mask in the struct, but that adds extra expense when
it's not needed - everyone should know how many bits are in their
counter at build time, and if not, they shouldn't penalize those who do.
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 01/13] ARM: sched_clock: provide common infrastructure for sched_clock()
2010-12-16 9:27 ` [PATCH 01/13] ARM: sched_clock: provide common infrastructure for sched_clock() Russell King - ARM Linux
@ 2010-12-16 12:52 ` Russell King - ARM Linux
0 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 12:52 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 16, 2010 at 09:27:47AM +0000, Russell King - ARM Linux wrote:
> Provide common sched_clock() infrastructure for platforms to use to
> create a 64-bit ns based sched_clock() implementation from a counter
> running at a non-variable clock rate.
Some of the comments in the patch were only relevant to a previous
version. He's an updated version with proper comments to avoid
confusion.
arch/arm/Kconfig | 3 +
arch/arm/include/asm/sched_clock.h | 115 ++++++++++++++++++++++++++++++++++++
arch/arm/kernel/Makefile | 1 +
arch/arm/kernel/sched_clock.c | 69 +++++++++++++++++++++
4 files changed, 188 insertions(+), 0 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 49778bb..ed7a0a7 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -38,6 +38,9 @@ config HAVE_PWM
config SYS_SUPPORTS_APM_EMULATION
bool
+config HAVE_SCHED_CLOCK
+ bool
+
config GENERIC_GPIO
bool
diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h
new file mode 100644
index 0000000..82d4d3f
--- /dev/null
+++ b/arch/arm/include/asm/sched_clock.h
@@ -0,0 +1,115 @@
+/*
+ * 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
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+struct clock_data {
+ u64 epoch_ns;
+ u32 epoch_cyc;
+ u32 epoch_cyc_copy;
+ u32 mult;
+ u32 shift;
+};
+
+#define DEFINE_CLOCK_DATA(name) struct clock_data name
+
+static inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift)
+{
+ return (cyc * mult) >> shift;
+}
+
+/*
+ * Atomically update the sched_clock epoch. Your update callback will
+ * be called from a timer before the counter wraps - read the current
+ * counter value, and call this function to safely move the epochs
+ * forward.
+ */
+static inline void update_sched_clock(struct clock_data *cd, u32 cyc, u32 mask)
+{
+ u64 ns = cd->epoch_ns +
+ cyc_to_ns((cyc - cd->epoch_cyc) & 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().
+ */
+ cd->epoch_cyc = cyc;
+ smp_wmb();
+ cd->epoch_ns = ns;
+ smp_wmb();
+ cd->epoch_cyc_copy = cyc;
+}
+
+/*
+ * If your clock rate is known at compile time, using this will allow
+ * you to optimize the mult/shift loads away. This is paired with
+ * init_fixed_sched_clock() to ensure that your mult/shift are correct.
+ */
+static inline unsigned long long cyc_to_fixed_sched_clock(struct clock_data *cd,
+ u32 cyc, u32 mask, u32 mult, u32 shift)
+{
+ 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, mult, shift);
+}
+
+/*
+ * Otherwise, you need to use this, which will obtain the mult/shift
+ * from the clock_data structure. Use init_sched_clock() with this.
+ */
+static inline unsigned long long cyc_to_sched_clock(struct clock_data *cd,
+ u32 cyc, u32 mask)
+{
+ return cyc_to_fixed_sched_clock(cd, cyc, mask, cd->mult, cd->shift);
+}
+
+/*
+ * Initialize the clock data - calculate the appropriate multiplier
+ * and shift. Also setup a timer to ensure that the epoch is refreshed
+ * at the appropriate time interval, which will call your update
+ * handler.
+ */
+void init_sched_clock(struct clock_data *, void (*)(void),
+ unsigned int, unsigned long);
+
+/*
+ * Use this initialization function rather than init_sched_clock() if
+ * you're using cyc_to_fixed_sched_clock, which will warn if your
+ * constants are incorrect.
+ */
+static inline void init_fixed_sched_clock(struct clock_data *cd,
+ void (*update)(void), unsigned int bits, unsigned long rate,
+ u32 mult, u32 shift)
+{
+ init_sched_clock(cd, update, bits, rate);
+ if (cd->mult != mult || cd->shift != shift) {
+ pr_crit("sched_clock: wrong multiply/shift: %u>>%u vs calculated %u>>%u\n"
+ "sched_clock: fix multiply/shift to avoid scheduler hiccups\n",
+ mult, shift, cd->mult, cd->shift);
+ }
+}
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 679851a..fd3ec49 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_MODULES) += armksyms.o module.o
obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o isa.o
+obj-$(CONFIG_HAVE_SCHED_CLOCK) += sched_clock.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c
new file mode 100644
index 0000000..b10b02b
--- /dev/null
+++ b/arch/arm/kernel/sched_clock.c
@@ -0,0 +1,69 @@
+/*
+ * 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/sched.h>
+#include <linux/timer.h>
+
+#include <asm/sched_clock.h>
+
+static void sched_clock_poll(unsigned long wrap_ticks);
+static DEFINE_TIMER(sched_clock_timer, sched_clock_poll, 0, 0);
+static void (*sched_clock_update_fn)(void);
+
+static void sched_clock_poll(unsigned long wrap_ticks)
+{
+ mod_timer(&sched_clock_timer, round_jiffies(jiffies + wrap_ticks));
+ sched_clock_update_fn();
+}
+
+void __init init_sched_clock(struct clock_data *cd, void (*update)(void),
+ unsigned int clock_bits, unsigned long rate)
+{
+ unsigned long r, w;
+ u64 res, wrap;
+ char r_unit;
+
+ sched_clock_update_fn = update;
+
+ /* calculate the mult/shift to convert counter ticks to ns. */
+ clocks_calc_mult_shift(&cd->mult, &cd->shift, rate, NSEC_PER_SEC, 60);
+
+ r = rate;
+ if (r >= 4000000) {
+ r /= 1000000;
+ r_unit = 'M';
+ } else {
+ r /= 1000;
+ r_unit = 'k';
+ }
+
+ /* calculate how many ns until we wrap */
+ wrap = cyc_to_ns((1ULL << clock_bits) - 1, cd->mult, cd->shift);
+ do_div(wrap, 1000000);
+ 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",
+ clock_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));
+ sched_clock_poll(sched_clock_timer.data);
+
+ /*
+ * Ensure that sched_clock() starts off at 0ns
+ */
+ cd->epoch_ns = 0;
+}
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 0/13] 64-bit sched_clock
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
` (12 preceding siblings ...)
2010-12-16 9:32 ` [PATCH 13/13] ARM: vexpress: add sched_clock() for Versatile Express Russell King - ARM Linux
@ 2010-12-16 13:04 ` Russell King - ARM Linux
2010-12-19 22:23 ` Linus Walleij
13 siblings, 1 reply; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-16 13:04 UTC (permalink / raw)
To: linux-arm-kernel
There is one issue which remains with this code - that is the
initialization ordering.
init/main.c:
setup_arch(&command_line);
...
sched_init();
...
early_irq_init();
init_IRQ();
...
timekeeping_init();
time_init();
sched_init() calls init_idle() which calls sched_clock() to set the
start time for the idle thread. However, our sched_clock() is not
initialized (in general) until init/main.c calls time_init().
This results in a big step in the sched_clock() return value during
boot, which is probably not desirable. At the moment, I work around
this by explicitly setting the epoch_ns to zero after the first update
callback - but this means that sched_clock() will only start at this
point, which may not be desirable for everyone.
Some sched_clock() implementations rely upon clocksource code (they
use the clocksource read function) and this is only initialized when
the clocksource is registered. There's also the matter of the clock
rate.
It's probably not a good idea to register clocksources before
timekeeping_init() has happened either.
I can provide an init_early() method in the machine description which
setup_arch() can call as the last thing it does - this will happen
after the ->reserve and ->map_io callbacks, but before interrupts and
timers have been setup. This is the ideal time to setup the
sched_clock(). However, that means digging through lots of arch code
to sort out what can be moved where, and that's going to be inherently
unreliable.
I think init_early() has merit in other ways - it'll allow us to remove
crap from the ->map_io callbacks which is doing stuff like registering
clk structures and so forth.
Any other suggestions?
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 0/13] 64-bit sched_clock
2010-12-16 13:04 ` [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
@ 2010-12-19 22:23 ` Linus Walleij
2010-12-19 23:39 ` Russell King - ARM Linux
0 siblings, 1 reply; 20+ messages in thread
From: Linus Walleij @ 2010-12-19 22:23 UTC (permalink / raw)
To: linux-arm-kernel
2010/12/16 Russell King - ARM Linux <linux@arm.linux.org.uk>:
> I can provide an init_early() method in the machine description which
> setup_arch() can call as the last thing it does - this will happen
> after the ->reserve and ->map_io callbacks, but before interrupts and
> timers have been setup. ?This is the ideal time to setup the
> sched_clock(). ?However, that means digging through lots of arch code
> to sort out what can be moved where, and that's going to be inherently
> unreliable.
>
> I think init_early() has merit in other ways - it'll allow us to remove
> crap from the ->map_io callbacks which is doing stuff like registering
> clk structures and so forth.
I agree. I was contemplating a clock_init() or similar a way back
for setting up clock trees early. We would have to move clock tree
registration in front of the init_sched_clock() call in the init_early()
handler in many cases since the rate often comes from the clock
framework, but this is indeed where it belongs.
So a clear ACK for this approach.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 0/13] 64-bit sched_clock
2010-12-19 22:23 ` Linus Walleij
@ 2010-12-19 23:39 ` Russell King - ARM Linux
0 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2010-12-19 23:39 UTC (permalink / raw)
To: linux-arm-kernel
On Sun, Dec 19, 2010 at 11:23:02PM +0100, Linus Walleij wrote:
> 2010/12/16 Russell King - ARM Linux <linux@arm.linux.org.uk>:
>
> > I can provide an init_early() method in the machine description which
> > setup_arch() can call as the last thing it does - this will happen
> > after the ->reserve and ->map_io callbacks, but before interrupts and
> > timers have been setup. ?This is the ideal time to setup the
> > sched_clock(). ?However, that means digging through lots of arch code
> > to sort out what can be moved where, and that's going to be inherently
> > unreliable.
> >
> > I think init_early() has merit in other ways - it'll allow us to remove
> > crap from the ->map_io callbacks which is doing stuff like registering
> > clk structures and so forth.
>
> I agree. I was contemplating a clock_init() or similar a way back
> for setting up clock trees early. We would have to move clock tree
> registration in front of the init_sched_clock() call in the init_early()
> handler in many cases since the rate often comes from the clock
> framework, but this is indeed where it belongs.
Yes, that was indeed one of the subsystems that I thought platforms
should initialize there - one of my motivations is in connection with
6433/1 and 6434/1.
If we have everyone setting up their clock subsystems in init_early,
then we can have both the TWD and SP804 always getting their clock
rates from the clk API, rather than having the conditional approach
in those two patches.
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2010-12-19 23:39 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-16 9:25 [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
2010-12-16 9:27 ` [PATCH 01/13] ARM: sched_clock: provide common infrastructure for sched_clock() Russell King - ARM Linux
2010-12-16 12:52 ` Russell King - ARM Linux
2010-12-16 9:28 ` [PATCH 02/13] ARM: ixp4xx: convert sched_clock() to use new infrastructure Russell King - ARM Linux
2010-12-16 9:28 ` [PATCH 03/13] ARM: mmp: " Russell King - ARM Linux
2010-12-16 9:28 ` [PATCH 04/13] ARM: pxa: " Russell King - ARM Linux
2010-12-16 10:05 ` Uwe Kleine-König
2010-12-16 10:14 ` Russell King - ARM Linux
2010-12-16 9:29 ` [PATCH 05/13] ARM: sa1100: " Russell King - ARM Linux
2010-12-16 9:29 ` [PATCH 06/13] ARM: tegra: " Russell King - ARM Linux
2010-12-16 9:29 ` [PATCH 07/13] ARM: u300: " Russell King - ARM Linux
2010-12-16 9:30 ` [PATCH 08/13] ARM: iop: " Russell King - ARM Linux
2010-12-16 9:30 ` [PATCH 09/13] ARM: nomadik: " Russell King - ARM Linux
2010-12-16 9:31 ` [PATCH 10/13] ARM: omap: " Russell King - ARM Linux
2010-12-16 9:31 ` [PATCH 11/13] ARM: orion: " Russell King - ARM Linux
2010-12-16 9:31 ` [PATCH 12/13] ARM: versatile: " Russell King - ARM Linux
2010-12-16 9:32 ` [PATCH 13/13] ARM: vexpress: add sched_clock() for Versatile Express Russell King - ARM Linux
2010-12-16 13:04 ` [PATCH 0/13] 64-bit sched_clock Russell King - ARM Linux
2010-12-19 22:23 ` Linus Walleij
2010-12-19 23:39 ` Russell King - ARM Linux
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).