summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Zhao <richard.zhao@linaro.org>2012-03-09 10:41:52 +0000
committerJon Medhurst <tixy@linaro.org>2012-07-19 15:45:00 +0100
commit527ccf55a87d6515d49ad2505a8e51f5f1a0479d (patch)
tree4bc2b05ba55b58c7aa25765c3bc2bc164bca392d
parentb09bd92f3b0727c46aabe1e41260ae4fb87ee0b9 (diff)
downloadvexpress-a9-527ccf55a87d6515d49ad2505a8e51f5f1a0479d.tar.gz
ARM: add cpufreq transiton notifier to adjust loops_per_jiffy for smp
If CONFIG_SMP, cpufreq skips loops_per_jiffy update, because different arch has different per-cpu loops_per_jiffy definition. Signed-off-by: Richard Zhao <richard.zhao@linaro.org> Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> Notes by Sudeep KarkadaNagesha <Sudeep.KarkadaNagesha@arm.com>: Even though this patch is accepted by Russell, its not in the mainline kernel yet. However this can be removed if Will Deacon's "Use architected timers for delay loop" is accepted. http://www.spinics.net/lists/arm-kernel/msg180836.html
-rw-r--r--arch/arm/kernel/smp.c54
1 files changed, 54 insertions, 0 deletions
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 853f4e94a20..d02413f6cdf 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -25,6 +25,7 @@
#include <linux/percpu.h>
#include <linux/clockchips.h>
#include <linux/completion.h>
+#include <linux/cpufreq.h>
#include <linux/atomic.h>
#include <asm/cacheflush.h>
@@ -583,3 +584,56 @@ int setup_profiling_timer(unsigned int multiplier)
{
return -EINVAL;
}
+
+#ifdef CONFIG_CPU_FREQ
+
+static DEFINE_PER_CPU(unsigned long, l_p_j_ref);
+static DEFINE_PER_CPU(unsigned long, l_p_j_ref_freq);
+static unsigned long global_l_p_j_ref;
+static unsigned long global_l_p_j_ref_freq;
+
+static int cpufreq_callback(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct cpufreq_freqs *freq = data;
+ int cpu = freq->cpu;
+
+ if (freq->flags & CPUFREQ_CONST_LOOPS)
+ return NOTIFY_OK;
+
+ if (!per_cpu(l_p_j_ref, cpu)) {
+ per_cpu(l_p_j_ref, cpu) =
+ per_cpu(cpu_data, cpu).loops_per_jiffy;
+ per_cpu(l_p_j_ref_freq, cpu) = freq->old;
+ if (!global_l_p_j_ref) {
+ global_l_p_j_ref = loops_per_jiffy;
+ global_l_p_j_ref_freq = freq->old;
+ }
+ }
+
+ if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) ||
+ (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||
+ (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
+ loops_per_jiffy = cpufreq_scale(global_l_p_j_ref,
+ global_l_p_j_ref_freq,
+ freq->new);
+ per_cpu(cpu_data, cpu).loops_per_jiffy =
+ cpufreq_scale(per_cpu(l_p_j_ref, cpu),
+ per_cpu(l_p_j_ref_freq, cpu),
+ freq->new);
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_notifier = {
+ .notifier_call = cpufreq_callback,
+};
+
+static int __init register_cpufreq_notifier(void)
+{
+ return cpufreq_register_notifier(&cpufreq_notifier,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+core_initcall(register_cpufreq_notifier);
+
+#endif