ARM: smp_twd: Avoid recalibrating local timer
authorColin Cross <ccross@android.com>
Thu, 30 Dec 2010 03:51:35 +0000 (19:51 -0800)
committerColin Cross <ccross@android.com>
Tue, 4 Jan 2011 22:55:24 +0000 (14:55 -0800)
Change-Id: I10af3139ecd0dc1ef54e7a8e5258ee6fb29bfb0c
Signed-off-by: Colin Cross <ccross@android.com>
arch/arm/kernel/smp_twd.c

index 014862ae170c22eccfa58be45134cda5c687c56a..97f93496785021acbf0728d0963cf8dcf8388e35 100644 (file)
@@ -26,7 +26,7 @@ void __iomem *twd_base;
 
 static unsigned long twd_timer_rate;
 static unsigned long twd_periphclk_prescaler;
-static unsigned long twd_target_rate;
+static unsigned long twd_cpu_rate;
 
 static void twd_set_mode(enum clock_event_mode mode,
                        struct clock_event_device *clk)
@@ -82,6 +82,12 @@ int twd_timer_ack(void)
        return 0;
 }
 
+/*
+ * Recalculate the twd prescaler value when the cpu frequency changes. To
+ * prevent early timer interrupts, must be called before changing the cpu
+ * frequency if the frequency is increasing, or after if the frequency is
+ * decreasing.
+ */
 void twd_recalc_prescaler(unsigned long new_rate)
 {
        u32 ctrl;
@@ -90,6 +96,8 @@ void twd_recalc_prescaler(unsigned long new_rate)
 
        BUG_ON(twd_periphclk_prescaler == 0 || twd_timer_rate == 0);
 
+       twd_cpu_rate = new_rate;
+
        periphclk_rate = new_rate / twd_periphclk_prescaler;
 
        prescaler = DIV_ROUND_UP(periphclk_rate, twd_timer_rate);
@@ -106,55 +114,60 @@ static void __cpuinit twd_calibrate_rate(unsigned long target_rate,
 {
        unsigned long load, count;
        u64 waitjiffies;
-       unsigned long cpu_rate;
 
        /*
         * If this is the first time round, we need to work out how fast
         * the timer ticks
         */
-       printk(KERN_INFO "Calibrating local timer... ");
+       if (twd_timer_rate == 0) {
+               printk(KERN_INFO "Calibrating local timer... ");
 
-       /* Wait for a tick to start */
-       waitjiffies = get_jiffies_64() + 1;
+               /* Wait for a tick to start */
+               waitjiffies = get_jiffies_64() + 1;
 
-       while (get_jiffies_64() < waitjiffies)
-               udelay(10);
+               while (get_jiffies_64() < waitjiffies)
+                       udelay(10);
 
-       /* OK, now the tick has started, let's get the timer going */
-       waitjiffies += 5;
+               /* OK, now the tick has started, let's get the timer going */
+               waitjiffies += 5;
 
-                        /* enable, no interrupt or reload */
-       __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
+                                /* enable, no interrupt or reload */
+               __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
 
-                        /* maximum value */
-       __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
+                                /* maximum value */
+               __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
 
-       while (get_jiffies_64() < waitjiffies)
-               udelay(10);
+               while (get_jiffies_64() < waitjiffies)
+                       udelay(10);
 
-       count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
+               count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
 
-       twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
+               twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
 
-       /*
-        * If a target rate has been requested, adjust the TWD prescaler
-        * to get the closest lower frequency.
-        */
-       if (target_rate) {
-               twd_periphclk_prescaler = periphclk_prescaler;
-               twd_target_rate = target_rate;
+               /*
+                * If a target rate has been requested, adjust the TWD prescaler
+                * to get the closest lower frequency.
+                */
+               if (target_rate) {
+                       twd_periphclk_prescaler = periphclk_prescaler;
+
+                       printk("%lu.%02luMHz, setting to ",
+                               twd_timer_rate / 1000000,
+                               (twd_timer_rate / 10000) % 100);
+                       twd_cpu_rate = twd_timer_rate * periphclk_prescaler;
+                       twd_timer_rate = target_rate;
+                       twd_recalc_prescaler(twd_cpu_rate);
+               }
 
-               printk("%lu.%02luMHz, setting to ",
-                       twd_timer_rate / 1000000,
+               printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
                        (twd_timer_rate / 10000) % 100);
-               cpu_rate = twd_timer_rate * periphclk_prescaler;
-               twd_timer_rate = twd_target_rate;
-               twd_recalc_prescaler(cpu_rate);
+       } else {
+               if (target_rate) {
+                       BUG_ON(target_rate != twd_timer_rate);
+                       twd_recalc_prescaler(twd_cpu_rate);
+               }
        }
 
-       printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
-               (twd_timer_rate / 10000) % 100);
-
        load = twd_timer_rate / HZ;
 
        __raw_writel(load, twd_base + TWD_TIMER_LOAD);