Linux Audio

Check our new training course

Linux BSP development engineering services

Need help to port Linux and bootloaders to your hardware?
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/platform_device.h>
 20
 21#include <plat/cpu.h>
 22
 23#include "exynos-cpufreq.h"
 24
 25static struct exynos_dvfs_info *exynos_info;
 26static struct regulator *arm_regulator;
 27static unsigned int locking_frequency;
 28
 29static int exynos_cpufreq_get_index(unsigned int freq)
 30{
 31	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
 32	int index;
 33
 34	for (index = 0;
 35		freq_table[index].frequency != CPUFREQ_TABLE_END; index++)
 36		if (freq_table[index].frequency == freq)
 37			break;
 38
 39	if (freq_table[index].frequency == CPUFREQ_TABLE_END)
 40		return -EINVAL;
 41
 42	return index;
 43}
 44
 45static int exynos_cpufreq_scale(unsigned int target_freq)
 46{
 47	struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
 48	unsigned int *volt_table = exynos_info->volt_table;
 49	struct cpufreq_policy *policy = cpufreq_cpu_get(0);
 50	unsigned int arm_volt, safe_arm_volt = 0;
 51	unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz;
 52	unsigned int old_freq;
 53	int index, old_index;
 54	int ret = 0;
 55
 56	old_freq = policy->cur;
 57
 58	/*
 59	 * The policy max have been changed so that we cannot get proper
 60	 * old_index with cpufreq_frequency_table_target(). Thus, ignore
 61	 * policy and get the index from the raw frequency table.
 62	 */
 63	old_index = exynos_cpufreq_get_index(old_freq);
 64	if (old_index < 0) {
 65		ret = old_index;
 66		goto out;
 67	}
 68
 69	index = exynos_cpufreq_get_index(target_freq);
 70	if (index < 0) {
 71		ret = index;
 72		goto out;
 73	}
 74
 75	/*
 76	 * ARM clock source will be changed APLL to MPLL temporary
 77	 * To support this level, need to control regulator for
 78	 * required voltage level
 79	 */
 80	if (exynos_info->need_apll_change != NULL) {
 81		if (exynos_info->need_apll_change(old_index, index) &&
 82		   (freq_table[index].frequency < mpll_freq_khz) &&
 83		   (freq_table[old_index].frequency < mpll_freq_khz))
 84			safe_arm_volt = volt_table[exynos_info->pll_safe_idx];
 85	}
 86	arm_volt = volt_table[index];
 87
 88	/* When the new frequency is higher than current frequency */
 89	if ((target_freq > old_freq) && !safe_arm_volt) {
 90		/* Firstly, voltage up to increase frequency */
 91		ret = regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
 92		if (ret) {
 93			pr_err("%s: failed to set cpu voltage to %d\n",
 94				__func__, arm_volt);
 95			return ret;
 96		}
 97	}
 98
 99	if (safe_arm_volt) {
100		ret = regulator_set_voltage(arm_regulator, safe_arm_volt,
101				      safe_arm_volt);
102		if (ret) {
103			pr_err("%s: failed to set cpu voltage to %d\n",
104				__func__, safe_arm_volt);
105			return ret;
106		}
107	}
108
109	exynos_info->set_freq(old_index, index);
110
111	/* When the new frequency is lower than current frequency */
112	if ((target_freq < old_freq) ||
113	   ((target_freq > old_freq) && safe_arm_volt)) {
114		/* down the voltage after frequency change */
115		ret = regulator_set_voltage(arm_regulator, arm_volt,
116				arm_volt);
117		if (ret) {
118			pr_err("%s: failed to set cpu voltage to %d\n",
119				__func__, arm_volt);
120			goto out;
121		}
122	}
123
124out:
125	cpufreq_cpu_put(policy);
126
127	return ret;
128}
129
130static int exynos_target(struct cpufreq_policy *policy, unsigned int index)
131{
132	return exynos_cpufreq_scale(exynos_info->freq_table[index].frequency);
133}
134
135static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
136{
137	policy->clk = exynos_info->cpu_clk;
138	policy->suspend_freq = locking_frequency;
139	return cpufreq_generic_init(policy, exynos_info->freq_table, 100000);
140}
141
142static struct cpufreq_driver exynos_driver = {
143	.flags		= CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
144	.verify		= cpufreq_generic_frequency_table_verify,
145	.target_index	= exynos_target,
146	.get		= cpufreq_generic_get,
147	.init		= exynos_cpufreq_cpu_init,
148	.name		= "exynos_cpufreq",
149	.attr		= cpufreq_generic_attr,
150#ifdef CONFIG_ARM_EXYNOS_CPU_FREQ_BOOST_SW
151	.boost_supported = true,
152#endif
153#ifdef CONFIG_PM
154	.suspend	= cpufreq_generic_suspend,
155#endif
156};
157
158static int exynos_cpufreq_probe(struct platform_device *pdev)
159{
160	int ret = -EINVAL;
161
162	exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL);
163	if (!exynos_info)
164		return -ENOMEM;
165
166	if (soc_is_exynos4210())
167		ret = exynos4210_cpufreq_init(exynos_info);
168	else if (soc_is_exynos4212() || soc_is_exynos4412())
169		ret = exynos4x12_cpufreq_init(exynos_info);
170	else if (soc_is_exynos5250())
171		ret = exynos5250_cpufreq_init(exynos_info);
172	else
173		return 0;
174
175	if (ret)
176		goto err_vdd_arm;
177
178	if (exynos_info->set_freq == NULL) {
179		pr_err("%s: No set_freq function (ERR)\n", __func__);
180		goto err_vdd_arm;
181	}
182
183	arm_regulator = regulator_get(NULL, "vdd_arm");
184	if (IS_ERR(arm_regulator)) {
185		pr_err("%s: failed to get resource vdd_arm\n", __func__);
186		goto err_vdd_arm;
187	}
188
189	/* Done here as we want to capture boot frequency */
190	locking_frequency = clk_get_rate(exynos_info->cpu_clk) / 1000;
191
192	if (!cpufreq_register_driver(&exynos_driver))
193		return 0;
194
195	pr_err("%s: failed to register cpufreq driver\n", __func__);
196	regulator_put(arm_regulator);
197err_vdd_arm:
198	kfree(exynos_info);
199	return -EINVAL;
200}
201
202static struct platform_driver exynos_cpufreq_platdrv = {
203	.driver = {
204		.name	= "exynos-cpufreq",
205		.owner	= THIS_MODULE,
206	},
207	.probe = exynos_cpufreq_probe,
208};
209module_platform_driver(exynos_cpufreq_platdrv);