blob: 39ae8511a1376f1681989687d3d4e2c84e843c24 [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. */
Andi Kleend5a26012006-07-28 14:44:42 +020036 if (!user_mode(regs) && in_lock_functions(pc)) {
H. Peter Anvin65ea5b02008-01-30 13:30:56 +010037 unsigned long *sp = (unsigned long *)regs->sp;
Andi Kleen31679f32006-09-26 10:52:28 +020038 if (sp[0] >> 22)
39 return sp[0];
40 if (sp[1] >> 22)
41 return sp[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 }
43 return pc;
44}
45EXPORT_SYMBOL(profile_pc);
46
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020047static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
48{
Thomas Gleixner4e77ae32007-10-12 23:04:07 +020049 add_pda(irq0_irqs, 1);
50
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +020051 global_clock_event->event_handler(global_clock_event);
52
53 return IRQ_HANDLED;
54}
55
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020056/* calibrate_cpu is used on systems with fixed rate TSCs to determine
57 * processor frequency */
58#define TICK_COUNT 100000000
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +010059unsigned long __init native_calculate_cpu_khz(void)
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020060{
Thomas Gleixner2618f862007-07-21 17:10:18 +020061 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;
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020065
Thomas Gleixner2618f862007-07-21 17:10:18 +020066 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);
Li Zefan3eb05672008-02-08 04:19:25 -080080 /* start measuring cycles, incrementing from 0 */
Thomas Gleixner2618f862007-07-21 17:10:18 +020081 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);
Andi Kleen6d63de82008-01-30 13:32:39 +010086 tsc_now = get_cycles();
Thomas Gleixner2618f862007-07-21 17:10:18 +020087 } while ((tsc_now - tsc_start) < TICK_COUNT);
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020088
Thomas Gleixner2618f862007-07-21 17:10:18 +020089 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 }
Joerg Roedel6b37f5a2007-05-02 19:27:06 +020098
Thomas Gleixner2618f862007-07-21 17:10:18 +020099 return pmc_now * tsc_khz / (tsc_now - tsc_start);
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200100}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102static struct irqaction irq0 = {
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200103 .handler = timer_event_interrupt,
Venki Pallipadi5fa3a242007-10-12 23:04:06 +0200104 .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
Bernhard Wallee6d828f2007-05-08 00:35:28 -0700105 .mask = CPU_MASK_NONE,
Thomas Gleixner2618f862007-07-21 17:10:18 +0200106 .name = "timer"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107};
108
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100109void __init hpet_time_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110{
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200111 if (!hpet_enable())
112 setup_pit_timer();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
Thomas Gleixnerb8ce3352007-10-12 23:04:07 +0200114 setup_irq(0, &irq0);
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100115}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100117void __init time_init(void)
118{
Thomas Gleixnerd3716982007-10-12 23:04:06 +0200119 tsc_calibrate();
120
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200121 cpu_khz = tsc_khz;
122 if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) &&
Andreas Herrmann3519efbc2008-01-30 13:33:35 +0100123 (boot_cpu_data.x86_vendor == X86_VENDOR_AMD))
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100124 cpu_khz = calculate_cpu_khz();
Joerg Roedel6b37f5a2007-05-02 19:27:06 +0200125
Alok Katariaf3f31492008-06-23 18:21:56 -0700126 lpj_fine = ((unsigned long)tsc_khz * 1000)/HZ;
Alok Kataria3da757d2008-06-20 15:06:33 -0700127
Andi Kleen312df5f2005-05-16 21:53:28 -0700128 if (unsynchronized_tsc())
john stultz5a90cf22007-05-02 19:27:08 +0200129 mark_tsc_unstable("TSCs unsynchronized");
Vojtech Pavlika670fad2006-09-26 10:52:28 +0200130
john stultz2d0c87c2007-02-16 01:28:18 -0800131 if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP))
Vojtech Pavlikc08c8202006-09-26 10:52:28 +0200132 vgetcpu_mode = VGETCPU_RDTSCP;
133 else
134 vgetcpu_mode = VGETCPU_LSL;
135
Vojtech Pavlika670fad2006-09-26 10:52:28 +0200136 printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
137 cpu_khz / 1000, cpu_khz % 1000);
john stultz6bb74df2007-03-05 00:30:50 -0800138 init_tsc_clocksource();
Glauber de Oliveira Costaee238e52008-01-30 13:31:10 +0100139 late_time_init = choose_time_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140}