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> |
33c053d0 | 19 | #include <linux/mca.h> |
b8ce3359 | 20 | |
28318daf | 21 | #include <asm/i8253.h> |
c37e7bb5 | 22 | #include <asm/hpet.h> |
6b37f5a2 | 23 | #include <asm/nmi.h> |
2aae950b | 24 | #include <asm/vgtod.h> |
ee238e5c GOC |
25 | #include <asm/time.h> |
26 | #include <asm/timer.h> | |
1da177e4 | 27 | |
1da177e4 | 28 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; |
1da177e4 | 29 | |
1da177e4 LT |
30 | unsigned long profile_pc(struct pt_regs *regs) |
31 | { | |
32 | unsigned long pc = instruction_pointer(regs); | |
33 | ||
31679f38 | 34 | /* Assume the lock function has either no stack frame or a copy |
65ea5b03 | 35 | of flags from PUSHF |
31679f38 | 36 | Eflags always has bits 22 and up cleared unlike kernel addresses. */ |
8de0b8a7 | 37 | if (!user_mode_vm(regs) && in_lock_functions(pc)) { |
3927fa9e GC |
38 | #ifdef CONFIG_FRAME_POINTER |
39 | return *(unsigned long *)(regs->bp + sizeof(long)); | |
40 | #else | |
65ea5b03 | 41 | unsigned long *sp = (unsigned long *)regs->sp; |
31679f38 AK |
42 | if (sp[0] >> 22) |
43 | return sp[0]; | |
44 | if (sp[1] >> 22) | |
45 | return sp[1]; | |
3927fa9e | 46 | #endif |
1da177e4 LT |
47 | } |
48 | return pc; | |
49 | } | |
50 | EXPORT_SYMBOL(profile_pc); | |
51 | ||
57a37505 | 52 | static irqreturn_t timer_interrupt(int irq, void *dev_id) |
b8ce3359 | 53 | { |
8ae93669 | 54 | inc_irq_stat(irq0_irqs); |
4e77ae3e | 55 | |
b8ce3359 TG |
56 | global_clock_event->event_handler(global_clock_event); |
57 | ||
33c053d0 GC |
58 | #ifdef CONFIG_MCA |
59 | if (MCA_bus) { | |
60 | u8 irq_v = inb_p(0x61); /* read the current state */ | |
61 | outb_p(irq_v|0x80, 0x61); /* reset the IRQ */ | |
62 | } | |
63 | #endif | |
64 | ||
b8ce3359 | 65 | return IRQ_HANDLED; |
1da177e4 LT |
66 | } |
67 | ||
6b37f5a2 JR |
68 | /* calibrate_cpu is used on systems with fixed rate TSCs to determine |
69 | * processor frequency */ | |
70 | #define TICK_COUNT 100000000 | |
8fbbc4b4 | 71 | unsigned long __init calibrate_cpu(void) |
6b37f5a2 | 72 | { |
2618f86e TG |
73 | int tsc_start, tsc_now; |
74 | int i, no_ctr_free; | |
75 | unsigned long evntsel3 = 0, pmc3 = 0, pmc_now = 0; | |
76 | unsigned long flags; | |
77 | ||
78 | for (i = 0; i < 4; i++) | |
79 | if (avail_to_resrv_perfctr_nmi_bit(i)) | |
80 | break; | |
81 | no_ctr_free = (i == 4); | |
82 | if (no_ctr_free) { | |
8652cb4b PB |
83 | WARN(1, KERN_WARNING "Warning: AMD perfctrs busy ... " |
84 | "cpu_khz value may be incorrect.\n"); | |
2618f86e TG |
85 | i = 3; |
86 | rdmsrl(MSR_K7_EVNTSEL3, evntsel3); | |
87 | wrmsrl(MSR_K7_EVNTSEL3, 0); | |
88 | rdmsrl(MSR_K7_PERFCTR3, pmc3); | |
89 | } else { | |
90 | reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i); | |
91 | reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | |
92 | } | |
93 | local_irq_save(flags); | |
3eb05676 | 94 | /* start measuring cycles, incrementing from 0 */ |
2618f86e TG |
95 | wrmsrl(MSR_K7_PERFCTR0 + i, 0); |
96 | wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76); | |
97 | rdtscl(tsc_start); | |
98 | do { | |
99 | rdmsrl(MSR_K7_PERFCTR0 + i, pmc_now); | |
6d63de8d | 100 | tsc_now = get_cycles(); |
2618f86e TG |
101 | } while ((tsc_now - tsc_start) < TICK_COUNT); |
102 | ||
103 | local_irq_restore(flags); | |
104 | if (no_ctr_free) { | |
105 | wrmsrl(MSR_K7_EVNTSEL3, 0); | |
106 | wrmsrl(MSR_K7_PERFCTR3, pmc3); | |
107 | wrmsrl(MSR_K7_EVNTSEL3, evntsel3); | |
108 | } else { | |
109 | release_perfctr_nmi(MSR_K7_PERFCTR0 + i); | |
110 | release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); | |
111 | } | |
112 | ||
113 | return pmc_now * tsc_khz / (tsc_now - tsc_start); | |
6b37f5a2 | 114 | } |
1da177e4 | 115 | |
1da177e4 | 116 | static struct irqaction irq0 = { |
461ebd10 | 117 | .handler = timer_interrupt, |
5fa3a246 | 118 | .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING, |
e6d828f4 | 119 | .mask = CPU_MASK_NONE, |
2618f86e | 120 | .name = "timer" |
1da177e4 LT |
121 | }; |
122 | ||
ee238e5c | 123 | void __init hpet_time_init(void) |
a670fad0 | 124 | { |
b8ce3359 TG |
125 | if (!hpet_enable()) |
126 | setup_pit_timer(); | |
a3a00751 | 127 | |
2ff29837 | 128 | irq0.mask = cpumask_of_cpu(0); |
b8ce3359 | 129 | setup_irq(0, &irq0); |
ee238e5c | 130 | } |
1da177e4 | 131 | |
ee238e5c GOC |
132 | void __init time_init(void) |
133 | { | |
8fbbc4b4 | 134 | tsc_init(); |
c08c8205 | 135 | |
ee238e5c | 136 | late_time_init = choose_time_init(); |
1da177e4 | 137 | } |