diff options
Diffstat (limited to 'arch/x86_64')
-rw-r--r-- | arch/x86_64/kernel/time.c | 42 |
1 files changed, 28 insertions, 14 deletions
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index fb8c809b4cd9..66bf6ddeb0c3 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c | |||
@@ -64,6 +64,7 @@ static int notsc __initdata = 0; | |||
64 | unsigned int cpu_khz; /* TSC clocks / usec, not used here */ | 64 | unsigned int cpu_khz; /* TSC clocks / usec, not used here */ |
65 | static unsigned long hpet_period; /* fsecs / HPET clock */ | 65 | static unsigned long hpet_period; /* fsecs / HPET clock */ |
66 | unsigned long hpet_tick; /* HPET clocks / interrupt */ | 66 | unsigned long hpet_tick; /* HPET clocks / interrupt */ |
67 | static int hpet_use_timer; | ||
67 | unsigned long vxtime_hz = PIT_TICK_RATE; | 68 | unsigned long vxtime_hz = PIT_TICK_RATE; |
68 | int report_lost_ticks; /* command line option */ | 69 | int report_lost_ticks; /* command line option */ |
69 | unsigned long long monotonic_base; | 70 | unsigned long long monotonic_base; |
@@ -105,7 +106,9 @@ static inline unsigned int do_gettimeoffset_tsc(void) | |||
105 | 106 | ||
106 | static inline unsigned int do_gettimeoffset_hpet(void) | 107 | static inline unsigned int do_gettimeoffset_hpet(void) |
107 | { | 108 | { |
108 | return ((hpet_readl(HPET_COUNTER) - vxtime.last) * vxtime.quot) >> 32; | 109 | /* cap counter read to one tick to avoid inconsistencies */ |
110 | unsigned long counter = hpet_readl(HPET_COUNTER) - vxtime.last; | ||
111 | return (min(counter,hpet_tick) * vxtime.quot) >> 32; | ||
109 | } | 112 | } |
110 | 113 | ||
111 | unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc; | 114 | unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc; |
@@ -301,7 +304,7 @@ unsigned long long monotonic_clock(void) | |||
301 | 304 | ||
302 | last_offset = vxtime.last; | 305 | last_offset = vxtime.last; |
303 | base = monotonic_base; | 306 | base = monotonic_base; |
304 | this_offset = hpet_readl(HPET_T0_CMP) - hpet_tick; | 307 | this_offset = hpet_readl(HPET_COUNTER); |
305 | 308 | ||
306 | } while (read_seqretry(&xtime_lock, seq)); | 309 | } while (read_seqretry(&xtime_lock, seq)); |
307 | offset = (this_offset - last_offset); | 310 | offset = (this_offset - last_offset); |
@@ -377,7 +380,14 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
377 | 380 | ||
378 | write_seqlock(&xtime_lock); | 381 | write_seqlock(&xtime_lock); |
379 | 382 | ||
380 | if (vxtime.hpet_address) { | 383 | if (vxtime.hpet_address) |
384 | offset = hpet_readl(HPET_COUNTER); | ||
385 | |||
386 | if (hpet_use_timer) { | ||
387 | /* if we're using the hpet timer functionality, | ||
388 | * we can more accurately know the counter value | ||
389 | * when the timer interrupt occured. | ||
390 | */ | ||
381 | offset = hpet_readl(HPET_T0_CMP) - hpet_tick; | 391 | offset = hpet_readl(HPET_T0_CMP) - hpet_tick; |
382 | delay = hpet_readl(HPET_COUNTER) - offset; | 392 | delay = hpet_readl(HPET_COUNTER) - offset; |
383 | } else { | 393 | } else { |
@@ -803,17 +813,18 @@ static int hpet_timer_stop_set_go(unsigned long tick) | |||
803 | * Set up timer 0, as periodic with first interrupt to happen at hpet_tick, | 813 | * Set up timer 0, as periodic with first interrupt to happen at hpet_tick, |
804 | * and period also hpet_tick. | 814 | * and period also hpet_tick. |
805 | */ | 815 | */ |
806 | 816 | if (hpet_use_timer) { | |
807 | hpet_writel(HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | | 817 | hpet_writel(HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | |
808 | HPET_TN_32BIT, HPET_T0_CFG); | 818 | HPET_TN_32BIT, HPET_T0_CFG); |
809 | hpet_writel(hpet_tick, HPET_T0_CMP); | 819 | hpet_writel(hpet_tick, HPET_T0_CMP); |
810 | hpet_writel(hpet_tick, HPET_T0_CMP); /* AK: why twice? */ | 820 | hpet_writel(hpet_tick, HPET_T0_CMP); /* AK: why twice? */ |
811 | 821 | cfg |= HPET_CFG_LEGACY; | |
822 | } | ||
812 | /* | 823 | /* |
813 | * Go! | 824 | * Go! |
814 | */ | 825 | */ |
815 | 826 | ||
816 | cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY; | 827 | cfg |= HPET_CFG_ENABLE; |
817 | hpet_writel(cfg, HPET_CFG); | 828 | hpet_writel(cfg, HPET_CFG); |
818 | 829 | ||
819 | return 0; | 830 | return 0; |
@@ -834,8 +845,7 @@ static int hpet_init(void) | |||
834 | 845 | ||
835 | id = hpet_readl(HPET_ID); | 846 | id = hpet_readl(HPET_ID); |
836 | 847 | ||
837 | if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER) || | 848 | if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER)) |
838 | !(id & HPET_ID_LEGSUP)) | ||
839 | return -1; | 849 | return -1; |
840 | 850 | ||
841 | hpet_period = hpet_readl(HPET_PERIOD); | 851 | hpet_period = hpet_readl(HPET_PERIOD); |
@@ -845,6 +855,8 @@ static int hpet_init(void) | |||
845 | hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) / | 855 | hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) / |
846 | hpet_period; | 856 | hpet_period; |
847 | 857 | ||
858 | hpet_use_timer = (id & HPET_ID_LEGSUP); | ||
859 | |||
848 | return hpet_timer_stop_set_go(hpet_tick); | 860 | return hpet_timer_stop_set_go(hpet_tick); |
849 | } | 861 | } |
850 | 862 | ||
@@ -901,9 +913,11 @@ void __init time_init(void) | |||
901 | set_normalized_timespec(&wall_to_monotonic, | 913 | set_normalized_timespec(&wall_to_monotonic, |
902 | -xtime.tv_sec, -xtime.tv_nsec); | 914 | -xtime.tv_sec, -xtime.tv_nsec); |
903 | 915 | ||
904 | if (!hpet_init()) { | 916 | if (!hpet_init()) |
905 | vxtime_hz = (1000000000000000L + hpet_period / 2) / | 917 | vxtime_hz = (1000000000000000L + hpet_period / 2) / |
906 | hpet_period; | 918 | hpet_period; |
919 | |||
920 | if (hpet_use_timer) { | ||
907 | cpu_khz = hpet_calibrate_tsc(); | 921 | cpu_khz = hpet_calibrate_tsc(); |
908 | timename = "HPET"; | 922 | timename = "HPET"; |
909 | #ifdef CONFIG_X86_PM_TIMER | 923 | #ifdef CONFIG_X86_PM_TIMER |
@@ -968,7 +982,7 @@ void __init time_init_gtod(void) | |||
968 | if (unsynchronized_tsc()) | 982 | if (unsynchronized_tsc()) |
969 | notsc = 1; | 983 | notsc = 1; |
970 | if (vxtime.hpet_address && notsc) { | 984 | if (vxtime.hpet_address && notsc) { |
971 | timetype = "HPET"; | 985 | timetype = hpet_use_timer ? "HPET" : "PIT/HPET"; |
972 | vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick; | 986 | vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick; |
973 | vxtime.mode = VXTIME_HPET; | 987 | vxtime.mode = VXTIME_HPET; |
974 | do_gettimeoffset = do_gettimeoffset_hpet; | 988 | do_gettimeoffset = do_gettimeoffset_hpet; |
@@ -983,7 +997,7 @@ void __init time_init_gtod(void) | |||
983 | printk(KERN_INFO "Disabling vsyscall due to use of PM timer\n"); | 997 | printk(KERN_INFO "Disabling vsyscall due to use of PM timer\n"); |
984 | #endif | 998 | #endif |
985 | } else { | 999 | } else { |
986 | timetype = vxtime.hpet_address ? "HPET/TSC" : "PIT/TSC"; | 1000 | timetype = hpet_use_timer ? "HPET/TSC" : "PIT/TSC"; |
987 | vxtime.mode = VXTIME_TSC; | 1001 | vxtime.mode = VXTIME_TSC; |
988 | } | 1002 | } |
989 | 1003 | ||