Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * "High Precision Event Timer" based timekeeping. |
3 | * | |
4 | * Copyright (c) 1991,1992,1995 Linus Torvalds | |
5 | * Copyright (c) 1994 Alan Modra | |
6 | * Copyright (c) 1995 Markus Kuhn | |
7 | * Copyright (c) 1996 Ingo Molnar | |
8 | * Copyright (c) 1998 Andrea Arcangeli | |
2f82bde4 | 9 | * Copyright (c) 2002,2006 Vojtech Pavlik |
1da177e4 LT |
10 | * Copyright (c) 2003 Andi Kleen |
11 | * RTC support code taken from arch/i386/kernel/timers/time_hpet.c | |
12 | */ | |
13 | ||
081e10b9 | 14 | #include <linux/clockchips.h> |
1da177e4 | 15 | #include <linux/init.h> |
081e10b9 | 16 | #include <linux/interrupt.h> |
1da177e4 | 17 | #include <linux/module.h> |
081e10b9 | 18 | #include <linux/time.h> |
b8ce3359 | 19 | |
28318daf | 20 | #include <asm/i8253.h> |
c37e7bb5 | 21 | #include <asm/hpet.h> |
6b37f5a2 | 22 | #include <asm/nmi.h> |
2aae950b | 23 | #include <asm/vgtod.h> |
ee238e5c GOC |
24 | #include <asm/time.h> |
25 | #include <asm/timer.h> | |
1da177e4 | 26 | |
1da177e4 | 27 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; |
1da177e4 | 28 | |
1da177e4 LT |
29 | unsigned long profile_pc(struct pt_regs *regs) |
30 | { | |
31 | unsigned long pc = instruction_pointer(regs); | |
32 | ||
31679f38 | 33 | /* Assume the lock function has either no stack frame or a copy |
65ea5b03 | 34 | of flags from PUSHF |
31679f38 | 35 | Eflags always has bits 22 and up cleared unlike kernel addresses. */ |
d5a26017 | 36 | if (!user_mode(regs) && in_lock_functions(pc)) { |
65ea5b03 | 37 | unsigned long *sp = (unsigned long *)regs->sp; |
31679f38 AK |
38 | if (sp[0] >> 22) |
39 | return sp[0]; | |
40 | if (sp[1] >> 22) | |
41 | return sp[1]; | |
1da177e4 LT |
42 | } |
43 | return pc; | |
44 | } | |
45 | EXPORT_SYMBOL(profile_pc); | |
46 | ||
b8ce3359 TG |
47 | static irqreturn_t timer_event_interrupt(int irq, void *dev_id) |
48 | { | |
4e77ae3e TG |
49 | add_pda(irq0_irqs, 1); |
50 | ||
b8ce3359 TG |
51 | global_clock_event->event_handler(global_clock_event); |
52 | ||
53 | return IRQ_HANDLED; | |
1da177e4 LT |
54 | } |
55 | ||
6b37f5a2 JR |
56 | /* calibrate_cpu is used on systems with fixed rate TSCs to determine |
57 | * processor frequency */ | |
58 | #define TICK_COUNT 100000000 | |
8fbbc4b4 | 59 | unsigned long __init calibrate_cpu(void) |
6b37f5a2 | 60 | { |
2618f86e TG |
61 | int tsc_start, tsc_now; |
62 | int i, no_ctr_free; | |
63 | unsigned long evntsel3 = 0, pmc3 = 0, pmc_now = 0; | |
64 | unsigned long flags; | |
65 | ||
66 | for (i = 0; i < 4; i++) | |
67 | if (avail_to_resrv_perfctr_nmi_bit(i)) | |
68 | break; | |
69 | no_ctr_free = (i == 4); | |
70 | if (no_ctr_free) { | |
71 | i = 3; | |
72 | rdmsrl(MSR_K7_EVNTSEL3, evntsel3); | |
73 | wrmsrl(MSR_K7_EVNTSEL3, 0); | |
74 | rdmsrl(MSR_K7_PERFCTR3, pmc3); | |
75 | } else { | |
76 | reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i); | |
77 | reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | |
78 | } | |
79 | local_irq_save(flags); | |
3eb05676 | 80 | /* start measuring cycles, incrementing from 0 */ |
2618f86e TG |
81 | wrmsrl(MSR_K7_PERFCTR0 + i, 0); |
82 | wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76); | |
83 | rdtscl(tsc_start); | |
84 | do { | |
85 | rdmsrl(MSR_K7_PERFCTR0 + i, pmc_now); | |
6d63de8d | 86 | tsc_now = get_cycles(); |
2618f86e TG |
87 | } while ((tsc_now - tsc_start) < TICK_COUNT); |
88 | ||
89 | local_irq_restore(flags); | |
90 | if (no_ctr_free) { | |
91 | wrmsrl(MSR_K7_EVNTSEL3, 0); | |
92 | wrmsrl(MSR_K7_PERFCTR3, pmc3); | |
93 | wrmsrl(MSR_K7_EVNTSEL3, evntsel3); | |
94 | } else { | |
95 | release_perfctr_nmi(MSR_K7_PERFCTR0 + i); | |
96 | release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | |
97 | } | |
98 | ||
99 | return pmc_now * tsc_khz / (tsc_now - tsc_start); | |
6b37f5a2 | 100 | } |
1da177e4 | 101 | |
1da177e4 | 102 | static struct irqaction irq0 = { |
b8ce3359 | 103 | .handler = timer_event_interrupt, |
5fa3a246 | 104 | .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING, |
e6d828f4 | 105 | .mask = CPU_MASK_NONE, |
2618f86e | 106 | .name = "timer" |
1da177e4 LT |
107 | }; |
108 | ||
ee238e5c | 109 | void __init hpet_time_init(void) |
a670fad0 | 110 | { |
b8ce3359 TG |
111 | if (!hpet_enable()) |
112 | setup_pit_timer(); | |
a3a00751 | 113 | |
b8ce3359 | 114 | setup_irq(0, &irq0); |
ee238e5c | 115 | } |
1da177e4 | 116 | |
ee238e5c GOC |
117 | void __init time_init(void) |
118 | { | |
8fbbc4b4 | 119 | tsc_init(); |
2d0c87c3 | 120 | if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) |
c08c8205 VP |
121 | vgetcpu_mode = VGETCPU_RDTSCP; |
122 | else | |
123 | vgetcpu_mode = VGETCPU_LSL; | |
124 | ||
ee238e5c | 125 | late_time_init = choose_time_init(); |
1da177e4 | 126 | } |