Commit 7005cf03b87f06df282ccb1ce6dd4b76c88e1369

Authored by Russell King
Committed by Afzal Mohammed
1 parent c0fe5f278d
Exists in master

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>
[vaibhav.bedia@ti.com: Pull in for AM33xx]
Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>

Showing 1 changed file with 71 additions and 10 deletions Side-by-side Diff

drivers/cpufreq/omap-cpufreq.c
... ... @@ -23,9 +23,11 @@
23 23 #include <linux/clk.h>
24 24 #include <linux/io.h>
25 25 #include <linux/opp.h>
  26 +#include <linux/cpu.h>
26 27  
27 28 #include <asm/system.h>
28 29 #include <asm/smp_plat.h>
  30 +#include <asm/cpu.h>
29 31  
30 32 #include <plat/clock.h>
31 33 #include <plat/omap-pm.h>
... ... @@ -35,6 +37,16 @@
35 37  
36 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 50 static struct cpufreq_frequency_table *freq_table;
39 51 static struct clk *mpu_clk;
40 52  
... ... @@ -60,7 +72,7 @@
60 72 {
61 73 unsigned long rate;
62 74  
63   - if (cpu)
  75 + if (cpu >= NR_CPUS)
64 76 return 0;
65 77  
66 78 rate = clk_get_rate(mpu_clk) / 1000;
... ... @@ -71,7 +83,7 @@
71 83 unsigned int target_freq,
72 84 unsigned int relation)
73 85 {
74   - int ret = 0;
  86 + int i, ret = 0;
75 87 struct cpufreq_freqs freqs;
76 88  
77 89 /* Ensure desired rate is within allowed range. Some govenors
78 90  
79 91  
80 92  
81 93  
82 94  
... ... @@ -81,23 +93,58 @@
81 93 if (target_freq > policy->max)
82 94 target_freq = policy->max;
83 95  
84   - freqs.old = omap_getspeed(0);
  96 + freqs.old = omap_getspeed(policy->cpu);
85 97 freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000;
86   - freqs.cpu = 0;
  98 + freqs.cpu = policy->cpu;
87 99  
88 100 if (freqs.old == freqs.new)
89 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 109 #ifdef CONFIG_CPU_FREQ_DEBUG
94 110 pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new);
95 111 #endif
96 112  
97 113 ret = clk_set_rate(mpu_clk, freqs.new * 1000);
  114 + freqs.new = omap_getspeed(policy->cpu);
98 115  
99   - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  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 + }
100 128  
  129 + per_cpu(cpu_data, i).loops_per_jiffy =
  130 + cpufreq_scale(lpj->ref, lpj->freq, freqs.new);
  131 + }
  132 +
  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 + }
  147 +
101 148 return ret;
102 149 }
103 150  
... ... @@ -105,6 +152,7 @@
105 152 {
106 153 int result = 0;
107 154 struct device *mpu_dev;
  155 + static cpumask_var_t cpumask;
108 156  
109 157 if (cpu_is_omap24xx())
110 158 mpu_clk = clk_get(NULL, "virt_prcm_set");
111 159  
112 160  
... ... @@ -116,12 +164,12 @@
116 164 if (IS_ERR(mpu_clk))
117 165 return PTR_ERR(mpu_clk);
118 166  
119   - if (policy->cpu != 0)
  167 + if (policy->cpu >= NR_CPUS)
120 168 return -EINVAL;
121 169  
122   - policy->cur = policy->min = policy->max = omap_getspeed(0);
123   -
  170 + policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
124 171 mpu_dev = omap2_get_mpuss_device();
  172 +
125 173 if (!mpu_dev) {
126 174 pr_warning("%s: unable to get the mpu device\n", __func__);
127 175 return -EINVAL;
... ... @@ -141,7 +189,20 @@
141 189  
142 190 policy->min = policy->cpuinfo.min_freq;
143 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 207 /* FIXME: what's the actual transition time? */
147 208 policy->cpuinfo.transition_latency = 300 * 1000;