Merge branch 'x86-timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 12 Dec 2012 04:01:33 +0000 (20:01 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 12 Dec 2012 04:01:33 +0000 (20:01 -0800)
Pull x86 timer update from Ingo Molnar:
 "This tree includes HPET fixes and also implements a calibration-free,
  TSC match driven APIC timer interrupt mode: 'TSC deadline mode'
  supported in SandyBridge and later CPUs."

* 'x86-timers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86: hpet: Fix inverted return value check in arch_setup_hpet_msi()
  x86: hpet: Fix masking of MSI interrupts
  x86: apic: Use tsc deadline for oneshot when available

Documentation/kernel-parameters.txt
arch/x86/include/asm/msr-index.h
arch/x86/kernel/apic/apic.c
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/hpet.c

index 5190f17..28bd0f0 100644 (file)
@@ -1304,6 +1304,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        lapic           [X86-32,APIC] Enable the local APIC even if BIOS
                        disabled it.
 
+       lapic=          [x86,APIC] "notscdeadline" Do not use TSC deadline
+                       value for LAPIC timer one-shot implementation. Default
+                       back to the programmable timer unit in the LAPIC.
+
        lapic_timer_c2_ok       [X86,APIC] trust the local apic timer
                        in C2 power state.
 
index 7f0edce..e400cdb 100644 (file)
 #define MSR_IA32_MISC_ENABLE_TURBO_DISABLE     (1ULL << 38)
 #define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE   (1ULL << 39)
 
+#define MSR_IA32_TSC_DEADLINE          0x000006E0
+
 /* P4/Xeon+ specific */
 #define MSR_IA32_MCG_EAX               0x00000180
 #define MSR_IA32_MCG_EBX               0x00000181
index b17416e..b994cc8 100644 (file)
@@ -90,21 +90,6 @@ EXPORT_EARLY_PER_CPU_SYMBOL(x86_bios_cpu_apicid);
  */
 DEFINE_EARLY_PER_CPU_READ_MOSTLY(int, x86_cpu_to_logical_apicid, BAD_APICID);
 
-/*
- * Knob to control our willingness to enable the local APIC.
- *
- * +1=force-enable
- */
-static int force_enable_local_apic __initdata;
-/*
- * APIC command line parameters
- */
-static int __init parse_lapic(char *arg)
-{
-       force_enable_local_apic = 1;
-       return 0;
-}
-early_param("lapic", parse_lapic);
 /* Local APIC was disabled by the BIOS and enabled by the kernel */
 static int enabled_via_apicbase;
 
@@ -133,6 +118,25 @@ static inline void imcr_apic_to_pic(void)
 }
 #endif
 
+/*
+ * Knob to control our willingness to enable the local APIC.
+ *
+ * +1=force-enable
+ */
+static int force_enable_local_apic __initdata;
+/*
+ * APIC command line parameters
+ */
+static int __init parse_lapic(char *arg)
+{
+       if (config_enabled(CONFIG_X86_32) && !arg)
+               force_enable_local_apic = 1;
+       else if (!strncmp(arg, "notscdeadline", 13))
+               setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
+       return 0;
+}
+early_param("lapic", parse_lapic);
+
 #ifdef CONFIG_X86_64
 static int apic_calibrate_pmtmr __initdata;
 static __init int setup_apicpmtimer(char *s)
@@ -315,6 +319,7 @@ int lapic_get_maxlvt(void)
 
 /* Clock divisor */
 #define APIC_DIVISOR 16
+#define TSC_DIVISOR  32
 
 /*
  * This function sets up the local APIC timer, with a timeout of
@@ -333,6 +338,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
        lvtt_value = LOCAL_TIMER_VECTOR;
        if (!oneshot)
                lvtt_value |= APIC_LVT_TIMER_PERIODIC;
+       else if (boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER))
+               lvtt_value |= APIC_LVT_TIMER_TSCDEADLINE;
+
        if (!lapic_is_integrated())
                lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV);
 
@@ -341,6 +349,11 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
 
        apic_write(APIC_LVTT, lvtt_value);
 
+       if (lvtt_value & APIC_LVT_TIMER_TSCDEADLINE) {
+               printk_once(KERN_DEBUG "TSC deadline timer enabled\n");
+               return;
+       }
+
        /*
         * Divide PICLK by 16
         */
