Commit 35492df5ae0f36f717448b2aea908d3a8891d1c4

Authored by john stultz
Committed by Linus Torvalds
1 parent 5b7abc6fdc

[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>

Showing 5 changed files with 37 additions and 27 deletions Side-by-side Diff

arch/i386/kernel/time.c
... ... @@ -441,7 +441,7 @@
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  
arch/i386/kernel/time_hpet.c
... ... @@ -26,6 +26,7 @@
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 */
32 33  
33 34  
34 35  
... ... @@ -73,27 +74,30 @@
73 74 hpet_writel(0, HPET_COUNTER);
74 75 hpet_writel(0, HPET_COUNTER + 4);
75 76  
76   - /*
77   - * Set up timer 0, as periodic with first interrupt to happen at
78   - * hpet_tick, and period also hpet_tick.
79   - */
80   - cfg = hpet_readl(HPET_T0_CFG);
81   - cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
82   - HPET_TN_SETVAL | HPET_TN_32BIT;
83   - hpet_writel(cfg, HPET_T0_CFG);
  77 + if (hpet_use_timer) {
  78 + /*
  79 + * Set up timer 0, as periodic with first interrupt to happen at
  80 + * hpet_tick, and period also hpet_tick.
  81 + */
  82 + cfg = hpet_readl(HPET_T0_CFG);
  83 + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
  84 + HPET_TN_SETVAL | HPET_TN_32BIT;
  85 + hpet_writel(cfg, HPET_T0_CFG);
84 86  
  87 + /*
  88 + * The first write after writing TN_SETVAL to the config register sets
  89 + * the counter value, the second write sets the threshold.
  90 + */
  91 + hpet_writel(tick, HPET_T0_CMP);
  92 + hpet_writel(tick, HPET_T0_CMP);
  93 + }
85 94 /*
86   - * The first write after writing TN_SETVAL to the config register sets
87   - * the counter value, the second write sets the threshold.
88   - */
89   - hpet_writel(tick, HPET_T0_CMP);
90   - hpet_writel(tick, HPET_T0_CMP);
91   -
92   - /*
93 95 * Go!
94 96 */
95 97 cfg = hpet_readl(HPET_CFG);
96   - cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
  98 + if (hpet_use_timer)
  99 + cfg |= HPET_CFG_LEGACY;
  100 + cfg |= HPET_CFG_ENABLE;
97 101 hpet_writel(cfg, HPET_CFG);
98 102  
99 103 return 0;
100 104  
101 105  
102 106  
... ... @@ -128,13 +132,12 @@
128 132 * However, we can do with one timer otherwise using the
129 133 * the single HPET timer for system time.
130 134 */
131   - if (
132 135 #ifdef CONFIG_HPET_EMULATE_RTC
133   - !(id & HPET_ID_NUMBER) ||
134   -#endif
135   - !(id & HPET_ID_LEGSUP))
  136 + if (!(id & HPET_ID_NUMBER))
136 137 return -1;
  138 +#endif
137 139  
  140 +
138 141 hpet_period = hpet_readl(HPET_PERIOD);
139 142 if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period > HPET_MAX_PERIOD))
140 143 return -1;
... ... @@ -152,6 +155,8 @@
152 155 if (hpet_tick_rem > (hpet_period >> 1))
153 156 hpet_tick++; /* rounding the result */
154 157  
  158 + hpet_use_timer = id & HPET_ID_LEGSUP;
  159 +
155 160 if (hpet_timer_stop_set_go(hpet_tick))
156 161 return -1;
157 162  
... ... @@ -202,7 +207,8 @@
202 207 #endif
203 208  
204 209 #ifdef CONFIG_X86_LOCAL_APIC
205   - wait_timer_tick = wait_hpet_tick;
  210 + if (hpet_use_timer)
  211 + wait_timer_tick = wait_hpet_tick;
206 212 #endif
207 213 return 0;
208 214 }
arch/i386/kernel/timers/timer_hpet.c
... ... @@ -79,7 +79,7 @@
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,9 +105,12 @@
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;
arch/i386/kernel/timers/timer_tsc.c
... ... @@ -477,7 +477,7 @@
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);
include/asm-i386/hpet.h
... ... @@ -92,6 +92,7 @@
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);