Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes...
[sfrench/cifs-2.6.git] / arch / x86 / kernel / hpet.c
index 648b3a2a3a440afe542bd8caf3fd46c6641ebc29..81408b93f887116c7abf33f1ca79cf692ba97f62 100644 (file)
@@ -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();
@@ -722,7 +738,7 @@ static int hpet_cpuhp_notify(struct notifier_block *n,
 /*
  * Clock source related code
  */
-static cycle_t read_hpet(void)
+static cycle_t read_hpet(struct clocksource *cs)
 {
        return (cycle_t)hpet_readl(HPET_COUNTER);
 }
@@ -756,7 +772,7 @@ static int hpet_clocksource_register(void)
        hpet_restart_counter();
 
        /* Verify whether hpet counter works */
-       t1 = read_hpet();
+       t1 = hpet_readl(HPET_COUNTER);
        rdtscll(start);
 
        /*
@@ -770,7 +786,7 @@ static int hpet_clocksource_register(void)
                rdtscll(now);
        } while ((now - start) < 200000UL);
 
-       if (t1 == read_hpet()) {
+       if (t1 == hpet_readl(HPET_COUNTER)) {
                printk(KERN_WARNING
                       "HPET counter not counting. HPET disabled\n");
                return -ENODEV;