aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-s5pv310/cpufreq.c301
1 files changed, 160 insertions, 141 deletions
diff --git a/arch/arm/mach-s5pv310/cpufreq.c b/arch/arm/mach-s5pv310/cpufreq.c
index bcd4ebf93e56..b04cbc731128 100644
--- a/arch/arm/mach-s5pv310/cpufreq.c
+++ b/arch/arm/mach-s5pv310/cpufreq.c
@@ -24,6 +24,7 @@
24#include <mach/regs-mem.h> 24#include <mach/regs-mem.h>
25 25
26#include <plat/clock.h> 26#include <plat/clock.h>
27#include <plat/pm.h>
27 28
28static struct clk *cpu_clk; 29static struct clk *cpu_clk;
29static struct clk *moutcore; 30static struct clk *moutcore;
@@ -36,7 +37,6 @@ static struct regulator *int_regulator;
36#endif 37#endif
37 38
38static struct cpufreq_freqs freqs; 39static struct cpufreq_freqs freqs;
39static unsigned int armclk_use_apll;
40static unsigned int memtype; 40static unsigned int memtype;
41 41
42enum s5pv310_memory_type { 42enum s5pv310_memory_type {
@@ -46,19 +46,18 @@ enum s5pv310_memory_type {
46}; 46};
47 47
48enum cpufreq_level_index { 48enum cpufreq_level_index {
49 L0, L1, L2, L3, L4, CPUFREQ_LEVEL_END, 49 L0, L1, L2, L3, CPUFREQ_LEVEL_END,
50}; 50};
51 51
52static struct cpufreq_frequency_table s5pv310_freq_table[] = { 52static struct cpufreq_frequency_table s5pv310_freq_table[] = {
53 {L0, 1000*1000}, 53 {L0, 1000*1000},
54 {L1, 800*1000}, 54 {L1, 800*1000},
55 {L2, 400*1000}, 55 {L2, 400*1000},
56 {L3, 200*1000}, 56 {L3, 100*1000},
57 {L4, 100*1000},
58 {0, CPUFREQ_TABLE_END}, 57 {0, CPUFREQ_TABLE_END},
59}; 58};
60 59
61static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END + 1][7] = { 60static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END][7] = {
62 /* 61 /*
63 * Clock divider value for following 62 * Clock divider value for following
64 * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH, 63 * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
@@ -66,25 +65,38 @@ static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END + 1][7] = {
66 */ 65 */
67 66
68 /* ARM L0: 1000MHz */ 67 /* ARM L0: 1000MHz */
69 { 0, 3, 7, 3, 3, 0, 0 }, 68 { 0, 3, 7, 3, 3, 0, 1 },
70 69
71 /* ARM L1: 800MHz */ 70 /* ARM L1: 800MHz */
72 { 0, 3, 7, 3, 3, 0, 0 }, 71 { 0, 3, 7, 3, 3, 0, 1 },
73 72
74 /* ARM L2: 400MHz */ 73 /* ARM L2: 400MHz */
75 { 1, 1, 3, 1, 1, 0, 0 }, 74 { 0, 1, 3, 1, 3, 0, 1 },
76 75
77 /* ARM L3: 200MHz */ 76 /* ARM L3: 100MHz */
78 { 3, 0, 1, 0, 0, 0, 0 }, 77 { 0, 0, 1, 0, 3, 1, 1 },
78};
79 79
80 /* ARM L4A: 100MHz, for DDR2/3 */ 80static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = {
81 { 7, 0, 1, 0, 0, 0, 0 }, 81 /*
82 * Clock divider value for following
83 * { DIVCOPY, DIVHPM }
84 */
85
86 /* ARM L0: 1000MHz */
87 { 3, 0 },
88
89 /* ARM L1: 800MHz */
90 { 3, 0 },
82 91
83 /* ARM L4B: 100MHz, for LPDDR2 (SMDKV310 has LPDDR2) */ 92 /* ARM L2: 400MHz */
84 { 7, 0, 1, 0, 0, 0, 0 }, 93 { 3, 0 },
94
95 /* ARM L3: 100MHz */
96 { 3, 0 },
85}; 97};
86 98
87static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END + 1][8] = { 99static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END][8] = {
88 /* 100 /*
89 * Clock divider value for following 101 * Clock divider value for following
90 * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD 102 * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
@@ -97,20 +109,14 @@ static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END + 1][8] = {
97 /* DMC L1: 400MHz */ 109 /* DMC L1: 400MHz */
98 { 3, 1, 1, 1, 1, 1, 3, 1 }, 110 { 3, 1, 1, 1, 1, 1, 3, 1 },
99 111
100 /* DMC L2: 400MHz */ 112 /* DMC L2: 266.7MHz */
101 { 3, 1, 1, 1, 1, 1, 3, 1 }, 113 { 7, 1, 1, 2, 1, 1, 3, 1 },
102
103 /* DMC L3: 400MHz */
104 { 3, 1, 1, 1, 1, 1, 3, 1 },
105
106 /* DMC L4A: 400MHz, for DDR2/3 */
107 { 7, 1, 1, 1, 1, 1, 3, 1 },
108 114
109 /* DMC L4B: 200MHz, for LPDDR2 */ 115 /* DMC L3: 200MHz */
110 { 7, 1, 1, 3, 1, 1, 3, 1 }, 116 { 7, 1, 1, 3, 1, 1, 3, 1 },
111}; 117};
112 118
113static unsigned int clkdiv_top[CPUFREQ_LEVEL_END + 1][5] = { 119static unsigned int clkdiv_top[CPUFREQ_LEVEL_END][5] = {
114 /* 120 /*
115 * Clock divider value for following 121 * Clock divider value for following
116 * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND } 122 * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
@@ -122,20 +128,14 @@ static unsigned int clkdiv_top[CPUFREQ_LEVEL_END + 1][5] = {
122 /* ACLK200 L1: 200MHz */ 128 /* ACLK200 L1: 200MHz */
123 { 3, 7, 4, 5, 1 }, 129 { 3, 7, 4, 5, 1 },
124 130
125 /* ACLK200 L2: 200MHz */ 131 /* ACLK200 L2: 160MHz */
126 { 3, 7, 4, 5, 1 }, 132 { 4, 7, 5, 7, 1 },
127
128 /* ACLK200 L3: 200MHz */
129 { 3, 7, 4, 5, 1 },
130
131 /* ACLK200 L4A: 100MHz */
132 { 7, 7, 7, 7, 1 },
133 133
134 /* ACLK200 L4B: 100MHz */ 134 /* ACLK200 L3: 133.3MHz */
135 { 7, 7, 7, 7, 1 }, 135 { 5, 7, 7, 7, 1 },
136}; 136};
137 137
138static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END + 1][2] = { 138static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END][2] = {
139 /* 139 /*
140 * Clock divider value for following 140 * Clock divider value for following
141 * { DIVGDL/R, DIVGPL/R } 141 * { DIVGDL/R, DIVGPL/R }
@@ -147,17 +147,11 @@ static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END + 1][2] = {
147 /* ACLK_GDL/R L1: 200MHz */ 147 /* ACLK_GDL/R L1: 200MHz */
148 { 3, 1 }, 148 { 3, 1 },
149 149
150 /* ACLK_GDL/R L2: 200MHz */ 150 /* ACLK_GDL/R L2: 160MHz */
151 { 3, 1 }, 151 { 4, 1 },
152
153 /* ACLK_GDL/R L3: 200MHz */
154 { 3, 1 },
155 152
156 /* ACLK_GDL/R L4A: 100MHz */ 153 /* ACLK_GDL/R L3: 133.3MHz */
157 { 7, 1 }, 154 { 5, 1 },
158
159 /* ACLK_GDL/R L4B: 100MHz */
160 { 7, 1 },
161}; 155};
162 156
163struct cpufreq_voltage_table { 157struct cpufreq_voltage_table {
@@ -166,7 +160,7 @@ struct cpufreq_voltage_table {
166 unsigned int int_volt; 160 unsigned int int_volt;
167}; 161};
168 162
169static struct cpufreq_voltage_table s5pv310_volt_table[] = { 163static struct cpufreq_voltage_table s5pv310_volt_table[CPUFREQ_LEVEL_END] = {
170 { 164 {
171 .index = L0, 165 .index = L0,
172 .arm_volt = 1200000, 166 .arm_volt = 1200000,
@@ -177,19 +171,29 @@ static struct cpufreq_voltage_table s5pv310_volt_table[] = {
177 .int_volt = 1100000, 171 .int_volt = 1100000,
178 }, { 172 }, {
179 .index = L2, 173 .index = L2,
180 .arm_volt = 1050000, 174 .arm_volt = 1000000,
181 .int_volt = 1100000, 175 .int_volt = 1000000,
182 }, { 176 }, {
183 .index = L3, 177 .index = L3,
184 .arm_volt = 1050000, 178 .arm_volt = 900000,
185 .int_volt = 1100000,
186 }, {
187 .index = L4,
188 .arm_volt = 1000000,
189 .int_volt = 1000000, 179 .int_volt = 1000000,
190 }, 180 },
191}; 181};
192 182
183static unsigned int s5pv310_apll_pms_table[CPUFREQ_LEVEL_END] = {
184 /* APLL FOUT L0: 1000MHz */
185 ((250 << 16) | (6 << 8) | 1),
186
187 /* APLL FOUT L1: 800MHz */
188 ((200 << 16) | (6 << 8) | 1),
189
190 /* APLL FOUT L2 : 400MHz */
191 ((200 << 16) | (6 << 8) | 2),
192
193 /* APLL FOUT L3: 100MHz */
194 ((200 << 16) | (6 << 8) | 4),
195};
196
193int s5pv310_verify_speed(struct cpufreq_policy *policy) 197int s5pv310_verify_speed(struct cpufreq_policy *policy)
194{ 198{
195 return cpufreq_frequency_table_verify(policy, s5pv310_freq_table); 199 return cpufreq_frequency_table_verify(policy, s5pv310_freq_table);
@@ -227,6 +231,21 @@ void s5pv310_set_clkdiv(unsigned int div_index)
227 tmp = __raw_readl(S5P_CLKDIV_STATCPU); 231 tmp = __raw_readl(S5P_CLKDIV_STATCPU);
228 } while (tmp & 0x1111111); 232 } while (tmp & 0x1111111);
229 233
234 /* Change Divider - CPU1 */
235
236 tmp = __raw_readl(S5P_CLKDIV_CPU1);
237
238 tmp &= ~((0x7 << 4) | 0x7);
239
240 tmp |= ((clkdiv_cpu1[div_index][0] << 4) |
241 (clkdiv_cpu1[div_index][1] << 0));
242
243 __raw_writel(tmp, S5P_CLKDIV_CPU1);
244
245 do {
246 tmp = __raw_readl(S5P_CLKDIV_STATCPU1);
247 } while (tmp & 0x11);
248
230 /* Change Divider - DMC0 */ 249 /* Change Divider - DMC0 */
231 250
232 tmp = __raw_readl(S5P_CLKDIV_DMC0); 251 tmp = __raw_readl(S5P_CLKDIV_DMC0);
@@ -302,17 +321,101 @@ void s5pv310_set_clkdiv(unsigned int div_index)
302 } while (tmp & 0x11); 321 } while (tmp & 0x11);
303} 322}
304 323
324static void s5pv310_set_apll(unsigned int index)
325{
326 unsigned int tmp;
327
328 /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
329 clk_set_parent(moutcore, mout_mpll);
330
331 do {
332 tmp = (__raw_readl(S5P_CLKMUX_STATCPU)
333 >> S5P_CLKSRC_CPU_MUXCORE_SHIFT);
334 tmp &= 0x7;
335 } while (tmp != 0x2);
336
337 /* 2. Set APLL Lock time */
338 __raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK);
339
340 /* 3. Change PLL PMS values */
341 tmp = __raw_readl(S5P_APLL_CON0);
342 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
343 tmp |= s5pv310_apll_pms_table[index];
344 __raw_writel(tmp, S5P_APLL_CON0);
345
346 /* 4. wait_lock_time */
347 do {
348 tmp = __raw_readl(S5P_APLL_CON0);
349 } while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT)));
350
351 /* 5. MUX_CORE_SEL = APLL */
352 clk_set_parent(moutcore, mout_apll);
353
354 do {
355 tmp = __raw_readl(S5P_CLKMUX_STATCPU);
356 tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK;
357 } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT));
358}
359
360static void s5pv310_set_frequency(unsigned int old_index, unsigned int new_index)
361{
362 unsigned int tmp;
363
364 if (old_index > new_index) {
365 /* The frequency changing to L0 needs to change apll */
366 if (freqs.new == s5pv310_freq_table[L0].frequency) {
367 /* 1. Change the system clock divider values */
368 s5pv310_set_clkdiv(new_index);
369
370 /* 2. Change the apll m,p,s value */
371 s5pv310_set_apll(new_index);
372 } else {
373 /* 1. Change the system clock divider values */
374 s5pv310_set_clkdiv(new_index);
375
376 /* 2. Change just s value in apll m,p,s value */
377 tmp = __raw_readl(S5P_APLL_CON0);
378 tmp &= ~(0x7 << 0);
379 tmp |= (s5pv310_apll_pms_table[new_index] & 0x7);
380 __raw_writel(tmp, S5P_APLL_CON0);
381 }
382 }
383
384 else if (old_index < new_index) {
385 /* The frequency changing from L0 needs to change apll */
386 if (freqs.old == s5pv310_freq_table[L0].frequency) {
387 /* 1. Change the apll m,p,s value */
388 s5pv310_set_apll(new_index);
389
390 /* 2. Change the system clock divider values */
391 s5pv310_set_clkdiv(new_index);
392 } else {
393 /* 1. Change just s value in apll m,p,s value */
394 tmp = __raw_readl(S5P_APLL_CON0);
395 tmp &= ~(0x7 << 0);
396 tmp |= (s5pv310_apll_pms_table[new_index] & 0x7);
397 __raw_writel(tmp, S5P_APLL_CON0);
398
399 /* 2. Change the system clock divider values */
400 s5pv310_set_clkdiv(new_index);
401 }
402 }
403}
404
305static int s5pv310_target(struct cpufreq_policy *policy, 405static int s5pv310_target(struct cpufreq_policy *policy,
306 unsigned int target_freq, 406 unsigned int target_freq,
307 unsigned int relation) 407 unsigned int relation)
308{ 408{
309 unsigned int index, div_index, tmp; 409 unsigned int index, old_index;
310 unsigned int arm_volt, int_volt; 410 unsigned int arm_volt, int_volt;
311 unsigned int need_apll = 0;
312 411
313 freqs.old = s5pv310_getspeed(policy->cpu); 412 freqs.old = s5pv310_getspeed(policy->cpu);
314 413
315 if (cpufreq_frequency_table_target(policy, s5pv310_freq_table, 414 if (cpufreq_frequency_table_target(policy, s5pv310_freq_table,
415 freqs.old, relation, &old_index))
416 return -EINVAL;
417
418 if (cpufreq_frequency_table_target(policy, s5pv310_freq_table,
316 target_freq, relation, &index)) 419 target_freq, relation, &index))
317 return -EINVAL; 420 return -EINVAL;
318 421
@@ -322,19 +425,6 @@ static int s5pv310_target(struct cpufreq_policy *policy,
322 if (freqs.new == freqs.old) 425 if (freqs.new == freqs.old)
323 return 0; 426 return 0;
324 427
325 /*
326 * If freqs.new is higher than 800MHz
327 * cpufreq driver should turn on apll
328 */
329 if (index < L1)
330 need_apll = 1;
331
332 /* If the memory type is LPDDR2, use L4-B instead of L4-A */
333 if ((index == L4) && (memtype == LPDDR2))
334 div_index = index + 1;
335 else
336 div_index = index;
337
338 /* get the voltage value */ 428 /* get the voltage value */
339 arm_volt = s5pv310_volt_table[index].arm_volt; 429 arm_volt = s5pv310_volt_table[index].arm_volt;
340 int_volt = s5pv310_volt_table[index].int_volt; 430 int_volt = s5pv310_volt_table[index].int_volt;
@@ -351,69 +441,7 @@ static int s5pv310_target(struct cpufreq_policy *policy,
351 } 441 }
352 442
353 /* Clock Configuration Procedure */ 443 /* Clock Configuration Procedure */
354 444 s5pv310_set_frequency(old_index, index);
355 /* 1. Change the system clock divider values */
356 s5pv310_set_clkdiv(div_index);
357
358 /* 2. Change the divider values for special clocks in CMU_TOP */
359 /* currently nothing */
360
361 /* 3. Change the XPLL values or Select the parent XPLL */
362 if (need_apll) {
363 if (!armclk_use_apll) {
364 /*
365 * If the parent clock of armclk isn't apll
366 * here need to set apll (include m,p,s value)
367 */
368
369 /* a. MUX_CORE_SEL = MPLL,
370 * ARMCLK uses MPLL for lock time */
371 clk_set_parent(moutcore, mout_mpll);
372
373 do {
374 tmp = (__raw_readl(S5P_CLKMUX_STATCPU)
375 >> S5P_CLKSRC_CPU_MUXCORE_SHIFT);
376 tmp &= 0x7;
377 } while (tmp != 0x2);
378
379 /* b. Set APLL Lock time */
380 __raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK);
381
382 /* c. Change PLL PMS values */
383 __raw_writel(S5P_APLL_VAL_1000, S5P_APLL_CON0);
384
385 /* d. Turn on a PLL */
386 tmp = __raw_readl(S5P_APLL_CON0);
387 tmp |= (0x1 << S5P_APLLCON0_ENABLE_SHIFT);
388 __raw_writel(tmp, S5P_APLL_CON0);
389
390 /* e. wait_lock_time */
391 do {
392 tmp = __raw_readl(S5P_APLL_CON0);
393 } while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT)));
394
395 armclk_use_apll = 1;
396
397 }
398
399 /* MUX_CORE_SEL = APLL */
400 clk_set_parent(moutcore, mout_apll);
401
402 do {
403 tmp = __raw_readl(S5P_CLKMUX_STATCPU);
404 tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK;
405 } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT));
406
407 } else {
408 if (clk_get_parent(moutcore) != mout_mpll) {
409 clk_set_parent(moutcore, mout_mpll);
410
411 do {
412 tmp = __raw_readl(S5P_CLKMUX_STATCPU);
413 tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK;
414 } while (tmp != (0x2 << S5P_CLKSRC_CPU_MUXCORE_SHIFT));
415 }
416 }
417 445
418 /* control regulator */ 446 /* control regulator */
419 if (freqs.new < freqs.old) { 447 if (freqs.new < freqs.old) {
@@ -477,8 +505,6 @@ static struct cpufreq_driver s5pv310_driver = {
477 505
478static int __init s5pv310_cpufreq_init(void) 506static int __init s5pv310_cpufreq_init(void)
479{ 507{
480 unsigned int tmp;
481
482 cpu_clk = clk_get(NULL, "armclk"); 508 cpu_clk = clk_get(NULL, "armclk");
483 if (IS_ERR(cpu_clk)) 509 if (IS_ERR(cpu_clk))
484 return PTR_ERR(cpu_clk); 510 return PTR_ERR(cpu_clk);
@@ -509,13 +535,6 @@ static int __init s5pv310_cpufreq_init(void)
509 } 535 }
510#endif 536#endif
511 537
512 /* check parent clock of armclk */
513 tmp = __raw_readl(S5P_CLKSRC_CPU);
514 if (tmp & S5P_CLKSRC_CPU_MUXCORE_SHIFT)
515 armclk_use_apll = 0;
516 else
517 armclk_use_apll = 1;
518
519 /* 538 /*
520 * Check DRAM type. 539 * Check DRAM type.
521 * Because DVFS level is different according to DRAM type. 540 * Because DVFS level is different according to DRAM type.