aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2011-09-21 19:53:00 -0400
committerKevin Hilman <khilman@ti.com>2011-11-08 14:42:16 -0500
commit46c12216c81b470b957d7fdefd8630efc2edddd0 (patch)
tree8d0431c6cc43bd3d63759684cd215a6b02dca49b /drivers/cpufreq
parent731e0cc639364646d36981d90ab0b6af12b8face (diff)
cpufreq: OMAP: Add SMP support for OMAP4+
On OMAP SMP configuartion, both processors share the voltage and clock. So both CPUs needs to be scaled together and hence needs software co-ordination. Also, update lpj with reference value to avoid progressive error. Adjust _both_ the per-cpu loops_per_jiffy and global lpj. Calibrate them with with reference to the initial values to avoid a progressively bigger and bigger error in the value over time. While at this, re-use the notifiers for UP/SMP since on UP machine or UP_ON_SMP policy->cpus mask would contain only the boot CPU. Based on initial SMP support by Santosh Shilimkar. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> [khilman@ti.com: due to overlap/rework, combined original Santosh patch and Russell's rework] Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/omap-cpufreq.c81
1 files changed, 71 insertions, 10 deletions
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index a6b2be7ea5a9..1953f9d082ad 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -23,9 +23,11 @@
23#include <linux/clk.h> 23#include <linux/clk.h>
24#include <linux/io.h> 24#include <linux/io.h>
25#include <linux/opp.h> 25#include <linux/opp.h>
26#include <linux/cpu.h>
26 27
27#include <asm/system.h> 28#include <asm/system.h>
28#include <asm/smp_plat.h> 29#include <asm/smp_plat.h>
30#include <asm/cpu.h>
29 31
30#include <plat/clock.h> 32#include <plat/clock.h>
31#include <plat/omap-pm.h> 33#include <plat/omap-pm.h>
@@ -35,6 +37,16 @@
35 37
36#define VERY_HI_RATE 900000000 38#define VERY_HI_RATE 900000000
37 39
40#ifdef CONFIG_SMP
41struct lpj_info {
42 unsigned long ref;
43 unsigned int freq;
44};
45
46static DEFINE_PER_CPU(struct lpj_info, lpj_ref);
47static struct lpj_info global_lpj_ref;
48#endif
49
38static struct cpufreq_frequency_table *freq_table; 50static struct cpufreq_frequency_table *freq_table;
39static struct clk *mpu_clk; 51static struct clk *mpu_clk;
40 52
@@ -60,7 +72,7 @@ static unsigned int omap_getspeed(unsigned int cpu)
60{ 72{
61 unsigned long rate; 73 unsigned long rate;
62 74
63 if (cpu) 75 if (cpu >= NR_CPUS)
64 return 0; 76 return 0;
65 77
66 rate = clk_get_rate(mpu_clk) / 1000; 78 rate = clk_get_rate(mpu_clk) / 1000;
@@ -71,7 +83,7 @@ static int omap_target(struct cpufreq_policy *policy,
71 unsigned int target_freq, 83 unsigned int target_freq,
72 unsigned int relation) 84 unsigned int relation)
73{ 85{
74 int ret = 0; 86 int i, ret = 0;
75 struct cpufreq_freqs freqs; 87 struct cpufreq_freqs freqs;
76 88
77 /* Ensure desired rate is within allowed range. Some govenors 89 /* Ensure desired rate is within allowed range. Some govenors
@@ -81,22 +93,57 @@ static int omap_target(struct cpufreq_policy *policy,
81 if (target_freq > policy->max) 93 if (target_freq > policy->max)
82 target_freq = policy->max; 94 target_freq = policy->max;
83 95
84 freqs.old = omap_getspeed(0); 96 freqs.old = omap_getspeed(policy->cpu);
85 freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; 97 freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000;
86 freqs.cpu = 0; 98 freqs.cpu = policy->cpu;
87 99
88 if (freqs.old == freqs.new) 100 if (freqs.old == freqs.new)
89 return ret; 101 return ret;
90 102
91 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 103 /* notifiers */
104 for_each_cpu(i, policy->cpus) {
105 freqs.cpu = i;
106 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
107 }
92 108
93#ifdef CONFIG_CPU_FREQ_DEBUG 109#ifdef CONFIG_CPU_FREQ_DEBUG
94 pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); 110 pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new);
95#endif 111#endif
96 112
97 ret = clk_set_rate(mpu_clk, freqs.new * 1000); 113 ret = clk_set_rate(mpu_clk, freqs.new * 1000);
114 freqs.new = omap_getspeed(policy->cpu);
115
116#ifdef CONFIG_SMP
117 /*
118 * Note that loops_per_jiffy is not updated on SMP systems in
119 * cpufreq driver. So, update the per-CPU loops_per_jiffy value
120 * on frequency transition. We need to update all dependent CPUs.
121 */
122 for_each_cpu(i, policy->cpus) {
123 struct lpj_info *lpj = &per_cpu(lpj_ref, i);
124 if (!lpj->freq) {
125 lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy;
126 lpj->freq = freqs.old;
127 }
128
129 per_cpu(cpu_data, i).loops_per_jiffy =
130 cpufreq_scale(lpj->ref, lpj->freq, freqs.new);
131 }
98 132
99 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 133 /* And don't forget to adjust the global one */
134 if (!global_lpj_ref.freq) {
135 global_lpj_ref.ref = loops_per_jiffy;
136 global_lpj_ref.freq = freqs.old;
137 }
138 loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, global_lpj_ref.freq,
139 freqs.new);
140#endif
141
142 /* notifiers */
143 for_each_cpu(i, policy->cpus) {
144 freqs.cpu = i;
145 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
146 }
100 147
101 return ret; 148 return ret;
102} 149}
@@ -105,6 +152,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
105{ 152{
106 int result = 0; 153 int result = 0;
107 struct device *mpu_dev; 154 struct device *mpu_dev;
155 static cpumask_var_t cpumask;
108 156
109 if (cpu_is_omap24xx()) 157 if (cpu_is_omap24xx())
110 mpu_clk = clk_get(NULL, "virt_prcm_set"); 158 mpu_clk = clk_get(NULL, "virt_prcm_set");
@@ -116,12 +164,12 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
116 if (IS_ERR(mpu_clk)) 164 if (IS_ERR(mpu_clk))
117 return PTR_ERR(mpu_clk); 165 return PTR_ERR(mpu_clk);
118 166
119 if (policy->cpu != 0) 167 if (policy->cpu >= NR_CPUS)
120 return -EINVAL; 168 return -EINVAL;
121 169
122 policy->cur = policy->min = policy->max = omap_getspeed(0); 170 policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
123
124 mpu_dev = omap2_get_mpuss_device(); 171 mpu_dev = omap2_get_mpuss_device();
172
125 if (!mpu_dev) { 173 if (!mpu_dev) {
126 pr_warning("%s: unable to get the mpu device\n", __func__); 174 pr_warning("%s: unable to get the mpu device\n", __func__);
127 return -EINVAL; 175 return -EINVAL;
@@ -141,7 +189,20 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
141 189
142 policy->min = policy->cpuinfo.min_freq; 190 policy->min = policy->cpuinfo.min_freq;
143 policy->max = policy->cpuinfo.max_freq; 191 policy->max = policy->cpuinfo.max_freq;
144 policy->cur = omap_getspeed(0); 192 policy->cur = omap_getspeed(policy->cpu);
193
194 /*
195 * On OMAP SMP configuartion, both processors share the voltage
196 * and clock. So both CPUs needs to be scaled together and hence
197 * needs software co-ordination. Use cpufreq affected_cpus
198 * interface to handle this scenario. Additional is_smp() check
199 * is to keep SMP_ON_UP build working.
200 */
201 if (is_smp()) {
202 policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
203 cpumask_or(cpumask, cpumask_of(policy->cpu), cpumask);
204 cpumask_copy(policy->cpus, cpumask);
205 }
145 206
146 /* FIXME: what's the actual transition time? */ 207 /* FIXME: what's the actual transition time? */
147 policy->cpuinfo.transition_latency = 300 * 1000; 208 policy->cpuinfo.transition_latency = 300 * 1000;