diff options
| -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 | ||
