x86/tsc: Split native_calibrate_cpu() into early and late parts
[linux-2.6-microblaze.git] / arch / x86 / kernel / tsc.c
index 74392d9..6058677 100644 (file)
@@ -33,16 +33,13 @@ EXPORT_SYMBOL(cpu_khz);
 unsigned int __read_mostly tsc_khz;
 EXPORT_SYMBOL(tsc_khz);
 
+#define KHZ    1000
+
 /*
  * TSC can be unstable due to cpufreq or due to unsynced TSCs
  */
 static int __read_mostly tsc_unstable;
 
-/* native_sched_clock() is called before tsc_init(), so
-   we must start with the TSC soft disabled to prevent
-   erroneous rdtsc usage on !boot_cpu_has(X86_FEATURE_TSC) processors */
-static int __read_mostly tsc_disabled = -1;
-
 static DEFINE_STATIC_KEY_FALSE(__use_tsc);
 
 int tsc_clocksource_reliable;
@@ -106,23 +103,6 @@ void cyc2ns_read_end(void)
  *                      -johnstul@us.ibm.com "math is hard, lets go shopping!"
  */
 
-static void cyc2ns_data_init(struct cyc2ns_data *data)
-{
-       data->cyc2ns_mul = 0;
-       data->cyc2ns_shift = 0;
-       data->cyc2ns_offset = 0;
-}
-
-static void __init cyc2ns_init(int cpu)
-{
-       struct cyc2ns *c2n = &per_cpu(cyc2ns, cpu);
-
-       cyc2ns_data_init(&c2n->data[0]);
-       cyc2ns_data_init(&c2n->data[1]);
-
-       seqcount_init(&c2n->seq);
-}
-
 static inline unsigned long long cycles_2_ns(unsigned long long cyc)
 {
        struct cyc2ns_data data;
@@ -138,18 +118,11 @@ static inline unsigned long long cycles_2_ns(unsigned long long cyc)
        return ns;
 }
 
-static void set_cyc2ns_scale(unsigned long khz, int cpu, unsigned long long tsc_now)
+static void __set_cyc2ns_scale(unsigned long khz, int cpu, unsigned long long tsc_now)
 {
        unsigned long long ns_now;
        struct cyc2ns_data data;
        struct cyc2ns *c2n;
-       unsigned long flags;
-
-       local_irq_save(flags);
-       sched_clock_idle_sleep_event();
-
-       if (!khz)
-               goto done;
 
        ns_now = cycles_2_ns(tsc_now);
 
@@ -181,12 +154,55 @@ static void set_cyc2ns_scale(unsigned long khz, int cpu, unsigned long long tsc_
        c2n->data[0] = data;
        raw_write_seqcount_latch(&c2n->seq);
        c2n->data[1] = data;
+}
+
+static void set_cyc2ns_scale(unsigned long khz, int cpu, unsigned long long tsc_now)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       sched_clock_idle_sleep_event();
+
+       if (khz)
+               __set_cyc2ns_scale(khz, cpu, tsc_now);
 
-done:
        sched_clock_idle_wakeup_event();
        local_irq_restore(flags);
 }
 
+/*
+ * Initialize cyc2ns for boot cpu
+ */
+static void __init cyc2ns_init_boot_cpu(void)
+{
+       struct cyc2ns *c2n = this_cpu_ptr(&cyc2ns);
+
+       seqcount_init(&c2n->seq);
+       __set_cyc2ns_scale(tsc_khz, smp_processor_id(), rdtsc());
+}
+
+/*
+ * Secondary CPUs do not run through cyc2ns_init(), so set up
+ * all the scale factors for all CPUs, assuming the same
+ * speed as the bootup CPU. (cpufreq notifiers will fix this
+ * up if their speed diverges)
+ */
+static void __init cyc2ns_init_secondary_cpus(void)
+{
+       unsigned int cpu, this_cpu = smp_processor_id();
+       struct cyc2ns *c2n = this_cpu_ptr(&cyc2ns);
+       struct cyc2ns_data *data = c2n->data;
+
+       for_each_possible_cpu(cpu) {
+               if (cpu != this_cpu) {
+                       seqcount_init(&c2n->seq);
+                       c2n = per_cpu_ptr(&cyc2ns, cpu);
+                       c2n->data[0] = data[0];
+                       c2n->data[1] = data[1];
+               }
+       }
+}
+
 /*
  * Scheduler clock - returns current time in nanosec units.
  */
