Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / i386 / kernel / time_hpet.c
index 52fc526..a529f0c 100644 (file)
 #include <asm/hpet.h>
 #include <linux/hpet.h>
 
-unsigned long hpet_period;     /* fsecs / HPET clock */
-unsigned long hpet_tick;       /* hpet clks count per tick */
-unsigned long hpet_address;    /* hpet memory map physical address */
+static unsigned long hpet_period;      /* fsecs / HPET clock */
+unsigned long hpet_tick;               /* hpet clks count per tick */
+unsigned long hpet_address;            /* hpet memory map physical address */
+int hpet_use_timer;
 
 static int use_hpet;           /* can be used for runtime check of hpet */
 static int boot_hpet_disable;  /* boottime override for HPET timer */
@@ -38,7 +39,7 @@ int hpet_readl(unsigned long a)
        return readl(hpet_virt_address + a);
 }
 
-void hpet_writel(unsigned long d, unsigned long a)
+static void hpet_writel(unsigned long d, unsigned long a)
 {
        writel(d, hpet_virt_address + a);
 }
@@ -49,7 +50,7 @@ void hpet_writel(unsigned long d, unsigned long a)
  * comparator value and continue. Next tick can be caught by checking
  * for a change in the comparator value. Used in apic.c.
  */
-void __init wait_hpet_tick(void)
+static void __devinit wait_hpet_tick(void)
 {
        unsigned int start_cmp_val, end_cmp_val;
 
@@ -60,13 +61,55 @@ void __init wait_hpet_tick(void)
 }
 #endif
 
+static int hpet_timer_stop_set_go(unsigned long tick)
+{
+       unsigned int cfg;
+
+       /*
+        * Stop the timers and reset the main counter.
+        */
+       cfg = hpet_readl(HPET_CFG);
+       cfg &= ~HPET_CFG_ENABLE;
+       hpet_writel(cfg, HPET_CFG);
+       hpet_writel(0, HPET_COUNTER);
+       hpet_writel(0, HPET_COUNTER + 4);
+
+       if (hpet_use_timer) {
+               /*
+                * Set up timer 0, as periodic with first interrupt to happen at
+                * hpet_tick, and period also hpet_tick.
+                */
+               cfg = hpet_readl(HPET_T0_CFG);
+               cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
+                      HPET_TN_SETVAL | HPET_TN_32BIT;
+               hpet_writel(cfg, HPET_T0_CFG);
+
+               /*
+                * The first write after writing TN_SETVAL to the config register sets
+                * the counter value, the second write sets the threshold.
+                */
+               hpet_writel(tick, HPET_T0_CMP);
+               hpet_writel(tick, HPET_T0_CMP);
+       }
+       /*
+        * Go!
+        */
+       cfg = hpet_readl(HPET_CFG);
+       if (hpet_use_timer)
+               cfg |= HPET_CFG_LEGACY;
+       cfg |= HPET_CFG_ENABLE;
+       hpet_writel(cfg, HPET_CFG);
+
+       return 0;
+}
+
 /*
  * Check whether HPET was found by ACPI boot parse. If yes setup HPET
  * counter 0 for kernel base timer.
  */
 int __init hpet_enable(void)
 {
-       unsigned int cfg, id;
+       unsigned int id;
        unsigned long tick_fsec_low, tick_fsec_high; /* tick in femto sec */
        unsigned long hpet_tick_rem;
 
@@ -83,13 +126,17 @@ int __init hpet_enable(void)
        id = hpet_readl(HPET_ID);
 
        /*
-        * We are checking for value '1' or more in number field.
-        * So, we are OK with HPET_EMULATE_RTC part too, where we need
-        * to have atleast 2 timers.
+        * We are checking for value '1' or more in number field if
+        * CONFIG_HPET_EMULATE_RTC is set because we will need an
+        * additional timer for RTC emulation.
+        * However, we can do with one timer otherwise using the
+        * the single HPET timer for system time.
         */
-       if (!(id & HPET_ID_NUMBER) ||
-           !(id & HPET_ID_LEGSUP))
+#ifdef CONFIG_HPET_EMULATE_RTC
+       if (!(id & HPET_ID_NUMBER))
                return -1;
+#endif
+
 
        hpet_period = hpet_readl(HPET_PERIOD);
        if ((hpet_period < HPET_MIN_PERIOD) || (hpet_period > HPET_MAX_PERIOD))
@@ -108,31 +155,10 @@ int __init hpet_enable(void)
        if (hpet_tick_rem > (hpet_period >> 1))
                hpet_tick++; /* rounding the result */
 
-       /*
-        * Stop the timers and reset the main counter.
-        */
-       cfg = hpet_readl(HPET_CFG);
-       cfg &= ~HPET_CFG_ENABLE;
-       hpet_writel(cfg, HPET_CFG);
-       hpet_writel(0, HPET_COUNTER);
-       hpet_writel(0, HPET_COUNTER + 4);
-
-       /*
-        * Set up timer 0, as periodic with first interrupt to happen at
-        * hpet_tick, and period also hpet_tick.
-        */
-       cfg = hpet_readl(HPET_T0_CFG);
-       cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
-              HPET_TN_SETVAL | HPET_TN_32BIT;
-       hpet_writel(cfg, HPET_T0_CFG);
-       hpet_writel(hpet_tick, HPET_T0_CMP);
+       hpet_use_timer = id & HPET_ID_LEGSUP;
 
-       /*
-        * Go!
-        */
-       cfg = hpet_readl(HPET_CFG);
-       cfg |= HPET_CFG_ENABLE | HPET_CFG_LEGACY;
-       hpet_writel(cfg, HPET_CFG);
+       if (hpet_timer_stop_set_go(hpet_tick))
+               return -1;
 
        use_hpet = 1;
 
@@ -151,6 +177,7 @@ int __init hpet_enable(void)
                 * Register with driver.
                 * Timer0 and Timer1 is used by platform.
                 */
+               hd.hd_phys_address = hpet_address;
                hd.hd_address = hpet_virt_address;
                hd.hd_nirqs = ntimer;
                hd.hd_flags = HPET_DATA_PLATFORM;
@@ -180,11 +207,17 @@ int __init hpet_enable(void)
 #endif
 
 #ifdef CONFIG_X86_LOCAL_APIC
-       wait_timer_tick = wait_hpet_tick;
+       if (hpet_use_timer)
+               wait_timer_tick = wait_hpet_tick;
 #endif
        return 0;
 }
 
