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 30 static 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 44 static struct clk *cpu_clk; 45 static struct clk *pll_x_clk; 46 static struct clk *pll_p_clk; 47 static struct clk *emc_clk; 48 static bool pll_x_prepared; 49 50 static unsigned int tegra_get_intermediate(struct cpufreq_policy *policy, 51 unsigned int index) 52 { 53 unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000; 54 55 /* 56 * Don't switch to intermediate freq if: 57 * - we are already at it, i.e. policy->cur == ifreq 58 * - index corresponds to ifreq 59 */ 60 if ((freq_table[index].frequency == ifreq) || (policy->cur == ifreq)) 61 return 0; 62 63 return ifreq; 64 } 65 66 static int tegra_target_intermediate(struct cpufreq_policy *policy, 67 unsigned int index) 68 { 69 int ret; 70 71 /* 72 * Take an extra reference to the main pll so it doesn't turn 73 * off when we move the cpu off of it as enabling it again while we 74 * switch to it from tegra_target() would take additional time. 75 * 76 * When target-freq is equal to intermediate freq we don't need to 77 * switch to an intermediate freq and so this routine isn't called. 78 * Also, we wouldn't be using pll_x anymore and must not take extra 79 * reference to it, as it can be disabled now to save some power. 80 */ 81 clk_prepare_enable(pll_x_clk); 82 83 ret = clk_set_parent(cpu_clk, pll_p_clk); 84 if (ret) 85 clk_disable_unprepare(pll_x_clk); 86 else 87 pll_x_prepared = true; 88 89 return ret; 90 } 91 92 static int tegra_target(struct cpufreq_policy *policy, unsigned int index) 93 { 94 unsigned long rate = freq_table[index].frequency; 95 unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000; 96 int ret = 0; 97 98 /* 99 * Vote on memory bus frequency based on cpu frequency 100 * This sets the minimum frequency, display or avp may request higher 101 */ 102 if (rate >= 816000) 103 clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */ 104 else if (rate >= 456000) 105 clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */ 106 else 107 clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */ 108 109 /* 110 * target freq == pll_p, don't need to take extra reference to pll_x_clk 111 * as it isn't used anymore. 112 */ 113 if (rate == ifreq) 114 return clk_set_parent(cpu_clk, pll_p_clk); 115 116 ret = clk_set_rate(pll_x_clk, rate * 1000); 117 /* Restore to earlier frequency on error, i.e. pll_x */ 118 if (ret) 119 pr_err("Failed to change pll_x to %lu\n", rate); 120 121 ret = clk_set_parent(cpu_clk, pll_x_clk); 122 /* This shouldn't fail while changing or restoring */ 123 WARN_ON(ret); 124 125 /* 126 * Drop count to pll_x clock only if we switched to intermediate freq 127 * earlier while transitioning to a target frequency. 128 */ 129 if (pll_x_prepared) { 130 clk_disable_unprepare(pll_x_clk); 131 pll_x_prepared = false; 132 } 133 134 return ret; 135 } 136 137 static int tegra_cpu_init(struct cpufreq_policy *policy) 138 { 139 int ret; 140 141 if (policy->cpu >= NUM_CPUS) 142 return -EINVAL; 143 144 clk_prepare_enable(emc_clk); 145 clk_prepare_enable(cpu_clk); 146 147 /* FIXME: what's the actual transition time? */ 148 ret = cpufreq_generic_init(policy, freq_table, 300 * 1000); 149 if (ret) { 150 clk_disable_unprepare(cpu_clk); 151 clk_disable_unprepare(emc_clk); 152 return ret; 153 } 154 155 policy->clk = cpu_clk; 156 policy->suspend_freq = freq_table[0].frequency; 157 return 0; 158 } 159 160 static int tegra_cpu_exit(struct cpufreq_policy *policy) 161 { 162 clk_disable_unprepare(cpu_clk); 163 clk_disable_unprepare(emc_clk); 164 return 0; 165 } 166 167 static struct cpufreq_driver tegra_cpufreq_driver = { 168 .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, 169 .verify = cpufreq_generic_frequency_table_verify, 170 .get_intermediate = tegra_get_intermediate, 171 .target_intermediate = tegra_target_intermediate, 172 .target_index = tegra_target, 173 .get = cpufreq_generic_get, 174 .init = tegra_cpu_init, 175 .exit = tegra_cpu_exit, 176 .name = "tegra", 177 .attr = cpufreq_generic_attr, 178 .suspend = cpufreq_generic_suspend, 179 }; 180 181 static int __init tegra_cpufreq_init(void) 182 { 183 cpu_clk = clk_get_sys(NULL, "cclk"); 184 if (IS_ERR(cpu_clk)) 185 return PTR_ERR(cpu_clk); 186 187 pll_x_clk = clk_get_sys(NULL, "pll_x"); 188 if (IS_ERR(pll_x_clk)) 189 return PTR_ERR(pll_x_clk); 190 191 pll_p_clk = clk_get_sys(NULL, "pll_p"); 192 if (IS_ERR(pll_p_clk)) 193 return PTR_ERR(pll_p_clk); 194 195 emc_clk = clk_get_sys("cpu", "emc"); 196 if (IS_ERR(emc_clk)) { 197 clk_put(cpu_clk); 198 return PTR_ERR(emc_clk); 199 } 200 201 return cpufreq_register_driver(&tegra_cpufreq_driver); 202 } 203 204 static void __exit tegra_cpufreq_exit(void) 205 { 206 cpufreq_unregister_driver(&tegra_cpufreq_driver); 207 clk_put(emc_clk); 208 clk_put(cpu_clk); 209 } 210 211 212 MODULE_AUTHOR("Colin Cross <ccross@android.com>"); 213 MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2"); 214 MODULE_LICENSE("GPL"); 215 module_init(tegra_cpufreq_init); 216 module_exit(tegra_cpufreq_exit); 217