blob: cb19d650c21643599aa1423a57b64a85ee26afe8 [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 Pavlik2f82bde2006-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>
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020020
Thomas Gleixner28318da2007-07-21 17:11:18 +020021#include <asm/i8253.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <asm/hpet.h>
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020023#include <asm/nmi.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
Glauber Costa461ebd12008-07-11 15:25:13 -030052irqreturn_t timer_interrupt(int irq, void *dev_id)
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020053{
Thomas Gleixner4e77ae32007-10-12 23:04:07 +020054 add_pda(irq0_irqs, 1);
55
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) {
83 i = 3;
84 rdmsrl(MSR_K7_EVNTSEL3, evntsel3);
85 wrmsrl(MSR_K7_EVNTSEL3, 0);
86 rdmsrl(MSR_K7_PERFCTR3, pmc3);
87 } else {
88 reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i);
89 reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
90 }
91 local_irq_save(flags);
Li Zefan3eb05672008-02-08 04:19:25 -080092 /* start measuring cycles, incrementing from 0 */
Thomas Gleixner2618f862007-07-21 17:10:18 +020093 wrmsrl(MSR_K7_PERFCTR0 + i, 0);
94 wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76);
95 rdtscl(tsc_start);
96 do {
97 rdmsrl(MSR_K7_PERFCTR0 + i, pmc_now);
Andi Kleen6d63de82008-01-30 13:32:39 +010098 tsc_now = get_cycles();
Thomas Gleixner2618f862007-07-21 17:10:18 +020099 } while ((tsc_now - tsc_start) < TICK_COUNT);
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200100
Thomas Gleixner2618f862007-07-21 17:10:18 +0200101 local_irq_restore(flags);
102 if (no_ctr_free) {
103 wrmsrl(MSR_K7_EVNTSEL3, 0);
104 wrmsrl(MSR_K7_PERFCTR3, pmc3);
105 wrmsrl(MSR_K7_EVNTSEL3, evntsel3);
106 } else {
107 release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
108 release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
109 }
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200110
Thomas Gleixner2618f862007-07-21 17:10:18 +0200111 return pmc_now * tsc_khz / (tsc_now - tsc_start);
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200112}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114static struct irqaction irq0 = {
Glauber Costa461ebd12008-07-11 15:25:13 -0300115 .handler = timer_interrupt,
Venki Pallipadi5fa3a242007-10-12 23:04:06 +0200116 .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
Bernhard Wallee6d828f2007-05-08 00:35:28 -0700117 .mask = CPU_MASK_NONE,
Thomas Gleixner2618f862007-07-21 17:10:18 +0200118 .name = "timer"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119};
120
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100121void __init hpet_time_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200123 if (!hpet_enable())
124 setup_pit_timer();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
Glauber Costa2ff29832008-07-11 14:21:29 -0300126 irq0.mask = cpumask_of_cpu(0);
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}