]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - sitara-epos/sitara-epos-kernel.git/blob - drivers/cpufreq/omap-cpufreq.c
ARM: OMAP: AM33XX: PM: Remove voltage scaling from PM code
[sitara-epos/sitara-epos-kernel.git] / drivers / cpufreq / omap-cpufreq.c
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;
233 static inline void freq_table_free(void)
235         if (atomic_dec_and_test(&freq_table_users))
236                 opp_free_cpufreq_table(mpu_dev, &freq_table);
239 static int omap_pm_notify(struct notifier_block *nb, unsigned long event,
240         void *dummy)
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;
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)
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;
335 static int omap_cpu_exit(struct cpufreq_policy *policy)
337         freq_table_free();
338         clk_put(mpu_clk);
339         return 0;
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)
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);
381 static void __exit omap_cpufreq_exit(void)
383         cpufreq_unregister_driver(&omap_driver);
386 MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs");
387 MODULE_LICENSE("GPL");
388 module_init(omap_cpufreq_init);
389 module_exit(omap_cpufreq_exit);