Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
exynos-cpufreq.c
Go to the documentation of this file.
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>
18 #include <linux/cpufreq.h>
19 #include <linux/suspend.h>
20 
21 #include <mach/cpufreq.h>
22 
23 #include <plat/cpu.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 
35 {
36  return cpufreq_frequency_table_verify(policy,
37  exynos_info->freq_table);
38 }
39 
40 unsigned int exynos_getspeed(unsigned int cpu)
41 {
42  return clk_get_rate(exynos_info->cpu_clk) / 1000;
43 }
44 
45 static int exynos_target(struct cpufreq_policy *policy,
46  unsigned int target_freq,
47  unsigned int relation)
48 {
49  unsigned int index, old_index;
50  unsigned int arm_volt, safe_arm_volt = 0;
51  int ret = 0;
52  struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
53  unsigned int *volt_table = exynos_info->volt_table;
54  unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz;
55 
56  mutex_lock(&cpufreq_lock);
57 
58  freqs.old = policy->cur;
59 
60  if (frequency_locked && target_freq != locking_frequency) {
61  ret = -EAGAIN;
62  goto out;
63  }
64 
65  /*
66  * The policy max have been changed so that we cannot get proper
67  * old_index with cpufreq_frequency_table_target(). Thus, ignore
68  * policy and get the index from the raw freqeuncy table.
69  */
70  for (old_index = 0;
71  freq_table[old_index].frequency != CPUFREQ_TABLE_END;
72  old_index++)
73  if (freq_table[old_index].frequency == freqs.old)
74  break;
75 
76  if (freq_table[old_index].frequency == CPUFREQ_TABLE_END) {
77  ret = -EINVAL;
78  goto out;
79  }
80 
81  if (cpufreq_frequency_table_target(policy, freq_table,
82  target_freq, relation, &index)) {
83  ret = -EINVAL;
84  goto out;
85  }
86 
87  freqs.new = freq_table[index].frequency;
88  freqs.cpu = policy->cpu;
89 
90  /*
91  * ARM clock source will be changed APLL to MPLL temporary
92  * To support this level, need to control regulator for
93  * required voltage level
94  */
95  if (exynos_info->need_apll_change != NULL) {
96  if (exynos_info->need_apll_change(old_index, index) &&
97  (freq_table[index].frequency < mpll_freq_khz) &&
98  (freq_table[old_index].frequency < mpll_freq_khz))
99  safe_arm_volt = volt_table[exynos_info->pll_safe_idx];
100  }
101  arm_volt = volt_table[index];
102 
104 
105  /* When the new frequency is higher than current frequency */
106  if ((freqs.new > freqs.old) && !safe_arm_volt) {
107  /* Firstly, voltage up to increase frequency */
108  regulator_set_voltage(arm_regulator, arm_volt,
109  arm_volt);
110  }
111 
112  if (safe_arm_volt)
113  regulator_set_voltage(arm_regulator, safe_arm_volt,
114  safe_arm_volt);
115  if (freqs.new != freqs.old)
116  exynos_info->set_freq(old_index, index);
117 
119 
120  /* When the new frequency is lower than current frequency */
121  if ((freqs.new < freqs.old) ||
122  ((freqs.new > freqs.old) && safe_arm_volt)) {
123  /* down the voltage after frequency change */
124  regulator_set_voltage(arm_regulator, arm_volt,
125  arm_volt);
126  }
127 
128 out:
129  mutex_unlock(&cpufreq_lock);
130 
131  return ret;
132 }
133 
134 #ifdef CONFIG_PM
135 static int exynos_cpufreq_suspend(struct cpufreq_policy *policy)
136 {
137  return 0;
138 }
139 
140 static int exynos_cpufreq_resume(struct cpufreq_policy *policy)
141 {
142  return 0;
143 }
144 #endif
145 
161 static int exynos_cpufreq_pm_notifier(struct notifier_block *notifier,
162  unsigned long pm_event, void *v)
163 {
164  struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
165  static unsigned int saved_frequency;
166  unsigned int temp;
167 
168  mutex_lock(&cpufreq_lock);
169  switch (pm_event) {
170  case PM_SUSPEND_PREPARE:
171  if (frequency_locked)
172  goto out;
173 
174  frequency_locked = true;
175 
176  if (locking_frequency) {
177  saved_frequency = exynos_getspeed(0);
178 
179  mutex_unlock(&cpufreq_lock);
180  exynos_target(policy, locking_frequency,
182  mutex_lock(&cpufreq_lock);
183  }
184  break;
185 
186  case PM_POST_SUSPEND:
187  if (saved_frequency) {
188  /*
189  * While frequency_locked, only locking_frequency
190  * is valid for target(). In order to use
191  * saved_frequency while keeping frequency_locked,
192  * we temporarly overwrite locking_frequency.
193  */
194  temp = locking_frequency;
195  locking_frequency = saved_frequency;
196 
197  mutex_unlock(&cpufreq_lock);
198  exynos_target(policy, locking_frequency,
200  mutex_lock(&cpufreq_lock);
201 
202  locking_frequency = temp;
203  }
204  frequency_locked = false;
205  break;
206  }
207 out:
208  mutex_unlock(&cpufreq_lock);
209 
210  return NOTIFY_OK;
211 }
212 
213 static struct notifier_block exynos_cpufreq_nb = {
214  .notifier_call = exynos_cpufreq_pm_notifier,
215 };
216 
217 static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
218 {
219  policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu);
220 
221  cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu);
222 
223  locking_frequency = exynos_getspeed(0);
224 
225  /* set the transition latency value */
226  policy->cpuinfo.transition_latency = 100000;
227 
228  /*
229  * EXYNOS4 multi-core processors has 2 cores
230  * that the frequency cannot be set independently.
231  * Each cpu is bound to the same speed.
232  * So the affected cpu is all of the cpus.
233  */
234  if (num_online_cpus() == 1) {
235  cpumask_copy(policy->related_cpus, cpu_possible_mask);
236  cpumask_copy(policy->cpus, cpu_online_mask);
237  } else {
238  cpumask_setall(policy->cpus);
239  }
240 
241  return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table);
242 }
243 
244 static struct cpufreq_driver exynos_driver = {
245  .flags = CPUFREQ_STICKY,
246  .verify = exynos_verify_speed,
247  .target = exynos_target,
248  .get = exynos_getspeed,
249  .init = exynos_cpufreq_cpu_init,
250  .name = "exynos_cpufreq",
251 #ifdef CONFIG_PM
252  .suspend = exynos_cpufreq_suspend,
253  .resume = exynos_cpufreq_resume,
254 #endif
255 };
256 
257 static int __init exynos_cpufreq_init(void)
258 {
259  int ret = -EINVAL;
260 
261  exynos_info = kzalloc(sizeof(struct exynos_dvfs_info), GFP_KERNEL);
262  if (!exynos_info)
263  return -ENOMEM;
264 
265  if (soc_is_exynos4210())
266  ret = exynos4210_cpufreq_init(exynos_info);
267  else if (soc_is_exynos4212() || soc_is_exynos4412())
268  ret = exynos4x12_cpufreq_init(exynos_info);
269  else if (soc_is_exynos5250())
270  ret = exynos5250_cpufreq_init(exynos_info);
271  else
272  pr_err("%s: CPU type not found\n", __func__);
273 
274  if (ret)
275  goto err_vdd_arm;
276 
277  if (exynos_info->set_freq == NULL) {
278  pr_err("%s: No set_freq function (ERR)\n", __func__);
279  goto err_vdd_arm;
280  }
281 
282  arm_regulator = regulator_get(NULL, "vdd_arm");
283  if (IS_ERR(arm_regulator)) {
284  pr_err("%s: failed to get resource vdd_arm\n", __func__);
285  goto err_vdd_arm;
286  }
287 
288  register_pm_notifier(&exynos_cpufreq_nb);
289 
290  if (cpufreq_register_driver(&exynos_driver)) {
291  pr_err("%s: failed to register cpufreq driver\n", __func__);
292  goto err_cpufreq;
293  }
294 
295  return 0;
296 err_cpufreq:
297  unregister_pm_notifier(&exynos_cpufreq_nb);
298 
299  if (!IS_ERR(arm_regulator))
300  regulator_put(arm_regulator);
301 err_vdd_arm:
302  kfree(exynos_info);
303  pr_debug("%s: failed initialization\n", __func__);
304  return -EINVAL;
305 }
306 late_initcall(exynos_cpufreq_init);