aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64
diff options
context:
space:
mode:
authorjohn stultz <johnstul@us.ibm.com>2007-02-16 04:28:20 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-16 11:14:00 -0500
commit1489939f0ab64b96998e04068c516c39afe29654 (patch)
tree6bb3ca772edf1dd8877482dc3b6bcc6f0d699e72 /arch/x86_64
parentc37e7bb5d2ce36ef377caabfced0b132bb1bf6a7 (diff)
[PATCH] time: x86_64: convert x86_64 to use GENERIC_TIME
This patch converts x86_64 to use the GENERIC_TIME infrastructure and adds clocksource structures for both TSC and HPET (ACPI PM is shared w/ i386). [akpm@osdl.org: fix printk timestamps] [akpm@osdl.org: fix printk ckeanups] [akpm@osdl.org: hpet build fix] Signed-off-by: John Stultz <johnstul@us.ibm.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Andi Kleen <ak@muc.de> Cc: Roman Zippel <zippel@linux-m68k.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/x86_64')
-rw-r--r--arch/x86_64/Kconfig4
-rw-r--r--arch/x86_64/kernel/apic.c2
-rw-r--r--arch/x86_64/kernel/hpet.c64
-rw-r--r--arch/x86_64/kernel/pmtimer.c58
-rw-r--r--arch/x86_64/kernel/smpboot.c1
-rw-r--r--arch/x86_64/kernel/time.c300
-rw-r--r--arch/x86_64/kernel/tsc.c103
7 files changed, 124 insertions, 408 deletions
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig
index 7982cbc3bc94..286b4606fb1e 100644
--- a/arch/x86_64/Kconfig
+++ b/arch/x86_64/Kconfig
@@ -24,6 +24,10 @@ config X86
24 bool 24 bool
25 default y 25 default y
26 26
27config GENERIC_TIME
28 bool
29 default y
30
27config ZONE_DMA32 31config ZONE_DMA32
28 bool 32 bool
29 default y 33 default y
diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c
index 7ce779779640..723417d924c0 100644
--- a/arch/x86_64/kernel/apic.c
+++ b/arch/x86_64/kernel/apic.c
@@ -786,7 +786,7 @@ static void setup_APIC_timer(unsigned int clocks)
786 /* Turn off PIT interrupt if we use APIC timer as main timer. 786 /* Turn off PIT interrupt if we use APIC timer as main timer.
787 Only works with the PM timer right now 787 Only works with the PM timer right now
788 TBD fix it for HPET too. */ 788 TBD fix it for HPET too. */
789 if (vxtime.mode == VXTIME_PMTMR && 789 if ((pmtmr_ioport != 0) &&
790 smp_processor_id() == boot_cpu_id && 790 smp_processor_id() == boot_cpu_id &&
791 apic_runs_main_timer == 1 && 791 apic_runs_main_timer == 1 &&
792 !cpu_isset(boot_cpu_id, timer_interrupt_broadcast_ipi_mask)) { 792 !cpu_isset(boot_cpu_id, timer_interrupt_broadcast_ipi_mask)) {
diff --git a/arch/x86_64/kernel/hpet.c b/arch/x86_64/kernel/hpet.c
index 08ab37c966c2..c23738899ae9 100644
--- a/arch/x86_64/kernel/hpet.c
+++ b/arch/x86_64/kernel/hpet.c
@@ -21,12 +21,6 @@ unsigned long hpet_tick; /* HPET clocks / interrupt */
21int hpet_use_timer; /* Use counter of hpet for time keeping, 21int hpet_use_timer; /* Use counter of hpet for time keeping,
22 * otherwise PIT 22 * otherwise PIT
23 */ 23 */
24unsigned int do_gettimeoffset_hpet(void)
25{
26 /* cap counter read to one tick to avoid inconsistencies */
27 unsigned long counter = hpet_readl(HPET_COUNTER) - vxtime.last;
28 return (min(counter,hpet_tick) * vxtime.quot) >> US_SCALE;
29}
30 24
31#ifdef CONFIG_HPET 25#ifdef CONFIG_HPET
32static __init int late_hpet_init(void) 26static __init int late_hpet_init(void)
@@ -451,3 +445,61 @@ static int __init nohpet_setup(char *s)
451 445
452__setup("nohpet", nohpet_setup); 446__setup("nohpet", nohpet_setup);
453 447
448#define HPET_MASK 0xFFFFFFFF
449#define HPET_SHIFT 22
450
451/* FSEC = 10^-15 NSEC = 10^-9 */
452#define FSEC_PER_NSEC 1000000
453
454static void *hpet_ptr;
455
456static cycle_t read_hpet(void)
457{
458 return (cycle_t)readl(hpet_ptr);
459}
460
461struct clocksource clocksource_hpet = {
462 .name = "hpet",
463 .rating = 250,
464 .read = read_hpet,
465 .mask = (cycle_t)HPET_MASK,
466 .mult = 0, /* set below */
467 .shift = HPET_SHIFT,
468 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
469};
470
471static int __init init_hpet_clocksource(void)
472{
473 unsigned long hpet_period;
474 void __iomem *hpet_base;
475 u64 tmp;
476
477 if (!hpet_address)
478 return -ENODEV;
479
480 /* calculate the hpet address: */
481 hpet_base = ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
482 hpet_ptr = hpet_base + HPET_COUNTER;
483
484 /* calculate the frequency: */
485 hpet_period = readl(hpet_base + HPET_PERIOD);
486
487 /*
488 * hpet period is in femto seconds per cycle
489 * so we need to convert this to ns/cyc units
490 * aproximated by mult/2^shift
491 *
492 * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift
493 * fsec/cyc * 1ns/1000000fsec * 2^shift = mult
494 * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult
495 * (fsec/cyc << shift)/1000000 = mult
496 * (hpet_period << shift)/FSEC_PER_NSEC = mult
497 */
498 tmp = (u64)hpet_period << HPET_SHIFT;
499 do_div(tmp, FSEC_PER_NSEC);
500 clocksource_hpet.mult = (u32)tmp;
501
502 return clocksource_register(&clocksource_hpet);
503}
504
505module_init(init_hpet_clocksource);
diff --git a/arch/x86_64/kernel/pmtimer.c b/arch/x86_64/kernel/pmtimer.c
index 7554458dc9cb..ae8f91214f15 100644
--- a/arch/x86_64/kernel/pmtimer.c
+++ b/arch/x86_64/kernel/pmtimer.c
@@ -24,15 +24,6 @@
24#include <asm/msr.h> 24#include <asm/msr.h>
25#include <asm/vsyscall.h> 25#include <asm/vsyscall.h>
26 26
27/* The I/O port the PMTMR resides at.
28 * The location is detected during setup_arch(),
29 * in arch/i386/kernel/acpi/boot.c */
30u32 pmtmr_ioport __read_mostly;
31
32/* value of the Power timer at last timer interrupt */
33static u32 offset_delay;
34static u32 last_pmtmr_tick;
35
36#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */ 27#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
37 28
38static inline u32 cyc2us(u32 cycles) 29static inline u32 cyc2us(u32 cycles)
@@ -48,38 +39,6 @@ static inline u32 cyc2us(u32 cycles)
48 return (cycles >> 10); 39 return (cycles >> 10);
49} 40}
50 41
51int pmtimer_mark_offset(void)
52{
53 static int first_run = 1;
54 unsigned long tsc;
55 u32 lost;
56
57 u32 tick = inl(pmtmr_ioport);
58 u32 delta;
59
60 delta = cyc2us((tick - last_pmtmr_tick) & ACPI_PM_MASK);
61
62 last_pmtmr_tick = tick;
63 monotonic_base += delta * NSEC_PER_USEC;
64
65 delta += offset_delay;
66
67 lost = delta / (USEC_PER_SEC / HZ);
68 offset_delay = delta % (USEC_PER_SEC / HZ);
69
70 rdtscll(tsc);
71 vxtime.last_tsc = tsc - offset_delay * (u64)cpu_khz / 1000;
72
73 /* don't calculate delay for first run,
74 or if we've got less then a tick */
75 if (first_run || (lost < 1)) {
76 first_run = 0;
77 offset_delay = 0;
78 }
79
80 return lost - 1;
81}
82
83static unsigned pmtimer_wait_tick(void) 42static unsigned pmtimer_wait_tick(void)
84{ 43{
85 u32 a, b; 44 u32 a, b;
@@ -101,23 +60,6 @@ void pmtimer_wait(unsigned us)
101 } while (cyc2us(b - a) < us); 60 } while (cyc2us(b - a) < us);
102} 61}
103 62
104void pmtimer_resume(void)
105{
106 last_pmtmr_tick = inl(pmtmr_ioport);
107}
108
109unsigned int do_gettimeoffset_pm(void)
110{
111 u32 now, offset, delta = 0;
112
113 offset = last_pmtmr_tick;
114 now = inl(pmtmr_ioport);
115 delta = (now - offset) & ACPI_PM_MASK;
116
117 return offset_delay + cyc2us(delta);
118}
119
120
121static int __init nopmtimer_setup(char *s) 63static int __init nopmtimer_setup(char *s)
122{ 64{
123 pmtmr_ioport = 0; 65 pmtmr_ioport = 0;
diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c
index 62d828433c30..35443729aad8 100644
--- a/arch/x86_64/kernel/smpboot.c
+++ b/arch/x86_64/kernel/smpboot.c
@@ -982,7 +982,6 @@ void __init smp_cpus_done(unsigned int max_cpus)
982 smp_cleanup_boot(); 982 smp_cleanup_boot();
983 setup_ioapic_dest(); 983 setup_ioapic_dest();
984 check_nmi_watchdog(); 984 check_nmi_watchdog();
985 time_init_gtod();
986} 985}
987 986
988#ifdef CONFIG_HOTPLUG_CPU 987#ifdef CONFIG_HOTPLUG_CPU
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c
index 216fc9d74247..d84208e3c618 100644
--- a/arch/x86_64/kernel/time.c
+++ b/arch/x86_64/kernel/time.c
@@ -44,9 +44,6 @@
44#include <asm/apic.h> 44#include <asm/apic.h>
45#include <asm/hpet.h> 45#include <asm/hpet.h>
46 46
47#ifdef CONFIG_CPU_FREQ
48extern void cpufreq_delayed_get(void);
49#endif
50extern void i8254_timer_resume(void); 47extern void i8254_timer_resume(void);
51extern int using_apic_timer; 48extern int using_apic_timer;
52 49
@@ -57,8 +54,6 @@ EXPORT_SYMBOL(rtc_lock);
57DEFINE_SPINLOCK(i8253_lock); 54DEFINE_SPINLOCK(i8253_lock);
58 55
59unsigned long vxtime_hz = PIT_TICK_RATE; 56unsigned long vxtime_hz = PIT_TICK_RATE;
60int report_lost_ticks; /* command line option */
61unsigned long long monotonic_base;
62 57
63struct vxtime_data __vxtime __section_vxtime; /* for vsyscalls */ 58struct vxtime_data __vxtime __section_vxtime; /* for vsyscalls */
64 59
@@ -66,76 +61,6 @@ volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
66struct timespec __xtime __section_xtime; 61struct timespec __xtime __section_xtime;
67struct timezone __sys_tz __section_sys_tz; 62struct timezone __sys_tz __section_sys_tz;
68 63
69unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc;
70
71/*
72 * This version of gettimeofday() has microsecond resolution and better than
73 * microsecond precision, as we're using at least a 10 MHz (usually 14.31818
74 * MHz) HPET timer.
75 */
76
77void do_gettimeofday(struct timeval *tv)
78{
79 unsigned long seq;
80 unsigned int sec, usec;
81
82 do {
83 seq = read_seqbegin(&xtime_lock);
84
85 sec = xtime.tv_sec;
86 usec = xtime.tv_nsec / NSEC_PER_USEC;
87
88 /* i386 does some correction here to keep the clock
89 monotonous even when ntpd is fixing drift.
90 But they didn't work for me, there is a non monotonic
91 clock anyways with ntp.
92 I dropped all corrections now until a real solution can
93 be found. Note when you fix it here you need to do the same
94 in arch/x86_64/kernel/vsyscall.c and export all needed
95 variables in vmlinux.lds. -AK */
96 usec += do_gettimeoffset();
97
98 } while (read_seqretry(&xtime_lock, seq));
99
100 tv->tv_sec = sec + usec / USEC_PER_SEC;
101 tv->tv_usec = usec % USEC_PER_SEC;
102}
103
104EXPORT_SYMBOL(do_gettimeofday);
105
106/*
107 * settimeofday() first undoes the correction that gettimeofday would do
108 * on the time, and then saves it. This is ugly, but has been like this for
109 * ages already.
110 */
111
112int do_settimeofday(struct timespec *tv)
113{
114 time_t wtm_sec, sec = tv->tv_sec;
115 long wtm_nsec, nsec = tv->tv_nsec;
116
117 if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
118 return -EINVAL;
119
120 write_seqlock_irq(&xtime_lock);
121
122 nsec -= do_gettimeoffset() * NSEC_PER_USEC;
123
124 wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
125 wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
126
127 set_normalized_timespec(&xtime, sec, nsec);
128 set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
129
130 ntp_clear();
131
132 write_sequnlock_irq(&xtime_lock);
133 clock_was_set();
134 return 0;
135}
136
137EXPORT_SYMBOL(do_settimeofday);
138
139unsigned long profile_pc(struct pt_regs *regs) 64unsigned long profile_pc(struct pt_regs *regs)
140{ 65{
141 unsigned long pc = instruction_pointer(regs); 66 unsigned long pc = instruction_pointer(regs);
@@ -225,85 +150,9 @@ static void set_rtc_mmss(unsigned long nowtime)
225} 150}
226 151
227 152
228/* monotonic_clock(): returns # of nanoseconds passed since time_init()
229 * Note: This function is required to return accurate
230 * time even in the absence of multiple timer ticks.
231 */
232extern unsigned long long cycles_2_ns(unsigned long long cyc);
233unsigned long long monotonic_clock(void)
234{
235 unsigned long seq;
236 u32 last_offset, this_offset, offset;
237 unsigned long long base;
238
239 if (vxtime.mode == VXTIME_HPET) {
240 do {
241 seq = read_seqbegin(&xtime_lock);
242
243 last_offset = vxtime.last;
244 base = monotonic_base;
245 this_offset = hpet_readl(HPET_COUNTER);
246 } while (read_seqretry(&xtime_lock, seq));
247 offset = (this_offset - last_offset);
248 offset *= NSEC_PER_TICK / hpet_tick;
249 } else {
250 do {
251 seq = read_seqbegin(&xtime_lock);
252
253 last_offset = vxtime.last_tsc;
254 base = monotonic_base;
255 } while (read_seqretry(&xtime_lock, seq));
256 this_offset = get_cycles_sync();
257 offset = cycles_2_ns(this_offset - last_offset);
258 }
259 return base + offset;
260}
261EXPORT_SYMBOL(monotonic_clock);
262
263static noinline void handle_lost_ticks(int lost)
264{
265 static long lost_count;
266 static int warned;
267 if (report_lost_ticks) {
268 printk(KERN_WARNING "time.c: Lost %d timer tick(s)! ", lost);
269 print_symbol("rip %s)\n", get_irq_regs()->rip);
270 }
271
272 if (lost_count == 1000 && !warned) {
273 printk(KERN_WARNING "warning: many lost ticks.\n"
274 KERN_WARNING "Your time source seems to be instable or "
275 "some driver is hogging interupts\n");
276 print_symbol("rip %s\n", get_irq_regs()->rip);
277 if (vxtime.mode == VXTIME_TSC && hpet_address) {
278 printk(KERN_WARNING "Falling back to HPET\n");
279 if (hpet_use_timer)
280 vxtime.last = hpet_readl(HPET_T0_CMP) -
281 hpet_tick;
282 else
283 vxtime.last = hpet_readl(HPET_COUNTER);
284 vxtime.mode = VXTIME_HPET;
285 vxtime.hpet_address = hpet_address;
286 do_gettimeoffset = do_gettimeoffset_hpet;
287 }
288 /* else should fall back to PIT, but code missing. */
289 warned = 1;
290 } else
291 lost_count++;
292
293#ifdef CONFIG_CPU_FREQ
294 /* In some cases the CPU can change frequency without us noticing
295 Give cpufreq a change to catch up. */
296 if ((lost_count+1) % 25 == 0)
297 cpufreq_delayed_get();
298#endif
299}
300
301void main_timer_handler(void) 153void main_timer_handler(void)
302{ 154{
303 static unsigned long rtc_update = 0; 155 static unsigned long rtc_update = 0;
304 unsigned long tsc;
305 int delay = 0, offset = 0, lost = 0;
306
307/* 156/*
308 * Here we are in the timer irq handler. We have irqs locally disabled (so we 157 * Here we are in the timer irq handler. We have irqs locally disabled (so we
309 * don't need spin_lock_irqsave()) but we don't know if the timer_bh is running 158 * don't need spin_lock_irqsave()) but we don't know if the timer_bh is running
@@ -313,72 +162,11 @@ void main_timer_handler(void)
313 162
314 write_seqlock(&xtime_lock); 163 write_seqlock(&xtime_lock);
315 164
316 if (hpet_address)
317 offset = hpet_readl(HPET_COUNTER);
318
319 if (hpet_use_timer) {
320 /* if we're using the hpet timer functionality,
321 * we can more accurately know the counter value
322 * when the timer interrupt occured.
323 */
324 offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
325 delay = hpet_readl(HPET_COUNTER) - offset;
326 } else if (!pmtmr_ioport) {
327 spin_lock(&i8253_lock);
328 outb_p(0x00, 0x43);
329 delay = inb_p(0x40);
330 delay |= inb(0x40) << 8;
331 spin_unlock(&i8253_lock);
332 delay = LATCH - 1 - delay;
333 }
334
335 tsc = get_cycles_sync();
336
337 if (vxtime.mode == VXTIME_HPET) {
338 if (offset - vxtime.last > hpet_tick) {
339 lost = (offset - vxtime.last) / hpet_tick - 1;
340 }
341
342 monotonic_base +=
343 (offset - vxtime.last) * NSEC_PER_TICK / hpet_tick;
344
345 vxtime.last = offset;
346#ifdef CONFIG_X86_PM_TIMER
347 } else if (vxtime.mode == VXTIME_PMTMR) {
348 lost = pmtimer_mark_offset();
349#endif
350 } else {
351 offset = (((tsc - vxtime.last_tsc) *
352 vxtime.tsc_quot) >> US_SCALE) - USEC_PER_TICK;
353
354 if (offset < 0)
355 offset = 0;
356
357 if (offset > USEC_PER_TICK) {
358 lost = offset / USEC_PER_TICK;
359 offset %= USEC_PER_TICK;
360 }
361
362 monotonic_base += cycles_2_ns(tsc - vxtime.last_tsc);
363
364 vxtime.last_tsc = tsc - vxtime.quot * delay / vxtime.tsc_quot;
365
366 if ((((tsc - vxtime.last_tsc) *
367 vxtime.tsc_quot) >> US_SCALE) < offset)
368 vxtime.last_tsc = tsc -
369 (((long) offset << US_SCALE) / vxtime.tsc_quot) - 1;
370 }
371
372 if (lost > 0)
373 handle_lost_ticks(lost);
374 else
375 lost = 0;
376
377/* 165/*
378 * Do the timer stuff. 166 * Do the timer stuff.
379 */ 167 */
380 168
381 do_timer(lost + 1); 169 do_timer(1);
382#ifndef CONFIG_SMP 170#ifndef CONFIG_SMP
383 update_process_times(user_mode(get_irq_regs())); 171 update_process_times(user_mode(get_irq_regs()));
384#endif 172#endif
@@ -537,12 +325,6 @@ void __init stop_timer_interrupt(void)
537 printk(KERN_INFO "timer: %s interrupt stopped.\n", name); 325 printk(KERN_INFO "timer: %s interrupt stopped.\n", name);
538} 326}
539 327
540int __init time_setup(char *str)
541{
542 report_lost_ticks = 1;
543 return 1;
544}
545
546static struct irqaction irq0 = { 328static struct irqaction irq0 = {
547 timer_interrupt, IRQF_DISABLED, CPU_MASK_NONE, "timer", NULL, NULL 329 timer_interrupt, IRQF_DISABLED, CPU_MASK_NONE, "timer", NULL, NULL
548}; 330};
@@ -557,9 +339,7 @@ void __init time_init(void)
557 set_normalized_timespec(&wall_to_monotonic, 339 set_normalized_timespec(&wall_to_monotonic,
558 -xtime.tv_sec, -xtime.tv_nsec); 340 -xtime.tv_sec, -xtime.tv_nsec);
559 341
560 if (!hpet_arch_init()) 342 if (hpet_arch_init())
561 vxtime_hz = (FSEC_PER_SEC + hpet_period / 2) / hpet_period;
562 else
563 hpet_address = 0; 343 hpet_address = 0;
564 344
565 if (hpet_use_timer) { 345 if (hpet_use_timer) {
@@ -567,82 +347,26 @@ void __init time_init(void)
567 tick_nsec = TICK_NSEC_HPET; 347 tick_nsec = TICK_NSEC_HPET;
568 cpu_khz = hpet_calibrate_tsc(); 348 cpu_khz = hpet_calibrate_tsc();
569 timename = "HPET"; 349 timename = "HPET";
570#ifdef CONFIG_X86_PM_TIMER
571 } else if (pmtmr_ioport && !hpet_address) {
572 vxtime_hz = PM_TIMER_FREQUENCY;
573 timename = "PM";
574 pit_init();
575 cpu_khz = pit_calibrate_tsc();
576#endif
577 } else { 350 } else {
578 pit_init(); 351 pit_init();
579 cpu_khz = pit_calibrate_tsc(); 352 cpu_khz = pit_calibrate_tsc();
580 timename = "PIT"; 353 timename = "PIT";
581 } 354 }
582 355
583 vxtime.mode = VXTIME_TSC;
584 vxtime.quot = (USEC_PER_SEC << US_SCALE) / vxtime_hz;
585 vxtime.tsc_quot = (USEC_PER_MSEC << US_SCALE) / cpu_khz;
586 vxtime.last_tsc = get_cycles_sync();
587 set_cyc2ns_scale(cpu_khz);
588 setup_irq(0, &irq0);
589
590#ifndef CONFIG_SMP
591 time_init_gtod();
592#endif
593}
594
595/*
596 * Decide what mode gettimeofday should use.
597 */
598void time_init_gtod(void)
599{
600 char *timetype;
601
602 if (unsynchronized_tsc()) 356 if (unsynchronized_tsc())
603 notsc = 1; 357 mark_tsc_unstable();
604 358
605 if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) 359 if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP))
606 vgetcpu_mode = VGETCPU_RDTSCP; 360 vgetcpu_mode = VGETCPU_RDTSCP;
607 else 361 else
608 vgetcpu_mode = VGETCPU_LSL; 362 vgetcpu_mode = VGETCPU_LSL;
609 363
610 if (hpet_address && notsc) { 364 set_cyc2ns_scale(cpu_khz);
611 timetype = hpet_use_timer ? "HPET" : "PIT/HPET";
612 if (hpet_use_timer)
613 vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
614 else
615 vxtime.last = hpet_readl(HPET_COUNTER);
616 vxtime.mode = VXTIME_HPET;
617 vxtime.hpet_address = hpet_address;
618 do_gettimeoffset = do_gettimeoffset_hpet;
619#ifdef CONFIG_X86_PM_TIMER
620 /* Using PM for gettimeofday is quite slow, but we have no other
621 choice because the TSC is too unreliable on some systems. */
622 } else if (pmtmr_ioport && !hpet_address && notsc) {
623 timetype = "PM";
624 do_gettimeoffset = do_gettimeoffset_pm;
625 vxtime.mode = VXTIME_PMTMR;
626 sysctl_vsyscall = 0;
627 printk(KERN_INFO "Disabling vsyscall due to use of PM timer\n");
628#endif
629 } else {
630 timetype = hpet_use_timer ? "HPET/TSC" : "PIT/TSC";
631 vxtime.mode = VXTIME_TSC;
632 }
633
634 printk(KERN_INFO "time.c: Using %ld.%06ld MHz WALL %s GTOD %s timer.\n",
635 vxtime_hz / 1000000, vxtime_hz % 1000000, timename, timetype);
636 printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", 365 printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
637 cpu_khz / 1000, cpu_khz % 1000); 366 cpu_khz / 1000, cpu_khz % 1000);
638 vxtime.quot = (USEC_PER_SEC << US_SCALE) / vxtime_hz; 367 setup_irq(0, &irq0);
639 vxtime.tsc_quot = (USEC_PER_MSEC << US_SCALE) / cpu_khz;
640 vxtime.last_tsc = get_cycles_sync();
641
642 set_cyc2ns_scale(cpu_khz);
643} 368}
644 369
645__setup("report_lost_ticks", time_setup);
646 370
647static long clock_cmos_diff; 371static long clock_cmos_diff;
648static unsigned long sleep_start; 372static unsigned long sleep_start;
@@ -688,20 +412,8 @@ static int timer_resume(struct sys_device *dev)
688 write_seqlock_irqsave(&xtime_lock,flags); 412 write_seqlock_irqsave(&xtime_lock,flags);
689 xtime.tv_sec = sec; 413 xtime.tv_sec = sec;
690 xtime.tv_nsec = 0; 414 xtime.tv_nsec = 0;
691 if (vxtime.mode == VXTIME_HPET) {
692 if (hpet_use_timer)
693 vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
694 else
695 vxtime.last = hpet_readl(HPET_COUNTER);
696#ifdef CONFIG_X86_PM_TIMER
697 } else if (vxtime.mode == VXTIME_PMTMR) {
698 pmtimer_resume();
699#endif
700 } else
701 vxtime.last_tsc = get_cycles_sync();
702 write_sequnlock_irqrestore(&xtime_lock,flags);
703 jiffies += sleep_length; 415 jiffies += sleep_length;
704 monotonic_base += sleep_length * (NSEC_PER_SEC/HZ); 416 write_sequnlock_irqrestore(&xtime_lock,flags);
705 touch_softlockup_watchdog(); 417 touch_softlockup_watchdog();
706 return 0; 418 return 0;
707} 419}
diff --git a/arch/x86_64/kernel/tsc.c b/arch/x86_64/kernel/tsc.c
index 2dbac15ab1f0..8c92f2fe7e2e 100644
--- a/arch/x86_64/kernel/tsc.c
+++ b/arch/x86_64/kernel/tsc.c
@@ -9,32 +9,11 @@
9 9
10#include <asm/timex.h> 10#include <asm/timex.h>
11 11
12int notsc __initdata = 0; 12static int notsc __initdata = 0;
13 13
14unsigned int cpu_khz; /* TSC clocks / usec, not used here */ 14unsigned int cpu_khz; /* TSC clocks / usec, not used here */
15EXPORT_SYMBOL(cpu_khz); 15EXPORT_SYMBOL(cpu_khz);
16 16
17/*
18 * do_gettimeoffset() returns microseconds since last timer interrupt was
19 * triggered by hardware. A memory read of HPET is slower than a register read
20 * of TSC, but much more reliable. It's also synchronized to the timer
21 * interrupt. Note that do_gettimeoffset() may return more than hpet_tick, if a
22 * timer interrupt has happened already, but vxtime.trigger wasn't updated yet.
23 * This is not a problem, because jiffies hasn't updated either. They are bound
24 * together by xtime_lock.
25 */
26
27unsigned int do_gettimeoffset_tsc(void)
28{
29 unsigned long t;
30 unsigned long x;
31 t = get_cycles_sync();
32 if (t < vxtime.last_tsc)
33 t = vxtime.last_tsc; /* hack */
34 x = ((t - vxtime.last_tsc) * vxtime.tsc_quot) >> US_SCALE;
35 return x;
36}
37
38static unsigned int cyc2ns_scale __read_mostly; 17static unsigned int cyc2ns_scale __read_mostly;
39 18
40void set_cyc2ns_scale(unsigned long khz) 19void set_cyc2ns_scale(unsigned long khz)
@@ -42,7 +21,7 @@ void set_cyc2ns_scale(unsigned long khz)
42 cyc2ns_scale = (NSEC_PER_MSEC << NS_SCALE) / khz; 21 cyc2ns_scale = (NSEC_PER_MSEC << NS_SCALE) / khz;
43} 22}
44 23
45unsigned long long cycles_2_ns(unsigned long long cyc) 24static unsigned long long cycles_2_ns(unsigned long long cyc)
46{ 25{
47 return (cyc * cyc2ns_scale) >> NS_SCALE; 26 return (cyc * cyc2ns_scale) >> NS_SCALE;
48} 27}
@@ -61,6 +40,12 @@ unsigned long long sched_clock(void)
61 return cycles_2_ns(a); 40 return cycles_2_ns(a);
62} 41}
63 42
43static int tsc_unstable;
44
45static inline int check_tsc_unstable(void)
46{
47 return tsc_unstable;
48}
64#ifdef CONFIG_CPU_FREQ 49#ifdef CONFIG_CPU_FREQ
65 50
66/* Frequency scaling support. Adjust the TSC based timer when the cpu frequency 51/* Frequency scaling support. Adjust the TSC based timer when the cpu frequency
@@ -89,24 +74,6 @@ static void handle_cpufreq_delayed_get(struct work_struct *v)
89 cpufreq_delayed_issched = 0; 74 cpufreq_delayed_issched = 0;
90} 75}
91 76
92/* if we notice lost ticks, schedule a call to cpufreq_get() as it tries
93 * to verify the CPU frequency the timing core thinks the CPU is running
94 * at is still correct.
95 */
96void cpufreq_delayed_get(void)
97{
98 static int warned;
99 if (cpufreq_init && !cpufreq_delayed_issched) {
100 cpufreq_delayed_issched = 1;
101 if (!warned) {
102 warned = 1;
103 printk(KERN_DEBUG "Losing some ticks... "
104 "checking if CPU frequency changed.\n");
105 }
106 schedule_work(&cpufreq_delayed_get_work);
107 }
108}
109
110static unsigned int ref_freq = 0; 77static unsigned int ref_freq = 0;
111static unsigned long loops_per_jiffy_ref = 0; 78static unsigned long loops_per_jiffy_ref = 0;
112 79
@@ -142,7 +109,7 @@ static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
142 109
143 cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new); 110 cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);
144 if (!(freq->flags & CPUFREQ_CONST_LOOPS)) 111 if (!(freq->flags & CPUFREQ_CONST_LOOPS))
145 vxtime.tsc_quot = (USEC_PER_MSEC << US_SCALE) / cpu_khz; 112 mark_tsc_unstable();
146 } 113 }
147 114
148 set_cyc2ns_scale(cpu_khz_ref); 115 set_cyc2ns_scale(cpu_khz_ref);
@@ -169,12 +136,6 @@ core_initcall(cpufreq_tsc);
169 136
170static int tsc_unstable = 0; 137static int tsc_unstable = 0;
171 138
172void mark_tsc_unstable(void)
173{
174 tsc_unstable = 1;
175}
176EXPORT_SYMBOL_GPL(mark_tsc_unstable);
177
178/* 139/*
179 * Make an educated guess if the TSC is trustworthy and synchronized 140 * Make an educated guess if the TSC is trustworthy and synchronized
180 * over all CPUs. 141 * over all CPUs.
@@ -210,3 +171,49 @@ int __init notsc_setup(char *s)
210} 171}
211 172
212__setup("notsc", notsc_setup); 173__setup("notsc", notsc_setup);
174
175
176/* clock source code: */
177static cycle_t read_tsc(void)
178{
179 cycle_t ret = (cycle_t)get_cycles_sync();
180 return ret;
181}
182
183static struct clocksource clocksource_tsc = {
184 .name = "tsc",
185 .rating = 300,
186 .read = read_tsc,
187 .mask = CLOCKSOURCE_MASK(64),
188 .shift = 22,
189 .flags = CLOCK_SOURCE_IS_CONTINUOUS |
190 CLOCK_SOURCE_MUST_VERIFY,
191};
192
193void mark_tsc_unstable(void)
194{
195 if (!tsc_unstable) {
196 tsc_unstable = 1;
197 /* Change only the rating, when not registered */
198 if (clocksource_tsc.mult)
199 clocksource_change_rating(&clocksource_tsc, 0);
200 else
201 clocksource_tsc.rating = 0;
202 }
203}
204EXPORT_SYMBOL_GPL(mark_tsc_unstable);
205
206static int __init init_tsc_clocksource(void)
207{
208 if (!notsc) {
209 clocksource_tsc.mult = clocksource_khz2mult(cpu_khz,
210 clocksource_tsc.shift);
211 if (check_tsc_unstable())
212 clocksource_tsc.rating = 0;
213
214 return clocksource_register(&clocksource_tsc);
215 }
216 return 0;
217}
218
219module_init(init_tsc_clocksource);