summaryrefslogtreecommitdiff
path: root/arch/arm/kernel
diff options
context:
space:
mode:
authorErik Gilling <konkers@android.com>2011-01-04 17:51:22 -0800
committerErik Gilling <konkers@android.com>2011-01-04 17:51:27 -0800
commit00756cc5246293c02669eb9c13f2245f3cfa2706 (patch)
tree7a126bb8e717cad0271d13d29eab067e930d3483 /arch/arm/kernel
parent4d86495ef99f6a8de7154794eeb765001658e44c (diff)
parentb4c92f36c0368fe9bc7a780bc6ca9fe9ef199c55 (diff)
Merge branch linux-tegra-2.6.36 into android-tegra-2.6.36
Change-Id: Iec42f1fa234c03c6267f91ba0553d1c651b9ba94
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r--arch/arm/kernel/smp_twd.c110
1 files changed, 76 insertions, 34 deletions
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 014862ae170c..3dc62ee45639 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -17,6 +17,7 @@
#include <linux/clockchips.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/cpufreq.h>
#include <asm/smp_twd.h>
#include <asm/hardware/gic.h>
@@ -26,7 +27,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,7 +83,13 @@ int twd_timer_ack(void)
return 0;
}
-void twd_recalc_prescaler(unsigned long new_rate)
+/*
+ * 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.
+ */
+static void twd_update_prescaler(void *data)
{
u32 ctrl;
int prescaler;
@@ -90,7 +97,7 @@ void twd_recalc_prescaler(unsigned long new_rate)
BUG_ON(twd_periphclk_prescaler == 0 || twd_timer_rate == 0);
- periphclk_rate = new_rate / twd_periphclk_prescaler;
+ periphclk_rate = twd_cpu_rate / twd_periphclk_prescaler;
prescaler = DIV_ROUND_UP(periphclk_rate, twd_timer_rate);
prescaler = clamp(prescaler - 1, 0, 0xFF);
@@ -101,60 +108,95 @@ void twd_recalc_prescaler(unsigned long new_rate)
__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
}
+static int twd_cpufreq_transition(struct notifier_block *nb,
+ unsigned long state, void *data)
+{
+ struct cpufreq_freqs *freqs = data;
+ if (((freqs->new > freqs->old) && state == CPUFREQ_PRECHANGE) ||
+ ((freqs->old > freqs->new) && state == CPUFREQ_POSTCHANGE)) {
+ /* freqs->new is in kHz, twd_cpu_rate is in Hz */
+ twd_cpu_rate = freqs->new * 1000;
+
+ smp_call_function_single(freqs->cpu, twd_update_prescaler,
+ NULL, 1);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block twd_cpufreq_nb = {
+ .notifier_call = twd_cpufreq_transition,
+};
+
+static int twd_cpufreq_init(void)
+{
+ if (twd_cpu_rate)
+ return cpufreq_register_notifier(&twd_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ return 0;
+}
+core_initcall(twd_cpufreq_init);
+
static void __cpuinit twd_calibrate_rate(unsigned long target_rate,
unsigned int periphclk_prescaler)
{
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,
+ 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_update_prescaler(NULL);
+ }
+
+ 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_update_prescaler(NULL);
+ }
}
- 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);