Merge branch 'irq-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / init / calibrate.c
index 24fe022c55f976004c7acfb1c22ed6b7f0ce0954..76ac9194cbc437911e2394f8e94f4530eff716c8 100644 (file)
@@ -110,8 +110,8 @@ static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;}
 
 /*
  * This is the number of bits of precision for the loops_per_jiffy.  Each
- * bit takes on average 1.5/HZ seconds.  This (like the original) is a little
- * better than 1%
+ * time we refine our estimate after the first takes 1.5/HZ seconds, so try
+ * to start with a good estimate.
  * For the boot cpu we can skip the delay calibration and assign it a value
  * calculated based on the timer frequency.
  * For the rest of the CPUs we cannot assume that the timer frequency is same as
@@ -119,10 +119,72 @@ static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;}
  */
 #define LPS_PREC 8
 
+static unsigned long __cpuinit calibrate_delay_converge(void)
+{
+       /* First stage - slowly accelerate to find initial bounds */
+       unsigned long lpj, lpj_base, ticks, loopadd, loopadd_base, chop_limit;
+       int trials = 0, band = 0, trial_in_band = 0;
+
+       lpj = (1<<12);
+
+       /* wait for "start of" clock tick */
+       ticks = jiffies;
+       while (ticks == jiffies)
+               ; /* nothing */
+       /* Go .. */
+       ticks = jiffies;
+       do {
+               if (++trial_in_band == (1<<band)) {
+                       ++band;
+                       trial_in_band = 0;
+               }
+               __delay(lpj * band);
+               trials += band;
+       } while (ticks == jiffies);
+       /*
+        * We overshot, so retreat to a clear underestimate. Then estimate
+        * the largest likely undershoot. This defines our chop bounds.
+        */
+       trials -= band;
+       loopadd_base = lpj * band;
+       lpj_base = lpj * trials;
+
+recalibrate:
+       lpj = lpj_base;
+       loopadd = loopadd_base;
+
+       /*
+        * Do a binary approximation to get lpj set to
+        * equal one clock (up to LPS_PREC bits)
+        */
+       chop_limit = lpj >> LPS_PREC;
+       while (loopadd > chop_limit) {
+               lpj += loopadd;
+               ticks = jiffies;
+               while (ticks == jiffies)
+                       ; /* nothing */
+               ticks = jiffies;
+               __delay(lpj);
+               if (jiffies != ticks)   /* longer than 1 tick */
+                       lpj -= loopadd;
+               loopadd >>= 1;
+       }
+       /*
+        * If we incremented every single time possible, presume we've
+        * massively underestimated initially, and retry with a higher
+        * start, and larger range. (Only seen on x86_64, due to SMIs)
+        */
+       if (lpj + loopadd * 2 == lpj_base + loopadd_base * 2) {
+               lpj_base = lpj;
+               loopadd_base <<= 2;
+               goto recalibrate;
+       }
+
+       return lpj;
+}
+
 void __cpuinit calibrate_delay(void)
 {
-       unsigned long ticks, loopbit;
-       int lps_precision = LPS_PREC;
        static bool printed;
 
        if (preset_lpj) {
@@ -139,39 +201,9 @@ void __cpuinit calibrate_delay(void)
                        pr_info("Calibrating delay using timer "
                                "specific routine.. ");
        } else {
-               loops_per_jiffy = (1<<12);
-
                if (!printed)
                        pr_info("Calibrating delay loop... ");
-               while ((loops_per_jiffy <<= 1) != 0) {
-                       /* wait for "start of" clock tick */
-                       ticks = jiffies;
-                       while (ticks == jiffies)
-                               /* nothing */;
-                       /* Go .. */
-                       ticks = jiffies;
-                       __delay(loops_per_jiffy);
-                       ticks = jiffies - ticks;
-                       if (ticks)
-                               break;
-               }
-
-               /*
-                * Do a binary approximation to get loops_per_jiffy set to
-                * equal one clock (up to lps_precision bits)
-                */
-               loops_per_jiffy >>= 1;
-               loopbit = loops_per_jiffy;
-               while (lps_precision-- && (loopbit >>= 1)) {
-                       loops_per_jiffy |= loopbit;
-                       ticks = jiffies;
-                       while (ticks == jiffies)
-                               /* nothing */;
-                       ticks = jiffies;
-                       __delay(loops_per_jiffy);
-                       if (jiffies != ticks)   /* longer than 1 tick */
-                               loops_per_jiffy &= ~loopbit;
-               }
+               loops_per_jiffy = calibrate_delay_converge();
        }
        if (!printed)
                pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n",