Commit 279f1461 authored by Suresh Siddha's avatar Suresh Siddha Committed by Thomas Gleixner

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: default avatarSuresh 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.comSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 1e207eb1
...@@ -1304,6 +1304,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -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 lapic [X86-32,APIC] Enable the local APIC even if BIOS
disabled it. 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 lapic_timer_c2_ok [X86,APIC] trust the local apic timer
in C2 power state. in C2 power state.
......
...@@ -337,6 +337,8 @@ ...@@ -337,6 +337,8 @@
#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE (1ULL << 38) #define MSR_IA32_MISC_ENABLE_TURBO_DISABLE (1ULL << 38)
#define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE (1ULL << 39) #define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE (1ULL << 39)
#define MSR_IA32_TSC_DEADLINE 0x000006E0
/* P4/Xeon+ specific */ /* P4/Xeon+ specific */
#define MSR_IA32_MCG_EAX 0x00000180 #define MSR_IA32_MCG_EAX 0x00000180
#define MSR_IA32_MCG_EBX 0x00000181 #define MSR_IA32_MCG_EBX 0x00000181
......
...@@ -90,21 +90,6 @@ EXPORT_EARLY_PER_CPU_SYMBOL(x86_bios_cpu_apicid); ...@@ -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); 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 */ /* Local APIC was disabled by the BIOS and enabled by the kernel */
static int enabled_via_apicbase; static int enabled_via_apicbase;
...@@ -133,6 +118,25 @@ static inline void imcr_apic_to_pic(void) ...@@ -133,6 +118,25 @@ static inline void imcr_apic_to_pic(void)
} }
#endif #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 #ifdef CONFIG_X86_64
static int apic_calibrate_pmtmr __initdata; static int apic_calibrate_pmtmr __initdata;
static __init int setup_apicpmtimer(char *s) static __init int setup_apicpmtimer(char *s)
...@@ -315,6 +319,7 @@ int lapic_get_maxlvt(void) ...@@ -315,6 +319,7 @@ int lapic_get_maxlvt(void)
/* Clock divisor */ /* Clock divisor */
#define APIC_DIVISOR 16 #define APIC_DIVISOR 16
#define TSC_DIVISOR 32
/* /*
* This function sets up the local APIC timer, with a timeout of * 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,6 +338,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
lvtt_value = LOCAL_TIMER_VECTOR; lvtt_value = LOCAL_TIMER_VECTOR;
if (!oneshot) if (!oneshot)
lvtt_value |= APIC_LVT_TIMER_PERIODIC; 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()) if (!lapic_is_integrated())
lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); 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) ...@@ -341,6 +349,11 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
apic_write(APIC_LVTT, lvtt_value); 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 * Divide PICLK by 16
*/ */
...@@ -453,6 +466,16 @@ static int lapic_next_event(unsigned long delta, ...@@ -453,6 +466,16 @@ static int lapic_next_event(unsigned long delta,
return 0; 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 * Setup the lapic timer in periodic or oneshot mode
*/ */
...@@ -533,7 +556,15 @@ static void __cpuinit setup_APIC_timer(void) ...@@ -533,7 +556,15 @@ static void __cpuinit setup_APIC_timer(void)
memcpy(levt, &lapic_clockevent, sizeof(*levt)); memcpy(levt, &lapic_clockevent, sizeof(*levt));
levt->cpumask = cpumask_of(smp_processor_id()); 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) ...@@ -661,7 +692,9 @@ static int __init calibrate_APIC_clock(void)
* in the clockevent structure and return. * 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", apic_printk(APIC_VERBOSE, "lapic timer already calibrated %d\n",
lapic_timer_frequency); lapic_timer_frequency);
lapic_clockevent.mult = div_sc(lapic_timer_frequency/APIC_DIVISOR, lapic_clockevent.mult = div_sc(lapic_timer_frequency/APIC_DIVISOR,
...@@ -674,6 +707,9 @@ static int __init calibrate_APIC_clock(void) ...@@ -674,6 +707,9 @@ static int __init calibrate_APIC_clock(void)
return 0; return 0;
} }
apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"
"calibrating APIC timer ...\n");
local_irq_disable(); local_irq_disable();
/* Replace the global interrupt handler */ /* Replace the global interrupt handler */
...@@ -811,9 +847,6 @@ void __init setup_boot_APIC_clock(void) ...@@ -811,9 +847,6 @@ void __init setup_boot_APIC_clock(void)
return; return;
} }
apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"
"calibrating APIC timer ...\n");
if (calibrate_APIC_clock()) { if (calibrate_APIC_clock()) {
/* No broadcast on UP ! */ /* No broadcast on UP ! */
if (num_possible_cpus() > 1) if (num_possible_cpus() > 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment