diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-09-21 19:53:00 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@ti.com> | 2011-11-08 14:42:16 -0500 |
commit | 46c12216c81b470b957d7fdefd8630efc2edddd0 (patch) | |
tree | 8d0431c6cc43bd3d63759684cd215a6b02dca49b /drivers/cpufreq | |
parent | 731e0cc639364646d36981d90ab0b6af12b8face (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.c | 81 |
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 | ||
41 | struct lpj_info { | ||
42 | unsigned long ref; | ||
43 | unsigned int freq; | ||
44 | }; | ||
45 | |||
46 | static DEFINE_PER_CPU(struct lpj_info, lpj_ref); | ||
47 | static struct lpj_info global_lpj_ref; | ||
48 | #endif | ||
49 | |||
38 | static struct cpufreq_frequency_table *freq_table; | 50 | static struct cpufreq_frequency_table *freq_table; |
39 | static struct clk *mpu_clk; | 51 | static 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; |