diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-10-12 17:04:07 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@inhelltoy.tec.linutronix.de> | 2007-10-12 17:04:07 -0400 |
commit | b8ce33590687888ebb900d09557b8807c4539022 (patch) | |
tree | 0e51543c7d4febff8ff6ad7660268bea2035f9ce /arch | |
parent | ba7eda4c60e1d070b2f6586d42719ec1d5302d3b (diff) |
x86_64: convert to clock events
Finally switch to the clockevents code. Share code with i386 for
hpet and PIT.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/Makefile_64 | 4 | ||||
-rw-r--r-- | arch/x86/kernel/apic_64.c | 90 | ||||
-rw-r--r-- | arch/x86/kernel/i8259_64.c | 46 | ||||
-rw-r--r-- | arch/x86/kernel/smpboot_64.c | 4 | ||||
-rw-r--r-- | arch/x86/kernel/time_64.c | 107 | ||||
-rw-r--r-- | arch/x86_64/Kconfig | 6 |
6 files changed, 70 insertions, 187 deletions
diff --git a/arch/x86/kernel/Makefile_64 b/arch/x86/kernel/Makefile_64 index 3ab017a0a3b9..080154e31502 100644 --- a/arch/x86/kernel/Makefile_64 +++ b/arch/x86/kernel/Makefile_64 | |||
@@ -8,8 +8,8 @@ obj-y := process_64.o signal_64.o entry_64.o traps_64.o irq_64.o \ | |||
8 | ptrace_64.o time_64.o ioport_64.o ldt_64.o setup_64.o i8259_64.o sys_x86_64.o \ | 8 | ptrace_64.o time_64.o ioport_64.o ldt_64.o setup_64.o i8259_64.o sys_x86_64.o \ |
9 | x8664_ksyms_64.o i387_64.o syscall_64.o vsyscall_64.o \ | 9 | x8664_ksyms_64.o i387_64.o syscall_64.o vsyscall_64.o \ |
10 | setup64.o bootflag.o e820_64.o reboot_64.o quirks.o i8237.o \ | 10 | setup64.o bootflag.o e820_64.o reboot_64.o quirks.o i8237.o \ |
11 | pci-dma_64.o pci-nommu_64.o alternative.o hpet_64.o tsc_64.o bugs_64.o \ | 11 | pci-dma_64.o pci-nommu_64.o alternative.o hpet_32.o tsc_64.o bugs_64.o \ |
12 | perfctr-watchdog.o | 12 | perfctr-watchdog.o i8253_32.o |
13 | 13 | ||
14 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 14 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
15 | obj-$(CONFIG_X86_MCE) += mce_64.o therm_throt.o | 15 | obj-$(CONFIG_X86_MCE) += mce_64.o therm_throt.o |
diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 2c2807abe1d4..b0237caff712 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c | |||
@@ -857,25 +857,12 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) | |||
857 | 857 | ||
858 | static void setup_APIC_timer(void) | 858 | static void setup_APIC_timer(void) |
859 | { | 859 | { |
860 | unsigned long flags; | 860 | struct clock_event_device *levt = &__get_cpu_var(lapic_events); |
861 | int irqen; | ||
862 | 861 | ||
863 | local_irq_save(flags); | 862 | memcpy(levt, &lapic_clockevent, sizeof(*levt)); |
863 | levt->cpumask = cpumask_of_cpu(smp_processor_id()); | ||
864 | 864 | ||
865 | irqen = ! cpu_isset(smp_processor_id(), | 865 | clockevents_register_device(levt); |
866 | timer_interrupt_broadcast_ipi_mask); | ||
867 | __setup_APIC_LVTT(calibration_result, 0, irqen); | ||
868 | /* Turn off PIT interrupt if we use APIC timer as main timer. | ||
869 | Only works with the PM timer right now | ||
870 | TBD fix it for HPET too. */ | ||
871 | if ((pmtmr_ioport != 0) && | ||
872 | smp_processor_id() == boot_cpu_id && | ||
873 | apic_runs_main_timer == 1 && | ||
874 | !cpu_isset(boot_cpu_id, timer_interrupt_broadcast_ipi_mask)) { | ||
875 | stop_timer_interrupt(); | ||
876 | apic_runs_main_timer++; | ||
877 | } | ||
878 | local_irq_restore(flags); | ||
879 | } | 866 | } |
880 | 867 | ||
881 | /* | 868 | /* |
@@ -950,18 +937,34 @@ static void __init calibrate_APIC_clock(void) | |||
950 | 937 | ||
951 | void __init setup_boot_APIC_clock (void) | 938 | void __init setup_boot_APIC_clock (void) |
952 | { | 939 | { |
940 | /* | ||
941 | * The local apic timer can be disabled via the kernel commandline. | ||
942 | * Register the lapic timer as a dummy clock event source on SMP | ||
943 | * systems, so the broadcast mechanism is used. On UP systems simply | ||
944 | * ignore it. | ||
945 | */ | ||
953 | if (disable_apic_timer) { | 946 | if (disable_apic_timer) { |
954 | printk(KERN_INFO "Disabling APIC timer\n"); | 947 | printk(KERN_INFO "Disabling APIC timer\n"); |
948 | /* No broadcast on UP ! */ | ||
949 | if (num_possible_cpus() > 1) | ||
950 | setup_APIC_timer(); | ||
955 | return; | 951 | return; |
956 | } | 952 | } |
957 | 953 | ||
958 | printk(KERN_INFO "Using local APIC timer interrupts.\n"); | 954 | printk(KERN_INFO "Using local APIC timer interrupts.\n"); |
959 | using_apic_timer = 1; | ||
960 | |||
961 | calibrate_APIC_clock(); | 955 | calibrate_APIC_clock(); |
956 | |||
962 | /* | 957 | /* |
963 | * Now set up the timer for real. | 958 | * If nmi_watchdog is set to IO_APIC, we need the |
959 | * PIT/HPET going. Otherwise register lapic as a dummy | ||
960 | * device. | ||
964 | */ | 961 | */ |
962 | if (nmi_watchdog != NMI_IO_APIC) | ||
963 | lapic_clockevent.features &= ~CLOCK_EVT_FEAT_DUMMY; | ||
964 | else | ||
965 | printk(KERN_WARNING "APIC timer registered as dummy," | ||
966 | " due to nmi_watchdog=1!\n"); | ||
967 | |||
965 | setup_APIC_timer(); | 968 | setup_APIC_timer(); |
966 | } | 969 | } |
967 | 970 | ||
@@ -1073,22 +1076,34 @@ void setup_APIC_extended_lvt(unsigned char lvt_off, unsigned char vector, | |||
1073 | 1076 | ||
1074 | void smp_local_timer_interrupt(void) | 1077 | void smp_local_timer_interrupt(void) |
1075 | { | 1078 | { |
1076 | profile_tick(CPU_PROFILING); | 1079 | int cpu = smp_processor_id(); |
1077 | #ifdef CONFIG_SMP | 1080 | struct clock_event_device *evt = &per_cpu(lapic_events, cpu); |
1078 | update_process_times(user_mode(get_irq_regs())); | 1081 | |
1079 | #endif | ||
1080 | if (apic_runs_main_timer > 1 && smp_processor_id() == boot_cpu_id) | ||
1081 | main_timer_handler(); | ||
1082 | /* | 1082 | /* |
1083 | * We take the 'long' return path, and there every subsystem | 1083 | * Normally we should not be here till LAPIC has been initialized but |
1084 | * grabs the appropriate locks (kernel lock/ irq lock). | 1084 | * in some cases like kdump, its possible that there is a pending LAPIC |
1085 | * | 1085 | * timer interrupt from previous kernel's context and is delivered in |
1086 | * We might want to decouple profiling from the 'long path', | 1086 | * new kernel the moment interrupts are enabled. |
1087 | * and do the profiling totally in assembly. | ||
1088 | * | 1087 | * |
1089 | * Currently this isn't too much of an issue (performance wise), | 1088 | * Interrupts are enabled early and LAPIC is setup much later, hence |
1090 | * we can take more than 100K local irqs per second on a 100 MHz P5. | 1089 | * its possible that when we get here evt->event_handler is NULL. |
1090 | * Check for event_handler being NULL and discard the interrupt as | ||
1091 | * spurious. | ||
1091 | */ | 1092 | */ |
1093 | if (!evt->event_handler) { | ||
1094 | printk(KERN_WARNING | ||
1095 | "Spurious LAPIC timer interrupt on cpu %d\n", cpu); | ||
1096 | /* Switch it off */ | ||
1097 | lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, evt); | ||
1098 | return; | ||
1099 | } | ||
1100 | |||
1101 | /* | ||
1102 | * the NMI deadlock-detector uses this. | ||
1103 | */ | ||
1104 | add_pda(apic_timer_irqs, 1); | ||
1105 | |||
1106 | evt->event_handler(evt); | ||
1092 | } | 1107 | } |
1093 | 1108 | ||
1094 | /* | 1109 | /* |
@@ -1104,11 +1119,6 @@ void smp_apic_timer_interrupt(struct pt_regs *regs) | |||
1104 | struct pt_regs *old_regs = set_irq_regs(regs); | 1119 | struct pt_regs *old_regs = set_irq_regs(regs); |
1105 | 1120 | ||
1106 | /* | 1121 | /* |
1107 | * the NMI deadlock-detector uses this. | ||
1108 | */ | ||
1109 | add_pda(apic_timer_irqs, 1); | ||
1110 | |||
1111 | /* | ||
1112 | * NOTE! We'd better ACK the irq immediately, | 1122 | * NOTE! We'd better ACK the irq immediately, |
1113 | * because timer handling can be slow. | 1123 | * because timer handling can be slow. |
1114 | */ | 1124 | */ |
@@ -1291,7 +1301,7 @@ static __init int setup_noapictimer(char *str) | |||
1291 | static __init int setup_apicmaintimer(char *str) | 1301 | static __init int setup_apicmaintimer(char *str) |
1292 | { | 1302 | { |
1293 | apic_runs_main_timer = 1; | 1303 | apic_runs_main_timer = 1; |
1294 | nohpet = 1; | 1304 | |
1295 | return 1; | 1305 | return 1; |
1296 | } | 1306 | } |
1297 | __setup("apicmaintimer", setup_apicmaintimer); | 1307 | __setup("apicmaintimer", setup_apicmaintimer); |
@@ -1307,7 +1317,7 @@ static __init int setup_apicpmtimer(char *s) | |||
1307 | { | 1317 | { |
1308 | apic_calibrate_pmtmr = 1; | 1318 | apic_calibrate_pmtmr = 1; |
1309 | notsc_setup(NULL); | 1319 | notsc_setup(NULL); |
1310 | return setup_apicmaintimer(NULL); | 1320 | return 0; |
1311 | } | 1321 | } |
1312 | __setup("apicpmtimer", setup_apicpmtimer); | 1322 | __setup("apicpmtimer", setup_apicpmtimer); |
1313 | 1323 | ||
diff --git a/arch/x86/kernel/i8259_64.c b/arch/x86/kernel/i8259_64.c index 948cae646099..eb72976cc13c 100644 --- a/arch/x86/kernel/i8259_64.c +++ b/arch/x86/kernel/i8259_64.c | |||
@@ -444,46 +444,6 @@ void __init init_ISA_irqs (void) | |||
444 | } | 444 | } |
445 | } | 445 | } |
446 | 446 | ||
447 | static void setup_timer_hardware(void) | ||
448 | { | ||
449 | outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ | ||
450 | udelay(10); | ||
451 | outb_p(LATCH & 0xff , 0x40); /* LSB */ | ||
452 | udelay(10); | ||
453 | outb(LATCH >> 8 , 0x40); /* MSB */ | ||
454 | } | ||
455 | |||
456 | static int timer_resume(struct sys_device *dev) | ||
457 | { | ||
458 | setup_timer_hardware(); | ||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | void i8254_timer_resume(void) | ||
463 | { | ||
464 | setup_timer_hardware(); | ||
465 | } | ||
466 | |||
467 | static struct sysdev_class timer_sysclass = { | ||
468 | set_kset_name("timer_pit"), | ||
469 | .resume = timer_resume, | ||
470 | }; | ||
471 | |||
472 | static struct sys_device device_timer = { | ||
473 | .id = 0, | ||
474 | .cls = &timer_sysclass, | ||
475 | }; | ||
476 | |||
477 | static int __init init_timer_sysfs(void) | ||
478 | { | ||
479 | int error = sysdev_class_register(&timer_sysclass); | ||
480 | if (!error) | ||
481 | error = sysdev_register(&device_timer); | ||
482 | return error; | ||
483 | } | ||
484 | |||
485 | device_initcall(init_timer_sysfs); | ||
486 | |||
487 | void __init init_IRQ(void) | 447 | void __init init_IRQ(void) |
488 | { | 448 | { |
489 | int i; | 449 | int i; |
@@ -533,12 +493,6 @@ void __init init_IRQ(void) | |||
533 | set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); | 493 | set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); |
534 | set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); | 494 | set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); |
535 | 495 | ||
536 | /* | ||
537 | * Set the clock to HZ Hz, we already have a valid | ||
538 | * vector now: | ||
539 | */ | ||
540 | setup_timer_hardware(); | ||
541 | |||
542 | if (!acpi_ioapic) | 496 | if (!acpi_ioapic) |
543 | setup_irq(2, &irq2); | 497 | setup_irq(2, &irq2); |
544 | } | 498 | } |
diff --git a/arch/x86/kernel/smpboot_64.c b/arch/x86/kernel/smpboot_64.c index 32f50783edc8..57ccf7cb6b91 100644 --- a/arch/x86/kernel/smpboot_64.c +++ b/arch/x86/kernel/smpboot_64.c | |||
@@ -223,8 +223,6 @@ void __cpuinit smp_callin(void) | |||
223 | local_irq_disable(); | 223 | local_irq_disable(); |
224 | Dprintk("Stack at about %p\n",&cpuid); | 224 | Dprintk("Stack at about %p\n",&cpuid); |
225 | 225 | ||
226 | disable_APIC_timer(); | ||
227 | |||
228 | /* | 226 | /* |
229 | * Save our processor parameters | 227 | * Save our processor parameters |
230 | */ | 228 | */ |
@@ -348,8 +346,6 @@ void __cpuinit start_secondary(void) | |||
348 | enable_8259A_irq(0); | 346 | enable_8259A_irq(0); |
349 | } | 347 | } |
350 | 348 | ||
351 | enable_APIC_timer(); | ||
352 | |||
353 | /* | 349 | /* |
354 | * The sibling maps must be set before turing the online map on for | 350 | * The sibling maps must be set before turing the online map on for |
355 | * this cpu | 351 | * this cpu |
diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index d899216e01ca..7781df1d50e3 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <linux/cpu.h> | 28 | #include <linux/cpu.h> |
29 | #include <linux/kallsyms.h> | 29 | #include <linux/kallsyms.h> |
30 | #include <linux/acpi.h> | 30 | #include <linux/acpi.h> |
31 | #include <linux/clockchips.h> | ||
32 | |||
31 | #ifdef CONFIG_ACPI | 33 | #ifdef CONFIG_ACPI |
32 | #include <acpi/achware.h> /* for PM timer frequency */ | 34 | #include <acpi/achware.h> /* for PM timer frequency */ |
33 | #include <acpi/acpi_bus.h> | 35 | #include <acpi/acpi_bus.h> |
@@ -46,12 +48,8 @@ | |||
46 | #include <asm/nmi.h> | 48 | #include <asm/nmi.h> |
47 | #include <asm/vgtod.h> | 49 | #include <asm/vgtod.h> |
48 | 50 | ||
49 | static char *timename = NULL; | ||
50 | |||
51 | DEFINE_SPINLOCK(rtc_lock); | 51 | DEFINE_SPINLOCK(rtc_lock); |
52 | EXPORT_SYMBOL(rtc_lock); | 52 | EXPORT_SYMBOL(rtc_lock); |
53 | DEFINE_SPINLOCK(i8253_lock); | ||
54 | EXPORT_SYMBOL(i8253_lock); | ||
55 | 53 | ||
56 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; | 54 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; |
57 | 55 | ||
@@ -194,6 +192,13 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id) | |||
194 | return IRQ_HANDLED; | 192 | return IRQ_HANDLED; |
195 | } | 193 | } |
196 | 194 | ||
195 | static irqreturn_t timer_event_interrupt(int irq, void *dev_id) | ||
196 | { | ||
197 | global_clock_event->event_handler(global_clock_event); | ||
198 | |||
199 | return IRQ_HANDLED; | ||
200 | } | ||
201 | |||
197 | unsigned long read_persistent_clock(void) | 202 | unsigned long read_persistent_clock(void) |
198 | { | 203 | { |
199 | unsigned int year, mon, day, hour, min, sec; | 204 | unsigned int year, mon, day, hour, min, sec; |
@@ -291,42 +296,8 @@ static unsigned int __init tsc_calibrate_cpu_khz(void) | |||
291 | return pmc_now * tsc_khz / (tsc_now - tsc_start); | 296 | return pmc_now * tsc_khz / (tsc_now - tsc_start); |
292 | } | 297 | } |
293 | 298 | ||
294 | static void __pit_init(int val, u8 mode) | ||
295 | { | ||
296 | unsigned long flags; | ||
297 | |||
298 | spin_lock_irqsave(&i8253_lock, flags); | ||
299 | outb_p(mode, PIT_MODE); | ||
300 | outb_p(val & 0xff, PIT_CH0); /* LSB */ | ||
301 | outb_p(val >> 8, PIT_CH0); /* MSB */ | ||
302 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
303 | } | ||
304 | |||
305 | void __init pit_init(void) | ||
306 | { | ||
307 | __pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */ | ||
308 | } | ||
309 | |||
310 | void pit_stop_interrupt(void) | ||
311 | { | ||
312 | __pit_init(0, 0x30); /* mode 0 */ | ||
313 | } | ||
314 | |||
315 | void stop_timer_interrupt(void) | ||
316 | { | ||
317 | char *name; | ||
318 | if (hpet_address) { | ||
319 | name = "HPET"; | ||
320 | hpet_timer_stop_set_go(0); | ||
321 | } else { | ||
322 | name = "PIT"; | ||
323 | pit_stop_interrupt(); | ||
324 | } | ||
325 | printk(KERN_INFO "timer: %s interrupt stopped.\n", name); | ||
326 | } | ||
327 | |||
328 | static struct irqaction irq0 = { | 299 | static struct irqaction irq0 = { |
329 | .handler = timer_interrupt, | 300 | .handler = timer_event_interrupt, |
330 | .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING, | 301 | .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING, |
331 | .mask = CPU_MASK_NONE, | 302 | .mask = CPU_MASK_NONE, |
332 | .name = "timer" | 303 | .name = "timer" |
@@ -334,20 +305,10 @@ static struct irqaction irq0 = { | |||
334 | 305 | ||
335 | void __init time_init(void) | 306 | void __init time_init(void) |
336 | { | 307 | { |
337 | if (nohpet) | 308 | if (!hpet_enable()) |
338 | hpet_address = 0; | 309 | setup_pit_timer(); |
339 | |||
340 | if (hpet_arch_init()) | ||
341 | hpet_address = 0; | ||
342 | 310 | ||
343 | if (hpet_use_timer) { | 311 | setup_irq(0, &irq0); |
344 | /* set tick_nsec to use the proper rate for HPET */ | ||
345 | tick_nsec = TICK_NSEC_HPET; | ||
346 | timename = "HPET"; | ||
347 | } else { | ||
348 | pit_init(); | ||
349 | timename = "PIT"; | ||
350 | } | ||
351 | 312 | ||
352 | tsc_calibrate(); | 313 | tsc_calibrate(); |
353 | 314 | ||
@@ -369,46 +330,4 @@ void __init time_init(void) | |||
369 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", | 330 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", |
370 | cpu_khz / 1000, cpu_khz % 1000); | 331 | cpu_khz / 1000, cpu_khz % 1000); |
371 | init_tsc_clocksource(); | 332 | init_tsc_clocksource(); |
372 | |||
373 | setup_irq(0, &irq0); | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * sysfs support for the timer. | ||
378 | */ | ||
379 | |||
380 | static int timer_suspend(struct sys_device *dev, pm_message_t state) | ||
381 | { | ||
382 | return 0; | ||
383 | } | 333 | } |
384 | |||
385 | static int timer_resume(struct sys_device *dev) | ||
386 | { | ||
387 | if (hpet_address) | ||
388 | hpet_reenable(); | ||
389 | else | ||
390 | i8254_timer_resume(); | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static struct sysdev_class timer_sysclass = { | ||
395 | .resume = timer_resume, | ||
396 | .suspend = timer_suspend, | ||
397 | set_kset_name("timer"), | ||
398 | }; | ||
399 | |||
400 | /* XXX this sysfs stuff should probably go elsewhere later -john */ | ||
401 | static struct sys_device device_timer = { | ||
402 | .id = 0, | ||
403 | .cls = &timer_sysclass, | ||
404 | }; | ||
405 | |||
406 | static int time_init_device(void) | ||
407 | { | ||
408 | int error = sysdev_class_register(&timer_sysclass); | ||
409 | if (!error) | ||
410 | error = sysdev_register(&device_timer); | ||
411 | return error; | ||
412 | } | ||
413 | |||
414 | device_initcall(time_init_device); | ||
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index a9534c21c306..eb80f5aca54e 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig | |||
@@ -40,7 +40,11 @@ config CLOCKSOURCE_WATCHDOG | |||
40 | bool | 40 | bool |
41 | default y | 41 | default y |
42 | 42 | ||
43 | config GENERIC_CLOCKEVENTS_MIGR | 43 | config GENERIC_CLOCKEVENTS |
44 | bool | ||
45 | default y | ||
46 | |||
47 | config GENERIC_CLOCKEVENTS_BROADCAST | ||
44 | bool | 48 | bool |
45 | default y | 49 | default y |
46 | 50 | ||