diff options
author | Suresh Siddha <suresh.b.siddha@intel.com> | 2012-10-22 17:37:58 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2012-11-02 06:23:37 -0400 |
commit | 279f1461432ccdec0b98c0bcbe0a8e2c0f6fdda5 (patch) | |
tree | 1adaa912a846b6d45cff5c8b03db8f97d30ac779 /arch | |
parent | 1e207eb1c3f0e8b690401f02fe08e7b53903f010 (diff) |
x86: apic: Use tsc deadline for oneshot when available
If the TSC deadline mode is supported, LAPIC timer one-shot mode can be
implemented using IA32_TSC_DEADLINE MSR. An interrupt will be generated
when the TSC value equals or exceeds the value in the IA32_TSC_DEADLINE
MSR.
This enables us to skip the APIC calibration during boot. Also, in
xapic mode, this enables us to skip the uncached apic access to re-arm
the APIC timer.
As this timer ticks at the high frequency TSC rate, we use the
TSC_DIVISOR (32) to work with the 32-bit restrictions in the
clockevent API's to avoid 64-bit divides etc (frequency is u32 and
"unsigned long" in the set_next_event(), max_delta limits the next
event to 32-bit for 32-bit kernel).
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: venki@google.com
Cc: len.brown@intel.com
Link: http://lkml.kernel.org/r/1350941878.6017.31.camel@sbsiddha-desk.sc.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/msr-index.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/apic/apic.c | 73 |
2 files changed, 55 insertions, 20 deletions
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 7f0edceb7563..e400cdb2dd65 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h | |||
@@ -337,6 +337,8 @@ | |||
337 | #define MSR_IA32_MISC_ENABLE_TURBO_DISABLE (1ULL << 38) | 337 | #define MSR_IA32_MISC_ENABLE_TURBO_DISABLE (1ULL << 38) |
338 | #define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE (1ULL << 39) | 338 | #define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE (1ULL << 39) |
339 | 339 | ||
340 | #define MSR_IA32_TSC_DEADLINE 0x000006E0 | ||
341 | |||
340 | /* P4/Xeon+ specific */ | 342 | /* P4/Xeon+ specific */ |
341 | #define MSR_IA32_MCG_EAX 0x00000180 | 343 | #define MSR_IA32_MCG_EAX 0x00000180 |
342 | #define MSR_IA32_MCG_EBX 0x00000181 | 344 | #define MSR_IA32_MCG_EBX 0x00000181 |
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index b17416e72fbd..b994cc84aa7e 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c | |||
@@ -90,21 +90,6 @@ EXPORT_EARLY_PER_CPU_SYMBOL(x86_bios_cpu_apicid); | |||
90 | */ | 90 | */ |
91 | DEFINE_EARLY_PER_CPU_READ_MOSTLY(int, x86_cpu_to_logical_apicid, BAD_APICID); | 91 | DEFINE_EARLY_PER_CPU_READ_MOSTLY(int, x86_cpu_to_logical_apicid, BAD_APICID); |
92 | 92 | ||
93 | /* | ||
94 | * Knob to control our willingness to enable the local APIC. | ||
95 | * | ||
96 | * +1=force-enable | ||
97 | */ | ||
98 | static int force_enable_local_apic __initdata; | ||
99 | /* | ||
100 | * APIC command line parameters | ||
101 | */ | ||
102 | static int __init parse_lapic(char *arg) | ||
103 | { | ||
104 | force_enable_local_apic = 1; | ||
105 | return 0; | ||
106 | } | ||
107 | early_param("lapic", parse_lapic); | ||
108 | /* Local APIC was disabled by the BIOS and enabled by the kernel */ | 93 | /* Local APIC was disabled by the BIOS and enabled by the kernel */ |
109 | static int enabled_via_apicbase; | 94 | static int enabled_via_apicbase; |
110 | 95 | ||
@@ -133,6 +118,25 @@ static inline void imcr_apic_to_pic(void) | |||
133 | } | 118 | } |
134 | #endif | 119 | #endif |
135 | 120 | ||
121 | /* | ||
122 | * Knob to control our willingness to enable the local APIC. | ||
123 | * | ||
124 | * +1=force-enable | ||
125 | */ | ||
126 | static int force_enable_local_apic __initdata; | ||
127 | /* | ||
128 | * APIC command line parameters | ||
129 | */ | ||
130 | static int __init parse_lapic(char *arg) | ||
131 | { | ||
132 | if (config_enabled(CONFIG_X86_32) && !arg) | ||
133 | force_enable_local_apic = 1; | ||
134 | else if (!strncmp(arg, "notscdeadline", 13)) | ||
135 | setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER); | ||
136 | return 0; | ||
137 | } | ||
138 | early_param("lapic", parse_lapic); | ||
139 | |||
136 | #ifdef CONFIG_X86_64 | 140 | #ifdef CONFIG_X86_64 |
137 | static int apic_calibrate_pmtmr __initdata; | 141 | static int apic_calibrate_pmtmr __initdata; |
138 | static __init int setup_apicpmtimer(char *s) | 142 | static __init int setup_apicpmtimer(char *s) |
@@ -315,6 +319,7 @@ int lapic_get_maxlvt(void) | |||
315 | 319 | ||
316 | /* Clock divisor */ | 320 | /* Clock divisor */ |
317 | #define APIC_DIVISOR 16 | 321 | #define APIC_DIVISOR 16 |
322 | #define TSC_DIVISOR 32 | ||
318 | 323 | ||
319 | /* | 324 | /* |
320 | * This function sets up the local APIC timer, with a timeout of | 325 | * 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) | |||
333 | lvtt_value = LOCAL_TIMER_VECTOR; | 338 | lvtt_value = LOCAL_TIMER_VECTOR; |
334 | if (!oneshot) | 339 | if (!oneshot) |
335 | lvtt_value |= APIC_LVT_TIMER_PERIODIC; | 340 | lvtt_value |= APIC_LVT_TIMER_PERIODIC; |
341 | else if (boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) | ||
342 | lvtt_value |= APIC_LVT_TIMER_TSCDEADLINE; | ||
343 | |||
336 | if (!lapic_is_integrated()) | 344 | if (!lapic_is_integrated()) |
337 | lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); | 345 | lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); |
338 | 346 | ||
@@ -341,6 +349,11 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) | |||
341 | 349 | ||
342 | apic_write(APIC_LVTT, lvtt_value); | 350 | apic_write(APIC_LVTT, lvtt_value); |
343 | 351 | ||
352 | if (lvtt_value & APIC_LVT_TIMER_TSCDEADLINE) { | ||
353 | printk_once(KERN_DEBUG "TSC deadline timer enabled\n"); | ||
354 | return; | ||
355 | } | ||
356 | |||
344 | /* | 357 | /* |
345 | * Divide PICLK by 16 | 358 | * Divide PICLK by 16 |
346 | */ | 359 | */ |
@@ -453,6 +466,16 @@ static int lapic_next_event(unsigned long delta, | |||
453 | return 0; | 466 | return 0; |
454 | } | 467 | } |
455 | 468 | ||
469 | static int lapic_next_deadline(unsigned long delta, | ||
470 | struct clock_event_device *evt) | ||
471 | { | ||
472 | u64 tsc; | ||
473 | |||
474 | rdtscll(tsc); | ||
475 | wrmsrl(MSR_IA32_TSC_DEADLINE, tsc + (((u64) delta) * TSC_DIVISOR)); | ||
476 | return 0; | ||
477 | } | ||
478 | |||
456 | /* | 479 | /* |
457 | * Setup the lapic timer in periodic or oneshot mode | 480 | * Setup the lapic timer in periodic or oneshot mode |
458 | */ | 481 | */ |
@@ -533,7 +556,15 @@ static void __cpuinit setup_APIC_timer(void) | |||
533 | memcpy(levt, &lapic_clockevent, sizeof(*levt)); | 556 | memcpy(levt, &lapic_clockevent, sizeof(*levt)); |
534 | levt->cpumask = cpumask_of(smp_processor_id()); | 557 | levt->cpumask = cpumask_of(smp_processor_id()); |
535 | 558 | ||
536 | clockevents_register_device(levt); | 559 | if (this_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) { |
560 | levt->features &= ~(CLOCK_EVT_FEAT_PERIODIC | | ||
561 | CLOCK_EVT_FEAT_DUMMY); | ||
562 | levt->set_next_event = lapic_next_deadline; | ||
563 | clockevents_config_and_register(levt, | ||
564 | (tsc_khz / TSC_DIVISOR) * 1000, | ||
565 | 0xF, ~0UL); | ||
566 | } else | ||
567 | clockevents_register_device(levt); | ||
537 | } | 568 | } |
538 | 569 | ||
539 | /* | 570 | /* |
@@ -661,7 +692,9 @@ static int __init calibrate_APIC_clock(void) | |||
661 | * in the clockevent structure and return. | 692 | * in the clockevent structure and return. |
662 | */ | 693 | */ |
663 | 694 | ||
664 | if (lapic_timer_frequency) { | 695 | if (boot_cpu_has(X86_FEATURE_TSC_DEADLINE_TIMER)) { |
696 | return 0; | ||
697 | } else if (lapic_timer_frequency) { | ||
665 | apic_printk(APIC_VERBOSE, "lapic timer already calibrated %d\n", | 698 | apic_printk(APIC_VERBOSE, "lapic timer already calibrated %d\n", |
666 | lapic_timer_frequency); | 699 | lapic_timer_frequency); |
667 | lapic_clockevent.mult = div_sc(lapic_timer_frequency/APIC_DIVISOR, | 700 | lapic_clockevent.mult = div_sc(lapic_timer_frequency/APIC_DIVISOR, |
@@ -674,6 +707,9 @@ static int __init calibrate_APIC_clock(void) | |||
674 | return 0; | 707 | return 0; |
675 | } | 708 | } |
676 | 709 | ||
710 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" | ||
711 | "calibrating APIC timer ...\n"); | ||
712 | |||
677 | local_irq_disable(); | 713 | local_irq_disable(); |
678 | 714 | ||
679 | /* Replace the global interrupt handler */ | 715 | /* Replace the global interrupt handler */ |
@@ -811,9 +847,6 @@ void __init setup_boot_APIC_clock(void) | |||
811 | return; | 847 | return; |
812 | } | 848 | } |
813 | 849 | ||
814 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" | ||
815 | "calibrating APIC timer ...\n"); | ||
816 | |||
817 | if (calibrate_APIC_clock()) { | 850 | if (calibrate_APIC_clock()) { |
818 | /* No broadcast on UP ! */ | 851 | /* No broadcast on UP ! */ |
819 | if (num_possible_cpus() > 1) | 852 | if (num_possible_cpus() > 1) |