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.9-rc2 542 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.cpu = 0; 260 freqs.flags = 0; 261 freqs.old = s3c_freq->is_dvs ? FREQ_DVS 262 : clk_get_rate(s3c_freq->armclk) / 1000; 263 264 /* When leavin dvs mode, always switch the armdiv to the hclk rate 265 * The S3C2416 has stability issues when switching directly to 266 * higher frequencies. 267 */ 268 freqs.new = (s3c_freq->is_dvs && !to_dvs) 269 ? clk_get_rate(s3c_freq->hclk) / 1000 270 : s3c_freq->freq_table[i].frequency; 271 272 pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new); 273 274 if (!to_dvs && freqs.old == freqs.new) 275 goto out; 276 277 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 278 279 if (to_dvs) { 280 pr_debug("cpufreq: enter dvs\n"); 281 ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx); 282 } else if (s3c_freq->is_dvs) { 283 pr_debug("cpufreq: leave dvs\n"); 284 ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx); 285 } else { 286 pr_debug("cpufreq: change armdiv to %dkHz\n", freqs.new); 287 ret = s3c2416_cpufreq_set_armdiv(s3c_freq, freqs.new); 288 } 289 290 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 291 292out: 293 mutex_unlock(&cpufreq_lock); 294 295 return ret; 296} 297 298#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 299static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq) 300{ 301 int count, v, i, found; 302 struct cpufreq_frequency_table *freq; 303 struct s3c2416_dvfs *dvfs; 304 305 count = regulator_count_voltages(s3c_freq->vddarm); 306 if (count < 0) { 307 pr_err("cpufreq: Unable to check supported voltages\n"); 308 return; 309 } 310 311 freq = s3c_freq->freq_table; 312 while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) { 313 if (freq->frequency == CPUFREQ_ENTRY_INVALID) 314 continue; 315 316 dvfs = &s3c2416_dvfs_table[freq->index]; 317 found = 0; 318 319 /* Check only the min-voltage, more is always ok on S3C2416 */ 320 for (i = 0; i < count; i++) { 321 v = regulator_list_voltage(s3c_freq->vddarm, i); 322 if (v >= dvfs->vddarm_min) 323 found = 1; 324 } 325 326 if (!found) { 327 pr_debug("cpufreq: %dkHz unsupported by regulator\n", 328 freq->frequency); 329 freq->frequency = CPUFREQ_ENTRY_INVALID; 330 } 331 332 freq++; 333 } 334 335 /* Guessed */ 336 s3c_freq->regulator_latency = 1 * 1000 * 1000; 337} 338#endif 339 340static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this, 341 unsigned long event, void *ptr) 342{ 343 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 344 int ret; 345 346 mutex_lock(&cpufreq_lock); 347 348 /* disable further changes */ 349 s3c_freq->disable_dvs = 1; 350 351 mutex_unlock(&cpufreq_lock); 352 353 /* some boards don't reconfigure the regulator on reboot, which 354 * could lead to undervolting the cpu when the clock is reset. 355 * Therefore we always leave the DVS mode on reboot. 356 */ 357 if (s3c_freq->is_dvs) { 358 pr_debug("cpufreq: leave dvs on reboot\n"); 359 ret = cpufreq_driver_target(cpufreq_cpu_get(0), FREQ_SLEEP, 0); 360 if (ret < 0) 361 return NOTIFY_BAD; 362 } 363 364 return NOTIFY_DONE; 365} 366 367static struct notifier_block s3c2416_cpufreq_reboot_notifier = { 368 .notifier_call = s3c2416_cpufreq_reboot_notifier_evt, 369}; 370 371static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) 372{ 373 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 374 struct cpufreq_frequency_table *freq; 375 struct clk *msysclk; 376 unsigned long rate; 377 int ret; 378 379 if (policy->cpu != 0) 380 return -EINVAL; 381 382 msysclk = clk_get(NULL, "msysclk"); 383 if (IS_ERR(msysclk)) { 384 ret = PTR_ERR(msysclk); 385 pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret); 386 return ret; 387 } 388 389 /* 390 * S3C2416 and S3C2450 share the same processor-ID and also provide no 391 * other means to distinguish them other than through the rate of 392 * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz. 393 */ 394 rate = clk_get_rate(msysclk); 395 if (rate == 800 * 1000 * 1000) { 396 pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n", 397 rate / 1000); 398 s3c_freq->freq_table = s3c2416_freq_table; 399 policy->cpuinfo.max_freq = 400000; 400 } else if (rate / 1000 == 534000) { 401 pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n", 402 rate / 1000); 403 s3c_freq->freq_table = s3c2450_freq_table; 404 policy->cpuinfo.max_freq = 534000; 405 } 406 407 /* not needed anymore */ 408 clk_put(msysclk); 409 410 if (s3c_freq->freq_table == NULL) { 411 pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n", 412 rate / 1000); 413 return -ENODEV; 414 } 415 416 s3c_freq->is_dvs = 0; 417 418 s3c_freq->armdiv = clk_get(NULL, "armdiv"); 419 if (IS_ERR(s3c_freq->armdiv)) { 420 ret = PTR_ERR(s3c_freq->armdiv); 421 pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret); 422 return ret; 423 } 424 425 s3c_freq->hclk = clk_get(NULL, "hclk"); 426 if (IS_ERR(s3c_freq->hclk)) { 427 ret = PTR_ERR(s3c_freq->hclk); 428 pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret); 429 goto err_hclk; 430 } 431 432 /* chech hclk rate, we only support the common 133MHz for now 433 * hclk could also run at 66MHz, but this not often used 434 */ 435 rate = clk_get_rate(s3c_freq->hclk); 436 if (rate < 133 * 1000 * 1000) { 437 pr_err("cpufreq: HCLK not at 133MHz\n"); 438 clk_put(s3c_freq->hclk); 439 ret = -EINVAL; 440 goto err_armclk; 441 } 442 443 s3c_freq->armclk = clk_get(NULL, "armclk"); 444 if (IS_ERR(s3c_freq->armclk)) { 445 ret = PTR_ERR(s3c_freq->armclk); 446 pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret); 447 goto err_armclk; 448 } 449 450#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 451 s3c_freq->vddarm = regulator_get(NULL, "vddarm"); 452 if (IS_ERR(s3c_freq->vddarm)) { 453 ret = PTR_ERR(s3c_freq->vddarm); 454 pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret); 455 goto err_vddarm; 456 } 457 458 s3c2416_cpufreq_cfg_regulator(s3c_freq); 459#else 460 s3c_freq->regulator_latency = 0; 461#endif 462 463 freq = s3c_freq->freq_table; 464 while (freq->frequency != CPUFREQ_TABLE_END) { 465 /* special handling for dvs mode */ 466 if (freq->index == 0) { 467 if (!s3c_freq->hclk) { 468 pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n", 469 freq->frequency); 470 freq->frequency = CPUFREQ_ENTRY_INVALID; 471 } else { 472 freq++; 473 continue; 474 } 475 } 476 477 /* Check for frequencies we can generate */ 478 rate = clk_round_rate(s3c_freq->armdiv, 479 freq->frequency * 1000); 480 rate /= 1000; 481 if (rate != freq->frequency) { 482 pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n", 483 freq->frequency, rate); 484 freq->frequency = CPUFREQ_ENTRY_INVALID; 485 } 486 487 freq++; 488 } 489 490 policy->cur = clk_get_rate(s3c_freq->armclk) / 1000; 491 492 /* Datasheet says PLL stabalisation time must be at least 300us, 493 * so but add some fudge. (reference in LOCKCON0 register description) 494 */ 495 policy->cpuinfo.transition_latency = (500 * 1000) + 496 s3c_freq->regulator_latency; 497 498 ret = cpufreq_frequency_table_cpuinfo(policy, s3c_freq->freq_table); 499 if (ret) 500 goto err_freq_table; 501 502 cpufreq_frequency_table_get_attr(s3c_freq->freq_table, 0); 503 504 register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier); 505 506 return 0; 507 508err_freq_table: 509#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 510 regulator_put(s3c_freq->vddarm); 511err_vddarm: 512#endif 513 clk_put(s3c_freq->armclk); 514err_armclk: 515 clk_put(s3c_freq->hclk); 516err_hclk: 517 clk_put(s3c_freq->armdiv); 518 519 return ret; 520} 521 522static struct freq_attr *s3c2416_cpufreq_attr[] = { 523 &cpufreq_freq_attr_scaling_available_freqs, 524 NULL, 525}; 526 527static struct cpufreq_driver s3c2416_cpufreq_driver = { 528 .owner = THIS_MODULE, 529 .flags = 0, 530 .verify = s3c2416_cpufreq_verify_speed, 531 .target = s3c2416_cpufreq_set_target, 532 .get = s3c2416_cpufreq_get_speed, 533 .init = s3c2416_cpufreq_driver_init, 534 .name = "s3c2416", 535 .attr = s3c2416_cpufreq_attr, 536}; 537 538static int __init s3c2416_cpufreq_init(void) 539{ 540 return cpufreq_register_driver(&s3c2416_cpufreq_driver); 541} 542module_init(s3c2416_cpufreq_init);