diff options
author | Sunyoung Kang <sy0816.kang@samsung.com> | 2010-09-16 04:59:21 -0400 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2010-12-23 00:53:40 -0500 |
commit | f40f91fefcf3a9049bcfa31ac53bc0e775444dab (patch) | |
tree | b4a55889902d5f23aed516efb49ecc75d6935040 /arch/arm | |
parent | dd0b7e20da906b40d55f24bb2dc21abd58ed3f55 (diff) |
ARM: S5PV310: Add support CPUFREQ
This patch adds support CPUFREQ driver for S5PV310 and S5PC210. This can
support DVFS(Dynamic Voltage and Frequency Scaling). The voltage scaling
depends on existence of regulator.
Sigend-off-by: Sunyoung Kang <sy0816.kang@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-s5pv310/cpufreq.c | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/arch/arm/mach-s5pv310/cpufreq.c b/arch/arm/mach-s5pv310/cpufreq.c new file mode 100644 index 000000000000..bcd4ebf93e56 --- /dev/null +++ b/arch/arm/mach-s5pv310/cpufreq.c | |||
@@ -0,0 +1,561 @@ | |||
1 | /* linux/arch/arm/mach-s5pv310/cpufreq.c | ||
2 | * | ||
3 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com | ||
5 | * | ||
6 | * S5PV310 - CPU frequency scaling support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/types.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/clk.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/regulator/consumer.h> | ||
20 | #include <linux/cpufreq.h> | ||
21 | |||
22 | #include <mach/map.h> | ||
23 | #include <mach/regs-clock.h> | ||
24 | #include <mach/regs-mem.h> | ||
25 | |||
26 | #include <plat/clock.h> | ||
27 | |||
28 | static struct clk *cpu_clk; | ||
29 | static struct clk *moutcore; | ||
30 | static struct clk *mout_mpll; | ||
31 | static struct clk *mout_apll; | ||
32 | |||
33 | #ifdef CONFIG_REGULATOR | ||
34 | static struct regulator *arm_regulator; | ||
35 | static struct regulator *int_regulator; | ||
36 | #endif | ||
37 | |||
38 | static struct cpufreq_freqs freqs; | ||
39 | static unsigned int armclk_use_apll; | ||
40 | static unsigned int memtype; | ||
41 | |||
42 | enum s5pv310_memory_type { | ||
43 | DDR2 = 4, | ||
44 | LPDDR2, | ||
45 | DDR3, | ||
46 | }; | ||
47 | |||
48 | enum cpufreq_level_index { | ||
49 | L0, L1, L2, L3, L4, CPUFREQ_LEVEL_END, | ||
50 | }; | ||
51 | |||
52 | static struct cpufreq_frequency_table s5pv310_freq_table[] = { | ||
53 | {L0, 1000*1000}, | ||
54 | {L1, 800*1000}, | ||
55 | {L2, 400*1000}, | ||
56 | {L3, 200*1000}, | ||
57 | {L4, 100*1000}, | ||
58 | {0, CPUFREQ_TABLE_END}, | ||
59 | }; | ||
60 | |||
61 | static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END + 1][7] = { | ||
62 | /* | ||
63 | * Clock divider value for following | ||
64 | * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH, | ||
65 | * DIVATB, DIVPCLK_DBG, DIVAPLL } | ||
66 | */ | ||
67 | |||
68 | /* ARM L0: 1000MHz */ | ||
69 | { 0, 3, 7, 3, 3, 0, 0 }, | ||
70 | |||
71 | /* ARM L1: 800MHz */ | ||
72 | { 0, 3, 7, 3, 3, 0, 0 }, | ||
73 | |||
74 | /* ARM L2: 400MHz */ | ||
75 | { 1, 1, 3, 1, 1, 0, 0 }, | ||
76 | |||
77 | /* ARM L3: 200MHz */ | ||
78 | { 3, 0, 1, 0, 0, 0, 0 }, | ||
79 | |||
80 | /* ARM L4A: 100MHz, for DDR2/3 */ | ||
81 | { 7, 0, 1, 0, 0, 0, 0 }, | ||
82 | |||
83 | /* ARM L4B: 100MHz, for LPDDR2 (SMDKV310 has LPDDR2) */ | ||
84 | { 7, 0, 1, 0, 0, 0, 0 }, | ||
85 | }; | ||
86 | |||
87 | static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END + 1][8] = { | ||
88 | /* | ||
89 | * Clock divider value for following | ||
90 | * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD | ||
91 | * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS } | ||
92 | */ | ||
93 | |||
94 | /* DMC L0: 400MHz */ | ||
95 | { 3, 1, 1, 1, 1, 1, 3, 1 }, | ||
96 | |||
97 | /* DMC L1: 400MHz */ | ||
98 | { 3, 1, 1, 1, 1, 1, 3, 1 }, | ||
99 | |||
100 | /* DMC L2: 400MHz */ | ||
101 | { 3, 1, 1, 1, 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 | |||
109 | /* DMC L4B: 200MHz, for LPDDR2 */ | ||
110 | { 7, 1, 1, 3, 1, 1, 3, 1 }, | ||
111 | }; | ||
112 | |||
113 | static unsigned int clkdiv_top[CPUFREQ_LEVEL_END + 1][5] = { | ||
114 | /* | ||
115 | * Clock divider value for following | ||
116 | * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND } | ||
117 | */ | ||
118 | |||
119 | /* ACLK200 L0: 200MHz */ | ||
120 | { 3, 7, 4, 5, 1 }, | ||
121 | |||
122 | /* ACLK200 L1: 200MHz */ | ||
123 | { 3, 7, 4, 5, 1 }, | ||
124 | |||
125 | /* ACLK200 L2: 200MHz */ | ||
126 | { 3, 7, 4, 5, 1 }, | ||
127 | |||
128 | /* ACLK200 L3: 200MHz */ | ||
129 | { 3, 7, 4, 5, 1 }, | ||
130 | |||
131 | /* ACLK200 L4A: 100MHz */ | ||
132 | { 7, 7, 7, 7, 1 }, | ||
133 | |||
134 | /* ACLK200 L4B: 100MHz */ | ||
135 | { 7, 7, 7, 7, 1 }, | ||
136 | }; | ||
137 | |||
138 | static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END + 1][2] = { | ||
139 | /* | ||
140 | * Clock divider value for following | ||
141 | * { DIVGDL/R, DIVGPL/R } | ||
142 | */ | ||
143 | |||
144 | /* ACLK_GDL/R L0: 200MHz */ | ||
145 | { 3, 1 }, | ||
146 | |||
147 | /* ACLK_GDL/R L1: 200MHz */ | ||
148 | { 3, 1 }, | ||
149 | |||
150 | /* ACLK_GDL/R L2: 200MHz */ | ||
151 | { 3, 1 }, | ||
152 | |||
153 | /* ACLK_GDL/R L3: 200MHz */ | ||
154 | { 3, 1 }, | ||
155 | |||
156 | /* ACLK_GDL/R L4A: 100MHz */ | ||
157 | { 7, 1 }, | ||
158 | |||
159 | /* ACLK_GDL/R L4B: 100MHz */ | ||
160 | { 7, 1 }, | ||
161 | }; | ||
162 | |||
163 | struct cpufreq_voltage_table { | ||
164 | unsigned int index; /* any */ | ||
165 | unsigned int arm_volt; /* uV */ | ||
166 | unsigned int int_volt; | ||
167 | }; | ||
168 | |||
169 | static struct cpufreq_voltage_table s5pv310_volt_table[] = { | ||
170 | { | ||
171 | .index = L0, | ||
172 | .arm_volt = 1200000, | ||
173 | .int_volt = 1100000, | ||
174 | }, { | ||
175 | .index = L1, | ||
176 | .arm_volt = 1100000, | ||
177 | .int_volt = 1100000, | ||
178 | }, { | ||
179 | .index = L2, | ||
180 | .arm_volt = 1050000, | ||
181 | .int_volt = 1100000, | ||
182 | }, { | ||
183 | .index = L3, | ||
184 | .arm_volt = 1050000, | ||
185 | .int_volt = 1100000, | ||
186 | }, { | ||
187 | .index = L4, | ||
188 | .arm_volt = 1000000, | ||
189 | .int_volt = 1000000, | ||
190 | }, | ||
191 | }; | ||
192 | |||
193 | int s5pv310_verify_speed(struct cpufreq_policy *policy) | ||
194 | { | ||
195 | return cpufreq_frequency_table_verify(policy, s5pv310_freq_table); | ||
196 | } | ||
197 | |||
198 | unsigned int s5pv310_getspeed(unsigned int cpu) | ||
199 | { | ||
200 | return clk_get_rate(cpu_clk) / 1000; | ||
201 | } | ||
202 | |||
203 | void s5pv310_set_clkdiv(unsigned int div_index) | ||
204 | { | ||
205 | unsigned int tmp; | ||
206 | |||
207 | /* Change Divider - CPU0 */ | ||
208 | |||
209 | tmp = __raw_readl(S5P_CLKDIV_CPU); | ||
210 | |||
211 | tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | S5P_CLKDIV_CPU0_COREM0_MASK | | ||
212 | S5P_CLKDIV_CPU0_COREM1_MASK | S5P_CLKDIV_CPU0_PERIPH_MASK | | ||
213 | S5P_CLKDIV_CPU0_ATB_MASK | S5P_CLKDIV_CPU0_PCLKDBG_MASK | | ||
214 | S5P_CLKDIV_CPU0_APLL_MASK); | ||
215 | |||
216 | tmp |= ((clkdiv_cpu0[div_index][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) | | ||
217 | (clkdiv_cpu0[div_index][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) | | ||
218 | (clkdiv_cpu0[div_index][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) | | ||
219 | (clkdiv_cpu0[div_index][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) | | ||
220 | (clkdiv_cpu0[div_index][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) | | ||
221 | (clkdiv_cpu0[div_index][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) | | ||
222 | (clkdiv_cpu0[div_index][6] << S5P_CLKDIV_CPU0_APLL_SHIFT)); | ||
223 | |||
224 | __raw_writel(tmp, S5P_CLKDIV_CPU); | ||
225 | |||
226 | do { | ||
227 | tmp = __raw_readl(S5P_CLKDIV_STATCPU); | ||
228 | } while (tmp & 0x1111111); | ||
229 | |||
230 | /* Change Divider - DMC0 */ | ||
231 | |||
232 | tmp = __raw_readl(S5P_CLKDIV_DMC0); | ||
233 | |||
234 | tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK | S5P_CLKDIV_DMC0_ACPPCLK_MASK | | ||
235 | S5P_CLKDIV_DMC0_DPHY_MASK | S5P_CLKDIV_DMC0_DMC_MASK | | ||
236 | S5P_CLKDIV_DMC0_DMCD_MASK | S5P_CLKDIV_DMC0_DMCP_MASK | | ||
237 | S5P_CLKDIV_DMC0_COPY2_MASK | S5P_CLKDIV_DMC0_CORETI_MASK); | ||
238 | |||
239 | tmp |= ((clkdiv_dmc0[div_index][0] << S5P_CLKDIV_DMC0_ACP_SHIFT) | | ||
240 | (clkdiv_dmc0[div_index][1] << S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) | | ||
241 | (clkdiv_dmc0[div_index][2] << S5P_CLKDIV_DMC0_DPHY_SHIFT) | | ||
242 | (clkdiv_dmc0[div_index][3] << S5P_CLKDIV_DMC0_DMC_SHIFT) | | ||
243 | (clkdiv_dmc0[div_index][4] << S5P_CLKDIV_DMC0_DMCD_SHIFT) | | ||
244 | (clkdiv_dmc0[div_index][5] << S5P_CLKDIV_DMC0_DMCP_SHIFT) | | ||
245 | (clkdiv_dmc0[div_index][6] << S5P_CLKDIV_DMC0_COPY2_SHIFT) | | ||
246 | (clkdiv_dmc0[div_index][7] << S5P_CLKDIV_DMC0_CORETI_SHIFT)); | ||
247 | |||
248 | __raw_writel(tmp, S5P_CLKDIV_DMC0); | ||
249 | |||
250 | do { | ||
251 | tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0); | ||
252 | } while (tmp & 0x11111111); | ||
253 | |||
254 | /* Change Divider - TOP */ | ||
255 | |||
256 | tmp = __raw_readl(S5P_CLKDIV_TOP); | ||
257 | |||
258 | tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK | S5P_CLKDIV_TOP_ACLK100_MASK | | ||
259 | S5P_CLKDIV_TOP_ACLK160_MASK | S5P_CLKDIV_TOP_ACLK133_MASK | | ||
260 | S5P_CLKDIV_TOP_ONENAND_MASK); | ||
261 | |||
262 | tmp |= ((clkdiv_top[div_index][0] << S5P_CLKDIV_TOP_ACLK200_SHIFT) | | ||
263 | (clkdiv_top[div_index][1] << S5P_CLKDIV_TOP_ACLK100_SHIFT) | | ||
264 | (clkdiv_top[div_index][2] << S5P_CLKDIV_TOP_ACLK160_SHIFT) | | ||
265 | (clkdiv_top[div_index][3] << S5P_CLKDIV_TOP_ACLK133_SHIFT) | | ||
266 | (clkdiv_top[div_index][4] << S5P_CLKDIV_TOP_ONENAND_SHIFT)); | ||
267 | |||
268 | __raw_writel(tmp, S5P_CLKDIV_TOP); | ||
269 | |||
270 | do { | ||
271 | tmp = __raw_readl(S5P_CLKDIV_STAT_TOP); | ||
272 | } while (tmp & 0x11111); | ||
273 | |||
274 | /* Change Divider - LEFTBUS */ | ||
275 | |||
276 | tmp = __raw_readl(S5P_CLKDIV_LEFTBUS); | ||
277 | |||
278 | tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); | ||
279 | |||
280 | tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) | | ||
281 | (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT)); | ||
282 | |||
283 | __raw_writel(tmp, S5P_CLKDIV_LEFTBUS); | ||
284 | |||
285 | do { | ||
286 | tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS); | ||
287 | } while (tmp & 0x11); | ||
288 | |||
289 | /* Change Divider - RIGHTBUS */ | ||
290 | |||
291 | tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS); | ||
292 | |||
293 | tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); | ||
294 | |||
295 | tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) | | ||
296 | (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT)); | ||
297 | |||
298 | __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS); | ||
299 | |||
300 | do { | ||
301 | tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS); | ||
302 | } while (tmp & 0x11); | ||
303 | } | ||
304 | |||
305 | static int s5pv310_target(struct cpufreq_policy *policy, | ||
306 | unsigned int target_freq, | ||
307 | unsigned int relation) | ||
308 | { | ||
309 | unsigned int index, div_index, tmp; | ||
310 | unsigned int arm_volt, int_volt; | ||
311 | unsigned int need_apll = 0; | ||
312 | |||
313 | freqs.old = s5pv310_getspeed(policy->cpu); | ||
314 | |||
315 | if (cpufreq_frequency_table_target(policy, s5pv310_freq_table, | ||
316 | target_freq, relation, &index)) | ||
317 | return -EINVAL; | ||
318 | |||
319 | freqs.new = s5pv310_freq_table[index].frequency; | ||
320 | freqs.cpu = policy->cpu; | ||
321 | |||
322 | if (freqs.new == freqs.old) | ||
323 | return 0; | ||
324 | |||
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 */ | ||
339 | arm_volt = s5pv310_volt_table[index].arm_volt; | ||
340 | int_volt = s5pv310_volt_table[index].int_volt; | ||
341 | |||
342 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
343 | |||
344 | /* control regulator */ | ||
345 | if (freqs.new > freqs.old) { | ||
346 | /* Voltage up */ | ||
347 | #ifdef CONFIG_REGULATOR | ||
348 | regulator_set_voltage(arm_regulator, arm_volt, arm_volt); | ||
349 | regulator_set_voltage(int_regulator, int_volt, int_volt); | ||
350 | #endif | ||
351 | } | ||
352 | |||
353 | /* Clock Configuration Procedure */ | ||
354 | |||
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 | |||
418 | /* control regulator */ | ||
419 | if (freqs.new < freqs.old) { | ||
420 | /* Voltage down */ | ||
421 | #ifdef CONFIG_REGULATOR | ||
422 | regulator_set_voltage(arm_regulator, arm_volt, arm_volt); | ||
423 | regulator_set_voltage(int_regulator, int_volt, int_volt); | ||
424 | #endif | ||
425 | } | ||
426 | |||
427 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | #ifdef CONFIG_PM | ||
433 | static int s5pv310_cpufreq_suspend(struct cpufreq_policy *policy, | ||
434 | pm_message_t pmsg) | ||
435 | { | ||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | static int s5pv310_cpufreq_resume(struct cpufreq_policy *policy) | ||
440 | { | ||
441 | return 0; | ||
442 | } | ||
443 | #endif | ||
444 | |||
445 | static int s5pv310_cpufreq_cpu_init(struct cpufreq_policy *policy) | ||
446 | { | ||
447 | policy->cur = policy->min = policy->max = s5pv310_getspeed(policy->cpu); | ||
448 | |||
449 | cpufreq_frequency_table_get_attr(s5pv310_freq_table, policy->cpu); | ||
450 | |||
451 | /* set the transition latency value */ | ||
452 | policy->cpuinfo.transition_latency = 100000; | ||
453 | |||
454 | /* | ||
455 | * S5PV310 multi-core processors has 2 cores | ||
456 | * that the frequency cannot be set independently. | ||
457 | * Each cpu is bound to the same speed. | ||
458 | * So the affected cpu is all of the cpus. | ||
459 | */ | ||
460 | cpumask_setall(policy->cpus); | ||
461 | |||
462 | return cpufreq_frequency_table_cpuinfo(policy, s5pv310_freq_table); | ||
463 | } | ||
464 | |||
465 | static struct cpufreq_driver s5pv310_driver = { | ||
466 | .flags = CPUFREQ_STICKY, | ||
467 | .verify = s5pv310_verify_speed, | ||
468 | .target = s5pv310_target, | ||
469 | .get = s5pv310_getspeed, | ||
470 | .init = s5pv310_cpufreq_cpu_init, | ||
471 | .name = "s5pv310_cpufreq", | ||
472 | #ifdef CONFIG_PM | ||
473 | .suspend = s5pv310_cpufreq_suspend, | ||
474 | .resume = s5pv310_cpufreq_resume, | ||
475 | #endif | ||
476 | }; | ||
477 | |||
478 | static int __init s5pv310_cpufreq_init(void) | ||
479 | { | ||
480 | unsigned int tmp; | ||
481 | |||
482 | cpu_clk = clk_get(NULL, "armclk"); | ||
483 | if (IS_ERR(cpu_clk)) | ||
484 | return PTR_ERR(cpu_clk); | ||
485 | |||
486 | moutcore = clk_get(NULL, "moutcore"); | ||
487 | if (IS_ERR(moutcore)) | ||
488 | goto out; | ||
489 | |||
490 | mout_mpll = clk_get(NULL, "mout_mpll"); | ||
491 | if (IS_ERR(mout_mpll)) | ||
492 | goto out; | ||
493 | |||
494 | mout_apll = clk_get(NULL, "mout_apll"); | ||
495 | if (IS_ERR(mout_apll)) | ||
496 | goto out; | ||
497 | |||
498 | #ifdef CONFIG_REGULATOR | ||
499 | arm_regulator = regulator_get(NULL, "vdd_arm"); | ||
500 | if (IS_ERR(arm_regulator)) { | ||
501 | printk(KERN_ERR "failed to get resource %s\n", "vdd_arm"); | ||
502 | goto out; | ||
503 | } | ||
504 | |||
505 | int_regulator = regulator_get(NULL, "vdd_int"); | ||
506 | if (IS_ERR(int_regulator)) { | ||
507 | printk(KERN_ERR "failed to get resource %s\n", "vdd_int"); | ||
508 | goto out; | ||
509 | } | ||
510 | #endif | ||
511 | |||
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 | /* | ||
520 | * Check DRAM type. | ||
521 | * Because DVFS level is different according to DRAM type. | ||
522 | */ | ||
523 | memtype = __raw_readl(S5P_VA_DMC0 + S5P_DMC0_MEMCON_OFFSET); | ||
524 | memtype = (memtype >> S5P_DMC0_MEMTYPE_SHIFT); | ||
525 | memtype &= S5P_DMC0_MEMTYPE_MASK; | ||
526 | |||
527 | if ((memtype < DDR2) && (memtype > DDR3)) { | ||
528 | printk(KERN_ERR "%s: wrong memtype= 0x%x\n", __func__, memtype); | ||
529 | goto out; | ||
530 | } else { | ||
531 | printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype); | ||
532 | } | ||
533 | |||
534 | return cpufreq_register_driver(&s5pv310_driver); | ||
535 | |||
536 | out: | ||
537 | if (!IS_ERR(cpu_clk)) | ||
538 | clk_put(cpu_clk); | ||
539 | |||
540 | if (!IS_ERR(moutcore)) | ||
541 | clk_put(moutcore); | ||
542 | |||
543 | if (!IS_ERR(mout_mpll)) | ||
544 | clk_put(mout_mpll); | ||
545 | |||
546 | if (!IS_ERR(mout_apll)) | ||
547 | clk_put(mout_apll); | ||
548 | |||
549 | #ifdef CONFIG_REGULATOR | ||
550 | if (!IS_ERR(arm_regulator)) | ||
551 | regulator_put(arm_regulator); | ||
552 | |||
553 | if (!IS_ERR(int_regulator)) | ||
554 | regulator_put(int_regulator); | ||
555 | #endif | ||
556 | |||
557 | printk(KERN_ERR "%s: failed initialization\n", __func__); | ||
558 | |||
559 | return -EINVAL; | ||
560 | } | ||
561 | late_initcall(s5pv310_cpufreq_init); | ||