blob: 5ba343e6184449775c51f21717ef985f8d7249d0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * "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
Vojtech Pavlik2f82bde42006-06-26 13:58:38 +02009 * Copyright (c) 2002,2006 Vojtech Pavlik
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 * Copyright (c) 2003 Andi Kleen
11 * RTC support code taken from arch/i386/kernel/timers/time_hpet.c
12 */
13
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020014#include <linux/clockchips.h>
Thomas Gleixner081e10b2008-01-30 13:30:27 +010015#include <linux/init.h>
16#include <linux/interrupt.h>
17#include <linux/module.h>
18#include <linux/time.h>
Glauber Costa33c053d2008-07-21 18:42:52 -030019#include <linux/mca.h>
Jaswinder Singh Rajputfe331182009-01-04 16:28:22 +053020#include <linux/nmi.h>
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020021
Thomas Gleixner28318da2007-07-21 17:11:18 +020022#include <asm/i8253.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include <asm/hpet.h>
Andi Kleen2aae9502007-07-21 17:10:01 +020024#include <asm/vgtod.h>
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +010025#include <asm/time.h>
26#include <asm/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Linus Torvalds1da177e2005-04-16 15:20:36 -070028volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Linus Torvalds1da177e2005-04-16 15:20:36 -070030unsigned long profile_pc(struct pt_regs *regs)
31{
32 unsigned long pc = instruction_pointer(regs);
33
Andi Kleen31679f32006-09-26 10:52:28 +020034 /* Assume the lock function has either no stack frame or a copy
H. Peter Anvin65ea5b02008-01-30 13:30:56 +010035 of flags from PUSHF
Andi Kleen31679f32006-09-26 10:52:28 +020036 Eflags always has bits 22 and up cleared unlike kernel addresses. */
Glauber Costa8de0b8a2008-07-11 14:10:13 -030037 if (!user_mode_vm(regs) && in_lock_functions(pc)) {
Glauber Costa3927fa92008-07-11 13:53:43 -030038#ifdef CONFIG_FRAME_POINTER
39 return *(unsigned long *)(regs->bp + sizeof(long));
40#else
H. Peter Anvin65ea5b02008-01-30 13:30:56 +010041 unsigned long *sp = (unsigned long *)regs->sp;
Andi Kleen31679f32006-09-26 10:52:28 +020042 if (sp[0] >> 22)
43 return sp[0];
44 if (sp[1] >> 22)
45 return sp[1];
Glauber Costa3927fa92008-07-11 13:53:43 -030046#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 }
48 return pc;
49}
50EXPORT_SYMBOL(profile_pc);
51
Jaswinder Singh57a37502008-12-17 23:17:21 +053052static irqreturn_t timer_interrupt(int irq, void *dev_id)
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020053{
Hiroshi Shimamoto8ae93662008-12-12 15:52:26 -080054 inc_irq_stat(irq0_irqs);
Thomas Gleixner4e77ae32007-10-12 23:04:07 +020055
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020056 global_clock_event->event_handler(global_clock_event);
57
Glauber Costa33c053d2008-07-21 18:42:52 -030058#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
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020065 return IRQ_HANDLED;
66}
67
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020068/* calibrate_cpu is used on systems with fixed rate TSCs to determine
69 * processor frequency */
70#define TICK_COUNT 100000000
Alok Kataria8fbbc4b2008-07-01 11:43:34 -070071unsigned long __init calibrate_cpu(void)
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020072{
Thomas Gleixner2618f862007-07-21 17:10:18 +020073 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;
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020077
Thomas Gleixner2618f862007-07-21 17:10:18 +020078 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) {
Prarit Bhargava8652cb42008-11-12 13:35:00 -050083 WARN(1, KERN_WARNING "Warning: AMD perfctrs busy ... "
84 "cpu_khz value may be incorrect.\n");
Thomas Gleixner2618f862007-07-21 17:10:18 +020085 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);
Li Zefan3eb05672008-02-08 04:19:25 -080094 /* start measuring cycles, incrementing from 0 */
Thomas Gleixner2618f862007-07-21 17:10:18 +020095 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);
Andi Kleen6d63de82008-01-30 13:32:39 +0100100 tsc_now = get_cycles();
Thomas Gleixner2618f862007-07-21 17:10:18 +0200101 } while ((tsc_now - tsc_start) < TICK_COUNT);
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200102
Thomas Gleixner2618f862007-07-21 17:10:18 +0200103 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 }
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200112
Thomas Gleixner2618f862007-07-21 17:10:18 +0200113 return pmc_now * tsc_khz / (tsc_now - tsc_start);
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200114}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116static struct irqaction irq0 = {
Glauber Costa461ebd12008-07-11 15:25:13 -0300117 .handler = timer_interrupt,
Linus Torvalds936577c2009-02-22 10:27:49 -0800118 .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING | IRQF_TIMER,
Thomas Gleixner2618f862007-07-21 17:10:18 +0200119 .name = "timer"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120};
121
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100122void __init hpet_time_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123{
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200124 if (!hpet_enable())
125 setup_pit_timer();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200127 setup_irq(0, &irq0);
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100128}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100130void __init time_init(void)
131{
Alok Kataria8fbbc4b2008-07-01 11:43:34 -0700132 tsc_init();
Vojtech Pavlikc08c8202006-09-26 10:52:28 +0200133
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100134 late_time_init = choose_time_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135}