]> rtime.felk.cvut.cz Git - linux-imx.git/blob - drivers/cpufreq/exynos-cpufreq.c
78057a357ddba9cc9b2a94272ac25ac42855d370
[linux-imx.git] / drivers / cpufreq / exynos-cpufreq.c
1 /*
2  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
3  *              http://www.samsung.com
4  *
5  * EXYNOS - CPU frequency scaling support for EXYNOS series
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10 */
11
12 #include <linux/kernel.h>
13 #include <linux/err.h>
14 #include <linux/clk.h>
15 #include <linux/io.h>
16 #include <linux/slab.h>
17 #include <linux/regulator/consumer.h>
18 #include <linux/cpufreq.h>
19 #include <linux/suspend.h>
20
21 #include <plat/cpu.h>
22
23 #include "exynos-cpufreq.h"
24
25 static struct exynos_dvfs_info *exynos_info;
26
27 static struct regulator *arm_regulator;
28 static struct cpufreq_freqs freqs;
29
30 static unsigned int locking_frequency;
31 static bool frequency_locked;
32 static DEFINE_MUTEX(cpufreq_lock);
33
34 static int exynos_verify_speed(struct cpufreq_policy *policy)
35 {
36         return cpufreq_frequency_table_verify(policy,
37                                               exynos_info->freq_table);
38 }
39
40 static unsigned int exynos_getspeed(unsigned int cpu)
41 {
42         return clk_get_rate(exynos_info->cpu_clk) / 1000;
43 }
44
45 static int exynos_cpufreq_get_index(unsigned int freq)
46 {
47         struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
48         int index;
49
50         for (index = 0;
51                 freq_table[index].frequency != CPUFREQ_TABLE_END; index++)
52                 if (freq_table[index].frequency == freq)
53                         break;
54
55         if (freq_table[index].frequency == CPUFREQ_TABLE_END)
56                 return -EINVAL;
57
58         return index;
59 }
60
61 static int exynos_cpufreq_scale(unsigned int target_freq)
62 {
63         struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
64         unsigned int *volt_table = exynos_info->volt_table;
65         struct cpufreq_policy *policy = cpufreq_cpu_get(0);
66         unsigned int arm_volt, safe_arm_volt = 0;
67         unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz;
68         int index, old_index;
69         int ret = 0;
70
71         freqs.old = policy->cur;
72         freqs.new = target_freq;
73         freqs.cpu = policy->cpu;
74
75         if (freqs.new == freqs.old)
76                 goto out;
77
78         /*
79          * The policy max have been changed so that we cannot get proper
80          * old_index with cpufreq_frequency_table_target(). Thus, ignore
81          * policy and get the index from the raw freqeuncy table.
82          */
83         old_index = exynos_cpufreq_get_index(freqs.old);
84         if (old_index < 0) {
85                 ret = old_index;
86                 goto out;
87         }
88
89         index = exynos_cpufreq_get_index(target_freq);
90         if (index < 0) {
91                 ret = index;
92                 goto out;
93         }
94
95         /*
96          * ARM clock source will be changed APLL to MPLL temporary
97          * To support this level, need to control regulator for
98          * required voltage level
99          */
100         if (exynos_info->need_apll_change != NULL) {
101                 if (exynos_info->need_apll_change(old_index, index) &&
102                    (freq_table[index].frequency < mpll_freq_khz) &&
103                    (freq_table[old_index].frequency < mpll_freq_khz))
104                         safe_arm_volt = volt_table[exynos_info->pll_safe_idx];
105         }
106         arm_volt = volt_table[index];
107
108         for_each_cpu(freqs.cpu, policy->cpus)
109                 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
110
111         /* When the new frequency is higher than current frequency */
112         if ((freqs.new > freqs.old) && !safe_arm_volt) {
113                 /* Firstly, voltage up to increase frequency */
114                 ret = regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
115                 if (ret) {
116                         pr_err("%s: failed to set cpu voltage to %d\n",
117                                 __func__, arm_volt);
118                         goto out;
119                 }
120         }
121
122         if (safe_arm_volt) {
123                 ret = regulator_set_voltage(arm_regulator, safe_arm_volt,
124                                       safe_arm_volt);
125                 if (ret) {
126                         pr_err("%s: failed to set cpu voltage to %d\n",
127                                 __func__, safe_arm_volt);
128                         goto out;
129                 }
130         }
131
132         exynos_info->set_freq(old_index, index);
133
134         for_each_cpu(freqs.cpu, policy->cpus)
135                 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
136
137         /* When the new frequency is lower than current frequency */
138         if ((freqs.new < freqs.old) ||
139            ((freqs.new > freqs.old) && safe_arm_volt)) {
140                 /* down the voltage after frequency change */
141                 regulator_set_voltage(arm_regulator, arm_volt,
142                                 arm_volt);
143                 if (ret) {
144                         pr_err("%s: failed to set cpu voltage to %d\n",
145                                 __func__, arm_volt);
146                         goto out;
147                 }
148         }
149
150 out:
151
152         cpufreq_cpu_put(policy);
153
154         return ret;
155 }
156
157 static int exynos_target(struct cpufreq_policy *policy,
158                           unsigned int target_freq,
159                           unsigned int relation)
160 {
161         struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
162         unsigned int index;
163         unsigned int new_freq;
164         int ret = 0;
165
166         mutex_lock(&cpufreq_lock);
167
168         if (frequency_locked)
169                 goto out;
170
171         if (cpufreq_frequency_table_target(policy, freq_table,
172                                            target_freq, relation, &index)) {
173                 ret = -EINVAL;
174                 goto out;
175         }
176
177         new_freq = freq_table[index].frequency;
178
179         ret = exynos_cpufreq_scale(new_freq);
180
181 out:
182         mutex_unlock(&cpufreq_lock);
183
184         return ret;
185 }
186
187 #ifdef CONFIG_PM
188 static int exynos_cpufreq_suspend(struct cpufreq_policy *policy)
189 {
190         return 0;
191 }
192
193 static int exynos_cpufreq_resume(struct cpufreq_policy *policy)
194 {
195         return 0;
196 }
197 #endif
198
199 /**
200  * exynos_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
201  *                      context
202  * @notifier
203  * @pm_event
204  * @v
205  *
206  * While frequency_locked == true, target() ignores every frequency but
207  * locking_frequency. The locking_frequency value is the initial frequency,
208  * which is set by the bootloader. In order to eliminate possible
209  * inconsistency in clock values, we save and restore frequencies during
210  * suspend and resume and block CPUFREQ activities. Note that the standard
211  * suspend/resume cannot be used as they are too deep (syscore_ops) for
212  * regulator actions.
213  */
214 static int exynos_cpufreq_pm_notifier(struct notifier_block *notifier,
215                                        unsigned long pm_event, void *v)
216 {
217         int ret;
218
219         switch (pm_event) {
220         case PM_SUSPEND_PREPARE:
221                 mutex_lock(&cpufreq_lock);
222                 frequency_locked = true;
223                 mutex_unlock(&cpufreq_lock);
224
225                 ret = exynos_cpufreq_scale(locking_frequency);
226                 if (ret < 0)
227                         return NOTIFY_BAD;
228
229                 break;
230
231         case PM_POST_SUSPEND:
232                 mutex_lock(&cpufreq_lock);
233                 frequency_locked = false;
234                 mutex_unlock(&cpufreq_lock);
235                 break;
236         }
237
238         return NOTIFY_OK;
239 }
240
241 static struct notifier_block exynos_cpufreq_nb = {
242         .notifier_call = exynos_cpufreq_pm_notifier,
243 };
244
245 static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
246 {
247         policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu);
248
249         cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu);
250
251         /* set the transition latency value */
252         policy->cpuinfo.transition_latency = 100000;
253
254         cpumask_setall(policy->cpus);
255
256         return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table);
257 }
258
259 static int exynos_cpufreq_cpu_exit(struct cpufreq_policy *policy)
260 {
261         cpufreq_frequency_table_put_attr(policy->cpu);
262         return 0;
263 }
264
265 static struct freq_attr *exynos_cpufreq_attr[] = {
266         &cpufreq_freq_attr_scaling_available_freqs,
267         NULL,
268 };
269
270 static struct cpufreq_driver exynos_driver = {
271         .flags          = CPUFREQ_STICKY,
272         .verify         = exynos_verify_speed,
273         .target         = exynos_target,
274         .get            = exynos_getspeed,
275         .init           = exynos_cpufreq_cpu_init,
276         .exit           = exynos_cpufreq_cpu_exit,
277         .name           = "exynos_cpufreq",
278         .attr           = exynos_cpufreq_attr,
279 #ifdef CONFIG_PM
280         .suspend        = exynos_cpufreq_suspend,
281         .resume         = exynos_cpufreq_resume,
282 #endif
283 };
284
285 static int __init exynos_cpufreq_init(void)
286 {
287         int ret = -EINVAL;
288
289         exynos_info = kzalloc(sizeof(struct exynos_dvfs_info), GFP_KERNEL);
290         if (!exynos_info)
291                 return -ENOMEM;
292
293         if (soc_is_exynos4210())
294                 ret = exynos4210_cpufreq_init(exynos_info);
295         else if (soc_is_exynos4212() || soc_is_exynos4412())
296                 ret = exynos4x12_cpufreq_init(exynos_info);
297         else if (soc_is_exynos5250())
298                 ret = exynos5250_cpufreq_init(exynos_info);
299         else
300                 pr_err("%s: CPU type not found\n", __func__);
301
302         if (ret)
303                 goto err_vdd_arm;
304
305         if (exynos_info->set_freq == NULL) {
306                 pr_err("%s: No set_freq function (ERR)\n", __func__);
307                 goto err_vdd_arm;
308         }
309
310         arm_regulator = regulator_get(NULL, "vdd_arm");
311         if (IS_ERR(arm_regulator)) {
312                 pr_err("%s: failed to get resource vdd_arm\n", __func__);
313                 goto err_vdd_arm;
314         }
315
316         locking_frequency = exynos_getspeed(0);
317
318         register_pm_notifier(&exynos_cpufreq_nb);
319
320         if (cpufreq_register_driver(&exynos_driver)) {
321                 pr_err("%s: failed to register cpufreq driver\n", __func__);
322                 goto err_cpufreq;
323         }
324
325         return 0;
326 err_cpufreq:
327         unregister_pm_notifier(&exynos_cpufreq_nb);
328
329         regulator_put(arm_regulator);
330 err_vdd_arm:
331         kfree(exynos_info);
332         pr_debug("%s: failed initialization\n", __func__);
333         return -EINVAL;
334 }
335 late_initcall(exynos_cpufreq_init);