Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1/*
  2 * Copyright (C) 2010 Google, Inc.
  3 *
  4 * Author:
  5 *	Colin Cross <ccross@google.com>
  6 *	Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
  7 *
  8 * This software is licensed under the terms of the GNU General Public
  9 * License version 2, as published by the Free Software Foundation, and
 10 * may be copied, distributed, and modified under those terms.
 11 *
 12 * This program is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15 * GNU General Public License for more details.
 16 *
 17 */
 18
 19#include <linux/kernel.h>
 20#include <linux/module.h>
 21#include <linux/types.h>
 22#include <linux/sched.h>
 23#include <linux/cpufreq.h>
 24#include <linux/delay.h>
 25#include <linux/init.h>
 26#include <linux/err.h>
 27#include <linux/clk.h>
 28#include <linux/io.h>
 29
 30static struct cpufreq_frequency_table freq_table[] = {
 31	{ .frequency = 216000 },
 32	{ .frequency = 312000 },
 33	{ .frequency = 456000 },
 34	{ .frequency = 608000 },
 35	{ .frequency = 760000 },
 36	{ .frequency = 816000 },
 37	{ .frequency = 912000 },
 38	{ .frequency = 1000000 },
 39	{ .frequency = CPUFREQ_TABLE_END },
 40};
 41
 42#define NUM_CPUS	2
 43
 44static struct clk *cpu_clk;
 45static struct clk *pll_x_clk;
 46static struct clk *pll_p_clk;
 47static struct clk *emc_clk;
 48
 49static int tegra_cpu_clk_set_rate(unsigned long rate)
 50{
 51	int ret;
 52
 53	/*
 54	 * Take an extra reference to the main pll so it doesn't turn
 55	 * off when we move the cpu off of it
 56	 */
 57	clk_prepare_enable(pll_x_clk);
 58
 59	ret = clk_set_parent(cpu_clk, pll_p_clk);
 60	if (ret) {
 61		pr_err("Failed to switch cpu to clock pll_p\n");
 62		goto out;
 63	}
 64
 65	if (rate == clk_get_rate(pll_p_clk))
 66		goto out;
 67
 68	ret = clk_set_rate(pll_x_clk, rate);
 69	if (ret) {
 70		pr_err("Failed to change pll_x to %lu\n", rate);
 71		goto out;
 72	}
 73
 74	ret = clk_set_parent(cpu_clk, pll_x_clk);
 75	if (ret) {
 76		pr_err("Failed to switch cpu to clock pll_x\n");
 77		goto out;
 78	}
 79
 80out:
 81	clk_disable_unprepare(pll_x_clk);
 82	return ret;
 83}
 84
 85static int tegra_update_cpu_speed(struct cpufreq_policy *policy,
 86		unsigned long rate)
 87{
 88	int ret = 0;
 89
 90	/*
 91	 * Vote on memory bus frequency based on cpu frequency
 92	 * This sets the minimum frequency, display or avp may request higher
 93	 */
 94	if (rate >= 816000)
 95		clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */
 96	else if (rate >= 456000)
 97		clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */
 98	else
 99		clk_set_rate(emc_clk, 100000000);  /* emc 50Mhz */
100
101	ret = tegra_cpu_clk_set_rate(rate * 1000);
102	if (ret)
103		pr_err("cpu-tegra: Failed to set cpu frequency to %lu kHz\n",
104			rate);
105
106	return ret;
107}
108
109static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
110{
111	return tegra_update_cpu_speed(policy, freq_table[index].frequency);
112}
113
114static int tegra_cpu_init(struct cpufreq_policy *policy)
115{
116	int ret;
117
118	if (policy->cpu >= NUM_CPUS)
119		return -EINVAL;
120
121	clk_prepare_enable(emc_clk);
122	clk_prepare_enable(cpu_clk);
123
124	/* FIXME: what's the actual transition time? */
125	ret = cpufreq_generic_init(policy, freq_table, 300 * 1000);
126	if (ret) {
127		clk_disable_unprepare(cpu_clk);
128		clk_disable_unprepare(emc_clk);
129		return ret;
130	}
131
132	policy->clk = cpu_clk;
133	policy->suspend_freq = freq_table[0].frequency;
134	return 0;
135}
136
137static int tegra_cpu_exit(struct cpufreq_policy *policy)
138{
139	clk_disable_unprepare(cpu_clk);
140	clk_disable_unprepare(emc_clk);
141	return 0;
142}
143
144static struct cpufreq_driver tegra_cpufreq_driver = {
145	.flags		= CPUFREQ_NEED_INITIAL_FREQ_CHECK,
146	.verify		= cpufreq_generic_frequency_table_verify,
147	.target_index	= tegra_target,
148	.get		= cpufreq_generic_get,
149	.init		= tegra_cpu_init,
150	.exit		= tegra_cpu_exit,
151	.name		= "tegra",
152	.attr		= cpufreq_generic_attr,
153#ifdef CONFIG_PM
154	.suspend	= cpufreq_generic_suspend,
155#endif
156};
157
158static int __init tegra_cpufreq_init(void)
159{
160	cpu_clk = clk_get_sys(NULL, "cclk");
161	if (IS_ERR(cpu_clk))
162		return PTR_ERR(cpu_clk);
163
164	pll_x_clk = clk_get_sys(NULL, "pll_x");
165	if (IS_ERR(pll_x_clk))
166		return PTR_ERR(pll_x_clk);
167
168	pll_p_clk = clk_get_sys(NULL, "pll_p");
169	if (IS_ERR(pll_p_clk))
170		return PTR_ERR(pll_p_clk);
171
172	emc_clk = clk_get_sys("cpu", "emc");
173	if (IS_ERR(emc_clk)) {
174		clk_put(cpu_clk);
175		return PTR_ERR(emc_clk);
176	}
177
178	return cpufreq_register_driver(&tegra_cpufreq_driver);
179}
180
181static void __exit tegra_cpufreq_exit(void)
182{
183        cpufreq_unregister_driver(&tegra_cpufreq_driver);
184	clk_put(emc_clk);
185	clk_put(cpu_clk);
186}
187
188
189MODULE_AUTHOR("Colin Cross <ccross@android.com>");
190MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2");
191MODULE_LICENSE("GPL");
192module_init(tegra_cpufreq_init);
193module_exit(tegra_cpufreq_exit);