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.1-rc8 568 lines 14 kB view raw
1/* 2 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 3 * http://www.samsung.com 4 * 5 * EXYNOS4 - CPU frequency scaling support 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10*/ 11 12#include <linux/types.h> 13#include <linux/kernel.h> 14#include <linux/err.h> 15#include <linux/clk.h> 16#include <linux/io.h> 17#include <linux/slab.h> 18#include <linux/regulator/consumer.h> 19#include <linux/cpufreq.h> 20 21#include <mach/map.h> 22#include <mach/regs-clock.h> 23#include <mach/regs-mem.h> 24 25#include <plat/clock.h> 26#include <plat/pm.h> 27 28static struct clk *cpu_clk; 29static struct clk *moutcore; 30static struct clk *mout_mpll; 31static struct clk *mout_apll; 32 33static struct regulator *arm_regulator; 34static struct regulator *int_regulator; 35 36static struct cpufreq_freqs freqs; 37static unsigned int memtype; 38 39enum exynos4_memory_type { 40 DDR2 = 4, 41 LPDDR2, 42 DDR3, 43}; 44 45enum cpufreq_level_index { 46 L0, L1, L2, L3, CPUFREQ_LEVEL_END, 47}; 48 49static struct cpufreq_frequency_table exynos4_freq_table[] = { 50 {L0, 1000*1000}, 51 {L1, 800*1000}, 52 {L2, 400*1000}, 53 {L3, 100*1000}, 54 {0, CPUFREQ_TABLE_END}, 55}; 56 57static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END][7] = { 58 /* 59 * Clock divider value for following 60 * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH, 61 * DIVATB, DIVPCLK_DBG, DIVAPLL } 62 */ 63 64 /* ARM L0: 1000MHz */ 65 { 0, 3, 7, 3, 3, 0, 1 }, 66 67 /* ARM L1: 800MHz */ 68 { 0, 3, 7, 3, 3, 0, 1 }, 69 70 /* ARM L2: 400MHz */ 71 { 0, 1, 3, 1, 3, 0, 1 }, 72 73 /* ARM L3: 100MHz */ 74 { 0, 0, 1, 0, 3, 1, 1 }, 75}; 76 77static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = { 78 /* 79 * Clock divider value for following 80 * { DIVCOPY, DIVHPM } 81 */ 82 83 /* ARM L0: 1000MHz */ 84 { 3, 0 }, 85 86 /* ARM L1: 800MHz */ 87 { 3, 0 }, 88 89 /* ARM L2: 400MHz */ 90 { 3, 0 }, 91 92 /* ARM L3: 100MHz */ 93 { 3, 0 }, 94}; 95 96static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END][8] = { 97 /* 98 * Clock divider value for following 99 * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD 100 * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS } 101 */ 102 103 /* DMC L0: 400MHz */ 104 { 3, 1, 1, 1, 1, 1, 3, 1 }, 105 106 /* DMC L1: 400MHz */ 107 { 3, 1, 1, 1, 1, 1, 3, 1 }, 108 109 /* DMC L2: 266.7MHz */ 110 { 7, 1, 1, 2, 1, 1, 3, 1 }, 111 112 /* DMC L3: 200MHz */ 113 { 7, 1, 1, 3, 1, 1, 3, 1 }, 114}; 115 116static unsigned int clkdiv_top[CPUFREQ_LEVEL_END][5] = { 117 /* 118 * Clock divider value for following 119 * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND } 120 */ 121 122 /* ACLK200 L0: 200MHz */ 123 { 3, 7, 4, 5, 1 }, 124 125 /* ACLK200 L1: 200MHz */ 126 { 3, 7, 4, 5, 1 }, 127 128 /* ACLK200 L2: 160MHz */ 129 { 4, 7, 5, 7, 1 }, 130 131 /* ACLK200 L3: 133.3MHz */ 132 { 5, 7, 7, 7, 1 }, 133}; 134 135static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END][2] = { 136 /* 137 * Clock divider value for following 138 * { DIVGDL/R, DIVGPL/R } 139 */ 140 141 /* ACLK_GDL/R L0: 200MHz */ 142 { 3, 1 }, 143 144 /* ACLK_GDL/R L1: 200MHz */ 145 { 3, 1 }, 146 147 /* ACLK_GDL/R L2: 160MHz */ 148 { 4, 1 }, 149 150 /* ACLK_GDL/R L3: 133.3MHz */ 151 { 5, 1 }, 152}; 153 154struct cpufreq_voltage_table { 155 unsigned int index; /* any */ 156 unsigned int arm_volt; /* uV */ 157 unsigned int int_volt; 158}; 159 160static struct cpufreq_voltage_table exynos4_volt_table[CPUFREQ_LEVEL_END] = { 161 { 162 .index = L0, 163 .arm_volt = 1200000, 164 .int_volt = 1100000, 165 }, { 166 .index = L1, 167 .arm_volt = 1100000, 168 .int_volt = 1100000, 169 }, { 170 .index = L2, 171 .arm_volt = 1000000, 172 .int_volt = 1000000, 173 }, { 174 .index = L3, 175 .arm_volt = 900000, 176 .int_volt = 1000000, 177 }, 178}; 179 180static unsigned int exynos4_apll_pms_table[CPUFREQ_LEVEL_END] = { 181 /* APLL FOUT L0: 1000MHz */ 182 ((250 << 16) | (6 << 8) | 1), 183 184 /* APLL FOUT L1: 800MHz */ 185 ((200 << 16) | (6 << 8) | 1), 186 187 /* APLL FOUT L2 : 400MHz */ 188 ((200 << 16) | (6 << 8) | 2), 189 190 /* APLL FOUT L3: 100MHz */ 191 ((200 << 16) | (6 << 8) | 4), 192}; 193 194static int exynos4_verify_speed(struct cpufreq_policy *policy) 195{ 196 return cpufreq_frequency_table_verify(policy, exynos4_freq_table); 197} 198 199static unsigned int exynos4_getspeed(unsigned int cpu) 200{ 201 return clk_get_rate(cpu_clk) / 1000; 202} 203 204static void exynos4_set_clkdiv(unsigned int div_index) 205{ 206 unsigned int tmp; 207 208 /* Change Divider - CPU0 */ 209 210 tmp = __raw_readl(S5P_CLKDIV_CPU); 211 212 tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | S5P_CLKDIV_CPU0_COREM0_MASK | 213 S5P_CLKDIV_CPU0_COREM1_MASK | S5P_CLKDIV_CPU0_PERIPH_MASK | 214 S5P_CLKDIV_CPU0_ATB_MASK | S5P_CLKDIV_CPU0_PCLKDBG_MASK | 215 S5P_CLKDIV_CPU0_APLL_MASK); 216 217 tmp |= ((clkdiv_cpu0[div_index][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) | 218 (clkdiv_cpu0[div_index][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) | 219 (clkdiv_cpu0[div_index][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) | 220 (clkdiv_cpu0[div_index][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) | 221 (clkdiv_cpu0[div_index][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) | 222 (clkdiv_cpu0[div_index][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) | 223 (clkdiv_cpu0[div_index][6] << S5P_CLKDIV_CPU0_APLL_SHIFT)); 224 225 __raw_writel(tmp, S5P_CLKDIV_CPU); 226 227 do { 228 tmp = __raw_readl(S5P_CLKDIV_STATCPU); 229 } while (tmp & 0x1111111); 230 231 /* Change Divider - CPU1 */ 232 233 tmp = __raw_readl(S5P_CLKDIV_CPU1); 234 235 tmp &= ~((0x7 << 4) | 0x7); 236 237 tmp |= ((clkdiv_cpu1[div_index][0] << 4) | 238 (clkdiv_cpu1[div_index][1] << 0)); 239 240 __raw_writel(tmp, S5P_CLKDIV_CPU1); 241 242 do { 243 tmp = __raw_readl(S5P_CLKDIV_STATCPU1); 244 } while (tmp & 0x11); 245 246 /* Change Divider - DMC0 */ 247 248 tmp = __raw_readl(S5P_CLKDIV_DMC0); 249 250 tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK | S5P_CLKDIV_DMC0_ACPPCLK_MASK | 251 S5P_CLKDIV_DMC0_DPHY_MASK | S5P_CLKDIV_DMC0_DMC_MASK | 252 S5P_CLKDIV_DMC0_DMCD_MASK | S5P_CLKDIV_DMC0_DMCP_MASK | 253 S5P_CLKDIV_DMC0_COPY2_MASK | S5P_CLKDIV_DMC0_CORETI_MASK); 254 255 tmp |= ((clkdiv_dmc0[div_index][0] << S5P_CLKDIV_DMC0_ACP_SHIFT) | 256 (clkdiv_dmc0[div_index][1] << S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) | 257 (clkdiv_dmc0[div_index][2] << S5P_CLKDIV_DMC0_DPHY_SHIFT) | 258 (clkdiv_dmc0[div_index][3] << S5P_CLKDIV_DMC0_DMC_SHIFT) | 259 (clkdiv_dmc0[div_index][4] << S5P_CLKDIV_DMC0_DMCD_SHIFT) | 260 (clkdiv_dmc0[div_index][5] << S5P_CLKDIV_DMC0_DMCP_SHIFT) | 261 (clkdiv_dmc0[div_index][6] << S5P_CLKDIV_DMC0_COPY2_SHIFT) | 262 (clkdiv_dmc0[div_index][7] << S5P_CLKDIV_DMC0_CORETI_SHIFT)); 263 264 __raw_writel(tmp, S5P_CLKDIV_DMC0); 265 266 do { 267 tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0); 268 } while (tmp & 0x11111111); 269 270 /* Change Divider - TOP */ 271 272 tmp = __raw_readl(S5P_CLKDIV_TOP); 273 274 tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK | S5P_CLKDIV_TOP_ACLK100_MASK | 275 S5P_CLKDIV_TOP_ACLK160_MASK | S5P_CLKDIV_TOP_ACLK133_MASK | 276 S5P_CLKDIV_TOP_ONENAND_MASK); 277 278 tmp |= ((clkdiv_top[div_index][0] << S5P_CLKDIV_TOP_ACLK200_SHIFT) | 279 (clkdiv_top[div_index][1] << S5P_CLKDIV_TOP_ACLK100_SHIFT) | 280 (clkdiv_top[div_index][2] << S5P_CLKDIV_TOP_ACLK160_SHIFT) | 281 (clkdiv_top[div_index][3] << S5P_CLKDIV_TOP_ACLK133_SHIFT) | 282 (clkdiv_top[div_index][4] << S5P_CLKDIV_TOP_ONENAND_SHIFT)); 283 284 __raw_writel(tmp, S5P_CLKDIV_TOP); 285 286 do { 287 tmp = __raw_readl(S5P_CLKDIV_STAT_TOP); 288 } while (tmp & 0x11111); 289 290 /* Change Divider - LEFTBUS */ 291 292 tmp = __raw_readl(S5P_CLKDIV_LEFTBUS); 293 294 tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); 295 296 tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) | 297 (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT)); 298 299 __raw_writel(tmp, S5P_CLKDIV_LEFTBUS); 300 301 do { 302 tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS); 303 } while (tmp & 0x11); 304 305 /* Change Divider - RIGHTBUS */ 306 307 tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS); 308 309 tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); 310 311 tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) | 312 (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT)); 313 314 __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS); 315 316 do { 317 tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS); 318 } while (tmp & 0x11); 319} 320 321static void exynos4_set_apll(unsigned int index) 322{ 323 unsigned int tmp; 324 325 /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */ 326 clk_set_parent(moutcore, mout_mpll); 327 328 do { 329 tmp = (__raw_readl(S5P_CLKMUX_STATCPU) 330 >> S5P_CLKSRC_CPU_MUXCORE_SHIFT); 331 tmp &= 0x7; 332 } while (tmp != 0x2); 333 334 /* 2. Set APLL Lock time */ 335 __raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK); 336 337 /* 3. Change PLL PMS values */ 338 tmp = __raw_readl(S5P_APLL_CON0); 339 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); 340 tmp |= exynos4_apll_pms_table[index]; 341 __raw_writel(tmp, S5P_APLL_CON0); 342 343 /* 4. wait_lock_time */ 344 do { 345 tmp = __raw_readl(S5P_APLL_CON0); 346 } while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT))); 347 348 /* 5. MUX_CORE_SEL = APLL */ 349 clk_set_parent(moutcore, mout_apll); 350 351 do { 352 tmp = __raw_readl(S5P_CLKMUX_STATCPU); 353 tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK; 354 } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT)); 355} 356 357static void exynos4_set_frequency(unsigned int old_index, unsigned int new_index) 358{ 359 unsigned int tmp; 360 361 if (old_index > new_index) { 362 /* The frequency changing to L0 needs to change apll */ 363 if (freqs.new == exynos4_freq_table[L0].frequency) { 364 /* 1. Change the system clock divider values */ 365 exynos4_set_clkdiv(new_index); 366 367 /* 2. Change the apll m,p,s value */ 368 exynos4_set_apll(new_index); 369 } else { 370 /* 1. Change the system clock divider values */ 371 exynos4_set_clkdiv(new_index); 372 373 /* 2. Change just s value in apll m,p,s value */ 374 tmp = __raw_readl(S5P_APLL_CON0); 375 tmp &= ~(0x7 << 0); 376 tmp |= (exynos4_apll_pms_table[new_index] & 0x7); 377 __raw_writel(tmp, S5P_APLL_CON0); 378 } 379 } 380 381 else if (old_index < new_index) { 382 /* The frequency changing from L0 needs to change apll */ 383 if (freqs.old == exynos4_freq_table[L0].frequency) { 384 /* 1. Change the apll m,p,s value */ 385 exynos4_set_apll(new_index); 386 387 /* 2. Change the system clock divider values */ 388 exynos4_set_clkdiv(new_index); 389 } else { 390 /* 1. Change just s value in apll m,p,s value */ 391 tmp = __raw_readl(S5P_APLL_CON0); 392 tmp &= ~(0x7 << 0); 393 tmp |= (exynos4_apll_pms_table[new_index] & 0x7); 394 __raw_writel(tmp, S5P_APLL_CON0); 395 396 /* 2. Change the system clock divider values */ 397 exynos4_set_clkdiv(new_index); 398 } 399 } 400} 401 402static int exynos4_target(struct cpufreq_policy *policy, 403 unsigned int target_freq, 404 unsigned int relation) 405{ 406 unsigned int index, old_index; 407 unsigned int arm_volt, int_volt; 408 409 freqs.old = exynos4_getspeed(policy->cpu); 410 411 if (cpufreq_frequency_table_target(policy, exynos4_freq_table, 412 freqs.old, relation, &old_index)) 413 return -EINVAL; 414 415 if (cpufreq_frequency_table_target(policy, exynos4_freq_table, 416 target_freq, relation, &index)) 417 return -EINVAL; 418 419 freqs.new = exynos4_freq_table[index].frequency; 420 freqs.cpu = policy->cpu; 421 422 if (freqs.new == freqs.old) 423 return 0; 424 425 /* get the voltage value */ 426 arm_volt = exynos4_volt_table[index].arm_volt; 427 int_volt = exynos4_volt_table[index].int_volt; 428 429 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 430 431 /* control regulator */ 432 if (freqs.new > freqs.old) { 433 /* Voltage up */ 434 regulator_set_voltage(arm_regulator, arm_volt, arm_volt); 435 regulator_set_voltage(int_regulator, int_volt, int_volt); 436 } 437 438 /* Clock Configuration Procedure */ 439 exynos4_set_frequency(old_index, index); 440 441 /* control regulator */ 442 if (freqs.new < freqs.old) { 443 /* Voltage down */ 444 regulator_set_voltage(arm_regulator, arm_volt, arm_volt); 445 regulator_set_voltage(int_regulator, int_volt, int_volt); 446 } 447 448 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 449 450 return 0; 451} 452 453#ifdef CONFIG_PM 454static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy) 455{ 456 return 0; 457} 458 459static int exynos4_cpufreq_resume(struct cpufreq_policy *policy) 460{ 461 return 0; 462} 463#endif 464 465static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy) 466{ 467 policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu); 468 469 cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu); 470 471 /* set the transition latency value */ 472 policy->cpuinfo.transition_latency = 100000; 473 474 /* 475 * EXYNOS4 multi-core processors has 2 cores 476 * that the frequency cannot be set independently. 477 * Each cpu is bound to the same speed. 478 * So the affected cpu is all of the cpus. 479 */ 480 cpumask_setall(policy->cpus); 481 482 return cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table); 483} 484 485static struct cpufreq_driver exynos4_driver = { 486 .flags = CPUFREQ_STICKY, 487 .verify = exynos4_verify_speed, 488 .target = exynos4_target, 489 .get = exynos4_getspeed, 490 .init = exynos4_cpufreq_cpu_init, 491 .name = "exynos4_cpufreq", 492#ifdef CONFIG_PM 493 .suspend = exynos4_cpufreq_suspend, 494 .resume = exynos4_cpufreq_resume, 495#endif 496}; 497 498static int __init exynos4_cpufreq_init(void) 499{ 500 cpu_clk = clk_get(NULL, "armclk"); 501 if (IS_ERR(cpu_clk)) 502 return PTR_ERR(cpu_clk); 503 504 moutcore = clk_get(NULL, "moutcore"); 505 if (IS_ERR(moutcore)) 506 goto out; 507 508 mout_mpll = clk_get(NULL, "mout_mpll"); 509 if (IS_ERR(mout_mpll)) 510 goto out; 511 512 mout_apll = clk_get(NULL, "mout_apll"); 513 if (IS_ERR(mout_apll)) 514 goto out; 515 516 arm_regulator = regulator_get(NULL, "vdd_arm"); 517 if (IS_ERR(arm_regulator)) { 518 printk(KERN_ERR "failed to get resource %s\n", "vdd_arm"); 519 goto out; 520 } 521 522 int_regulator = regulator_get(NULL, "vdd_int"); 523 if (IS_ERR(int_regulator)) { 524 printk(KERN_ERR "failed to get resource %s\n", "vdd_int"); 525 goto out; 526 } 527 528 /* 529 * Check DRAM type. 530 * Because DVFS level is different according to DRAM type. 531 */ 532 memtype = __raw_readl(S5P_VA_DMC0 + S5P_DMC0_MEMCON_OFFSET); 533 memtype = (memtype >> S5P_DMC0_MEMTYPE_SHIFT); 534 memtype &= S5P_DMC0_MEMTYPE_MASK; 535 536 if ((memtype < DDR2) && (memtype > DDR3)) { 537 printk(KERN_ERR "%s: wrong memtype= 0x%x\n", __func__, memtype); 538 goto out; 539 } else { 540 printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype); 541 } 542 543 return cpufreq_register_driver(&exynos4_driver); 544 545out: 546 if (!IS_ERR(cpu_clk)) 547 clk_put(cpu_clk); 548 549 if (!IS_ERR(moutcore)) 550 clk_put(moutcore); 551 552 if (!IS_ERR(mout_mpll)) 553 clk_put(mout_mpll); 554 555 if (!IS_ERR(mout_apll)) 556 clk_put(mout_apll); 557 558 if (!IS_ERR(arm_regulator)) 559 regulator_put(arm_regulator); 560 561 if (!IS_ERR(int_regulator)) 562 regulator_put(int_regulator); 563 564 printk(KERN_ERR "%s: failed initialization\n", __func__); 565 566 return -EINVAL; 567} 568late_initcall(exynos4_cpufreq_init);