1 /*
2 * CPU frequency scaling for OMAP using OPP information
3 *
4 * Copyright (C) 2005 Nokia Corporation
5 * Written by Tony Lindgren <tony@atomide.com>
6 *
7 * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
8 *
9 * Copyright (C) 2007-2011 Texas Instruments, Inc.
10 * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15 */
16 #include <linux/types.h>
17 #include <linux/kernel.h>
18 #include <linux/sched.h>
19 #include <linux/cpufreq.h>
20 #include <linux/delay.h>
21 #include <linux/init.h>
22 #include <linux/err.h>
23 #include <linux/clk.h>
24 #include <linux/io.h>
25 #include <linux/opp.h>
26 #include <linux/cpu.h>
27 #include <linux/module.h>
28 #include <linux/regulator/consumer.h>
29 #include <linux/suspend.h>
31 #include <asm/system.h>
32 #include <asm/smp_plat.h>
33 #include <asm/cpu.h>
35 #include <plat/clock.h>
36 #include <plat/omap-pm.h>
37 #include <plat/common.h>
38 #include <plat/omap_device.h>
40 #include <mach/hardware.h>
42 /* Tolerance for MPU voltage is 4%, we have to pass +4% as a
43 * maximum voltage while setting the MPU regulator voltage.
44 * Which is taken from AM33XX datasheet */
45 #define MPU_TOLERANCE 4
46 #define PER_ROUND_VAL 100
48 #ifdef CONFIG_SMP
49 struct lpj_info {
50 unsigned long ref;
51 unsigned int freq;
52 };
54 static DEFINE_PER_CPU(struct lpj_info, lpj_ref);
55 static struct lpj_info global_lpj_ref;
56 #endif
58 static struct cpufreq_frequency_table *freq_table;
59 static atomic_t freq_table_users = ATOMIC_INIT(0);
60 static struct clk *mpu_clk;
61 static char *mpu_clk_name;
62 static struct device *mpu_dev;
63 static struct regulator *mpu_reg;
64 static DEFINE_MUTEX(omap_cpu_lock);
65 static bool is_suspended;
67 static int omap_verify_speed(struct cpufreq_policy *policy)
68 {
69 if (!freq_table)
70 return -EINVAL;
71 return cpufreq_frequency_table_verify(policy, freq_table);
72 }
74 static unsigned int omap_getspeed(unsigned int cpu)
75 {
76 unsigned long rate;
78 if (cpu >= NR_CPUS)
79 return 0;
81 rate = clk_get_rate(mpu_clk) / 1000;
82 return rate;
83 }
85 static int omap_target(struct cpufreq_policy *policy,
86 unsigned int target_freq,
87 unsigned int relation)
88 {
89 unsigned int i;
90 int ret = 0;
91 struct cpufreq_freqs freqs;
92 struct opp *opp;
93 int volt_old = 0, volt_new = 0;
95 if (is_suspended)
96 return -EBUSY;
98 if (!freq_table) {
99 dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__,
100 policy->cpu);
101 return -EINVAL;
102 }
104 ret = cpufreq_frequency_table_target(policy, freq_table, target_freq,
105 relation, &i);
106 if (ret) {
107 dev_dbg(mpu_dev, "%s: cpu%d: no freq match for %d(ret=%d)\n",
108 __func__, policy->cpu, target_freq, ret);
109 return ret;
110 }
111 freqs.new = freq_table[i].frequency;
112 if (!freqs.new) {
113 dev_err(mpu_dev, "%s: cpu%d: no match for freq %d\n", __func__,
114 policy->cpu, target_freq);
115 return -EINVAL;
116 }
118 freqs.old = omap_getspeed(policy->cpu);
119 freqs.cpu = policy->cpu;
121 if (freqs.old == freqs.new && policy->cur == freqs.new)
122 return ret;
124 opp = opp_find_freq_exact(mpu_dev, freqs.new * 1000, true);
125 if (IS_ERR(opp)) {
126 dev_err(mpu_dev, "%s: cpu%d: no opp match for freq %d\n",
127 __func__, policy->cpu, target_freq);
128 return -EINVAL;
129 }
131 volt_new = opp_get_voltage(opp);
132 if (!volt_new) {
133 dev_err(mpu_dev, "%s: cpu%d: no opp voltage for freq %d\n",
134 __func__, policy->cpu, target_freq);
135 return -EINVAL;
136 }
138 volt_old = regulator_get_voltage(mpu_reg);
140 #ifdef CONFIG_CPU_FREQ_DEBUG
141 pr_info("cpufreq-omap: frequency transition: %u --> %u\n",
142 freqs.old, freqs.new);
143 pr_info("cpufreq-omap: voltage transition: %d --> %d\n",
144 volt_old, volt_new);
145 #endif
147 if (freqs.new > freqs.old) {
148 ret = regulator_set_voltage(mpu_reg, volt_new,
149 volt_new + (volt_new * MPU_TOLERANCE) / PER_ROUND_VAL);
150 if (ret) {
151 dev_err(mpu_dev, "%s: unable to set voltage to %d uV (for %u MHz)\n",
152 __func__, volt_new, freqs.new/1000);
153 return ret;
154 }
155 }
157 /* notifiers */
158 for_each_cpu(i, policy->cpus) {
159 freqs.cpu = i;
160 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
161 }
163 ret = clk_set_rate(mpu_clk, freqs.new * 1000);
164 freqs.new = omap_getspeed(policy->cpu);
166 #ifdef CONFIG_SMP
167 /*
168 * Note that loops_per_jiffy is not updated on SMP systems in
169 * cpufreq driver. So, update the per-CPU loops_per_jiffy value
170 * on frequency transition. We need to update all dependent CPUs.
171 */
172 for_each_cpu(i, policy->cpus) {
173 struct lpj_info *lpj = &per_cpu(lpj_ref, i);
174 if (!lpj->freq) {
175 lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy;
176 lpj->freq = freqs.old;
177 }
179 per_cpu(cpu_data, i).loops_per_jiffy =
180 cpufreq_scale(lpj->ref, lpj->freq, freqs.new);
181 }
183 /* And don't forget to adjust the global one */
184 if (!global_lpj_ref.freq) {
185 global_lpj_ref.ref = loops_per_jiffy;
186 global_lpj_ref.freq = freqs.old;
187 }
188 loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, global_lpj_ref.freq,
189 freqs.new);
190 #endif
192 /* notifiers */
193 for_each_cpu(i, policy->cpus) {
194 freqs.cpu = i;
195 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
196 }
198 if (freqs.new < freqs.old) {
199 ret = regulator_set_voltage(mpu_reg, volt_new,
200 volt_new + (volt_new * MPU_TOLERANCE) / PER_ROUND_VAL);
201 if (ret) {
202 unsigned int temp;
204 dev_err(mpu_dev, "%s: unable to set voltage to %d uV (for %u MHz)\n",
205 __func__, volt_new, freqs.new/1000);
207 if (clk_set_rate(mpu_clk, freqs.old * 1000)) {
208 dev_err(mpu_dev,
209 "%s: failed restoring clock rate to %u MHz, clock rate is %u MHz",
210 __func__,
211 freqs.old/1000, freqs.new/1000);
212 return ret;
213 }
215 temp = freqs.new;
216 freqs.new = freqs.old;
217 freqs.old = temp;
219 for_each_cpu(i, policy->cpus) {
220 freqs.cpu = i;
221 cpufreq_notify_transition(&freqs,
222 CPUFREQ_PRECHANGE);
223 cpufreq_notify_transition(&freqs,
224 CPUFREQ_POSTCHANGE);
225 }
226 return ret;
227 }
228 }
230 return ret;
231 }
233 static inline void freq_table_free(void)
234 {
235 if (atomic_dec_and_test(&freq_table_users))
236 opp_free_cpufreq_table(mpu_dev, &freq_table);
237 }
239 static int omap_pm_notify(struct notifier_block *nb, unsigned long event,
240 void *dummy)
241 {
242 mutex_lock(&omap_cpu_lock);
243 if (event == PM_SUSPEND_PREPARE) {
244 is_suspended = true;
245 } else if (event == PM_POST_SUSPEND) {
246 is_suspended = false;
247 }
248 mutex_unlock(&omap_cpu_lock);
250 return NOTIFY_OK;
251 }
253 static struct notifier_block omap_cpu_pm_notifier = {
254 .notifier_call = omap_pm_notify,
255 };
257 static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
258 {
259 int result = 0;
261 mpu_clk = clk_get(NULL, mpu_clk_name);
262 if (IS_ERR(mpu_clk))
263 return PTR_ERR(mpu_clk);
265 mpu_reg = regulator_get(NULL, "vdd_mpu");
266 if (IS_ERR(mpu_reg)) {
267 result = -EINVAL;
268 goto fail_ck;
269 }
271 /* success of regulator_get doesn't gurantee presence of driver for
272 physical regulator and presence of physical regulator (this
273 situation arises if dummy regulator is enabled),so check voltage
274 to verify that physical regulator and it's driver is present
275 */
276 if (regulator_get_voltage(mpu_reg) < 0) {
277 result = -EINVAL;
278 goto fail_reg;
279 }
281 if (policy->cpu >= NR_CPUS) {
282 result = -EINVAL;
283 goto fail_reg;
284 }
286 policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
288 if (atomic_inc_return(&freq_table_users) == 1)
289 result = opp_init_cpufreq_table(mpu_dev, &freq_table);
291 if (result) {
292 dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n",
293 __func__, policy->cpu, result);
294 goto fail_reg;
295 }
297 result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
298 if (result)
299 goto fail_table;
301 cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
303 policy->min = policy->cpuinfo.min_freq;
304 policy->max = policy->cpuinfo.max_freq;
305 policy->cur = omap_getspeed(policy->cpu);
307 /*
308 * On OMAP SMP configuartion, both processors share the voltage
309 * and clock. So both CPUs needs to be scaled together and hence
310 * needs software co-ordination. Use cpufreq affected_cpus
311 * interface to handle this scenario. Additional is_smp() check
312 * is to keep SMP_ON_UP build working.
313 */
314 if (is_smp()) {
315 policy->shared_type = CPUFREQ_SHARED_TYPE_ANY;
316 cpumask_setall(policy->cpus);
317 }
319 /* FIXME: what's the actual transition time? */
320 policy->cpuinfo.transition_latency = 300 * 1000;
322 register_pm_notifier(&omap_cpu_pm_notifier);
324 return 0;
326 fail_table:
327 freq_table_free();
328 fail_reg:
329 regulator_put(mpu_reg);
330 fail_ck:
331 clk_put(mpu_clk);
332 return result;
333 }
335 static int omap_cpu_exit(struct cpufreq_policy *policy)
336 {
337 freq_table_free();
338 clk_put(mpu_clk);
339 return 0;
340 }
342 static struct freq_attr *omap_cpufreq_attr[] = {
343 &cpufreq_freq_attr_scaling_available_freqs,
344 NULL,
345 };
347 static struct cpufreq_driver omap_driver = {
348 .flags = CPUFREQ_STICKY,
349 .verify = omap_verify_speed,
350 .target = omap_target,
351 .get = omap_getspeed,
352 .init = omap_cpu_init,
353 .exit = omap_cpu_exit,
354 .name = "omap",
355 .attr = omap_cpufreq_attr,
356 };
358 static int __init omap_cpufreq_init(void)
359 {
360 if (cpu_is_omap24xx())
361 mpu_clk_name = "virt_prcm_set";
362 else if (cpu_is_omap34xx() && !cpu_is_am33xx())
363 mpu_clk_name = "dpll1_ck";
364 else if (cpu_is_omap44xx() || cpu_is_am33xx())
365 mpu_clk_name = "dpll_mpu_ck";
367 if (!mpu_clk_name) {
368 pr_err("%s: unsupported Silicon?\n", __func__);
369 return -EINVAL;
370 }
372 mpu_dev = omap_device_get_by_hwmod_name("mpu");
373 if (!mpu_dev) {
374 pr_warning("%s: unable to get the mpu device\n", __func__);
375 return -EINVAL;
376 }
378 return cpufreq_register_driver(&omap_driver);
379 }
381 static void __exit omap_cpufreq_exit(void)
382 {
383 cpufreq_unregister_driver(&omap_driver);
384 }
386 MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs");
387 MODULE_LICENSE("GPL");
388 module_init(omap_cpufreq_init);
389 module_exit(omap_cpufreq_exit);