]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/x86/kernel/tsc.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/ide
[karo-tx-linux.git] / arch / x86 / kernel / tsc.c
index c3f7602cd0386b2fb0a1a7437263f9c71f27d03c..c7c4d9c51e99fe582b71ab1f2e3ca9ffaae2226c 100644 (file)
@@ -168,21 +168,20 @@ static void cyc2ns_write_end(int cpu, struct cyc2ns_data *data)
  *              ns = cycles * cyc2ns_scale / SC
  *
  *      And since SC is a constant power of two, we can convert the div
- *  into a shift.
+ *  into a shift. The larger SC is, the more accurate the conversion, but
+ *  cyc2ns_scale needs to be a 32-bit value so that 32-bit multiplication
+ *  (64-bit result) can be used.
  *
- *  We can use khz divisor instead of mhz to keep a better precision, since
- *  cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
+ *  We can use khz divisor instead of mhz to keep a better precision.
  *  (mathieu.desnoyers@polymtl.ca)
  *
  *                      -johnstul@us.ibm.com "math is hard, lets go shopping!"
  */
 
-#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
-
 static void cyc2ns_data_init(struct cyc2ns_data *data)
 {
        data->cyc2ns_mul = 0;
-       data->cyc2ns_shift = CYC2NS_SCALE_FACTOR;
+       data->cyc2ns_shift = 0;
        data->cyc2ns_offset = 0;
        data->__count = 0;
 }
@@ -216,14 +215,14 @@ static inline unsigned long long cycles_2_ns(unsigned long long cyc)
 
        if (likely(data == tail)) {
                ns = data->cyc2ns_offset;
-               ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
+               ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, data->cyc2ns_shift);
        } else {
                data->__count++;
 
                barrier();
 
                ns = data->cyc2ns_offset;
-               ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
+               ns += mul_u64_u32_shr(cyc, data->cyc2ns_mul, data->cyc2ns_shift);
 
                barrier();
 
@@ -257,12 +256,22 @@ static void set_cyc2ns_scale(unsigned long cpu_khz, int cpu)
         * time function is continuous; see the comment near struct
         * cyc2ns_data.
         */
-       data->cyc2ns_mul =
-               DIV_ROUND_CLOSEST(NSEC_PER_MSEC << CYC2NS_SCALE_FACTOR,
-                                 cpu_khz);
-       data->cyc2ns_shift = CYC2NS_SCALE_FACTOR;
+       clocks_calc_mult_shift(&data->cyc2ns_mul, &data->cyc2ns_shift, cpu_khz,
+                              NSEC_PER_MSEC, 0);
+
+       /*
+        * cyc2ns_shift is exported via arch_perf_update_userpage() where it is
+        * not expected to be greater than 31 due to the original published
+        * conversion algorithm shifting a 32-bit value (now specifies a 64-bit
+        * value) - refer perf_event_mmap_page documentation in perf_event.h.
+        */
+       if (data->cyc2ns_shift == 32) {
+               data->cyc2ns_shift = 31;
+               data->cyc2ns_mul >>= 1;
+       }
+
        data->cyc2ns_offset = ns_now -
-               mul_u64_u32_shr(tsc_now, data->cyc2ns_mul, CYC2NS_SCALE_FACTOR);
+               mul_u64_u32_shr(tsc_now, data->cyc2ns_mul, data->cyc2ns_shift);
 
        cyc2ns_write_end(cpu, data);