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