Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] i386: fix hpet for systems that don't support legacy replacement

Currently the i386 HPET code assumes the entire HPET implementation from
the spec is present. This breaks on boxes that do not implement the
optional legacy timer replacement functionality portion of the spec.

This patch, which is very similar to my x86-64 patch for the same issue,
fixes the problem allowing i386 systems that cannot use the HPET for the
timer interrupt and RTC to still use the HPET as a time source. I've
tested this patch on a system systems without HPET, with HPET but without
legacy timer replacement, as well as HPET with legacy timer replacement.

Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

john stultz and committed by
Linus Torvalds
35492df5 5b7abc6f

+37 -27
+1 -1
arch/i386/kernel/time.c
··· 441 441 set_normalized_timespec(&wall_to_monotonic, 442 442 -xtime.tv_sec, -xtime.tv_nsec); 443 443 444 - if (hpet_enable() >= 0) { 444 + if ((hpet_enable() >= 0) && hpet_use_timer) { 445 445 printk("Using HPET for base-timer\n"); 446 446 } 447 447
+27 -21
arch/i386/kernel/time_hpet.c
··· 26 26 static unsigned long hpet_period; /* fsecs / HPET clock */ 27 27 unsigned long hpet_tick; /* hpet clks count per tick */ 28 28 unsigned long hpet_address; /* hpet memory map physical address */ 29 + int hpet_use_timer; 29 30 30 31 static int use_hpet; /* can be used for runtime check of hpet */ 31 32 static int boot_hpet_disable; /* boottime override for HPET timer */ ··· 74 73 hpet_writel(0, HPET_COUNTER); 75 74 hpet_writel(0, HPET_COUNTER + 4); 76 75 77 - /* 78 - * Set up timer 0, as periodic with first interrupt to happen at 79 - * hpet_tick, and period also hpet_tick. 80 - */ 81 - cfg = hpet_readl(HPET_T0_CFG); 82 - cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | 83 - HPET_TN_SETVAL | HPET_TN_32BIT; 84 - hpet_writel(cfg, HPET_T0_CFG); 76 + if (hpet_use_timer) { 77 + /* 78 + * Set up timer 0, as periodic with first interrupt to happen at 79 + * hpet_tick, and period also hpet_tick. 80 + */ 81 + cfg = hpet_readl(HPET_T0_CFG); 82 + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | 83 + HPET_TN_SETVAL | HPET_TN_32BIT; 84 + hpet_writel(cfg, HPET_T0_CFG); 85 85 86 - /* 87 - * The first write after writing TN_SETVAL to the config register sets 88 - * the counter value, the second write sets the threshold. 89 - */ 90 - hpet_writel(tick, HPET_T0_CMP); 91 - hpet_writel(tick, HPET_T0_CMP); 92 - 86 + /* 87 + * The first write after writing TN_SETVAL to the config register sets 88 + * the counter value, the second write sets the threshold. 89 + */ 90 + hpet_writel(tick, HPET_T0_CMP); 91 + hpet_writel(tick, HPET_T0_CMP); 92 + } 93 93 /* 94 94 * Go! 95 95 */ 96 96 cfg = hpet_readl(HPET_CFG); 97 - cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY; 97 + if (hpet_use_timer) 98 + cfg |= HPET_CFG_LEGACY; 99 + cfg |= HPET_CFG_ENABLE; 98 100 hpet_writel(cfg, HPET_CFG); 99 101 100 102 return 0; ··· 132 128 * However, we can do with one timer otherwise using the 133 129 * the single HPET timer for system time. 134 130 */ 135 - if ( 136 131 #ifdef CONFIG_HPET_EMULATE_RTC 137 - !(id & HPET_ID_NUMBER) || 138 - #endif 139 - !(id & HPET_ID_LEGSUP)) 132 + if (!(id & HPET_ID_NUMBER)) 140 133 return -1; 134 + #endif 135 + 141 136 142 137 hpet_period = hpet_readl(HPET_PERIOD); 143 138 if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period > HPET_MAX_PERIOD)) ··· 154 151 155 152 if (hpet_tick_rem > (hpet_period >> 1)) 156 153 hpet_tick++; /* rounding the result */ 154 + 155 + hpet_use_timer = id & HPET_ID_LEGSUP; 157 156 158 157 if (hpet_timer_stop_set_go(hpet_tick)) 159 158 return -1; ··· 207 202 #endif 208 203 209 204 #ifdef CONFIG_X86_LOCAL_APIC 210 - wait_timer_tick = wait_hpet_tick; 205 + if (hpet_use_timer) 206 + wait_timer_tick = wait_hpet_tick; 211 207 #endif 212 208 return 0; 213 209 }
+7 -4
arch/i386/kernel/timers/timer_hpet.c
··· 79 79 80 80 eax = hpet_readl(HPET_COUNTER); 81 81 eax -= hpet_last; /* hpet delta */ 82 - 82 + eax = min(hpet_tick, eax); 83 83 /* 84 84 * Time offset = (hpet delta) * ( usecs per HPET clock ) 85 85 * = (hpet delta) * ( usecs per tick / HPET clocks per tick) ··· 105 105 last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low; 106 106 rdtsc(last_tsc_low, last_tsc_high); 107 107 108 - offset = hpet_readl(HPET_T0_CMP) - hpet_tick; 109 - if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))) { 110 - int lost_ticks = (offset - hpet_last) / hpet_tick; 108 + if (hpet_use_timer) 109 + offset = hpet_readl(HPET_T0_CMP) - hpet_tick; 110 + else 111 + offset = hpet_readl(HPET_COUNTER); 112 + if (unlikely(((offset - hpet_last) >= (2*hpet_tick)) && (hpet_last != 0))) { 113 + int lost_ticks = ((offset - hpet_last) / hpet_tick) - 1; 111 114 jiffies_64 += lost_ticks; 112 115 } 113 116 hpet_last = offset;
+1 -1
arch/i386/kernel/timers/timer_tsc.c
··· 477 477 if (cpu_has_tsc) { 478 478 unsigned long tsc_quotient; 479 479 #ifdef CONFIG_HPET_TIMER 480 - if (is_hpet_enabled()){ 480 + if (is_hpet_enabled() && hpet_use_timer) { 481 481 unsigned long result, remain; 482 482 printk("Using TSC for gettimeofday\n"); 483 483 tsc_quotient = calibrate_tsc_hpet(NULL);
+1
include/asm-i386/hpet.h
··· 92 92 93 93 extern unsigned long hpet_tick; /* hpet clks count per tick */ 94 94 extern unsigned long hpet_address; /* hpet memory map physical address */ 95 + extern int hpet_use_timer; 95 96 96 97 extern int hpet_rtc_timer_init(void); 97 98 extern int hpet_enable(void);