diff options
Diffstat (limited to 'arch/x86/kernel/time_64.c')
-rw-r--r-- | arch/x86/kernel/time_64.c | 177 |
1 files changed, 11 insertions, 166 deletions
diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index 6d48a4e826d9..e0134d6c88da 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c | |||
@@ -28,11 +28,12 @@ | |||
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> |
34 | #endif | 36 | #endif |
35 | #include <asm/8253pit.h> | ||
36 | #include <asm/i8253.h> | 37 | #include <asm/i8253.h> |
37 | #include <asm/pgtable.h> | 38 | #include <asm/pgtable.h> |
38 | #include <asm/vsyscall.h> | 39 | #include <asm/vsyscall.h> |
@@ -47,12 +48,8 @@ | |||
47 | #include <asm/nmi.h> | 48 | #include <asm/nmi.h> |
48 | #include <asm/vgtod.h> | 49 | #include <asm/vgtod.h> |
49 | 50 | ||
50 | static char *timename = NULL; | ||
51 | |||
52 | DEFINE_SPINLOCK(rtc_lock); | 51 | DEFINE_SPINLOCK(rtc_lock); |
53 | EXPORT_SYMBOL(rtc_lock); | 52 | EXPORT_SYMBOL(rtc_lock); |
54 | DEFINE_SPINLOCK(i8253_lock); | ||
55 | EXPORT_SYMBOL(i8253_lock); | ||
56 | 53 | ||
57 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; | 54 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; |
58 | 55 | ||
@@ -153,45 +150,12 @@ int update_persistent_clock(struct timespec now) | |||
153 | return set_rtc_mmss(now.tv_sec); | 150 | return set_rtc_mmss(now.tv_sec); |
154 | } | 151 | } |
155 | 152 | ||
156 | void main_timer_handler(void) | 153 | static irqreturn_t timer_event_interrupt(int irq, void *dev_id) |
157 | { | 154 | { |
158 | /* | 155 | add_pda(irq0_irqs, 1); |
159 | * Here we are in the timer irq handler. We have irqs locally disabled (so we | ||
160 | * don't need spin_lock_irqsave()) but we don't know if the timer_bh is running | ||
161 | * on the other CPU, so we need a lock. We also need to lock the vsyscall | ||
162 | * variables, because both do_timer() and us change them -arca+vojtech | ||
163 | */ | ||
164 | |||
165 | write_seqlock(&xtime_lock); | ||
166 | 156 | ||
167 | /* | 157 | global_clock_event->event_handler(global_clock_event); |
168 | * Do the timer stuff. | ||
169 | */ | ||
170 | |||
171 | do_timer(1); | ||
172 | #ifndef CONFIG_SMP | ||
173 | update_process_times(user_mode(get_irq_regs())); | ||
174 | #endif | ||
175 | 158 | ||
176 | /* | ||
177 | * In the SMP case we use the local APIC timer interrupt to do the profiling, | ||
178 | * except when we simulate SMP mode on a uniprocessor system, in that case we | ||
179 | * have to call the local interrupt handler. | ||
180 | */ | ||
181 | |||
182 | if (!using_apic_timer) | ||
183 | smp_local_timer_interrupt(); | ||
184 | |||
185 | write_sequnlock(&xtime_lock); | ||
186 | } | ||
187 | |||
188 | static irqreturn_t timer_interrupt(int irq, void *dev_id) | ||
189 | { | ||
190 | if (apic_runs_main_timer > 1) | ||
191 | return IRQ_HANDLED; | ||
192 | main_timer_handler(); | ||
193 | if (using_apic_timer) | ||
194 | smp_send_timer_broadcast_ipi(); | ||
195 | return IRQ_HANDLED; | 159 | return IRQ_HANDLED; |
196 | } | 160 | } |
197 | 161 | ||
@@ -292,97 +256,21 @@ static unsigned int __init tsc_calibrate_cpu_khz(void) | |||
292 | return pmc_now * tsc_khz / (tsc_now - tsc_start); | 256 | return pmc_now * tsc_khz / (tsc_now - tsc_start); |
293 | } | 257 | } |
294 | 258 | ||
295 | /* | ||
296 | * pit_calibrate_tsc() uses the speaker output (channel 2) of | ||
297 | * the PIT. This is better than using the timer interrupt output, | ||
298 | * because we can read the value of the speaker with just one inb(), | ||
299 | * where we need three i/o operations for the interrupt channel. | ||
300 | * We count how many ticks the TSC does in 50 ms. | ||
301 | */ | ||
302 | |||
303 | static unsigned int __init pit_calibrate_tsc(void) | ||
304 | { | ||
305 | unsigned long start, end; | ||
306 | unsigned long flags; | ||
307 | |||
308 | spin_lock_irqsave(&i8253_lock, flags); | ||
309 | |||
310 | outb((inb(0x61) & ~0x02) | 0x01, 0x61); | ||
311 | |||
312 | outb(0xb0, 0x43); | ||
313 | outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | ||
314 | outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42); | ||
315 | start = get_cycles_sync(); | ||
316 | while ((inb(0x61) & 0x20) == 0); | ||
317 | end = get_cycles_sync(); | ||
318 | |||
319 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
320 | |||
321 | return (end - start) / 50; | ||
322 | } | ||
323 | |||
324 | #define PIT_MODE 0x43 | ||
325 | #define PIT_CH0 0x40 | ||
326 | |||
327 | static void __pit_init(int val, u8 mode) | ||
328 | { | ||
329 | unsigned long flags; | ||
330 | |||
331 | spin_lock_irqsave(&i8253_lock, flags); | ||
332 | outb_p(mode, PIT_MODE); | ||
333 | outb_p(val & 0xff, PIT_CH0); /* LSB */ | ||
334 | outb_p(val >> 8, PIT_CH0); /* MSB */ | ||
335 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
336 | } | ||
337 | |||
338 | void __init pit_init(void) | ||
339 | { | ||
340 | __pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */ | ||
341 | } | ||
342 | |||
343 | void pit_stop_interrupt(void) | ||
344 | { | ||
345 | __pit_init(0, 0x30); /* mode 0 */ | ||
346 | } | ||
347 | |||
348 | void stop_timer_interrupt(void) | ||
349 | { | ||
350 | char *name; | ||
351 | if (hpet_address) { | ||
352 | name = "HPET"; | ||
353 | hpet_timer_stop_set_go(0); | ||
354 | } else { | ||
355 | name = "PIT"; | ||
356 | pit_stop_interrupt(); | ||
357 | } | ||
358 | printk(KERN_INFO "timer: %s interrupt stopped.\n", name); | ||
359 | } | ||
360 | |||
361 | static struct irqaction irq0 = { | 259 | static struct irqaction irq0 = { |
362 | .handler = timer_interrupt, | 260 | .handler = timer_event_interrupt, |
363 | .flags = IRQF_DISABLED | IRQF_IRQPOLL, | 261 | .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING, |
364 | .mask = CPU_MASK_NONE, | 262 | .mask = CPU_MASK_NONE, |
365 | .name = "timer" | 263 | .name = "timer" |
366 | }; | 264 | }; |
367 | 265 | ||
368 | void __init time_init(void) | 266 | void __init time_init(void) |
369 | { | 267 | { |
370 | if (nohpet) | 268 | if (!hpet_enable()) |
371 | hpet_address = 0; | 269 | setup_pit_timer(); |
372 | 270 | ||
373 | if (hpet_arch_init()) | 271 | setup_irq(0, &irq0); |
374 | hpet_address = 0; | ||
375 | 272 | ||
376 | if (hpet_use_timer) { | 273 | tsc_calibrate(); |
377 | /* set tick_nsec to use the proper rate for HPET */ | ||
378 | tick_nsec = TICK_NSEC_HPET; | ||
379 | tsc_khz = hpet_calibrate_tsc(); | ||
380 | timename = "HPET"; | ||
381 | } else { | ||
382 | pit_init(); | ||
383 | tsc_khz = pit_calibrate_tsc(); | ||
384 | timename = "PIT"; | ||
385 | } | ||
386 | 274 | ||
387 | cpu_khz = tsc_khz; | 275 | cpu_khz = tsc_khz; |
388 | if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) && | 276 | if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) && |
@@ -398,50 +286,7 @@ void __init time_init(void) | |||
398 | else | 286 | else |
399 | vgetcpu_mode = VGETCPU_LSL; | 287 | vgetcpu_mode = VGETCPU_LSL; |
400 | 288 | ||
401 | set_cyc2ns_scale(tsc_khz); | ||
402 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", | 289 | printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", |
403 | cpu_khz / 1000, cpu_khz % 1000); | 290 | cpu_khz / 1000, cpu_khz % 1000); |
404 | init_tsc_clocksource(); | 291 | init_tsc_clocksource(); |
405 | |||
406 | setup_irq(0, &irq0); | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * sysfs support for the timer. | ||
411 | */ | ||
412 | |||
413 | static int timer_suspend(struct sys_device *dev, pm_message_t state) | ||
414 | { | ||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | static int timer_resume(struct sys_device *dev) | ||
419 | { | ||
420 | if (hpet_address) | ||
421 | hpet_reenable(); | ||
422 | else | ||
423 | i8254_timer_resume(); | ||
424 | return 0; | ||
425 | } | 292 | } |
426 | |||
427 | static struct sysdev_class timer_sysclass = { | ||
428 | .resume = timer_resume, | ||
429 | .suspend = timer_suspend, | ||
430 | set_kset_name("timer"), | ||
431 | }; | ||
432 | |||
433 | /* XXX this sysfs stuff should probably go elsewhere later -john */ | ||
434 | static struct sys_device device_timer = { | ||
435 | .id = 0, | ||
436 | .cls = &timer_sysclass, | ||
437 | }; | ||
438 | |||
439 | static int time_init_device(void) | ||
440 | { | ||
441 | int error = sysdev_class_register(&timer_sysclass); | ||
442 | if (!error) | ||
443 | error = sysdev_register(&device_timer); | ||
444 | return error; | ||
445 | } | ||
446 | |||
447 | device_initcall(time_init_device); | ||