summaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/cpufreq.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2012-04-14 13:18:27 +0200
committerIngo Molnar <mingo@kernel.org>2012-04-14 13:19:04 +0200
commit6ac1ef482d7ae0c690f1640bf6eb818ff9a2d91e (patch)
tree021cc9f6b477146fcebe6f3be4752abfa2ba18a9 /arch/sh/kernel/cpufreq.c
parent682968e0c425c60f0dde37977e5beb2b12ddc4cc (diff)
parenta385ec4f11bdcf81af094c03e2444ee9b7fad2e5 (diff)
Merge branch 'perf/core' into perf/uprobes
Merge in latest upstream (and the latest perf development tree), to prepare for tooling changes, and also to pick up v3.4 MM changes that the uprobes code needs to take care of. Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/sh/kernel/cpufreq.c')
-rw-r--r--arch/sh/kernel/cpufreq.c121
1 files changed, 82 insertions, 39 deletions
diff --git a/arch/sh/kernel/cpufreq.c b/arch/sh/kernel/cpufreq.c
index 0fffacea6ed..e68b45b6f3f 100644
--- a/arch/sh/kernel/cpufreq.c
+++ b/arch/sh/kernel/cpufreq.c
@@ -3,7 +3,7 @@
*
* cpufreq driver for the SuperH processors.
*
- * Copyright (C) 2002 - 2007 Paul Mundt
+ * Copyright (C) 2002 - 2012 Paul Mundt
* Copyright (C) 2002 M. R. Brown
*
* Clock framework bits from arch/avr32/mach-at32ap/cpufreq.c
@@ -14,6 +14,8 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
+#define pr_fmt(fmt) "cpufreq: " fmt
+
#include <linux/types.h>
#include <linux/cpufreq.h>
#include <linux/kernel.h>
@@ -21,15 +23,18 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/cpumask.h>
+#include <linux/cpu.h>
#include <linux/smp.h>
#include <linux/sched.h> /* set_cpus_allowed() */
#include <linux/clk.h>
+#include <linux/percpu.h>
+#include <linux/sh_clk.h>
-static struct clk *cpuclk;
+static DEFINE_PER_CPU(struct clk, sh_cpuclk);
static unsigned int sh_cpufreq_get(unsigned int cpu)
{
- return (clk_get_rate(cpuclk) + 500) / 1000;
+ return (clk_get_rate(&per_cpu(sh_cpuclk, cpu)) + 500) / 1000;
}
/*
@@ -40,8 +45,10 @@ static int sh_cpufreq_target(struct cpufreq_policy *policy,
unsigned int relation)
{
unsigned int cpu = policy->cpu;
+ struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
cpumask_t cpus_allowed;
struct cpufreq_freqs freqs;
+ struct device *dev;
long freq;
if (!cpu_online(cpu))
@@ -52,13 +59,15 @@ static int sh_cpufreq_target(struct cpufreq_policy *policy,
BUG_ON(smp_processor_id() != cpu);
+ dev = get_cpu_device(cpu);
+
/* Convert target_freq from kHz to Hz */
freq = clk_round_rate(cpuclk, target_freq * 1000);
if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
return -EINVAL;
- pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000);
+ dev_dbg(dev, "requested frequency %u Hz\n", target_freq * 1000);
freqs.cpu = cpu;
freqs.old = sh_cpufreq_get(cpu);
@@ -70,78 +79,112 @@ static int sh_cpufreq_target(struct cpufreq_policy *policy,
clk_set_rate(cpuclk, freq);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- pr_debug("cpufreq: set frequency %lu Hz\n", freq);
+ dev_dbg(dev, "set frequency %lu Hz\n", freq);
+
+ return 0;
+}
+
+static int sh_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ struct clk *cpuclk = &per_cpu(sh_cpuclk, policy->cpu);
+ struct cpufreq_frequency_table *freq_table;
+
+ freq_table = cpuclk->nr_freqs ? cpuclk->freq_table : NULL;
+ if (freq_table)
+ return cpufreq_frequency_table_verify(policy, freq_table);
+
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+
+ policy->min = (clk_round_rate(cpuclk, 1) + 500) / 1000;
+ policy->max = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
+
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
return 0;
}
static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
- if (!cpu_online(policy->cpu))
+ unsigned int cpu = policy->cpu;
+ struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
+ struct cpufreq_frequency_table *freq_table;
+ struct device *dev;
+
+ if (!cpu_online(cpu))
return -ENODEV;
- cpuclk = clk_get(NULL, "cpu_clk");
+ dev = get_cpu_device(cpu);
+
+ cpuclk = clk_get(dev, "cpu_clk");
if (IS_ERR(cpuclk)) {
- printk(KERN_ERR "cpufreq: couldn't get CPU#%d clk\n",
- policy->cpu);
+ dev_err(dev, "couldn't get CPU clk\n");
return PTR_ERR(cpuclk);
}
- /* cpuinfo and default policy values */
- policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000;
- policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
- policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+ policy->cur = policy->min = policy->max = sh_cpufreq_get(cpu);
- policy->cur = sh_cpufreq_get(policy->cpu);
- policy->min = policy->cpuinfo.min_freq;
- policy->max = policy->cpuinfo.max_freq;
+ freq_table = cpuclk->nr_freqs ? cpuclk->freq_table : NULL;
+ if (freq_table) {
+ int result;
- /*
- * Catch the cases where the clock framework hasn't been wired up
- * properly to support scaling.
- */
- if (unlikely(policy->min == policy->max)) {
- printk(KERN_ERR "cpufreq: clock framework rate rounding "
- "not supported on CPU#%d.\n", policy->cpu);
+ result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+ if (!result)
+ cpufreq_frequency_table_get_attr(freq_table, cpu);
+ } else {
+ dev_notice(dev, "no frequency table found, falling back "
+ "to rate rounding.\n");
- clk_put(cpuclk);
- return -EINVAL;
+ policy->cpuinfo.min_freq =
+ (clk_round_rate(cpuclk, 1) + 500) / 1000;
+ policy->cpuinfo.max_freq =
+ (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
}
- printk(KERN_INFO "cpufreq: CPU#%d Frequencies - Minimum %u.%03u MHz, "
+ policy->min = policy->cpuinfo.min_freq;
+ policy->max = policy->cpuinfo.max_freq;
+
+ policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
+
+ dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, "
"Maximum %u.%03u MHz.\n",
- policy->cpu, policy->min / 1000, policy->min % 1000,
+ policy->min / 1000, policy->min % 1000,
policy->max / 1000, policy->max % 1000);
return 0;
}
-static int sh_cpufreq_verify(struct cpufreq_policy *policy)
+static int sh_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
- return 0;
-}
+ unsigned int cpu = policy->cpu;
+ struct clk *cpuclk = &per_cpu(sh_cpuclk, cpu);
-static int sh_cpufreq_exit(struct cpufreq_policy *policy)
-{
+ cpufreq_frequency_table_put_attr(cpu);
clk_put(cpuclk);
+
return 0;
}
+static struct freq_attr *sh_freq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
static struct cpufreq_driver sh_cpufreq_driver = {
.owner = THIS_MODULE,
.name = "sh",
- .init = sh_cpufreq_cpu_init,
- .verify = sh_cpufreq_verify,
- .target = sh_cpufreq_target,
.get = sh_cpufreq_get,
- .exit = sh_cpufreq_exit,
+ .target = sh_cpufreq_target,
+ .verify = sh_cpufreq_verify,
+ .init = sh_cpufreq_cpu_init,
+ .exit = sh_cpufreq_cpu_exit,
+ .attr = sh_freq_attr,
};
static int __init sh_cpufreq_module_init(void)
{
- printk(KERN_INFO "cpufreq: SuperH CPU frequency driver.\n");
+ pr_notice("SuperH CPU frequency driver.\n");
return cpufreq_register_driver(&sh_cpufreq_driver);
}