Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.10-rc4 292 lines 6.7 kB view raw
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#include <linux/suspend.h> 30 31/* Frequency table index must be sequential starting at 0 */ 32static struct cpufreq_frequency_table freq_table[] = { 33 { 0, 216000 }, 34 { 1, 312000 }, 35 { 2, 456000 }, 36 { 3, 608000 }, 37 { 4, 760000 }, 38 { 5, 816000 }, 39 { 6, 912000 }, 40 { 7, 1000000 }, 41 { 8, CPUFREQ_TABLE_END }, 42}; 43 44#define NUM_CPUS 2 45 46static struct clk *cpu_clk; 47static struct clk *pll_x_clk; 48static struct clk *pll_p_clk; 49static struct clk *emc_clk; 50 51static unsigned long target_cpu_speed[NUM_CPUS]; 52static DEFINE_MUTEX(tegra_cpu_lock); 53static bool is_suspended; 54 55static int tegra_verify_speed(struct cpufreq_policy *policy) 56{ 57 return cpufreq_frequency_table_verify(policy, freq_table); 58} 59 60static unsigned int tegra_getspeed(unsigned int cpu) 61{ 62 unsigned long rate; 63 64 if (cpu >= NUM_CPUS) 65 return 0; 66 67 rate = clk_get_rate(cpu_clk) / 1000; 68 return rate; 69} 70 71static int tegra_cpu_clk_set_rate(unsigned long rate) 72{ 73 int ret; 74 75 /* 76 * Take an extra reference to the main pll so it doesn't turn 77 * off when we move the cpu off of it 78 */ 79 clk_prepare_enable(pll_x_clk); 80 81 ret = clk_set_parent(cpu_clk, pll_p_clk); 82 if (ret) { 83 pr_err("Failed to switch cpu to clock pll_p\n"); 84 goto out; 85 } 86 87 if (rate == clk_get_rate(pll_p_clk)) 88 goto out; 89 90 ret = clk_set_rate(pll_x_clk, rate); 91 if (ret) { 92 pr_err("Failed to change pll_x to %lu\n", rate); 93 goto out; 94 } 95 96 ret = clk_set_parent(cpu_clk, pll_x_clk); 97 if (ret) { 98 pr_err("Failed to switch cpu to clock pll_x\n"); 99 goto out; 100 } 101 102out: 103 clk_disable_unprepare(pll_x_clk); 104 return ret; 105} 106 107static int tegra_update_cpu_speed(struct cpufreq_policy *policy, 108 unsigned long rate) 109{ 110 int ret = 0; 111 struct cpufreq_freqs freqs; 112 113 freqs.old = tegra_getspeed(0); 114 freqs.new = rate; 115 116 if (freqs.old == freqs.new) 117 return ret; 118 119 /* 120 * Vote on memory bus frequency based on cpu frequency 121 * This sets the minimum frequency, display or avp may request higher 122 */ 123 if (rate >= 816000) 124 clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */ 125 else if (rate >= 456000) 126 clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */ 127 else 128 clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */ 129 130 cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); 131 132#ifdef CONFIG_CPU_FREQ_DEBUG 133 printk(KERN_DEBUG "cpufreq-tegra: transition: %u --> %u\n", 134 freqs.old, freqs.new); 135#endif 136 137 ret = tegra_cpu_clk_set_rate(freqs.new * 1000); 138 if (ret) { 139 pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n", 140 freqs.new); 141 return ret; 142 } 143 144 cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); 145 146 return 0; 147} 148 149static unsigned long tegra_cpu_highest_speed(void) 150{ 151 unsigned long rate = 0; 152 int i; 153 154 for_each_online_cpu(i) 155 rate = max(rate, target_cpu_speed[i]); 156 return rate; 157} 158 159static int tegra_target(struct cpufreq_policy *policy, 160 unsigned int target_freq, 161 unsigned int relation) 162{ 163 unsigned int idx; 164 unsigned int freq; 165 int ret = 0; 166 167 mutex_lock(&tegra_cpu_lock); 168 169 if (is_suspended) { 170 ret = -EBUSY; 171 goto out; 172 } 173 174 cpufreq_frequency_table_target(policy, freq_table, target_freq, 175 relation, &idx); 176 177 freq = freq_table[idx].frequency; 178 179 target_cpu_speed[policy->cpu] = freq; 180 181 ret = tegra_update_cpu_speed(policy, tegra_cpu_highest_speed()); 182 183out: 184 mutex_unlock(&tegra_cpu_lock); 185 return ret; 186} 187 188static int tegra_pm_notify(struct notifier_block *nb, unsigned long event, 189 void *dummy) 190{ 191 mutex_lock(&tegra_cpu_lock); 192 if (event == PM_SUSPEND_PREPARE) { 193 struct cpufreq_policy *policy = cpufreq_cpu_get(0); 194 is_suspended = true; 195 pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n", 196 freq_table[0].frequency); 197 tegra_update_cpu_speed(policy, freq_table[0].frequency); 198 cpufreq_cpu_put(policy); 199 } else if (event == PM_POST_SUSPEND) { 200 is_suspended = false; 201 } 202 mutex_unlock(&tegra_cpu_lock); 203 204 return NOTIFY_OK; 205} 206 207static struct notifier_block tegra_cpu_pm_notifier = { 208 .notifier_call = tegra_pm_notify, 209}; 210 211static int tegra_cpu_init(struct cpufreq_policy *policy) 212{ 213 if (policy->cpu >= NUM_CPUS) 214 return -EINVAL; 215 216 clk_prepare_enable(emc_clk); 217 clk_prepare_enable(cpu_clk); 218 219 cpufreq_frequency_table_cpuinfo(policy, freq_table); 220 cpufreq_frequency_table_get_attr(freq_table, policy->cpu); 221 policy->cur = tegra_getspeed(policy->cpu); 222 target_cpu_speed[policy->cpu] = policy->cur; 223 224 /* FIXME: what's the actual transition time? */ 225 policy->cpuinfo.transition_latency = 300 * 1000; 226 227 cpumask_copy(policy->cpus, cpu_possible_mask); 228 229 if (policy->cpu == 0) 230 register_pm_notifier(&tegra_cpu_pm_notifier); 231 232 return 0; 233} 234 235static int tegra_cpu_exit(struct cpufreq_policy *policy) 236{ 237 cpufreq_frequency_table_cpuinfo(policy, freq_table); 238 clk_disable_unprepare(emc_clk); 239 return 0; 240} 241 242static struct freq_attr *tegra_cpufreq_attr[] = { 243 &cpufreq_freq_attr_scaling_available_freqs, 244 NULL, 245}; 246 247static struct cpufreq_driver tegra_cpufreq_driver = { 248 .verify = tegra_verify_speed, 249 .target = tegra_target, 250 .get = tegra_getspeed, 251 .init = tegra_cpu_init, 252 .exit = tegra_cpu_exit, 253 .name = "tegra", 254 .attr = tegra_cpufreq_attr, 255}; 256 257static int __init tegra_cpufreq_init(void) 258{ 259 cpu_clk = clk_get_sys(NULL, "cpu"); 260 if (IS_ERR(cpu_clk)) 261 return PTR_ERR(cpu_clk); 262 263 pll_x_clk = clk_get_sys(NULL, "pll_x"); 264 if (IS_ERR(pll_x_clk)) 265 return PTR_ERR(pll_x_clk); 266 267 pll_p_clk = clk_get_sys(NULL, "pll_p_cclk"); 268 if (IS_ERR(pll_p_clk)) 269 return PTR_ERR(pll_p_clk); 270 271 emc_clk = clk_get_sys("cpu", "emc"); 272 if (IS_ERR(emc_clk)) { 273 clk_put(cpu_clk); 274 return PTR_ERR(emc_clk); 275 } 276 277 return cpufreq_register_driver(&tegra_cpufreq_driver); 278} 279 280static void __exit tegra_cpufreq_exit(void) 281{ 282 cpufreq_unregister_driver(&tegra_cpufreq_driver); 283 clk_put(emc_clk); 284 clk_put(cpu_clk); 285} 286 287 288MODULE_AUTHOR("Colin Cross <ccross@android.com>"); 289MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2"); 290MODULE_LICENSE("GPL"); 291module_init(tegra_cpufreq_init); 292module_exit(tegra_cpufreq_exit);