diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-02-16 04:28:04 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 11:13:59 -0500 |
commit | e9e2cdb412412326c4827fc78ba27f410d837e6e (patch) | |
tree | cd4ca03e6bdc3691619024492fb9414427b2f813 /arch/i386/kernel/apic.c | |
parent | 79bf2bb335b85db25d27421c798595a2fa2a0e82 (diff) |
[PATCH] clockevents: i386 drivers
Add clockevent drivers for i386: lapic (local) and PIT/HPET (global). Update
the timer IRQ to call into the PIT/HPET driver's event handler and the
lapic-timer IRQ to call into the lapic clockevent driver. The assignement of
timer functionality is delegated to the core framework code and replaces the
compile and runtime evalution in do_timer_interrupt_hook()
Use the clockevents broadcast support and implement the lapic_broadcast
function for ACPI.
No changes to existing functionality.
[ kdump fix from Vivek Goyal <vgoyal@in.ibm.com> ]
[ fixes based on review feedback from Arjan van de Ven <arjan@infradead.org> ]
Cleanups-from: Adrian Bunk <bunk@stusta.de>
Build-fixes-from: Andrew Morton <akpm@osdl.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Cc: Andi Kleen <ak@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/i386/kernel/apic.c')
-rw-r--r-- | arch/i386/kernel/apic.c | 291 |
1 files changed, 148 insertions, 143 deletions
diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index b56448f214a7..e98b5c750bdf 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/kernel_stat.h> | 25 | #include <linux/kernel_stat.h> |
26 | #include <linux/sysdev.h> | 26 | #include <linux/sysdev.h> |
27 | #include <linux/cpu.h> | 27 | #include <linux/cpu.h> |
28 | #include <linux/clockchips.h> | ||
28 | #include <linux/module.h> | 29 | #include <linux/module.h> |
29 | 30 | ||
30 | #include <asm/atomic.h> | 31 | #include <asm/atomic.h> |
@@ -52,28 +53,44 @@ | |||
52 | #endif | 53 | #endif |
53 | 54 | ||
54 | /* | 55 | /* |
55 | * cpu_mask that denotes the CPUs that needs timer interrupt coming in as | ||
56 | * IPIs in place of local APIC timers | ||
57 | */ | ||
58 | static cpumask_t timer_bcast_ipi; | ||
59 | |||
60 | /* | ||
61 | * Knob to control our willingness to enable the local APIC. | 56 | * Knob to control our willingness to enable the local APIC. |
62 | * | 57 | * |
63 | * -1=force-disable, +1=force-enable | 58 | * -1=force-disable, +1=force-enable |
64 | */ | 59 | */ |
65 | static int enable_local_apic __initdata = 0; | 60 | static int enable_local_apic __initdata = 0; |
66 | 61 | ||
62 | /* Enable local APIC timer for highres/dyntick on UP */ | ||
63 | static int enable_local_apic_timer __initdata = 0; | ||
64 | |||
67 | /* | 65 | /* |
68 | * Debug level, exported for io_apic.c | 66 | * Debug level, exported for io_apic.c |
69 | */ | 67 | */ |
70 | int apic_verbosity; | 68 | int apic_verbosity; |
71 | 69 | ||
72 | static void apic_pm_activate(void); | 70 | static unsigned int calibration_result; |
73 | 71 | ||
72 | static int lapic_next_event(unsigned long delta, | ||
73 | struct clock_event_device *evt); | ||
74 | static void lapic_timer_setup(enum clock_event_mode mode, | ||
75 | struct clock_event_device *evt); | ||
76 | static void lapic_timer_broadcast(cpumask_t mask); | ||
77 | static void apic_pm_activate(void); | ||
74 | 78 | ||
75 | /* Using APIC to generate smp_local_timer_interrupt? */ | 79 | /* |
76 | int using_apic_timer __read_mostly = 0; | 80 | * The local apic timer can be used for any function which is CPU local. |
81 | */ | ||
82 | static struct clock_event_device lapic_clockevent = { | ||
83 | .name = "lapic", | ||
84 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | ||
85 | | CLOCK_EVT_FEAT_C3STOP, | ||
86 | .shift = 32, | ||
87 | .set_mode = lapic_timer_setup, | ||
88 | .set_next_event = lapic_next_event, | ||
89 | .broadcast = lapic_timer_broadcast, | ||
90 | .rating = 100, | ||
91 | .irq = -1, | ||
92 | }; | ||
93 | static DEFINE_PER_CPU(struct clock_event_device, lapic_events); | ||
77 | 94 | ||
78 | /* Local APIC was disabled by the BIOS and enabled by the kernel */ | 95 | /* Local APIC was disabled by the BIOS and enabled by the kernel */ |
79 | static int enabled_via_apicbase; | 96 | static int enabled_via_apicbase; |
@@ -152,6 +169,11 @@ int lapic_get_maxlvt(void) | |||
152 | */ | 169 | */ |
153 | 170 | ||
154 | /* | 171 | /* |
172 | * FIXME: Move this to i8253.h. There is no need to keep the access to | ||
173 | * the PIT scattered all around the place -tglx | ||
174 | */ | ||
175 | |||
176 | /* | ||
155 | * The timer chip is already set up at HZ interrupts per second here, | 177 | * The timer chip is already set up at HZ interrupts per second here, |
156 | * but we do not accept timer interrupts yet. We only allow the BP | 178 | * but we do not accept timer interrupts yet. We only allow the BP |
157 | * to calibrate. | 179 | * to calibrate. |
@@ -209,16 +231,17 @@ void (*wait_timer_tick)(void) __devinitdata = wait_8254_wraparound; | |||
209 | 231 | ||
210 | #define APIC_DIVISOR 16 | 232 | #define APIC_DIVISOR 16 |
211 | 233 | ||
212 | static void __setup_APIC_LVTT(unsigned int clocks) | 234 | static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) |
213 | { | 235 | { |
214 | unsigned int lvtt_value, tmp_value; | 236 | unsigned int lvtt_value, tmp_value; |
215 | int cpu = smp_processor_id(); | ||
216 | 237 | ||
217 | lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; | 238 | lvtt_value = LOCAL_TIMER_VECTOR; |
239 | if (!oneshot) | ||
240 | lvtt_value |= APIC_LVT_TIMER_PERIODIC; | ||
218 | if (!lapic_is_integrated()) | 241 | if (!lapic_is_integrated()) |
219 | lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); | 242 | lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); |
220 | 243 | ||
221 | if (cpu_isset(cpu, timer_bcast_ipi)) | 244 | if (!irqen) |
222 | lvtt_value |= APIC_LVT_MASKED; | 245 | lvtt_value |= APIC_LVT_MASKED; |
223 | 246 | ||
224 | apic_write_around(APIC_LVTT, lvtt_value); | 247 | apic_write_around(APIC_LVTT, lvtt_value); |
@@ -231,31 +254,80 @@ static void __setup_APIC_LVTT(unsigned int clocks) | |||
231 | & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | 254 | & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) |
232 | | APIC_TDR_DIV_16); | 255 | | APIC_TDR_DIV_16); |
233 | 256 | ||
234 | apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR); | 257 | if (!oneshot) |
258 | apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR); | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * Program the next event, relative to now | ||
263 | */ | ||
264 | static int lapic_next_event(unsigned long delta, | ||
265 | struct clock_event_device *evt) | ||
266 | { | ||
267 | apic_write_around(APIC_TMICT, delta); | ||
268 | return 0; | ||
235 | } | 269 | } |
236 | 270 | ||
237 | static void __devinit setup_APIC_timer(unsigned int clocks) | 271 | /* |
272 | * Setup the lapic timer in periodic or oneshot mode | ||
273 | */ | ||
274 | static void lapic_timer_setup(enum clock_event_mode mode, | ||
275 | struct clock_event_device *evt) | ||
238 | { | 276 | { |
239 | unsigned long flags; | 277 | unsigned long flags; |
278 | unsigned int v; | ||
240 | 279 | ||
241 | local_irq_save(flags); | 280 | local_irq_save(flags); |
242 | 281 | ||
243 | /* | 282 | switch (mode) { |
244 | * Wait for IRQ0's slice: | 283 | case CLOCK_EVT_MODE_PERIODIC: |
245 | */ | 284 | case CLOCK_EVT_MODE_ONESHOT: |
246 | wait_timer_tick(); | 285 | __setup_APIC_LVTT(calibration_result, |
247 | 286 | mode != CLOCK_EVT_MODE_PERIODIC, 1); | |
248 | __setup_APIC_LVTT(clocks); | 287 | break; |
288 | case CLOCK_EVT_MODE_UNUSED: | ||
289 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
290 | v = apic_read(APIC_LVTT); | ||
291 | v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); | ||
292 | apic_write_around(APIC_LVTT, v); | ||
293 | break; | ||
294 | } | ||
249 | 295 | ||
250 | local_irq_restore(flags); | 296 | local_irq_restore(flags); |
251 | } | 297 | } |
252 | 298 | ||
253 | /* | 299 | /* |
300 | * Local APIC timer broadcast function | ||
301 | */ | ||
302 | static void lapic_timer_broadcast(cpumask_t mask) | ||
303 | { | ||
304 | #ifdef CONFIG_SMP | ||
305 | send_IPI_mask(mask, LOCAL_TIMER_VECTOR); | ||
306 | #endif | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Setup the local APIC timer for this CPU. Copy the initilized values | ||
311 | * of the boot CPU and register the clock event in the framework. | ||
312 | */ | ||
313 | static void __devinit setup_APIC_timer(void) | ||
314 | { | ||
315 | struct clock_event_device *levt = &__get_cpu_var(lapic_events); | ||
316 | |||
317 | memcpy(levt, &lapic_clockevent, sizeof(*levt)); | ||
318 | levt->cpumask = cpumask_of_cpu(smp_processor_id()); | ||
319 | |||
320 | clockevents_register_device(levt); | ||
321 | } | ||
322 | |||
323 | /* | ||
254 | * In this function we calibrate APIC bus clocks to the external | 324 | * In this function we calibrate APIC bus clocks to the external |
255 | * timer. Unfortunately we cannot use jiffies and the timer irq | 325 | * timer. Unfortunately we cannot use jiffies and the timer irq |
256 | * to calibrate, since some later bootup code depends on getting | 326 | * to calibrate, since some later bootup code depends on getting |
257 | * the first irq? Ugh. | 327 | * the first irq? Ugh. |
258 | * | 328 | * |
329 | * TODO: Fix this rather than saying "Ugh" -tglx | ||
330 | * | ||
259 | * We want to do the calibration only once since we | 331 | * We want to do the calibration only once since we |
260 | * want to have local timer irqs syncron. CPUs connected | 332 | * want to have local timer irqs syncron. CPUs connected |
261 | * by the same APIC bus have the very same bus frequency. | 333 | * by the same APIC bus have the very same bus frequency. |
@@ -278,7 +350,7 @@ static int __init calibrate_APIC_clock(void) | |||
278 | * value into the APIC clock, we just want to get the | 350 | * value into the APIC clock, we just want to get the |
279 | * counter running for calibration. | 351 | * counter running for calibration. |
280 | */ | 352 | */ |
281 | __setup_APIC_LVTT(1000000000); | 353 | __setup_APIC_LVTT(1000000000, 0, 0); |
282 | 354 | ||
283 | /* | 355 | /* |
284 | * The timer chip counts down to zero. Let's wait | 356 | * The timer chip counts down to zero. Let's wait |
@@ -315,6 +387,17 @@ static int __init calibrate_APIC_clock(void) | |||
315 | 387 | ||
316 | result = (tt1-tt2)*APIC_DIVISOR/LOOPS; | 388 | result = (tt1-tt2)*APIC_DIVISOR/LOOPS; |
317 | 389 | ||
390 | /* Calculate the scaled math multiplication factor */ | ||
391 | lapic_clockevent.mult = div_sc(tt1-tt2, TICK_NSEC * LOOPS, 32); | ||
392 | lapic_clockevent.max_delta_ns = | ||
393 | clockevent_delta2ns(0x7FFFFF, &lapic_clockevent); | ||
394 | lapic_clockevent.min_delta_ns = | ||
395 | clockevent_delta2ns(0xF, &lapic_clockevent); | ||
396 | |||
397 | apic_printk(APIC_VERBOSE, "..... tt1-tt2 %ld\n", tt1 - tt2); | ||
398 | apic_printk(APIC_VERBOSE, "..... mult: %ld\n", lapic_clockevent.mult); | ||
399 | apic_printk(APIC_VERBOSE, "..... calibration result: %ld\n", result); | ||
400 | |||
318 | if (cpu_has_tsc) | 401 | if (cpu_has_tsc) |
319 | apic_printk(APIC_VERBOSE, "..... CPU clock speed is " | 402 | apic_printk(APIC_VERBOSE, "..... CPU clock speed is " |
320 | "%ld.%04ld MHz.\n", | 403 | "%ld.%04ld MHz.\n", |
@@ -329,13 +412,10 @@ static int __init calibrate_APIC_clock(void) | |||
329 | return result; | 412 | return result; |
330 | } | 413 | } |
331 | 414 | ||
332 | static unsigned int calibration_result; | ||
333 | |||
334 | void __init setup_boot_APIC_clock(void) | 415 | void __init setup_boot_APIC_clock(void) |
335 | { | 416 | { |
336 | unsigned long flags; | 417 | unsigned long flags; |
337 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"); | 418 | apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n"); |
338 | using_apic_timer = 1; | ||
339 | 419 | ||
340 | local_irq_save(flags); | 420 | local_irq_save(flags); |
341 | 421 | ||
@@ -343,97 +423,47 @@ void __init setup_boot_APIC_clock(void) | |||
343 | /* | 423 | /* |
344 | * Now set up the timer for real. | 424 | * Now set up the timer for real. |
345 | */ | 425 | */ |
346 | setup_APIC_timer(calibration_result); | 426 | setup_APIC_timer(); |
347 | 427 | ||
348 | local_irq_restore(flags); | 428 | local_irq_restore(flags); |
349 | } | 429 | } |
350 | 430 | ||
351 | void __devinit setup_secondary_APIC_clock(void) | 431 | void __devinit setup_secondary_APIC_clock(void) |
352 | { | 432 | { |
353 | setup_APIC_timer(calibration_result); | 433 | setup_APIC_timer(); |
354 | } | ||
355 | |||
356 | void disable_APIC_timer(void) | ||
357 | { | ||
358 | if (using_apic_timer) { | ||
359 | unsigned long v; | ||
360 | |||
361 | v = apic_read(APIC_LVTT); | ||
362 | /* | ||
363 | * When an illegal vector value (0-15) is written to an LVT | ||
364 | * entry and delivery mode is Fixed, the APIC may signal an | ||
365 | * illegal vector error, with out regard to whether the mask | ||
366 | * bit is set or whether an interrupt is actually seen on | ||
367 | * input. | ||
368 | * | ||
369 | * Boot sequence might call this function when the LVTT has | ||
370 | * '0' vector value. So make sure vector field is set to | ||
371 | * valid value. | ||
372 | */ | ||
373 | v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); | ||
374 | apic_write_around(APIC_LVTT, v); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | void enable_APIC_timer(void) | ||
379 | { | ||
380 | int cpu = smp_processor_id(); | ||
381 | |||
382 | if (using_apic_timer && !cpu_isset(cpu, timer_bcast_ipi)) { | ||
383 | unsigned long v; | ||
384 | |||
385 | v = apic_read(APIC_LVTT); | ||
386 | apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED); | ||
387 | } | ||
388 | } | ||
389 | |||
390 | void switch_APIC_timer_to_ipi(void *cpumask) | ||
391 | { | ||
392 | cpumask_t mask = *(cpumask_t *)cpumask; | ||
393 | int cpu = smp_processor_id(); | ||
394 | |||
395 | if (cpu_isset(cpu, mask) && | ||
396 | !cpu_isset(cpu, timer_bcast_ipi)) { | ||
397 | disable_APIC_timer(); | ||
398 | cpu_set(cpu, timer_bcast_ipi); | ||
399 | } | ||
400 | } | ||
401 | EXPORT_SYMBOL(switch_APIC_timer_to_ipi); | ||
402 | |||
403 | void switch_ipi_to_APIC_timer(void *cpumask) | ||
404 | { | ||
405 | cpumask_t mask = *(cpumask_t *)cpumask; | ||
406 | int cpu = smp_processor_id(); | ||
407 | |||
408 | if (cpu_isset(cpu, mask) && | ||
409 | cpu_isset(cpu, timer_bcast_ipi)) { | ||
410 | cpu_clear(cpu, timer_bcast_ipi); | ||
411 | enable_APIC_timer(); | ||
412 | } | ||
413 | } | 434 | } |
414 | EXPORT_SYMBOL(switch_ipi_to_APIC_timer); | ||
415 | 435 | ||
416 | /* | 436 | /* |
417 | * Local timer interrupt handler. It does both profiling and | 437 | * The guts of the apic timer interrupt |
418 | * process statistics/rescheduling. | ||
419 | */ | 438 | */ |
420 | inline void smp_local_timer_interrupt(void) | 439 | static void local_apic_timer_interrupt(void) |
421 | { | 440 | { |
422 | profile_tick(CPU_PROFILING); | 441 | int cpu = smp_processor_id(); |
423 | #ifdef CONFIG_SMP | 442 | struct clock_event_device *evt = &per_cpu(lapic_events, cpu); |
424 | update_process_times(user_mode_vm(get_irq_regs())); | ||
425 | #endif | ||
426 | 443 | ||
427 | /* | 444 | /* |
428 | * We take the 'long' return path, and there every subsystem | 445 | * Normally we should not be here till LAPIC has been |
429 | * grabs the apropriate locks (kernel lock/ irq lock). | 446 | * initialized but in some cases like kdump, its possible that |
447 | * there is a pending LAPIC timer interrupt from previous | ||
448 | * kernel's context and is delivered in new kernel the moment | ||
449 | * interrupts are enabled. | ||
430 | * | 450 | * |
431 | * we might want to decouple profiling from the 'long path', | 451 | * Interrupts are enabled early and LAPIC is setup much later, |
432 | * and do the profiling totally in assembly. | 452 | * hence its possible that when we get here evt->event_handler |
433 | * | 453 | * is NULL. Check for event_handler being NULL and discard |
434 | * Currently this isn't too much of an issue (performance wise), | 454 | * the interrupt as spurious. |
435 | * we can take more than 100K local irqs per second on a 100 MHz P5. | ||
436 | */ | 455 | */ |
456 | if (!evt->event_handler) { | ||
457 | printk(KERN_WARNING | ||
458 | "Spurious LAPIC timer interrupt on cpu %d\n", cpu); | ||
459 | /* Switch it off */ | ||
460 | lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, evt); | ||
461 | return; | ||
462 | } | ||
463 | |||
464 | per_cpu(irq_stat, cpu).apic_timer_irqs++; | ||
465 | |||
466 | evt->event_handler(evt); | ||
437 | } | 467 | } |
438 | 468 | ||
439 | /* | 469 | /* |
@@ -445,15 +475,9 @@ inline void smp_local_timer_interrupt(void) | |||
445 | * interrupt as well. Thus we cannot inline the local irq ... ] | 475 | * interrupt as well. Thus we cannot inline the local irq ... ] |
446 | */ | 476 | */ |
447 | 477 | ||
448 | fastcall void smp_apic_timer_interrupt(struct pt_regs *regs) | 478 | void fastcall smp_apic_timer_interrupt(struct pt_regs *regs) |
449 | { | 479 | { |
450 | struct pt_regs *old_regs = set_irq_regs(regs); | 480 | struct pt_regs *old_regs = set_irq_regs(regs); |
451 | int cpu = smp_processor_id(); | ||
452 | |||
453 | /* | ||
454 | * the NMI deadlock-detector uses this. | ||
455 | */ | ||
456 | per_cpu(irq_stat, cpu).apic_timer_irqs++; | ||
457 | 481 | ||
458 | /* | 482 | /* |
459 | * NOTE! We'd better ACK the irq immediately, | 483 | * NOTE! We'd better ACK the irq immediately, |
@@ -467,41 +491,10 @@ fastcall void smp_apic_timer_interrupt(struct pt_regs *regs) | |||
467 | */ | 491 | */ |
468 | exit_idle(); | 492 | exit_idle(); |
469 | irq_enter(); | 493 | irq_enter(); |
470 | smp_local_timer_interrupt(); | 494 | local_apic_timer_interrupt(); |
471 | irq_exit(); | 495 | irq_exit(); |
472 | set_irq_regs(old_regs); | ||
473 | } | ||
474 | 496 | ||
475 | #ifndef CONFIG_SMP | 497 | set_irq_regs(old_regs); |
476 | static void up_apic_timer_interrupt_call(void) | ||
477 | { | ||
478 | int cpu = smp_processor_id(); | ||
479 | |||
480 | /* | ||
481 | * the NMI deadlock-detector uses this. | ||
482 | */ | ||
483 | per_cpu(irq_stat, cpu).apic_timer_irqs++; | ||
484 | |||
485 | smp_local_timer_interrupt(); | ||
486 | } | ||
487 | #endif | ||
488 | |||
489 | void smp_send_timer_broadcast_ipi(void) | ||
490 | { | ||
491 | cpumask_t mask; | ||
492 | |||
493 | cpus_and(mask, cpu_online_map, timer_bcast_ipi); | ||
494 | if (!cpus_empty(mask)) { | ||
495 | #ifdef CONFIG_SMP | ||
496 | send_IPI_mask(mask, LOCAL_TIMER_VECTOR); | ||
497 | #else | ||
498 | /* | ||
499 | * We can directly call the apic timer interrupt handler | ||
500 | * in UP case. Minus all irq related functions | ||
501 | */ | ||
502 | up_apic_timer_interrupt_call(); | ||
503 | #endif | ||
504 | } | ||
505 | } | 498 | } |
506 | 499 | ||
507 | int setup_profiling_timer(unsigned int multiplier) | 500 | int setup_profiling_timer(unsigned int multiplier) |
@@ -914,6 +907,11 @@ void __devinit setup_local_APIC(void) | |||
914 | printk(KERN_INFO "No ESR for 82489DX.\n"); | 907 | printk(KERN_INFO "No ESR for 82489DX.\n"); |
915 | } | 908 | } |
916 | 909 | ||
910 | /* Disable the local apic timer */ | ||
911 | value = apic_read(APIC_LVTT); | ||
912 | value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); | ||
913 | apic_write_around(APIC_LVTT, value); | ||
914 | |||
917 | setup_apic_nmi_watchdog(NULL); | 915 | setup_apic_nmi_watchdog(NULL); |
918 | apic_pm_activate(); | 916 | apic_pm_activate(); |
919 | } | 917 | } |
@@ -1128,6 +1126,13 @@ static int __init parse_nolapic(char *arg) | |||
1128 | } | 1126 | } |
1129 | early_param("nolapic", parse_nolapic); | 1127 | early_param("nolapic", parse_nolapic); |
1130 | 1128 | ||
1129 | static int __init apic_enable_lapic_timer(char *str) | ||
1130 | { | ||
1131 | enable_local_apic_timer = 1; | ||
1132 | return 0; | ||
1133 | } | ||
1134 | early_param("lapictimer", apic_enable_lapic_timer); | ||
1135 | |||
1131 | static int __init apic_set_verbosity(char *str) | 1136 | static int __init apic_set_verbosity(char *str) |
1132 | { | 1137 | { |
1133 | if (strcmp("debug", str) == 0) | 1138 | if (strcmp("debug", str) == 0) |
@@ -1147,7 +1152,7 @@ __setup("apic=", apic_set_verbosity); | |||
1147 | /* | 1152 | /* |
1148 | * This interrupt should _never_ happen with our APIC/SMP architecture | 1153 | * This interrupt should _never_ happen with our APIC/SMP architecture |
1149 | */ | 1154 | */ |
1150 | fastcall void smp_spurious_interrupt(struct pt_regs *regs) | 1155 | void smp_spurious_interrupt(struct pt_regs *regs) |
1151 | { | 1156 | { |
1152 | unsigned long v; | 1157 | unsigned long v; |
1153 | 1158 | ||
@@ -1171,7 +1176,7 @@ fastcall void smp_spurious_interrupt(struct pt_regs *regs) | |||
1171 | /* | 1176 | /* |
1172 | * This interrupt should never happen with our APIC/SMP architecture | 1177 | * This interrupt should never happen with our APIC/SMP architecture |
1173 | */ | 1178 | */ |
1174 | fastcall void smp_error_interrupt(struct pt_regs *regs) | 1179 | void smp_error_interrupt(struct pt_regs *regs) |
1175 | { | 1180 | { |
1176 | unsigned long v, v1; | 1181 | unsigned long v, v1; |
1177 | 1182 | ||