diff options
author | john stultz <johnstul@us.ibm.com> | 2007-02-16 04:28:20 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 11:14:00 -0500 |
commit | 1489939f0ab64b96998e04068c516c39afe29654 (patch) | |
tree | 6bb3ca772edf1dd8877482dc3b6bcc6f0d699e72 /arch/x86_64/kernel | |
parent | c37e7bb5d2ce36ef377caabfced0b132bb1bf6a7 (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/kernel')
-rw-r--r-- | arch/x86_64/kernel/apic.c | 2 | ||||
-rw-r--r-- | arch/x86_64/kernel/hpet.c | 64 | ||||
-rw-r--r-- | arch/x86_64/kernel/pmtimer.c | 58 | ||||
-rw-r--r-- | arch/x86_64/kernel/smpboot.c | 1 | ||||
-rw-r--r-- | arch/x86_64/kernel/time.c | 300 | ||||
-rw-r--r-- | arch/x86_64/kernel/tsc.c | 103 |
6 files changed, 120 insertions, 408 deletions
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 */ | |||
21 | int hpet_use_timer; /* Use counter of hpet for time keeping, | 21 | int hpet_use_timer; /* Use counter of hpet for time keeping, |
22 | * otherwise PIT | 22 | * otherwise PIT |
23 | */ | 23 | */ |
24 | unsigned 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 |
32 | static __init int late_hpet_init(void) | 26 | static __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 | |||
454 | static void *hpet_ptr; | ||
455 | |||
456 | static cycle_t read_hpet(void) | ||
457 | { | ||
458 | return (cycle_t)readl(hpet_ptr); | ||
459 | } | ||
460 | |||
461 | struct 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 | |||
471 | static 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 | |||
505 | module_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 */ | ||
30 | u32 pmtmr_ioport __read_mostly; | ||
31 | |||
32 | /* value of the Power timer at last timer interrupt */ | ||
33 | static u32 offset_delay; | ||
34 | static 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 | ||
38 | static inline u32 cyc2us(u32 cycles) | 29 | static 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 | ||
51 | int 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 | |||
83 | static unsigned pmtimer_wait_tick(void) | 42 | static 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 | ||
104 | void pmtimer_resume(void) | ||
105 | { | ||
106 | last_pmtmr_tick = inl(pmtmr_ioport); | ||
107 | } | ||
108 | |||
109 | unsigned 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 | |||
121 | static int __init nopmtimer_setup(char *s) | 63 | static 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 | ||
48 | extern void cpufreq_delayed_get(void); | ||
49 | #endif | ||
50 | extern void i8254_timer_resume(void); | 47 | extern void i8254_timer_resume(void); |
51 | extern int using_apic_timer; | 48 | extern int using_apic_timer; |
52 | 49 | ||
@@ -57,8 +54,6 @@ EXPORT_SYMBOL(rtc_lock); | |||
57 | DEFINE_SPINLOCK(i8253_lock); | 54 | DEFINE_SPINLOCK(i8253_lock); |
58 | 55 | ||
59 | unsigned long vxtime_hz = PIT_TICK_RATE; | 56 | unsigned long vxtime_hz = PIT_TICK_RATE; |
60 | int report_lost_ticks; /* command line option */ | ||
61 | unsigned long long monotonic_base; | ||
62 | 57 | ||
63 | struct vxtime_data __vxtime __section_vxtime; /* for vsyscalls */ | 58 | struct vxtime_data __vxtime __section_vxtime; /* for vsyscalls */ |
64 | 59 | ||
@@ -66,76 +61,6 @@ volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; | |||
66 | struct timespec __xtime __section_xtime; | 61 | struct timespec __xtime __section_xtime; |
67 | struct timezone __sys_tz __section_sys_tz; | 62 | struct timezone __sys_tz __section_sys_tz; |
68 | 63 | ||
69 | unsigned 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 | |||
77 | void 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 | |||
104 | EXPORT_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 | |||
112 | int 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 | |||
137 | EXPORT_SYMBOL(do_settimeofday); | ||
138 | |||
139 | unsigned long profile_pc(struct pt_regs *regs) | 64 | unsigned 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 | */ | ||
232 | extern unsigned long long cycles_2_ns(unsigned long long cyc); | ||
233 | unsigned 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 | } | ||
261 | EXPORT_SYMBOL(monotonic_clock); | ||
262 | |||
263 | static 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 | |||
301 | void main_timer_handler(void) | 153 | void 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 | ||
540 | int __init time_setup(char *str) | ||
541 | { | ||
542 | report_lost_ticks = 1; | ||
543 | return 1; | ||
544 | } | ||
545 | |||
546 | static struct irqaction irq0 = { | 328 | static 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 | */ | ||
598 | void 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 | ||
647 | static long clock_cmos_diff; | 371 | static long clock_cmos_diff; |
648 | static unsigned long sleep_start; | 372 | static 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 | ||
12 | int notsc __initdata = 0; | 12 | static int notsc __initdata = 0; |
13 | 13 | ||
14 | unsigned int cpu_khz; /* TSC clocks / usec, not used here */ | 14 | unsigned int cpu_khz; /* TSC clocks / usec, not used here */ |
15 | EXPORT_SYMBOL(cpu_khz); | 15 | EXPORT_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 | |||
27 | unsigned 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 | |||
38 | static unsigned int cyc2ns_scale __read_mostly; | 17 | static unsigned int cyc2ns_scale __read_mostly; |
39 | 18 | ||
40 | void set_cyc2ns_scale(unsigned long khz) | 19 | void 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 | ||
45 | unsigned long long cycles_2_ns(unsigned long long cyc) | 24 | static 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 | ||
43 | static int tsc_unstable; | ||
44 | |||
45 | static 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 | */ | ||
96 | void 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 | |||
110 | static unsigned int ref_freq = 0; | 77 | static unsigned int ref_freq = 0; |
111 | static unsigned long loops_per_jiffy_ref = 0; | 78 | static 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 | ||
170 | static int tsc_unstable = 0; | 137 | static int tsc_unstable = 0; |
171 | 138 | ||
172 | void mark_tsc_unstable(void) | ||
173 | { | ||
174 | tsc_unstable = 1; | ||
175 | } | ||
176 | EXPORT_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: */ | ||
177 | static cycle_t read_tsc(void) | ||
178 | { | ||
179 | cycle_t ret = (cycle_t)get_cycles_sync(); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | static 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 | |||
193 | void 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 | } | ||
204 | EXPORT_SYMBOL_GPL(mark_tsc_unstable); | ||
205 | |||
206 | static 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 | |||
219 | module_init(init_tsc_clocksource); | ||