blob: 0469243ae1bd44acbfff8cb9115dbd50bf556004 [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>
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020019
Thomas Gleixner28318da2007-07-21 17:11:18 +020020#include <asm/i8253.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <asm/hpet.h>
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020022#include <asm/nmi.h>
Andi Kleen2aae9502007-07-21 17:10:01 +020023#include <asm/vgtod.h>
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +010024#include <asm/time.h>
25#include <asm/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Linus Torvalds1da177e2005-04-16 15:20:36 -070027volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Linus Torvalds1da177e2005-04-16 15:20:36 -070029unsigned long profile_pc(struct pt_regs *regs)
30{
31 unsigned long pc = instruction_pointer(regs);
32
Andi Kleen31679f32006-09-26 10:52:28 +020033 /* Assume the lock function has either no stack frame or a copy
H. Peter Anvin65ea5b02008-01-30 13:30:56 +010034 of flags from PUSHF
Andi Kleen31679f32006-09-26 10:52:28 +020035 Eflags always has bits 22 and up cleared unlike kernel addresses. */
Glauber Costa8de0b8a2008-07-11 14:10:13 -030036 if (!user_mode_vm(regs) && in_lock_functions(pc)) {
Glauber Costa3927fa92008-07-11 13:53:43 -030037#ifdef CONFIG_FRAME_POINTER
38 return *(unsigned long *)(regs->bp + sizeof(long));
39#else
H. Peter Anvin65ea5b02008-01-30 13:30:56 +010040 unsigned long *sp = (unsigned long *)regs->sp;
Andi Kleen31679f32006-09-26 10:52:28 +020041 if (sp[0] >> 22)
42 return sp[0];
43 if (sp[1] >> 22)
44 return sp[1];
Glauber Costa3927fa92008-07-11 13:53:43 -030045#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 }
47 return pc;
48}
49EXPORT_SYMBOL(profile_pc);
50
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020051static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
52{
Thomas Gleixner4e77ae32007-10-12 23:04:07 +020053 add_pda(irq0_irqs, 1);
54
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020055 global_clock_event->event_handler(global_clock_event);
56
57 return IRQ_HANDLED;
58}
59
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020060/* calibrate_cpu is used on systems with fixed rate TSCs to determine
61 * processor frequency */
62#define TICK_COUNT 100000000
Alok Kataria8fbbc4b2008-07-01 11:43:34 -070063unsigned long __init calibrate_cpu(void)
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020064{
Thomas Gleixner2618f862007-07-21 17:10:18 +020065 int tsc_start, tsc_now;
66 int i, no_ctr_free;
67 unsigned long evntsel3 = 0, pmc3 = 0, pmc_now = 0;
68 unsigned long flags;
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020069
Thomas Gleixner2618f862007-07-21 17:10:18 +020070 for (i = 0; i < 4; i++)
71 if (avail_to_resrv_perfctr_nmi_bit(i))
72 break;
73 no_ctr_free = (i == 4);
74 if (no_ctr_free) {
75 i = 3;
76 rdmsrl(MSR_K7_EVNTSEL3, evntsel3);
77 wrmsrl(MSR_K7_EVNTSEL3, 0);
78 rdmsrl(MSR_K7_PERFCTR3, pmc3);
79 } else {
80 reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i);
81 reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
82 }
83 local_irq_save(flags);
Li Zefan3eb05672008-02-08 04:19:25 -080084 /* start measuring cycles, incrementing from 0 */
Thomas Gleixner2618f862007-07-21 17:10:18 +020085 wrmsrl(MSR_K7_PERFCTR0 + i, 0);
86 wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76);
87 rdtscl(tsc_start);
88 do {
89 rdmsrl(MSR_K7_PERFCTR0 + i, pmc_now);
Andi Kleen6d63de82008-01-30 13:32:39 +010090 tsc_now = get_cycles();
Thomas Gleixner2618f862007-07-21 17:10:18 +020091 } while ((tsc_now - tsc_start) < TICK_COUNT);
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020092
Thomas Gleixner2618f862007-07-21 17:10:18 +020093 local_irq_restore(flags);
94 if (no_ctr_free) {
95 wrmsrl(MSR_K7_EVNTSEL3, 0);
96 wrmsrl(MSR_K7_PERFCTR3, pmc3);
97 wrmsrl(MSR_K7_EVNTSEL3, evntsel3);
98 } else {
99 release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
100 release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
101 }
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200102
Thomas Gleixner2618f862007-07-21 17:10:18 +0200103 return pmc_now * tsc_khz / (tsc_now - tsc_start);
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200104}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106static struct irqaction irq0 = {
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200107 .handler = timer_event_interrupt,
Venki Pallipadi5fa3a242007-10-12 23:04:06 +0200108 .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
Bernhard Wallee6d828f2007-05-08 00:35:28 -0700109 .mask = CPU_MASK_NONE,
Thomas Gleixner2618f862007-07-21 17:10:18 +0200110 .name = "timer"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111};
112
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100113void __init hpet_time_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114{
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200115 if (!hpet_enable())
116 setup_pit_timer();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200118 setup_irq(0, &irq0);
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100119}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100121void __init time_init(void)
122{
Alok Kataria8fbbc4b2008-07-01 11:43:34 -0700123 tsc_init();
john stultz2d0c87c2007-02-16 01:28:18 -0800124 if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP))
Vojtech Pavlikc08c8202006-09-26 10:52:28 +0200125 vgetcpu_mode = VGETCPU_RDTSCP;
126 else
127 vgetcpu_mode = VGETCPU_LSL;
128
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100129 late_time_init = choose_time_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130}