|
@@ -236,6 +236,10 @@ static void hpet_stop_counter(void)
|
|
|
unsigned long cfg = hpet_readl(HPET_CFG);
|
|
|
cfg &= ~HPET_CFG_ENABLE;
|
|
|
hpet_writel(cfg, HPET_CFG);
|
|
|
+}
|
|
|
+
|
|
|
+static void hpet_reset_counter(void)
|
|
|
+{
|
|
|
hpet_writel(0, HPET_COUNTER);
|
|
|
hpet_writel(0, HPET_COUNTER + 4);
|
|
|
}
|
|
@@ -250,6 +254,7 @@ static void hpet_start_counter(void)
|
|
|
static void hpet_restart_counter(void)
|
|
|
{
|
|
|
hpet_stop_counter();
|
|
|
+ hpet_reset_counter();
|
|
|
hpet_start_counter();
|
|
|
}
|
|
|
|
|
@@ -309,7 +314,7 @@ static int hpet_setup_msi_irq(unsigned int irq);
|
|
|
static void hpet_set_mode(enum clock_event_mode mode,
|
|
|
struct clock_event_device *evt, int timer)
|
|
|
{
|
|
|
- unsigned long cfg;
|
|
|
+ unsigned long cfg, cmp, now;
|
|
|
uint64_t delta;
|
|
|
|
|
|
switch (mode) {
|
|
@@ -317,12 +322,23 @@ static void hpet_set_mode(enum clock_event_mode mode,
|
|
|
hpet_stop_counter();
|
|
|
delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult;
|
|
|
delta >>= evt->shift;
|
|
|
+ now = hpet_readl(HPET_COUNTER);
|
|
|
+ cmp = now + (unsigned long) delta;
|
|
|
cfg = hpet_readl(HPET_Tn_CFG(timer));
|
|
|
/* Make sure we use edge triggered interrupts */
|
|
|
cfg &= ~HPET_TN_LEVEL;
|
|
|
cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
|
|
|
HPET_TN_SETVAL | HPET_TN_32BIT;
|
|
|
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_start_counter();
|
|
|
hpet_print_config();
|