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

Configure Feed

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

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