Commit 7a6f9cbb authored by Andreas Herrmann's avatar Andreas Herrmann Committed by Ingo Molnar

x86: hpet: fix periodic mode programming on AMD 81xx

(See http://bugzilla.kernel.org/show_bug.cgi?id=12961)

It partially reverts commit c23e253e
(x86: hpet: stop HPET_COUNTER when programming periodic mode)

HPET on AMD 81xx chipset needs a second write (with HPET_TN_SETVAL
cleared) to T0_CMP register to set the period in periodic mode.

With this patch HPET_COUNTER is still stopped but not reset when HPET
is programmed in periodic mode. This should help to avoid races when
HPET is programmed in periodic mode and fixes a boot time hang that
I've observed on a machine when using 1000HZ.

[ Impact: fix boot time hang on machines with AMD 81xx chipset ]
Reported-by: default avatarJeff Mahoney <jeffm@suse.com>
Signed-off-by: default avatarAndreas Herrmann <andreas.herrmann3@amd.com>
Tested-by: default avatarJeff Mahoney <jeffm@suse.com>
LKML-Reference: <20090421180037.GA2763@alberich.amd.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 3568b71d
...@@ -236,6 +236,10 @@ static void hpet_stop_counter(void) ...@@ -236,6 +236,10 @@ static void hpet_stop_counter(void)
unsigned long cfg = hpet_readl(HPET_CFG); unsigned long cfg = hpet_readl(HPET_CFG);
cfg &= ~HPET_CFG_ENABLE; cfg &= ~HPET_CFG_ENABLE;
hpet_writel(cfg, HPET_CFG); hpet_writel(cfg, HPET_CFG);
}
static void hpet_reset_counter(void)
{
hpet_writel(0, HPET_COUNTER); hpet_writel(0, HPET_COUNTER);
hpet_writel(0, HPET_COUNTER + 4); hpet_writel(0, HPET_COUNTER + 4);
} }
...@@ -250,6 +254,7 @@ static void hpet_start_counter(void) ...@@ -250,6 +254,7 @@ static void hpet_start_counter(void)
static void hpet_restart_counter(void) static void hpet_restart_counter(void)
{ {
hpet_stop_counter(); hpet_stop_counter();
hpet_reset_counter();
hpet_start_counter(); hpet_start_counter();
} }
...@@ -309,7 +314,7 @@ static int hpet_setup_msi_irq(unsigned int irq); ...@@ -309,7 +314,7 @@ static int hpet_setup_msi_irq(unsigned int irq);
static void hpet_set_mode(enum clock_event_mode mode, static void hpet_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt, int timer) struct clock_event_device *evt, int timer)
{ {
unsigned long cfg; unsigned long cfg, cmp, now;
uint64_t delta; uint64_t delta;
switch (mode) { switch (mode) {
...@@ -317,12 +322,23 @@ static void hpet_set_mode(enum clock_event_mode mode, ...@@ -317,12 +322,23 @@ static void hpet_set_mode(enum clock_event_mode mode,
hpet_stop_counter(); hpet_stop_counter();
delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult;
delta >>= evt->shift; delta >>= evt->shift;
now = hpet_readl(HPET_COUNTER);
cmp = now + (unsigned long) delta;
cfg = hpet_readl(HPET_Tn_CFG(timer)); cfg = hpet_readl(HPET_Tn_CFG(timer));
/* Make sure we use edge triggered interrupts */ /* Make sure we use edge triggered interrupts */
cfg &= ~HPET_TN_LEVEL; cfg &= ~HPET_TN_LEVEL;
cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
HPET_TN_SETVAL | HPET_TN_32BIT; HPET_TN_SETVAL | HPET_TN_32BIT;
hpet_writel(cfg, HPET_Tn_CFG(timer)); hpet_writel(cfg, HPET_Tn_CFG(timer));
hpet_writel(cmp, HPET_Tn_CMP(timer));
udelay(1);
/*
* HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL
* cleared) to T0_CMP to set the period. The HPET_TN_SETVAL
* bit is automatically cleared after the first write.
* (See AMD-8111 HyperTransport I/O Hub Data Sheet,
* Publication # 24674)
*/
hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer));
hpet_start_counter(); hpet_start_counter();
hpet_print_config(); hpet_print_config();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment