clocksource: Provide a generic mult/shift factor calculation
authorThomas Gleixner <tglx@linutronix.de>
Wed, 11 Nov 2009 14:05:29 +0000 (14:05 +0000)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 13 Nov 2009 19:46:23 +0000 (20:46 +0100)
MIPS has two functions to calculcate the mult/shift factors for clock
sources and clock events at run time. ARM needs such functions as
well.

Implement a function which calculates the mult/shift factors based on
the frequencies to which and from which is converted. The function
also has a parameter to specify the minimum conversion range in
seconds. This range is guaranteed not to produce a 64bit overflow when
a value is multiplied with the calculated mult factor. The larger the
conversion range the less becomes the conversion accuracy.

Provide two inline wrappers which handle clock events and clock
sources. For clock events the "from" frequency is nano seconds per
second which corresponds to 1GHz and "to" is the device frequency. For
clock sources "from" is the device frequency and "to" is nano seconds
per second.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Mikael Pettersson <mikpe@it.uu.se>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Acked-by: Linus Walleij <linus.walleij@stericsson.com>
Cc: John Stultz <johnstul@us.ibm.com>
LKML-Reference: <20091111134229.766673305@linutronix.de>

include/linux/clockchips.h
include/linux/clocksource.h
kernel/time/clocksource.c

index 3b5841016276b8f9293393775b739daaed814389..4d438b0bc10a5c6849e7d56a8e102442ef67f27d 100644 (file)
@@ -130,6 +130,13 @@ extern int clockevents_program_event(struct clock_event_device *dev,
 
 extern void clockevents_handle_noop(struct clock_event_device *dev);
 
+static inline void
+clockevents_calc_mult_shift(struct clock_event_device *ce, u32 freq, u32 minsec)
+{
+       return clocks_calc_mult_shift(&ce->mult, &ce->shift, NSEC_PER_SEC,
+                                     freq, minsec);
+}
+
 #ifdef CONFIG_GENERIC_CLOCKEVENTS
 extern void clockevents_notify(unsigned long reason, void *arg);
 #else
index 83d2fbd81b93a056ab7a55c1a1323deb744ef75a..f57f88250526809c8e657cfcf24ca035e7a0b0bc 100644 (file)
@@ -279,6 +279,16 @@ extern void clocksource_resume(void);
 extern struct clocksource * __init __weak clocksource_default_clock(void);
 extern void clocksource_mark_unstable(struct clocksource *cs);
 
+extern void
+clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 minsec);
+
+static inline void
+clocksource_calc_mult_shift(struct clocksource *cs, u32 freq, u32 minsec)
+{
+       return clocks_calc_mult_shift(&cs->mult, &cs->shift, freq,
+                                     NSEC_PER_SEC, minsec);
+}
+
 #ifdef CONFIG_GENERIC_TIME_VSYSCALL
 extern void update_vsyscall(struct timespec *ts, struct clocksource *c);
 extern void update_vsyscall_tz(void);
index 5e18c6ab2c6ade6ee88263ed51330dcc2783c139..407c0894ef370d90268d1dd73e46f195919b8555 100644 (file)
@@ -107,6 +107,59 @@ u64 timecounter_cyc2time(struct timecounter *tc,
 }
 EXPORT_SYMBOL(timecounter_cyc2time);
 
+/**
+ * clocks_calc_mult_shift - calculate mult/shift factors for scaled math of clocks
+ * @mult:      pointer to mult variable
+ * @shift:     pointer to shift variable
+ * @from:      frequency to convert from
+ * @to:                frequency to convert to
+ * @minsec:    guaranteed runtime conversion range in seconds
+ *
+ * The function evaluates the shift/mult pair for the scaled math
+ * operations of clocksources and clockevents.
+ *
+ * @to and @from are frequency values in HZ. For clock sources @to is
+ * NSEC_PER_SEC == 1GHz and @from is the counter frequency. For clock
+ * event @to is the counter frequency and @from is NSEC_PER_SEC.
+ *
+ * The @minsec conversion range argument controls the time frame in
+ * seconds which must be covered by the runtime conversion with the
+ * calculated mult and shift factors. This guarantees that no 64bit
+ * overflow happens when the input value of the conversion is
+ * multiplied with the calculated mult factor. Larger ranges may
+ * reduce the conversion accuracy by chosing smaller mult and shift
+ * factors.
+ */
+void
+clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 minsec)
+{
+       u64 tmp;
+       u32 sft, sftacc= 32;
+
+       /*
+        * Calculate the shift factor which is limiting the conversion
+        * range:
+        */
+       tmp = ((u64)minsec * from) >> 32;
+       while (tmp) {
+               tmp >>=1;
+               sftacc--;
+       }
+
+       /*
+        * Find the conversion shift/mult pair which has the best
+        * accuracy and fits the maxsec conversion range:
+        */
+       for (sft = 32; sft > 0; sft--) {
+               tmp = (u64) to << sft;
+               do_div(tmp, from);
+               if ((tmp >> sftacc) == 0)
+                       break;
+       }
+       *mult = tmp;
+       *shift = sft;
+}
+
 /*[Clocksource internal variables]---------
  * curr_clocksource:
  *     currently selected clocksource.