@@ -453,6 +466,16 @@ static int lapic_next_event(unsigned long delta,
        return 0;
 }
 
+static int lapic_next_deadline(unsigned long delta,
+                              struct clock_event_device *evt)
+{
+       u64 tsc;
+
+       rdtscll(tsc);
+       wrmsrl(MSR_IA32_TSC_DEADLINE, tsc + (((u64) delta) * TSC_DIVISOR));
+       return 0;
+}
+
 /*
  * Setup the lapic timer in periodic or oneshot mode
  */
@@ -533,7 +556,15 @@ static void __cpuinit setup_APIC_timer(void)
        memcpy(levt, &lapic_clockevent, sizeof(*levt));
        levt->cpumask = cpumask_of(smp_processor_id());
 
-       clockevents_register_device(levt);
+       if (this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) {
+               levt->features &= ~(CLOCK_EVT_FEAT_PERIODIC |
+                                   CLOCK_EVT_FEAT_DUMMY);
+               levt->set_next_event = lapic_next_deadline;
+               clockevents_config_and_register(levt,
+                                               (tsc_khz / TSC_DIVISOR) * 1000,
+                                               0xF, ~0UL);
+       } else
+               clockevents_register_device(levt);
 }
 
 /*
@@ -661,7 +692,9 @@ static int __init calibrate_APIC_clock(void)
         * in the clockevent structure and return.
         */
 
-       if (lapic_timer_frequency) {
+       if (boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) {
+               return 0;
+       } else if (lapic_timer_frequency) {
                apic_printk(APIC_VERBOSE, "lapic timer already calibrated %d\n",
                                lapic_timer_frequency);
                lapic_clockevent.mult = div_sc(lapic_timer_frequency/APIC_DIVISOR,
@@ -674,6 +707,9 @@ static int __init calibrate_APIC_clock(void)
                return 0;
        }
 
+       apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"
+                   "calibrating APIC timer ...\n");
+
        local_irq_disable();
 
        /* Replace the global interrupt handler */
@@ -811,9 +847,6 @@ void __init setup_boot_APIC_clock(void)
                return;
        }
 
-       apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"
-                   "calibrating APIC timer ...\n");
-
        if (calibrate_APIC_clock()) {
                /* No broadcast on UP ! */
                if (num_possible_cpus() > 1)
index 70aa621..b739d39 100644 (file)
@@ -3305,8 +3305,9 @@ int arch_setup_hpet_msi(unsigned int irq, unsigned int id)
        int ret;
 
        if (irq_remapping_enabled) {
-               if (!setup_hpet_msi_remapped(irq, id))
-                       return -1;
+               ret = setup_hpet_msi_remapped(irq, id);
+               if (ret)
+                       return ret;
        }
 
        ret = msi_compose_msg(NULL, irq, &msg, id);
index 1460a5d..e28670f 100644 (file)
@@ -434,7 +434,7 @@ void hpet_msi_unmask(struct irq_data *data)
 
        /* unmask it */
        cfg = hpet_readl(HPET_Tn_CFG(hdev->num));
-       cfg |= HPET_TN_FSB;
+       cfg |= HPET_TN_ENABLE | HPET_TN_FSB;
        hpet_writel(cfg, HPET_Tn_CFG(hdev->num));
 }
 
@@ -445,7 +445,7 @@ void hpet_msi_mask(struct irq_data *data)
 
        /* mask it */
        cfg = hpet_readl(HPET_Tn_CFG(hdev->num));
-       cfg &= ~HPET_TN_FSB;
+       cfg &= ~(HPET_TN_ENABLE | HPET_TN_FSB);
        hpet_writel(cfg, HPET_Tn_CFG(hdev->num));
 }