aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/hpet.c
diff options
context:
space:
mode:
authorAndreas Herrmann <andreas.herrmann3@amd.com>2009-02-20 18:16:35 -0500
committerIngo Molnar <mingo@elte.hu>2009-02-22 12:01:18 -0500
commitc23e253e67c9d8a91a0ffa33c1f571a17f0a2403 (patch)
tree5e1253549e6df59b568341e4b1da6b93183e73b0 /arch/x86/kernel/hpet.c
parent8d6f0c8214928f7c5083dd54ecb69c5d615b516e (diff)
x86: hpet: stop HPET_COUNTER when programming periodic mode
Impact: fix system hang on some systems operating with HZ_1000 On a system that stalled with HZ_1000, the first value written to T0_CMP (when the main counter was not stopped) did not trigger an interrupt. Instead after the main counter wrapped around (after several minutes) an interrupt was triggered and afterwards the periodic interrupt took effect. This can be fixed by implementing HPET spec recommendation for programming the periodic mode (i.e. stopping the main counter). Signed-off-by: Andreas Herrmann <andreas.herrmann3@amd.com> Cc: Mark Hounschell <markh@compro.net> Cc: Borislav Petkov <borislav.petkov@amd.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/hpet.c')
-rw-r--r--arch/x86/kernel/hpet.c13
1 files changed, 3 insertions, 10 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 7ae1f1e8b9b7..648b3a2a3a44 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -309,29 +309,22 @@ static int hpet_setup_msi_irq(unsigned int irq);
309static void hpet_set_mode(enum clock_event_mode mode, 309static void hpet_set_mode(enum clock_event_mode mode,
310 struct clock_event_device *evt, int timer) 310 struct clock_event_device *evt, int timer)
311{ 311{
312 unsigned long cfg, cmp, now; 312 unsigned long cfg;
313 uint64_t delta; 313 uint64_t delta;
314 314
315 switch (mode) { 315 switch (mode) {
316 case CLOCK_EVT_MODE_PERIODIC: 316 case CLOCK_EVT_MODE_PERIODIC:
317 hpet_stop_counter();
317 delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; 318 delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult;
318 delta >>= evt->shift; 319 delta >>= evt->shift;
319 now = hpet_readl(HPET_COUNTER);
320 cmp = now + (unsigned long) delta;
321 cfg = hpet_readl(HPET_Tn_CFG(timer)); 320 cfg = hpet_readl(HPET_Tn_CFG(timer));
322 /* Make sure we use edge triggered interrupts */ 321 /* Make sure we use edge triggered interrupts */
323 cfg &= ~HPET_TN_LEVEL; 322 cfg &= ~HPET_TN_LEVEL;
324 cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | 323 cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
325 HPET_TN_SETVAL | HPET_TN_32BIT; 324 HPET_TN_SETVAL | HPET_TN_32BIT;
326 hpet_writel(cfg, HPET_Tn_CFG(timer)); 325 hpet_writel(cfg, HPET_Tn_CFG(timer));
327 /*
328 * The first write after writing TN_SETVAL to the
329 * config register sets the counter value, the second
330 * write sets the period.
331 */
332 hpet_writel(cmp, HPET_Tn_CMP(timer));
333 udelay(1);
334 hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); 326 hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer));
327 hpet_start_counter();
335 hpet_print_config(); 328 hpet_print_config();
336 break; 329 break;
337 330