@@ -248,8 +264,7 @@ EXPORT_SYMBOL_GPL(check_tsc_unstable);
 #ifdef CONFIG_X86_TSC
 int __init notsc_setup(char *str)
 {
-       pr_warn("Kernel compiled with CONFIG_X86_TSC, cannot disable TSC completely\n");
-       tsc_disabled = 1;
+       mark_tsc_unstable("boot parameter notsc");
        return 1;
 }
 #else
@@ -665,30 +680,17 @@ static unsigned long cpu_khz_from_cpuid(void)
        return eax_base_mhz * 1000;
 }
 
-/**
- * native_calibrate_cpu - calibrate the cpu on boot
+/*
+ * calibrate cpu using pit, hpet, and ptimer methods. They are available
+ * later in boot after acpi is initialized.
  */
-unsigned long native_calibrate_cpu(void)
+static unsigned long pit_hpet_ptimer_calibrate_cpu(void)
 {
        u64 tsc1, tsc2, delta, ref1, ref2;
        unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX;
-       unsigned long flags, latch, ms, fast_calibrate;
+       unsigned long flags, latch, ms;
        int hpet = is_hpet_enabled(), i, loopmin;
 
-       fast_calibrate = cpu_khz_from_cpuid();
-       if (fast_calibrate)
-               return fast_calibrate;
-
-       fast_calibrate = cpu_khz_from_msr();
-       if (fast_calibrate)
-               return fast_calibrate;
-
-       local_irq_save(flags);
-       fast_calibrate = quick_pit_calibrate();
-       local_irq_restore(flags);
-       if (fast_calibrate)
-               return fast_calibrate;
-
        /*
         * Run 5 calibration loops to get the lowest frequency value
         * (the best estimate). We use two different calibration modes
@@ -831,6 +833,37 @@ unsigned long native_calibrate_cpu(void)
        return tsc_pit_min;
 }
 
+/**
+ * native_calibrate_cpu_early - can calibrate the cpu early in boot
+ */
+unsigned long native_calibrate_cpu_early(void)
+{
+       unsigned long flags, fast_calibrate = cpu_khz_from_cpuid();
+
+       if (!fast_calibrate)
+               fast_calibrate = cpu_khz_from_msr();
+       if (!fast_calibrate) {
+               local_irq_save(flags);
+               fast_calibrate = quick_pit_calibrate();
+               local_irq_restore(flags);
+       }
+       return fast_calibrate;
+}
+
+
+/**
+ * native_calibrate_cpu - calibrate the cpu
+ */
+unsigned long native_calibrate_cpu(void)
+{
+       unsigned long tsc_freq = native_calibrate_cpu_early();
+
+       if (!tsc_freq)
+               tsc_freq = pit_hpet_ptimer_calibrate_cpu();
+
+       return tsc_freq;
+}
+
 void recalibrate_cpu_khz(void)
 {
 #ifndef CONFIG_SMP
@@ -1307,7 +1340,7 @@ unreg:
 
 static int __init init_tsc_clocksource(void)
 {
-       if (!boot_cpu_has(X86_FEATURE_TSC) || tsc_disabled > 0 || !tsc_khz)
+       if (!boot_cpu_has(X86_FEATURE_TSC) || !tsc_khz)
                return 0;
 
        if (tsc_unstable)
@@ -1341,34 +1374,10 @@ unreg:
  */
 device_initcall(init_tsc_clocksource);
 
-void __init tsc_early_delay_calibrate(void)
-{
-       unsigned long lpj;
-
-       if (!boot_cpu_has(X86_FEATURE_TSC))
-               return;
-
-       cpu_khz = x86_platform.calibrate_cpu();
-       tsc_khz = x86_platform.calibrate_tsc();
-
-       tsc_khz = tsc_khz ? : cpu_khz;
-       if (!tsc_khz)
-               return;
-
-       lpj = tsc_khz * 1000;
-       do_div(lpj, HZ);
-       loops_per_jiffy = lpj;
-}
-
-void __init tsc_init(void)
+static bool __init determine_cpu_tsc_frequencies(void)
 {
-       u64 lpj, cyc;
-       int cpu;
-
-       if (!boot_cpu_has(X86_FEATURE_TSC)) {
-               setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
-               return;
-       }
+       /* Make sure that cpu and tsc are not already calibrated */
+       WARN_ON(cpu_khz || tsc_khz);
 
        cpu_khz = x86_platform.calibrate_cpu();
        tsc_khz = x86_platform.calibrate_tsc();
@@ -1383,52 +1392,69 @@ void __init tsc_init(void)
        else if (abs(cpu_khz - tsc_khz) * 10 > tsc_khz)
                cpu_khz = tsc_khz;
 
-       if (!tsc_khz) {
-               mark_tsc_unstable("could not calculate TSC khz");
-               setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
-               return;
-       }
+       if (tsc_khz == 0)
+               return false;
 
        pr_info("Detected %lu.%03lu MHz processor\n",
-               (unsigned long)cpu_khz / 1000,
-               (unsigned long)cpu_khz % 1000);
+               (unsigned long)cpu_khz / KHZ,
+               (unsigned long)cpu_khz % KHZ);
 
        if (cpu_khz != tsc_khz) {
                pr_info("Detected %lu.%03lu MHz TSC",
-                       (unsigned long)tsc_khz / 1000,
-                       (unsigned long)tsc_khz % 1000);
+                       (unsigned long)tsc_khz / KHZ,
+                       (unsigned long)tsc_khz % KHZ);
        }
+       return true;
+}
+
+static unsigned long __init get_loops_per_jiffy(void)
+{
+       unsigned long lpj = tsc_khz * KHZ;
+
+       do_div(lpj, HZ);
+       return lpj;
+}
+
+void __init tsc_early_init(void)
+{
+       if (!boot_cpu_has(X86_FEATURE_TSC))
+               return;
+       if (!determine_cpu_tsc_frequencies())
+               return;
+       loops_per_jiffy = get_loops_per_jiffy();
 
        /* Sanitize TSC ADJUST before cyc2ns gets initialized */
        tsc_store_and_check_tsc_adjust(true);
+       cyc2ns_init_boot_cpu();
+       static_branch_enable(&__use_tsc);
+}
 
-       /*
-        * Secondary CPUs do not run through tsc_init(), so set up
-        * all the scale factors for all CPUs, assuming the same
-        * speed as the bootup CPU. (cpufreq notifiers will fix this
-        * up if their speed diverges)
-        */
-       cyc = rdtsc();
-       for_each_possible_cpu(cpu) {
-               cyc2ns_init(cpu);
-               set_cyc2ns_scale(tsc_khz, cpu, cyc);
-       }
-
-       if (tsc_disabled > 0)
+void __init tsc_init(void)
+{
+       if (!boot_cpu_has(X86_FEATURE_TSC)) {
+               setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
                return;
+       }
 
-       /* now allow native_sched_clock() to use rdtsc */
+       if (!tsc_khz) {
+               /* We failed to determine frequencies earlier, try again */
+               if (!determine_cpu_tsc_frequencies()) {
+                       mark_tsc_unstable("could not calculate TSC khz");
+                       setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
+                       return;
+               }
+               /* Sanitize TSC ADJUST before cyc2ns gets initialized */
+               tsc_store_and_check_tsc_adjust(true);
+               cyc2ns_init_boot_cpu();
+       }
 
-       tsc_disabled = 0;
+       cyc2ns_init_secondary_cpus();
        static_branch_enable(&__use_tsc);
 
        if (!no_sched_irq_time)
                enable_sched_clock_irqtime();
 
-       lpj = ((u64)tsc_khz * 1000);
-       do_div(lpj, HZ);
-       lpj_fine = lpj;
-
+       lpj_fine = get_loops_per_jiffy();
        use_tsc_delay();
 
        check_system_tsc_reliable();
@@ -1455,7 +1481,7 @@ unsigned long calibrate_delay_is_known(void)
        int constant_tsc = cpu_has(&cpu_data(cpu), X86_FEATURE_CONSTANT_TSC);
        const struct cpumask *mask = topology_core_cpumask(cpu);
 
-       if (tsc_disabled || !constant_tsc || !mask)
+       if (!constant_tsc || !mask)
                return 0;
 
        sibling = cpumask_any_but(mask, cpu);