Merge branch 'x86/cleanups' into x86/cpu, to pick up dependent changes
[linux-2.6-block.git] / arch / x86 / kernel / apic / apic.c
index 1bd91cb7b320a2363097374cd9264005e39b04a7..78f7e763f2cd7c8d2763d0d943049474bbf83adf 100644 (file)
@@ -183,7 +183,7 @@ EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok);
 /*
  * Debug level, exported for io_apic.c
  */
-unsigned int apic_verbosity;
+int apic_verbosity;
 
 int pic_mode;
 
@@ -590,21 +590,21 @@ static u32 skx_deadline_rev(void)
 static const struct x86_cpu_id deadline_match[] = {
        DEADLINE_MODEL_MATCH_FUNC( INTEL_FAM6_HASWELL_X,        hsx_deadline_rev),
        DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_BROADWELL_X,      0x0b000020),
-       DEADLINE_MODEL_MATCH_FUNC( INTEL_FAM6_BROADWELL_XEON_D, bdx_deadline_rev),
+       DEADLINE_MODEL_MATCH_FUNC( INTEL_FAM6_BROADWELL_D,      bdx_deadline_rev),
        DEADLINE_MODEL_MATCH_FUNC( INTEL_FAM6_SKYLAKE_X,        skx_deadline_rev),
 
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_HASWELL_CORE,     0x22),
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_HASWELL_ULT,      0x20),
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_HASWELL_GT3E,     0x17),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_HASWELL,          0x22),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_HASWELL_L,        0x20),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_HASWELL_G,        0x17),
 
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_BROADWELL_CORE,   0x25),
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_BROADWELL_GT3E,   0x17),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_BROADWELL,        0x25),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_BROADWELL_G,      0x17),
 
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_SKYLAKE_MOBILE,   0xb2),
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_SKYLAKE_DESKTOP,  0xb2),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_SKYLAKE_L,        0xb2),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_SKYLAKE,          0xb2),
 
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_KABYLAKE_MOBILE,  0x52),
-       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_KABYLAKE_DESKTOP, 0x52),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_KABYLAKE_L,       0x52),
+       DEADLINE_MODEL_MATCH_REV ( INTEL_FAM6_KABYLAKE,         0x52),
 
        {},
 };
@@ -722,7 +722,7 @@ static __initdata unsigned long lapic_cal_pm1, lapic_cal_pm2;
 static __initdata unsigned long lapic_cal_j1, lapic_cal_j2;
 
 /*
- * Temporary interrupt handler.
+ * Temporary interrupt handler and polled calibration function.
  */
 static void __init lapic_cal_handler(struct clock_event_device *dev)
 {
@@ -851,7 +851,8 @@ bool __init apic_needs_pit(void)
 static int __init calibrate_APIC_clock(void)
 {
        struct clock_event_device *levt = this_cpu_ptr(&lapic_events);
-       void (*real_handler)(struct clock_event_device *dev);
+       u64 tsc_perj = 0, tsc_start = 0;
+       unsigned long jif_start;
        unsigned long deltaj;
        long delta, deltatsc;
        int pm_referenced = 0;
@@ -878,28 +879,64 @@ static int __init calibrate_APIC_clock(void)
        apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"
                    "calibrating APIC timer ...\n");
 
+       /*
+        * There are platforms w/o global clockevent devices. Instead of
+        * making the calibration conditional on that, use a polling based
+        * approach everywhere.
+        */
        local_irq_disable();
 
-       /* Replace the global interrupt handler */
-       real_handler = global_clock_event->event_handler;
-       global_clock_event->event_handler = lapic_cal_handler;
-
        /*
         * Setup the APIC counter to maximum. There is no way the lapic
         * can underflow in the 100ms detection time frame
         */
        __setup_APIC_LVTT(0xffffffff, 0, 0);
 
-       /* Let the interrupts run */
+       /*
+        * Methods to terminate the calibration loop:
+        *  1) Global clockevent if available (jiffies)
+        *  2) TSC if available and frequency is known
+        */
+       jif_start = READ_ONCE(jiffies);
+
+       if (tsc_khz) {
+               tsc_start = rdtsc();
+               tsc_perj = div_u64((u64)tsc_khz * 1000, HZ);
+       }
+
+       /*
+        * Enable interrupts so the tick can fire, if a global
+        * clockevent device is available
+        */
        local_irq_enable();
 
-       while (lapic_cal_loops <= LAPIC_CAL_LOOPS)
-               cpu_relax();
+       while (lapic_cal_loops <= LAPIC_CAL_LOOPS) {
+               /* Wait for a tick to elapse */
+               while (1) {
+                       if (tsc_khz) {
+                               u64 tsc_now = rdtsc();
+                               if ((tsc_now - tsc_start) >= tsc_perj) {
+                                       tsc_start += tsc_perj;
+                                       break;
+                               }
+                       } else {
+                               unsigned long jif_now = READ_ONCE(jiffies);
 
-       local_irq_disable();
+                               if (time_after(jif_now, jif_start)) {
+                                       jif_start = jif_now;
+                                       break;
+                               }
+                       }
+                       cpu_relax();
+               }
 
-       /* Restore the real event handler */
-       global_clock_event->event_handler = real_handler;
+               /* Invoke the calibration routine */
+               local_irq_disable();
+               lapic_cal_handler(NULL);
+               local_irq_enable();
+       }
+
+       local_irq_disable();
 
        /* Build delta t1-t2 as apic timer counts down */
        delta = lapic_cal_t1 - lapic_cal_t2;
@@ -943,10 +980,11 @@ static int __init calibrate_APIC_clock(void)
        levt->features &= ~CLOCK_EVT_FEAT_DUMMY;
 
        /*
-        * PM timer calibration failed or not turned on
-        * so lets try APIC timer based calibration
+        * PM timer calibration failed or not turned on so lets try APIC
+        * timer based calibration, if a global clockevent device is
+        * available.
         */
-       if (!pm_referenced) {
+       if (!pm_referenced && global_clock_event) {
                apic_printk(APIC_VERBOSE, "... verify APIC timer\n");
 
                /*
@@ -1141,6 +1179,10 @@ void clear_local_APIC(void)
        apic_write(APIC_LVT0, v | APIC_LVT_MASKED);
        v = apic_read(APIC_LVT1);
        apic_write(APIC_LVT1, v | APIC_LVT_MASKED);
+       if (!x2apic_enabled()) {
+               v = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
+               apic_write(APIC_LDR, v);
+       }
        if (maxlvt >= 4) {
                v = apic_read(APIC_LVTPC);
                apic_write(APIC_LVTPC, v | APIC_LVT_MASKED);