[ARM] 4550/1: sched_clock on PXA should cope with run time clock rate selection
authorNicolas Pitre <nico@cam.org>
Fri, 17 Aug 2007 15:55:22 +0000 (16:55 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Fri, 12 Oct 2007 20:14:35 +0000 (21:14 +0100)
The previous implementation was relying on compile time optimizations
based on a constant clock rate.  However, support for different PXA
flavors in the same kernel binary requires that the clock be selected at
run time, so here it is.

Let's move this code to a more appropriate location while at it.

Signed-off-by: Nicolas Pitre <npitre@mvista.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mach-pxa/generic.c
arch/arm/mach-pxa/time.c

index 5510f6fdce55e4dc8b2f55f93359e1b245da619a..9d6a2c00d7628de71def0616e5411593c5facc46 100644 (file)
 #include <linux/pm.h>
 #include <linux/string.h>
 
-#include <linux/sched.h>
-#include <asm/cnt32_to_63.h>
-#include <asm/div64.h>
-
 #include <asm/hardware.h>
 #include <asm/irq.h>
 #include <asm/system.h>
 #include "devices.h"
 #include "generic.h"
 
-/*
- * This is the PXA2xx sched_clock implementation. This has a resolution
- * of at least 308ns and a maximum value that depends on the value of
- * CLOCK_TICK_RATE.
- *
- * 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.
- */
-unsigned long long sched_clock(void)
-{
-       unsigned long long v = cnt32_to_63(OSCR);
-       /* Note: top bit ov v needs cleared unless multiplier is even. */
-
-#if    CLOCK_TICK_RATE == 3686400
-       /* 1E9 / 3686400 => 78125 / 288, max value = 32025597s (370 days). */
-       /* The <<1 is used to get rid of tick.hi top bit */
-       v *= 78125<<1;
-       do_div(v, 288<<1);
-#elif  CLOCK_TICK_RATE == 3250000
-       /* 1E9 / 3250000 => 4000 / 13, max value = 709490156s (8211 days) */
-       v *= 4000;
-       do_div(v, 13);
-#elif  CLOCK_TICK_RATE == 3249600
-       /* 1E9 / 3249600 => 625000 / 2031, max value = 4541295s (52 days) */
-       v *= 625000;
-       do_div(v, 2031);
-#else
-#warning "consider fixing sched_clock for your value of CLOCK_TICK_RATE"
-       /*
-        * 96-bit math to perform tick * NSEC_PER_SEC / CLOCK_TICK_RATE for
-        * any value of CLOCK_TICK_RATE. Max value is in the 80 thousand
-        * years range and truncation to unsigned long long limits it to
-        * sched_clock's max range of ~584 years.  This is nice but with
-        * higher computation cost.
-        */
-       {
-               union {
-                       unsigned long long val;
-                       struct { unsigned long lo, hi; };
-               } x;
-               unsigned long long y;
-
-               x.val = v;
-               x.hi &= 0x7fffffff;
-               y = (unsigned long long)x.lo * NSEC_PER_SEC;
-               x.lo = y;
-               y = (y >> 32) + (unsigned long long)x.hi * NSEC_PER_SEC;
-               x.hi = do_div(y, CLOCK_TICK_RATE);
-               do_div(x.val, CLOCK_TICK_RATE);
-               x.hi += y;
-               v = x.val;
-       }
-#endif
-
-       return v;
-}
-
 /*
  * Handy function to set GPIO alternate functions
  */
index 98d27e646b0925a2266eb6fe63f9c29361a4c109..7916311547cad0f8a31e4543c0d5d6d846f4ce28 100644 (file)
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/clockchips.h>
+#include <linux/sched.h>
 
+#include <asm/div64.h>
+#include <asm/cnt32_to_63.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/time.h>
 #include <asm/arch/pxa-regs.h>
 
+/*
+ * This is PXA's sched_clock implementation. This has a resolution
+ * of at least 308 ns and a maximum value of 208 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 sched_clock() which should always be the case in practice.
+ */
+
+#define OSCR2NS_SCALE_FACTOR 10
+
+static unsigned long oscr2ns_scale;
+
+static void __init set_oscr2ns_scale(unsigned long oscr_rate)
+{
+       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++;
+}
+
+unsigned long long sched_clock(void)
+{
+       unsigned long long v = cnt32_to_63(OSCR);
+       return (v * oscr2ns_scale) >> OSCR2NS_SCALE_FACTOR;
+}
+
+
 static irqreturn_t
 pxa_ost0_interrupt(int irq, void *dev_id)
 {
@@ -152,6 +189,8 @@ static void __init pxa_timer_init(void)
        OIER = 0;
        OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3;
 
+       set_oscr2ns_scale(CLOCK_TICK_RATE);
+
        ckevt_pxa_osmr0.mult =
                div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, ckevt_pxa_osmr0.shift);
        ckevt_pxa_osmr0.max_delta_ns =