aboutsummaryrefslogtreecommitdiff
path: root/init/calibrate.c
diff options
context:
space:
mode:
Diffstat (limited to 'init/calibrate.c')
-rw-r--r--init/calibrate.c55
1 files changed, 41 insertions, 14 deletions
diff --git a/init/calibrate.c b/init/calibrate.c
index cfd7000c9d7..520702db9ac 100644
--- a/init/calibrate.c
+++ b/init/calibrate.c
@@ -9,6 +9,7 @@
#include <linux/init.h>
#include <linux/timex.h>
#include <linux/smp.h>
+#include <linux/percpu.h>
unsigned long lpj_fine;
unsigned long preset_lpj;
@@ -30,7 +31,7 @@ __setup("lpj=", lpj_setup);
#define DELAY_CALIBRATION_TICKS ((HZ < 100) ? 1 : (HZ/100))
#define MAX_DIRECT_CALIBRATION_RETRIES 5
-static unsigned long __cpuinit calibrate_delay_direct(void)
+static unsigned long calibrate_delay_direct(void)
{
unsigned long pre_start, start, post_start;
unsigned long pre_end, end, post_end;
@@ -93,9 +94,6 @@ static unsigned long __cpuinit calibrate_delay_direct(void)
* If the upper limit and lower limit of the timer_rate is
* >= 12.5% apart, redo calibration.
*/
- printk(KERN_DEBUG "calibrate_delay_direct() timer_rate_max=%lu "
- "timer_rate_min=%lu pre_start=%lu pre_end=%lu\n",
- timer_rate_max, timer_rate_min, pre_start, pre_end);
if (start >= post_end)
printk(KERN_NOTICE "calibrate_delay_direct() ignoring "
"timer_rate as we had a TSC wrap around"
@@ -168,7 +166,10 @@ static unsigned long __cpuinit calibrate_delay_direct(void)
return 0;
}
#else
-static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;}
+static unsigned long calibrate_delay_direct(void)
+{
+ return 0;
+}
#endif
/*
@@ -182,7 +183,7 @@ static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;}
*/
#define LPS_PREC 8
-static unsigned long __cpuinit calibrate_delay_converge(void)
+static unsigned long calibrate_delay_converge(void)
{
/* First stage - slowly accelerate to find initial bounds */
unsigned long lpj, lpj_base, ticks, loopadd, loopadd_base, chop_limit;
@@ -246,32 +247,58 @@ recalibrate:
return lpj;
}
-void __cpuinit calibrate_delay(void)
+static DEFINE_PER_CPU(unsigned long, cpu_loops_per_jiffy) = { 0 };
+
+/*
+ * Check if cpu calibration delay is already known. For example,
+ * some processors with multi-core sockets may have all cores
+ * with the same calibration delay.
+ *
+ * Architectures should override this function if a faster calibration
+ * method is available.
+ */
+unsigned long __attribute__((weak)) calibrate_delay_is_known(void)
{
+ return 0;
+}
+
+void calibrate_delay(void)
+{
+ unsigned long lpj;
static bool printed;
+ int this_cpu = smp_processor_id();
- if (preset_lpj) {
- loops_per_jiffy = preset_lpj;
+ if (per_cpu(cpu_loops_per_jiffy, this_cpu)) {
+ lpj = per_cpu(cpu_loops_per_jiffy, this_cpu);
+ if (!printed)
+ pr_info("Calibrating delay loop (skipped) "
+ "already calibrated this CPU");
+ } else if (preset_lpj) {
+ lpj = preset_lpj;
if (!printed)
pr_info("Calibrating delay loop (skipped) "
"preset value.. ");
} else if ((!printed) && lpj_fine) {
- loops_per_jiffy = lpj_fine;
+ lpj = lpj_fine;
pr_info("Calibrating delay loop (skipped), "
"value calculated using timer frequency.. ");
- } else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) {
+ } else if ((lpj = calibrate_delay_is_known())) {
+ ;
+ } else if ((lpj = calibrate_delay_direct()) != 0) {
if (!printed)
pr_info("Calibrating delay using timer "
"specific routine.. ");
} else {
if (!printed)
pr_info("Calibrating delay loop... ");
- loops_per_jiffy = calibrate_delay_converge();
+ lpj = calibrate_delay_converge();
}
+ per_cpu(cpu_loops_per_jiffy, this_cpu) = lpj;
if (!printed)
pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n",
- loops_per_jiffy/(500000/HZ),
- (loops_per_jiffy/(5000/HZ)) % 100, loops_per_jiffy);
+ lpj/(500000/HZ),
+ (lpj/(5000/HZ)) % 100, lpj);
+ loops_per_jiffy = lpj;
printed = true;
}