diff options
author | Andreas Herrmann <andreas.herrmann3@amd.com> | 2009-02-20 18:16:35 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-22 12:01:18 -0500 |
commit | c23e253e67c9d8a91a0ffa33c1f571a17f0a2403 (patch) | |
tree | 5e1253549e6df59b568341e4b1da6b93183e73b0 /arch/x86/kernel/hpet.c | |
parent | 8d6f0c8214928f7c5083dd54ecb69c5d615b516e (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.c | 13 |
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); | |||
309 | static void hpet_set_mode(enum clock_event_mode mode, | 309 | static 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 | ||