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 { 0, SOURCE_HCLK, FREQ_DVS }, 76 { 0, SOURCE_ARMDIV, 133333 }, 77 { 0, SOURCE_ARMDIV, 266666 }, 78 { 0, SOURCE_ARMDIV, 400000 }, 79 { 0, 0, CPUFREQ_TABLE_END }, 80}; 81 82static struct cpufreq_frequency_table s3c2450_freq_table[] = { 83 { 0, SOURCE_HCLK, FREQ_DVS }, 84 { 0, SOURCE_ARMDIV, 133500 }, 85 { 0, SOURCE_ARMDIV, 267000 }, 86 { 0, SOURCE_ARMDIV, 534000 }, 87 { 0, 0, CPUFREQ_TABLE_END }, 88}; 89 90static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu) 91{ 92 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 93 94 if (cpu != 0) 95 return 0; 96 97 /* return our pseudo-frequency when in dvs mode */ 98 if (s3c_freq->is_dvs) 99 return FREQ_DVS; 100 101 return clk_get_rate(s3c_freq->armclk) / 1000; 102} 103 104static int s3c2416_cpufreq_set_armdiv(struct s3c2416_data *s3c_freq, 105 unsigned int freq) 106{ 107 int ret; 108 109 if (clk_get_rate(s3c_freq->armdiv) / 1000 != freq) { 110 ret = clk_set_rate(s3c_freq->armdiv, freq * 1000); 111 if (ret < 0) { 112 pr_err("cpufreq: Failed to set armdiv rate %dkHz: %d\n", 113 freq, ret); 114 return ret; 115 } 116 } 117 118 return 0; 119} 120 121static int s3c2416_cpufreq_enter_dvs(struct s3c2416_data *s3c_freq, int idx) 122{ 123#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 124 struct s3c2416_dvfs *dvfs; 125#endif 126 int ret; 127 128 if (s3c_freq->is_dvs) { 129 pr_debug("cpufreq: already in dvs mode, nothing to do\n"); 130 return 0; 131 } 132 133 pr_debug("cpufreq: switching armclk to hclk (%lukHz)\n", 134 clk_get_rate(s3c_freq->hclk) / 1000); 135 ret = clk_set_parent(s3c_freq->armclk, s3c_freq->hclk); 136 if (ret < 0) { 137 pr_err("cpufreq: Failed to switch armclk to hclk: %d\n", ret); 138 return ret; 139 } 140 141#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 142 /* changing the core voltage is only allowed when in dvs mode */ 143 if (s3c_freq->vddarm) { 144 dvfs = &s3c2416_dvfs_table[idx]; 145 146 pr_debug("cpufreq: setting regulator to %d-%d\n", 147 dvfs->vddarm_min, dvfs->vddarm_max); 148 ret = regulator_set_voltage(s3c_freq->vddarm, 149 dvfs->vddarm_min, 150 dvfs->vddarm_max); 151 152 /* when lowering the voltage failed, there is nothing to do */ 153 if (ret != 0) 154 pr_err("cpufreq: Failed to set VDDARM: %d\n", ret); 155 } 156#endif 157 158 s3c_freq->is_dvs = 1; 159 160 return 0; 161} 162 163static int s3c2416_cpufreq_leave_dvs(struct s3c2416_data *s3c_freq, int idx) 164{ 165#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 166 struct s3c2416_dvfs *dvfs; 167#endif 168 int ret; 169 170 if (!s3c_freq->is_dvs) { 171 pr_debug("cpufreq: not in dvs mode, so can't leave\n"); 172 return 0; 173 } 174 175#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 176 if (s3c_freq->vddarm) { 177 dvfs = &s3c2416_dvfs_table[idx]; 178 179 pr_debug("cpufreq: setting regulator to %d-%d\n", 180 dvfs->vddarm_min, dvfs->vddarm_max); 181 ret = regulator_set_voltage(s3c_freq->vddarm, 182 dvfs->vddarm_min, 183 dvfs->vddarm_max); 184 if (ret != 0) { 185 pr_err("cpufreq: Failed to set VDDARM: %d\n", ret); 186 return ret; 187 } 188 } 189#endif 190 191 /* force armdiv to hclk frequency for transition from dvs*/ 192 if (clk_get_rate(s3c_freq->armdiv) > clk_get_rate(s3c_freq->hclk)) { 193 pr_debug("cpufreq: force armdiv to hclk frequency (%lukHz)\n", 194 clk_get_rate(s3c_freq->hclk) / 1000); 195 ret = s3c2416_cpufreq_set_armdiv(s3c_freq, 196 clk_get_rate(s3c_freq->hclk) / 1000); 197 if (ret < 0) { 198 pr_err("cpufreq: Failed to set the armdiv to %lukHz: %d\n", 199 clk_get_rate(s3c_freq->hclk) / 1000, ret); 200 return ret; 201 } 202 } 203 204 pr_debug("cpufreq: switching armclk parent to armdiv (%lukHz)\n", 205 clk_get_rate(s3c_freq->armdiv) / 1000); 206 207 ret = clk_set_parent(s3c_freq->armclk, s3c_freq->armdiv); 208 if (ret < 0) { 209 pr_err("cpufreq: Failed to switch armclk clock parent to armdiv: %d\n", 210 ret); 211 return ret; 212 } 213 214 s3c_freq->is_dvs = 0; 215 216 return 0; 217} 218 219static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, 220 unsigned int index) 221{ 222 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 223 unsigned int new_freq; 224 int idx, ret, to_dvs = 0; 225 226 mutex_lock(&cpufreq_lock); 227 228 idx = s3c_freq->freq_table[index].driver_data; 229 230 if (idx == SOURCE_HCLK) 231 to_dvs = 1; 232 233 /* switching to dvs when it's not allowed */ 234 if (to_dvs && s3c_freq->disable_dvs) { 235 pr_debug("cpufreq: entering dvs mode not allowed\n"); 236 ret = -EINVAL; 237 goto out; 238 } 239 240 /* When leavin dvs mode, always switch the armdiv to the hclk rate 241 * The S3C2416 has stability issues when switching directly to 242 * higher frequencies. 243 */ 244 new_freq = (s3c_freq->is_dvs && !to_dvs) 245 ? clk_get_rate(s3c_freq->hclk) / 1000 246 : s3c_freq->freq_table[index].frequency; 247 248 if (to_dvs) { 249 pr_debug("cpufreq: enter dvs\n"); 250 ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx); 251 } else if (s3c_freq->is_dvs) { 252 pr_debug("cpufreq: leave dvs\n"); 253 ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx); 254 } else { 255 pr_debug("cpufreq: change armdiv to %dkHz\n", new_freq); 256 ret = s3c2416_cpufreq_set_armdiv(s3c_freq, new_freq); 257 } 258 259out: 260 mutex_unlock(&cpufreq_lock); 261 262 return ret; 263} 264 265#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 266static void s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq) 267{ 268 int count, v, i, found; 269 struct cpufreq_frequency_table *pos; 270 struct s3c2416_dvfs *dvfs; 271 272 count = regulator_count_voltages(s3c_freq->vddarm); 273 if (count < 0) { 274 pr_err("cpufreq: Unable to check supported voltages\n"); 275 return; 276 } 277 278 if (!count) 279 goto out; 280 281 cpufreq_for_each_valid_entry(pos, s3c_freq->freq_table) { 282 dvfs = &s3c2416_dvfs_table[pos->driver_data]; 283 found = 0; 284 285 /* Check only the min-voltage, more is always ok on S3C2416 */ 286 for (i = 0; i < count; i++) { 287 v = regulator_list_voltage(s3c_freq->vddarm, i); 288 if (v >= dvfs->vddarm_min) 289 found = 1; 290 } 291 292 if (!found) { 293 pr_debug("cpufreq: %dkHz unsupported by regulator\n", 294 pos->frequency); 295 pos->frequency = CPUFREQ_ENTRY_INVALID; 296 } 297 } 298 299out: 300 /* Guessed */ 301 s3c_freq->regulator_latency = 1 * 1000 * 1000; 302} 303#endif 304 305static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this, 306 unsigned long event, void *ptr) 307{ 308 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 309 int ret; 310 311 mutex_lock(&cpufreq_lock); 312 313 /* disable further changes */ 314 s3c_freq->disable_dvs = 1; 315 316 mutex_unlock(&cpufreq_lock); 317 318 /* some boards don't reconfigure the regulator on reboot, which 319 * could lead to undervolting the cpu when the clock is reset. 320 * Therefore we always leave the DVS mode on reboot. 321 */ 322 if (s3c_freq->is_dvs) { 323 pr_debug("cpufreq: leave dvs on reboot\n"); 324 ret = cpufreq_driver_target(cpufreq_cpu_get(0), FREQ_SLEEP, 0); 325 if (ret < 0) 326 return NOTIFY_BAD; 327 } 328 329 return NOTIFY_DONE; 330} 331 332static struct notifier_block s3c2416_cpufreq_reboot_notifier = { 333 .notifier_call = s3c2416_cpufreq_reboot_notifier_evt, 334}; 335 336static int s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) 337{ 338 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq; 339 struct cpufreq_frequency_table *pos; 340 struct clk *msysclk; 341 unsigned long rate; 342 int ret; 343 344 if (policy->cpu != 0) 345 return -EINVAL; 346 347 msysclk = clk_get(NULL, "msysclk"); 348 if (IS_ERR(msysclk)) { 349 ret = PTR_ERR(msysclk); 350 pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret); 351 return ret; 352 } 353 354 /* 355 * S3C2416 and S3C2450 share the same processor-ID and also provide no 356 * other means to distinguish them other than through the rate of 357 * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz. 358 */ 359 rate = clk_get_rate(msysclk); 360 if (rate == 800 * 1000 * 1000) { 361 pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n", 362 rate / 1000); 363 s3c_freq->freq_table = s3c2416_freq_table; 364 policy->cpuinfo.max_freq = 400000; 365 } else if (rate / 1000 == 534000) { 366 pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n", 367 rate / 1000); 368 s3c_freq->freq_table = s3c2450_freq_table; 369 policy->cpuinfo.max_freq = 534000; 370 } 371 372 /* not needed anymore */ 373 clk_put(msysclk); 374 375 if (s3c_freq->freq_table == NULL) { 376 pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n", 377 rate / 1000); 378 return -ENODEV; 379 } 380 381 s3c_freq->is_dvs = 0; 382 383 s3c_freq->armdiv = clk_get(NULL, "armdiv"); 384 if (IS_ERR(s3c_freq->armdiv)) { 385 ret = PTR_ERR(s3c_freq->armdiv); 386 pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret); 387 return ret; 388 } 389 390 s3c_freq->hclk = clk_get(NULL, "hclk"); 391 if (IS_ERR(s3c_freq->hclk)) { 392 ret = PTR_ERR(s3c_freq->hclk); 393 pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret); 394 goto err_hclk; 395 } 396 397 /* chech hclk rate, we only support the common 133MHz for now 398 * hclk could also run at 66MHz, but this not often used 399 */ 400 rate = clk_get_rate(s3c_freq->hclk); 401 if (rate < 133 * 1000 * 1000) { 402 pr_err("cpufreq: HCLK not at 133MHz\n"); 403 clk_put(s3c_freq->hclk); 404 ret = -EINVAL; 405 goto err_armclk; 406 } 407 408 s3c_freq->armclk = clk_get(NULL, "armclk"); 409 if (IS_ERR(s3c_freq->armclk)) { 410 ret = PTR_ERR(s3c_freq->armclk); 411 pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret); 412 goto err_armclk; 413 } 414 415#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 416 s3c_freq->vddarm = regulator_get(NULL, "vddarm"); 417 if (IS_ERR(s3c_freq->vddarm)) { 418 ret = PTR_ERR(s3c_freq->vddarm); 419 pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret); 420 goto err_vddarm; 421 } 422 423 s3c2416_cpufreq_cfg_regulator(s3c_freq); 424#else 425 s3c_freq->regulator_latency = 0; 426#endif 427 428 cpufreq_for_each_entry(pos, s3c_freq->freq_table) { 429 /* special handling for dvs mode */ 430 if (pos->driver_data == 0) { 431 if (!s3c_freq->hclk) { 432 pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n", 433 pos->frequency); 434 pos->frequency = CPUFREQ_ENTRY_INVALID; 435 } else { 436 continue; 437 } 438 } 439 440 /* Check for frequencies we can generate */ 441 rate = clk_round_rate(s3c_freq->armdiv, 442 pos->frequency * 1000); 443 rate /= 1000; 444 if (rate != pos->frequency) { 445 pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n", 446 pos->frequency, rate); 447 pos->frequency = CPUFREQ_ENTRY_INVALID; 448 } 449 } 450 451 /* Datasheet says PLL stabalisation time must be at least 300us, 452 * so but add some fudge. (reference in LOCKCON0 register description) 453 */ 454 ret = cpufreq_generic_init(policy, s3c_freq->freq_table, 455 (500 * 1000) + s3c_freq->regulator_latency); 456 if (ret) 457 goto err_freq_table; 458 459 register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier); 460 461 return 0; 462 463err_freq_table: 464#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE 465 regulator_put(s3c_freq->vddarm); 466err_vddarm: 467#endif 468 clk_put(s3c_freq->armclk); 469err_armclk: 470 clk_put(s3c_freq->hclk); 471err_hclk: 472 clk_put(s3c_freq->armdiv); 473 474 return ret; 475} 476 477static struct cpufreq_driver s3c2416_cpufreq_driver = { 478 .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, 479 .verify = cpufreq_generic_frequency_table_verify, 480 .target_index = s3c2416_cpufreq_set_target, 481 .get = s3c2416_cpufreq_get_speed, 482 .init = s3c2416_cpufreq_driver_init, 483 .name = "s3c2416", 484 .attr = cpufreq_generic_attr, 485}; 486 487static int __init s3c2416_cpufreq_init(void) 488{ 489 return cpufreq_register_driver(&s3c2416_cpufreq_driver); 490} 491module_init(s3c2416_cpufreq_init); 492