Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 <mach/cpufreq.h>
 22
 23#include <plat/cpu.h>
 24
 25static struct exynos_dvfs_info *exynos_info;
 26
 27static struct regulator *arm_regulator;
 28static struct cpufreq_freqs freqs;
 29
 30static unsigned int locking_frequency;
 31static bool frequency_locked;
 32static DEFINE_MUTEX(cpufreq_lock);
 33
 34int exynos_verify_speed(struct cpufreq_policy *policy)
 35{
 36	return cpufreq_frequency_table_verify(policy,
 37					      exynos_info->freq_table);
 38}
 39
 40unsigned int exynos_getspeed(unsigned int cpu)
 41{
 42	return clk_get_rate(exynos_info->cpu_clk) / 1000;
 43}
 44
 45static 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	if (cpufreq_frequency_table_target(policy, freq_table,
 66					   freqs.old, relation, &old_index)) {
 67		ret = -EINVAL;
 68		goto out;
 69	}
 70
 71	if (cpufreq_frequency_table_target(policy, freq_table,
 72					   target_freq, relation, &index)) {
 73		ret = -EINVAL;
 74		goto out;
 75	}
 76
 77	freqs.new = freq_table[index].frequency;
 78	freqs.cpu = policy->cpu;
 79
 80	/*
 81	 * ARM clock source will be changed APLL to MPLL temporary
 82	 * To support this level, need to control regulator for
 83	 * required voltage level
 84	 */
 85	if (exynos_info->need_apll_change != NULL) {
 86		if (exynos_info->need_apll_change(old_index, index) &&
 87		   (freq_table[index].frequency < mpll_freq_khz) &&
 88		   (freq_table[old_index].frequency < mpll_freq_khz))
 89			safe_arm_volt = volt_table[exynos_info->pll_safe_idx];
 90	}
 91	arm_volt = volt_table[index];
 92
 93	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 94
 95	/* When the new frequency is higher than current frequency */
 96	if ((freqs.new > freqs.old) && !safe_arm_volt) {
 97		/* Firstly, voltage up to increase frequency */
 98		regulator_set_voltage(arm_regulator, arm_volt,
 99				arm_volt);
100	}
101
102	if (safe_arm_volt)
103		regulator_set_voltage(arm_regulator, safe_arm_volt,
104				      safe_arm_volt);
105	if (freqs.new != freqs.old)
106		exynos_info->set_freq(old_index, index);
107
108	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
109
110	/* When the new frequency is lower than current frequency */
111	if ((freqs.new < freqs.old) ||
112	   ((freqs.new > freqs.old) && safe_arm_volt)) {
113		/* down the voltage after frequency change */
114		regulator_set_voltage(arm_regulator, arm_volt,
115				arm_volt);
116	}
117
118out:
119	mutex_unlock(&cpufreq_lock);
120
121	return ret;
122}
123
124#ifdef CONFIG_PM
125static int exynos_cpufreq_suspend(struct cpufreq_policy *policy)
126{
127	return 0;
128}
129
130static int exynos_cpufreq_resume(struct cpufreq_policy *policy)
131{
132	return 0;
133}
134#endif
135
136/**
137 * exynos_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
138 *			context
139 * @notifier
140 * @pm_event
141 * @v
142 *
143 * While frequency_locked == true, target() ignores every frequency but
144 * locking_frequency. The locking_frequency value is the initial frequency,
145 * which is set by the bootloader. In order to eliminate possible
146 * inconsistency in clock values, we save and restore frequencies during
147 * suspend and resume and block CPUFREQ activities. Note that the standard
148 * suspend/resume cannot be used as they are too deep (syscore_ops) for
149 * regulator actions.
150 */
151static int exynos_cpufreq_pm_notifier(struct notifier_block *notifier,
152				       unsigned long pm_event, void *v)
153{
154	struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
155	static unsigned int saved_frequency;
156	unsigned int temp;
157
158	mutex_lock(&cpufreq_lock);
159	switch (pm_event) {
160	case PM_SUSPEND_PREPARE:
161		if (frequency_locked)
162			goto out;
163
164		frequency_locked = true;
165
166		if (locking_frequency) {
167			saved_frequency = exynos_getspeed(0);
168
169			mutex_unlock(&cpufreq_lock);
170			exynos_target(policy, locking_frequency,
171				      CPUFREQ_RELATION_H);
172			mutex_lock(&cpufreq_lock);
173		}
174		break;
175
176	case PM_POST_SUSPEND:
177		if (saved_frequency) {
178			/*
179			 * While frequency_locked, only locking_frequency
180			 * is valid for target(). In order to use
181			 * saved_frequency while keeping frequency_locked,
182			 * we temporarly overwrite locking_frequency.
183			 */
184			temp = locking_frequency;
185			locking_frequency = saved_frequency;
186
187			mutex_unlock(&cpufreq_lock);
188			exynos_target(policy, locking_frequency,
189				      CPUFREQ_RELATION_H);
190			mutex_lock(&cpufreq_lock);
191
192			locking_frequency = temp;
193		}
194		frequency_locked = false;
195		break;
196	}
197out:
198	mutex_unlock(&cpufreq_lock);
199
200	return NOTIFY_OK;
201}
202
203static struct notifier_block exynos_cpufreq_nb = {
204	.notifier_call = exynos_cpufreq_pm_notifier,
205};
206
207static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
208{
209	policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu);
210
211	cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu);
212
213	locking_frequency = exynos_getspeed(0);
214
215	/* set the transition latency value */
216	policy->cpuinfo.transition_latency = 100000;
217
218	/*
219	 * EXYNOS4 multi-core processors has 2 cores
220	 * that the frequency cannot be set independently.
221	 * Each cpu is bound to the same speed.
222	 * So the affected cpu is all of the cpus.
223	 */
224	if (num_online_cpus() == 1) {
225		cpumask_copy(policy->related_cpus, cpu_possible_mask);
226		cpumask_copy(policy->cpus, cpu_online_mask);
227	} else {
228		cpumask_setall(policy->cpus);
229	}
230
231	return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table);
232}
233
234static struct cpufreq_driver exynos_driver = {
235	.flags		= CPUFREQ_STICKY,
236	.verify		= exynos_verify_speed,
237	.target		= exynos_target,
238	.get		= exynos_getspeed,
239	.init		= exynos_cpufreq_cpu_init,
240	.name		= "exynos_cpufreq",
241#ifdef CONFIG_PM
242	.suspend	= exynos_cpufreq_suspend,
243	.resume		= exynos_cpufreq_resume,
244#endif
245};
246
247static int __init exynos_cpufreq_init(void)
248{
249	int ret = -EINVAL;
250
251	exynos_info = kzalloc(sizeof(struct exynos_dvfs_info), GFP_KERNEL);
252	if (!exynos_info)
253		return -ENOMEM;
254
255	if (soc_is_exynos4210())
256		ret = exynos4210_cpufreq_init(exynos_info);
257	else if (soc_is_exynos4212() || soc_is_exynos4412())
258		ret = exynos4x12_cpufreq_init(exynos_info);
259	else if (soc_is_exynos5250())
260		ret = exynos5250_cpufreq_init(exynos_info);
261	else
262		pr_err("%s: CPU type not found\n", __func__);
263
264	if (ret)
265		goto err_vdd_arm;
266
267	if (exynos_info->set_freq == NULL) {
268		pr_err("%s: No set_freq function (ERR)\n", __func__);
269		goto err_vdd_arm;
270	}
271
272	arm_regulator = regulator_get(NULL, "vdd_arm");
273	if (IS_ERR(arm_regulator)) {
274		pr_err("%s: failed to get resource vdd_arm\n", __func__);
275		goto err_vdd_arm;
276	}
277
278	register_pm_notifier(&exynos_cpufreq_nb);
279
280	if (cpufreq_register_driver(&exynos_driver)) {
281		pr_err("%s: failed to register cpufreq driver\n", __func__);
282		goto err_cpufreq;
283	}
284
285	return 0;
286err_cpufreq:
287	unregister_pm_notifier(&exynos_cpufreq_nb);
288
289	if (!IS_ERR(arm_regulator))
290		regulator_put(arm_regulator);
291err_vdd_arm:
292	kfree(exynos_info);
293	pr_debug("%s: failed initialization\n", __func__);
294	return -EINVAL;
295}
296late_initcall(exynos_cpufreq_init);