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 | |
| 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>
| -rw-r--r-- | arch/x86_64/Kconfig | 4 | ||||
| -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 | ||||
| -rw-r--r-- | drivers/char/hangcheck-timer.c | 2 | ||||
| -rw-r--r-- | include/asm-x86_64/proto.h | 2 | ||||
| -rw-r--r-- | include/asm-x86_64/timex.h | 5 |
10 files changed, 126 insertions, 415 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 | ||
| 27 | config GENERIC_TIME | ||
| 28 | bool | ||
| 29 | default y | ||
| 30 | |||
| 27 | config ZONE_DMA32 | 31 | config 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 */ | |||
| 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); | ||
diff --git a/drivers/char/hangcheck-timer.c b/drivers/char/hangcheck-timer.c index 1aa93a752a9c..ae76a9ffe89f 100644 --- a/drivers/char/hangcheck-timer.c +++ b/drivers/char/hangcheck-timer.c | |||
| @@ -117,7 +117,7 @@ __setup("hcheck_reboot", hangcheck_parse_reboot); | |||
| 117 | __setup("hcheck_dump_tasks", hangcheck_parse_dump_tasks); | 117 | __setup("hcheck_dump_tasks", hangcheck_parse_dump_tasks); |
| 118 | #endif /* not MODULE */ | 118 | #endif /* not MODULE */ |
| 119 | 119 | ||
| 120 | #if defined(CONFIG_X86_64) || defined(CONFIG_S390) | 120 | #if defined(CONFIG_S390) |
| 121 | # define HAVE_MONOTONIC | 121 | # define HAVE_MONOTONIC |
| 122 | # define TIMER_FREQ 1000000000ULL | 122 | # define TIMER_FREQ 1000000000ULL |
| 123 | #elif defined(CONFIG_IA64) | 123 | #elif defined(CONFIG_IA64) |
diff --git a/include/asm-x86_64/proto.h b/include/asm-x86_64/proto.h index 2ce3adf7bfdc..f590ae088f0d 100644 --- a/include/asm-x86_64/proto.h +++ b/include/asm-x86_64/proto.h | |||
| @@ -45,11 +45,9 @@ extern u32 pmtmr_ioport; | |||
| 45 | #else | 45 | #else |
| 46 | #define pmtmr_ioport 0 | 46 | #define pmtmr_ioport 0 |
| 47 | #endif | 47 | #endif |
| 48 | extern unsigned long long monotonic_base; | ||
| 49 | extern int sysctl_vsyscall; | 48 | extern int sysctl_vsyscall; |
| 50 | extern int nohpet; | 49 | extern int nohpet; |
| 51 | extern unsigned long vxtime_hz; | 50 | extern unsigned long vxtime_hz; |
| 52 | extern void time_init_gtod(void); | ||
| 53 | 51 | ||
| 54 | extern void early_printk(const char *fmt, ...) __attribute__((format(printf,1,2))); | 52 | extern void early_printk(const char *fmt, ...) __attribute__((format(printf,1,2))); |
| 55 | 53 | ||
diff --git a/include/asm-x86_64/timex.h b/include/asm-x86_64/timex.h index a0174757aee2..beb6d9014a63 100644 --- a/include/asm-x86_64/timex.h +++ b/include/asm-x86_64/timex.h | |||
| @@ -28,9 +28,6 @@ extern int read_current_timer(unsigned long *timer_value); | |||
| 28 | #define US_SCALE 32 /* 2^32, arbitralrily chosen */ | 28 | #define US_SCALE 32 /* 2^32, arbitralrily chosen */ |
| 29 | 29 | ||
| 30 | extern struct vxtime_data vxtime; | 30 | extern struct vxtime_data vxtime; |
| 31 | 31 | extern void mark_tsc_unstable(void); | |
| 32 | extern unsigned int do_gettimeoffset_hpet(void); | ||
| 33 | extern unsigned int do_gettimeoffset_tsc(void); | ||
| 34 | extern void set_cyc2ns_scale(unsigned long khz); | 32 | extern void set_cyc2ns_scale(unsigned long khz); |
| 35 | extern int notsc; | ||
| 36 | #endif | 33 | #endif |
