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/x86/kernel/apic_64.c | |
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/x86/kernel/apic_64.c')
-rw-r--r-- | arch/x86/kernel/apic_64.c | 90 |
1 files changed, 50 insertions, 40 deletions
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 | ||