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-rc2 541 lines 14 kB view raw
1/* 2 * S3C2416/2450 CPUfreq Support 3 * 4 * Copyright 2011 Heiko Stuebner <heiko@sntech.de> 5 * 6 * based on s3c64xx_cpufreq.c 7 * 8 * Copyright 2009 Wolfson Microelectronics plc 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 15#include <linux/kernel.h> 16#include <linux/types.h> 17#include <linux/init.h> 18#include <linux/cpufreq.h> 19#include <linux/clk.h> 20#include <linux/err.h> 21#include <linux/regulator/consumer.h> 22#include <linux/reboot.h> 23#include <linux/module.h> 24 25static DEFINE_MUTEX(cpufreq_lock); 26 27struct s3c2416_data { 28 struct clk *armdiv; 29 struct clk *armclk; 30 struct clk *hclk; 31 32 unsigned long regulator_latency; 33#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 34 struct regulator *vddarm; 35#endif 36 37 struct cpufreq_frequency_table *freq_table; 38 39 bool is_dvs; 40 bool disable_dvs; 41}; 42 43static struct s3c2416_data s3c2416_cpufreq; 44 45struct s3c2416_dvfs { 46 unsigned int vddarm_min; 47 unsigned int vddarm_max; 48}; 49 50/* pseudo-frequency for dvs mode */ 51#define FREQ_DVS 132333 52 53/* frequency to sleep and reboot in 54 * it's essential to leave dvs, as some boards do not reconfigure the 55 * regulator on reboot 56 */ 57#define FREQ_SLEEP 133333 58 59/* Sources for the ARMCLK */ 60#define SOURCE_HCLK 0 61#define SOURCE_ARMDIV 1 62 63#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 64/* S3C2416 only supports changing the voltage in the dvs-mode. 65 * Voltages down to 1.0V seem to work, so we take what the regulator 66 * can get us. 67 */ 68static struct s3c2416_dvfs s3c2416_dvfs_table[] = { 69 [SOURCE_HCLK] = { 950000, 1250000 }, 70 [SOURCE_ARMDIV] = { 1250000, 1350000 }, 71}; 72#endif 73 74static struct cpufreq_frequency_table s3c2416_freq_table[] = { 75 { SOURCE_HCLK, FREQ_DVS }, 76 { SOURCE_ARMDIV, 133333 }, 77 { SOURCE_ARMDIV, 266666 }, 78 { SOURCE_ARMDIV, 400000 }, 79 { 0, CPUFREQ_TABLE_END }, 80}; 81 82static struct cpufreq_frequency_table s3c2450_freq_table[] = { 83 { SOURCE_HCLK, FREQ_DVS }, 84 { SOURCE_ARMDIV, 133500 }, 85 { SOURCE_ARMDIV, 267000 }, 86 { SOURCE_ARMDIV, 534000 }, 87 { 0, CPUFREQ_TABLE_END }, 88}; 89 90static int s3c2416_cpufreq_verify_speed(struct cpufreq_policy *policy) 91{ 92 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 93 94 if (policy->cpu != 0) 95 return -EINVAL; 96 97 return cpufreq_frequency_table_verify(policy, s3c_freq->freq_table); 98} 99 100static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu) 101{ 102 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 103 104 if (cpu != 0) 105 return 0; 106 107 /* return our pseudo-frequency when in dvs mode */ 108 if (s3c_freq->is_dvs) 109 return FREQ_DVS; 110 111 return clk_get_rate(s3c_freq->armclk) / 1000; 112} 113 114static int s3c2416_cpufreq_set_armdiv(struct s3c2416_data *s3c_freq, 115 unsigned int freq) 116{ 117 int ret; 118 119 if (clk_get_rate(s3c_freq->armdiv) / 1000 != freq) { 120 ret = clk_set_rate(s3c_freq->armdiv, freq * 1000); 121 if (ret < 0) { 122 pr_err("cpufreq: Failed to set armdiv rate %dkHz: %d\n", 123 freq, ret); 124 return ret; 125 } 126 } 127 128 return 0; 129} 130 131static int s3c2416_cpufreq_enter_dvs(struct s3c2416_data *s3c_freq, int idx) 132{ 133#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 134 struct s3c2416_dvfs *dvfs; 135#endif 136 int ret; 137 138 if (s3c_freq->is_dvs) { 139 pr_debug("cpufreq: already in dvs mode, nothing to do\n"); 140 return 0; 141 } 142 143 pr_debug("cpufreq: switching armclk to hclk (%lukHz)\n", 144 clk_get_rate(s3c_freq->hclk) / 1000); 145 ret = clk_set_parent(s3c_freq->armclk, s3c_freq->hclk); 146 if (ret < 0) { 147 pr_err("cpufreq: Failed to switch armclk to hclk: %d\n", ret); 148 return ret; 149 } 150 151#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 152 /* changing the core voltage is only allowed when in dvs mode */ 153 if (s3c_freq->vddarm) { 154 dvfs = &s3c2416_dvfs_table[idx]; 155 156 pr_debug("cpufreq: setting regulator to %d-%d\n", 157 dvfs->vddarm_min, dvfs->vddarm_max); 158 ret = regulator_set_voltage(s3c_freq->vddarm, 159 dvfs->vddarm_min, 160 dvfs->vddarm_max); 161 162 /* when lowering the voltage failed, there is nothing to do */ 163 if (ret != 0) 164 pr_err("cpufreq: Failed to set VDDARM: %d\n", ret); 165 } 166#endif 167 168 s3c_freq->is_dvs = 1; 169 170 return 0; 171} 172 173static int s3c2416_cpufreq_leave_dvs(struct s3c2416_data *s3c_freq, int idx) 174{ 175#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 176 struct s3c2416_dvfs *dvfs; 177#endif 178 int ret; 179 180 if (!s3c_freq->is_dvs) { 181 pr_debug("cpufreq: not in dvs mode, so can't leave\n"); 182 return 0; 183 } 184 185#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 186 if (s3c_freq->vddarm) { 187 dvfs = &s3c2416_dvfs_table[idx]; 188 189 pr_debug("cpufreq: setting regulator to %d-%d\n", 190 dvfs->vddarm_min, dvfs->vddarm_max); 191 ret = regulator_set_voltage(s3c_freq->vddarm, 192 dvfs->vddarm_min, 193 dvfs->vddarm_max); 194 if (ret != 0) { 195 pr_err("cpufreq: Failed to set VDDARM: %d\n", ret); 196 return ret; 197 } 198 } 199#endif 200 201 /* force armdiv to hclk frequency for transition from dvs*/ 202 if (clk_get_rate(s3c_freq->armdiv) > clk_get_rate(s3c_freq->hclk)) { 203 pr_debug("cpufreq: force armdiv to hclk frequency (%lukHz)\n", 204 clk_get_rate(s3c_freq->hclk) / 1000); 205 ret = s3c2416_cpufreq_set_armdiv(s3c_freq, 206 clk_get_rate(s3c_freq->hclk) / 1000); 207 if (ret < 0) { 208 pr_err("cpufreq: Failed to to set the armdiv to %lukHz: %d\n", 209 clk_get_rate(s3c_freq->hclk) / 1000, ret); 210 return ret; 211 } 212 } 213 214 pr_debug("cpufreq: switching armclk parent to armdiv (%lukHz)\n", 215 clk_get_rate(s3c_freq->armdiv) / 1000); 216 217 ret = clk_set_parent(s3c_freq->armclk, s3c_freq->armdiv); 218 if (ret < 0) { 219 pr_err("cpufreq: Failed to switch armclk clock parent to armdiv: %d\n", 220 ret); 221 return ret; 222 } 223 224 s3c_freq->is_dvs = 0; 225 226 return 0; 227} 228 229static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, 230 unsigned int target_freq, 231 unsigned int relation) 232{ 233 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 234 struct cpufreq_freqs freqs; 235 int idx, ret, to_dvs = 0; 236 unsigned int i; 237 238 mutex_lock(&cpufreq_lock); 239 240 pr_debug("cpufreq: to %dKHz, relation %d\n", target_freq, relation); 241 242 ret = cpufreq_frequency_table_target(policy, s3c_freq->freq_table, 243 target_freq, relation, &i); 244 if (ret != 0) 245 goto out; 246 247 idx = s3c_freq->freq_table[i].index; 248 249 if (idx == SOURCE_HCLK) 250 to_dvs = 1; 251 252 /* switching to dvs when it's not allowed */ 253 if (to_dvs && s3c_freq->disable_dvs) { 254 pr_debug("cpufreq: entering dvs mode not allowed\n"); 255 ret = -EINVAL; 256 goto out; 257 } 258 259 freqs.flags = 0; 260 freqs.old = s3c_freq->is_dvs ? FREQ_DVS 261 : clk_get_rate(s3c_freq->armclk) / 1000; 262 263 /* When leavin dvs mode, always switch the armdiv to the hclk rate 264 * The S3C2416 has stability issues when switching directly to 265 * higher frequencies. 266 */ 267 freqs.new = (s3c_freq->is_dvs && !to_dvs) 268 ? clk_get_rate(s3c_freq->hclk) / 1000 269 : s3c_freq->freq_table[i].frequency; 270 271 pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new); 272 273 if (!to_dvs && freqs.old == freqs.new) 274 goto out; 275 276 cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); 277 278 if (to_dvs) { 279 pr_debug("cpufreq: enter dvs\n"); 280 ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx); 281 } else if (s3c_freq->is_dvs) { 282 pr_debug("cpufreq: leave dvs\n"); 283 ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx); 284 } else { 285 pr_debug("cpufreq: change armdiv to %dkHz\n", freqs.new); 286 ret = s3c2416_cpufreq_set_armdiv(s3c_freq, freqs.new); 287 } 288 289 cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); 290 291out: 292 mutex_unlock(&cpufreq_lock); 293 294 return ret; 295} 296 297#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 298static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq) 299{ 300 int count, v, i, found; 301 struct cpufreq_frequency_table *freq; 302 struct s3c2416_dvfs *dvfs; 303 304 count = regulator_count_voltages(s3c_freq->vddarm); 305 if (count < 0) { 306 pr_err("cpufreq: Unable to check supported voltages\n"); 307 return; 308 } 309 310 freq = s3c_freq->freq_table; 311 while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) { 312 if (freq->frequency == CPUFREQ_ENTRY_INVALID) 313 continue; 314 315 dvfs = &s3c2416_dvfs_table[freq->index]; 316 found = 0; 317 318 /* Check only the min-voltage, more is always ok on S3C2416 */ 319 for (i = 0; i < count; i++) { 320 v = regulator_list_voltage(s3c_freq->vddarm, i); 321 if (v >= dvfs->vddarm_min) 322 found = 1; 323 } 324 325 if (!found) { 326 pr_debug("cpufreq: %dkHz unsupported by regulator\n", 327 freq->frequency); 328 freq->frequency = CPUFREQ_ENTRY_INVALID; 329 } 330 331 freq++; 332 } 333 334 /* Guessed */ 335 s3c_freq->regulator_latency = 1 * 1000 * 1000; 336} 337#endif 338 339static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this, 340 unsigned long event, void *ptr) 341{ 342 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 343 int ret; 344 345 mutex_lock(&cpufreq_lock); 346 347 /* disable further changes */ 348 s3c_freq->disable_dvs = 1; 349 350 mutex_unlock(&cpufreq_lock); 351 352 /* some boards don't reconfigure the regulator on reboot, which 353 * could lead to undervolting the cpu when the clock is reset. 354 * Therefore we always leave the DVS mode on reboot. 355 */ 356 if (s3c_freq->is_dvs) { 357 pr_debug("cpufreq: leave dvs on reboot\n"); 358 ret = cpufreq_driver_target(cpufreq_cpu_get(0), FREQ_SLEEP, 0); 359 if (ret < 0) 360 return NOTIFY_BAD; 361 } 362 363 return NOTIFY_DONE; 364} 365 366static struct notifier_block s3c2416_cpufreq_reboot_notifier = { 367 .notifier_call = s3c2416_cpufreq_reboot_notifier_evt, 368}; 369 370static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) 371{ 372 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 373 struct cpufreq_frequency_table *freq; 374 struct clk *msysclk; 375 unsigned long rate; 376 int ret; 377 378 if (policy->cpu != 0) 379 return -EINVAL; 380 381 msysclk = clk_get(NULL, "msysclk"); 382 if (IS_ERR(msysclk)) { 383 ret = PTR_ERR(msysclk); 384 pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret); 385 return ret; 386 } 387 388 /* 389 * S3C2416 and S3C2450 share the same processor-ID and also provide no 390 * other means to distinguish them other than through the rate of 391 * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz. 392 */ 393 rate = clk_get_rate(msysclk); 394 if (rate == 800 * 1000 * 1000) { 395 pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n", 396 rate / 1000); 397 s3c_freq->freq_table = s3c2416_freq_table; 398 policy->cpuinfo.max_freq = 400000; 399 } else if (rate / 1000 == 534000) { 400 pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n", 401 rate / 1000); 402 s3c_freq->freq_table = s3c2450_freq_table; 403 policy->cpuinfo.max_freq = 534000; 404 } 405 406 /* not needed anymore */ 407 clk_put(msysclk); 408 409 if (s3c_freq->freq_table == NULL) { 410 pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n", 411 rate / 1000); 412 return -ENODEV; 413 } 414 415 s3c_freq->is_dvs = 0; 416 417 s3c_freq->armdiv = clk_get(NULL, "armdiv"); 418 if (IS_ERR(s3c_freq->armdiv)) { 419 ret = PTR_ERR(s3c_freq->armdiv); 420 pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret); 421 return ret; 422 } 423 424 s3c_freq->hclk = clk_get(NULL, "hclk"); 425 if (IS_ERR(s3c_freq->hclk)) { 426 ret = PTR_ERR(s3c_freq->hclk); 427 pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret); 428 goto err_hclk; 429 } 430 431 /* chech hclk rate, we only support the common 133MHz for now 432 * hclk could also run at 66MHz, but this not often used 433 */ 434 rate = clk_get_rate(s3c_freq->hclk); 435 if (rate < 133 * 1000 * 1000) { 436 pr_err("cpufreq: HCLK not at 133MHz\n"); 437 clk_put(s3c_freq->hclk); 438 ret = -EINVAL; 439 goto err_armclk; 440 } 441 442 s3c_freq->armclk = clk_get(NULL, "armclk"); 443 if (IS_ERR(s3c_freq->armclk)) { 444 ret = PTR_ERR(s3c_freq->armclk); 445 pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret); 446 goto err_armclk; 447 } 448 449#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 450 s3c_freq->vddarm = regulator_get(NULL, "vddarm"); 451 if (IS_ERR(s3c_freq->vddarm)) { 452 ret = PTR_ERR(s3c_freq->vddarm); 453 pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret); 454 goto err_vddarm; 455 } 456 457 s3c2416_cpufreq_cfg_regulator(s3c_freq); 458#else 459 s3c_freq->regulator_latency = 0; 460#endif 461 462 freq = s3c_freq->freq_table; 463 while (freq->frequency != CPUFREQ_TABLE_END) { 464 /* special handling for dvs mode */ 465 if (freq->index == 0) { 466 if (!s3c_freq->hclk) { 467 pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n", 468 freq->frequency); 469 freq->frequency = CPUFREQ_ENTRY_INVALID; 470 } else { 471 freq++; 472 continue; 473 } 474 } 475 476 /* Check for frequencies we can generate */ 477 rate = clk_round_rate(s3c_freq->armdiv, 478 freq->frequency * 1000); 479 rate /= 1000; 480 if (rate != freq->frequency) { 481 pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n", 482 freq->frequency, rate); 483 freq->frequency = CPUFREQ_ENTRY_INVALID; 484 } 485 486 freq++; 487 } 488 489 policy->cur = clk_get_rate(s3c_freq->armclk) / 1000; 490 491 /* Datasheet says PLL stabalisation time must be at least 300us, 492 * so but add some fudge. (reference in LOCKCON0 register description) 493 */ 494 policy->cpuinfo.transition_latency = (500 * 1000) + 495 s3c_freq->regulator_latency; 496 497 ret = cpufreq_frequency_table_cpuinfo(policy, s3c_freq->freq_table); 498 if (ret) 499 goto err_freq_table; 500 501 cpufreq_frequency_table_get_attr(s3c_freq->freq_table, 0); 502 503 register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier); 504 505 return 0; 506 507err_freq_table: 508#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 509 regulator_put(s3c_freq->vddarm); 510err_vddarm: 511#endif 512 clk_put(s3c_freq->armclk); 513err_armclk: 514 clk_put(s3c_freq->hclk); 515err_hclk: 516 clk_put(s3c_freq->armdiv); 517 518 return ret; 519} 520 521static struct freq_attr *s3c2416_cpufreq_attr[] = { 522 &cpufreq_freq_attr_scaling_available_freqs, 523 NULL, 524}; 525 526static struct cpufreq_driver s3c2416_cpufreq_driver = { 527 .owner = THIS_MODULE, 528 .flags = 0, 529 .verify = s3c2416_cpufreq_verify_speed, 530 .target = s3c2416_cpufreq_set_target, 531 .get = s3c2416_cpufreq_get_speed, 532 .init = s3c2416_cpufreq_driver_init, 533 .name = "s3c2416", 534 .attr = s3c2416_cpufreq_attr, 535}; 536 537static int __init s3c2416_cpufreq_init(void) 538{ 539 return cpufreq_register_driver(&s3c2416_cpufreq_driver); 540} 541module_init(s3c2416_cpufreq_init);