+int hpet_reenable(void)
+{
+       return hpet_timer_stop_set_go(hpet_tick);
+}
+
 int is_hpet_enabled(void)
 {
        return use_hpet;
@@ -226,8 +259,6 @@ __setup("hpet=", hpet_setup);
 #include <linux/mc146818rtc.h>
 #include <linux/rtc.h>
 
-extern irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-
 #define DEFAULT_RTC_INT_FREQ   64
 #define RTC_NUM_INTS           1
 
@@ -242,6 +273,7 @@ static unsigned long PIE_freq = DEFAULT_RTC_INT_FREQ;
 static unsigned long PIE_count;
 
 static unsigned long hpet_rtc_int_freq; /* RTC interrupt frequency */
+static unsigned int hpet_t1_cmp; /* cached comparator register */
 
 /*
  * Timer 1 for RTC, we do not use periodic interrupt feature,
@@ -273,10 +305,12 @@ int hpet_rtc_timer_init(void)
        cnt = hpet_readl(HPET_COUNTER);
        cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq);
        hpet_writel(cnt, HPET_T1_CMP);
+       hpet_t1_cmp = cnt;
        local_irq_restore(flags);
 
        cfg = hpet_readl(HPET_T1_CFG);
-       cfg |= HPET_TN_ENABLE | HPET_TN_SETVAL | HPET_TN_32BIT;
+       cfg &= ~HPET_TN_PERIODIC;
+       cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
        hpet_writel(cfg, HPET_T1_CFG);
 
        return 1;
@@ -286,8 +320,12 @@ static void hpet_rtc_timer_reinit(void)
 {
        unsigned int cfg, cnt;
 
-       if (!(PIE_on | AIE_on | UIE_on))
+       if (unlikely(!(PIE_on | AIE_on | UIE_on))) {
+               cfg = hpet_readl(HPET_T1_CFG);
+               cfg &= ~HPET_TN_ENABLE;
+               hpet_writel(cfg, HPET_T1_CFG);
                return;
+       }
 
        if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ))
                hpet_rtc_int_freq = PIE_freq;
@@ -295,15 +333,10 @@ static void hpet_rtc_timer_reinit(void)
                hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ;
 
        /* It is more accurate to use the comparator value than current count.*/
-       cnt = hpet_readl(HPET_T1_CMP);
+       cnt = hpet_t1_cmp;
        cnt += hpet_tick*HZ/hpet_rtc_int_freq;
        hpet_writel(cnt, HPET_T1_CMP);
-
-       cfg = hpet_readl(HPET_T1_CFG);
-       cfg |= HPET_TN_ENABLE | HPET_TN_SETVAL | HPET_TN_32BIT;
-       hpet_writel(cfg, HPET_T1_CFG);
-
-       return;
+       hpet_t1_cmp = cnt;
 }
 
 /*