1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/types.h>
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/err.h>
17#include <linux/clk.h>
18#include <linux/io.h>
19#include <linux/cpufreq.h>
20
21#include <mach/map.h>
22#include <mach/regs-clock.h>
23
24static struct clk *cpu_clk;
25static struct clk *dmc0_clk;
26static struct clk *dmc1_clk;
27static struct cpufreq_freqs freqs;
28
29
30#define APLL_VAL_1000 ((1 << 31) | (125 << 16) | (3 << 8) | 1)
31#define APLL_VAL_800 ((1 << 31) | (100 << 16) | (3 << 8) | 1)
32
33
34
35
36
37struct dram_conf {
38 unsigned long freq;
39 unsigned long refresh;
40};
41
42
43static struct dram_conf s5pv210_dram_conf[2];
44
45enum perf_level {
46 L0, L1, L2, L3, L4,
47};
48
49enum s5pv210_mem_type {
50 LPDDR = 0x1,
51 LPDDR2 = 0x2,
52 DDR2 = 0x4,
53};
54
55enum s5pv210_dmc_port {
56 DMC0 = 0,
57 DMC1,
58};
59
60static struct cpufreq_frequency_table s5pv210_freq_table[] = {
61 {L0, 1000*1000},
62 {L1, 800*1000},
63 {L2, 400*1000},
64 {L3, 200*1000},
65 {L4, 100*1000},
66 {0, CPUFREQ_TABLE_END},
67};
68
69static u32 clkdiv_val[5][11] = {
70
71
72
73
74
75
76
77
78 {0, 4, 4, 1, 3, 1, 4, 1, 3, 0, 0},
79
80
81 {0, 3, 3, 1, 3, 1, 4, 1, 3, 0, 0},
82
83
84 {1, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
85
86
87 {3, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
88
89
90 {7, 7, 0, 0, 7, 0, 9, 0, 7, 0, 0},
91};
92
93
94
95
96
97
98
99static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq)
100{
101 unsigned long tmp, tmp1;
102 void __iomem *reg = NULL;
103
104 if (ch == DMC0)
105 reg = (S5P_VA_DMC0 + 0x30);
106 else if (ch == DMC1)
107 reg = (S5P_VA_DMC1 + 0x30);
108 else
109 printk(KERN_ERR "Cannot find DMC port\n");
110
111
112 tmp = s5pv210_dram_conf[ch].freq;
113
114 do_div(tmp, freq);
115
116 tmp1 = s5pv210_dram_conf[ch].refresh;
117
118 do_div(tmp1, tmp);
119
120 __raw_writel(tmp1, reg);
121}
122
123int s5pv210_verify_speed(struct cpufreq_policy *policy)
124{
125 if (policy->cpu)
126 return -EINVAL;
127
128 return cpufreq_frequency_table_verify(policy, s5pv210_freq_table);
129}
130
131unsigned int s5pv210_getspeed(unsigned int cpu)
132{
133 if (cpu)
134 return 0;
135
136 return clk_get_rate(cpu_clk) / 1000;
137}
138
139static int s5pv210_target(struct cpufreq_policy *policy,
140 unsigned int target_freq,
141 unsigned int relation)
142{
143 unsigned long reg;
144 unsigned int index, priv_index;
145 unsigned int pll_changing = 0;
146 unsigned int bus_speed_changing = 0;
147
148 freqs.old = s5pv210_getspeed(0);
149
150 if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
151 target_freq, relation, &index))
152 return -EINVAL;
153
154 freqs.new = s5pv210_freq_table[index].frequency;
155 freqs.cpu = 0;
156
157 if (freqs.new == freqs.old)
158 return 0;
159
160
161 if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
162 freqs.old, relation, &priv_index))
163 return -EINVAL;
164
165 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
166
167 if (freqs.new > freqs.old) {
168
169 }
170
171
172 if ((index == L0) || (priv_index == L0))
173 pll_changing = 1;
174
175
176 if ((index == L4) || (priv_index == L4))
177 bus_speed_changing = 1;
178
179 if (bus_speed_changing) {
180
181
182
183
184
185 if (pll_changing)
186 s5pv210_set_refresh(DMC1, 83000);
187 else
188 s5pv210_set_refresh(DMC1, 100000);
189
190 s5pv210_set_refresh(DMC0, 83000);
191 }
192
193
194
195
196
197
198
199 if (pll_changing) {
200
201
202
203
204 reg = __raw_readl(S5P_CLK_DIV2);
205 reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
206 reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) |
207 (3 << S5P_CLKDIV2_MFC_SHIFT);
208 __raw_writel(reg, S5P_CLK_DIV2);
209
210
211 do {
212 reg = __raw_readl(S5P_CLKDIV_STAT0);
213 } while (reg & ((1 << 16) | (1 << 17)));
214
215
216
217
218
219 reg = __raw_readl(S5P_CLK_SRC2);
220 reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
221 reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) |
222 (1 << S5P_CLKSRC2_MFC_SHIFT);
223 __raw_writel(reg, S5P_CLK_SRC2);
224
225 do {
226 reg = __raw_readl(S5P_CLKMUX_STAT1);
227 } while (reg & ((1 << 7) | (1 << 3)));
228
229
230
231
232
233
234 if (!bus_speed_changing)
235 s5pv210_set_refresh(DMC1, 133000);
236
237
238 reg = __raw_readl(S5P_CLK_SRC0);
239 reg &= ~(S5P_CLKSRC0_MUX200_MASK);
240 reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
241 __raw_writel(reg, S5P_CLK_SRC0);
242
243 do {
244 reg = __raw_readl(S5P_CLKMUX_STAT0);
245 } while (reg & (0x1 << 18));
246
247 }
248
249
250 reg = __raw_readl(S5P_CLK_DIV0);
251
252 reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK |
253 S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK |
254 S5P_CLKDIV0_HCLK166_MASK | S5P_CLKDIV0_PCLK83_MASK |
255 S5P_CLKDIV0_HCLK133_MASK | S5P_CLKDIV0_PCLK66_MASK);
256
257 reg |= ((clkdiv_val[index][0] << S5P_CLKDIV0_APLL_SHIFT) |
258 (clkdiv_val[index][1] << S5P_CLKDIV0_A2M_SHIFT) |
259 (clkdiv_val[index][2] << S5P_CLKDIV0_HCLK200_SHIFT) |
260 (clkdiv_val[index][3] << S5P_CLKDIV0_PCLK100_SHIFT) |
261 (clkdiv_val[index][4] << S5P_CLKDIV0_HCLK166_SHIFT) |
262 (clkdiv_val[index][5] << S5P_CLKDIV0_PCLK83_SHIFT) |
263 (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) |
264 (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
265
266 __raw_writel(reg, S5P_CLK_DIV0);
267
268 do {
269 reg = __raw_readl(S5P_CLKDIV_STAT0);
270 } while (reg & 0xff);
271
272
273 reg = __raw_readl(S5P_ARM_MCS_CON);
274 reg &= ~0x3;
275 if (index >= L3)
276 reg |= 0x3;
277 else
278 reg |= 0x1;
279
280 __raw_writel(reg, S5P_ARM_MCS_CON);
281
282 if (pll_changing) {
283
284 __raw_writel(0x2cf, S5P_APLL_LOCK);
285
286
287
288
289
290
291 if (index == L0)
292 __raw_writel(APLL_VAL_1000, S5P_APLL_CON);
293 else
294 __raw_writel(APLL_VAL_800, S5P_APLL_CON);
295
296 do {
297 reg = __raw_readl(S5P_APLL_CON);
298 } while (!(reg & (0x1 << 29)));
299
300
301
302
303
304
305 reg = __raw_readl(S5P_CLK_SRC2);
306 reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
307 reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) |
308 (0 << S5P_CLKSRC2_MFC_SHIFT);
309 __raw_writel(reg, S5P_CLK_SRC2);
310
311 do {
312 reg = __raw_readl(S5P_CLKMUX_STAT1);
313 } while (reg & ((1 << 7) | (1 << 3)));
314
315
316
317
318
319 reg = __raw_readl(S5P_CLK_DIV2);
320 reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
321 reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
322 (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
323 __raw_writel(reg, S5P_CLK_DIV2);
324
325
326 do {
327 reg = __raw_readl(S5P_CLKDIV_STAT0);
328 } while (reg & ((1 << 16) | (1 << 17)));
329
330
331 reg = __raw_readl(S5P_CLK_SRC0);
332 reg &= ~(S5P_CLKSRC0_MUX200_MASK);
333 reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT);
334 __raw_writel(reg, S5P_CLK_SRC0);
335
336 do {
337 reg = __raw_readl(S5P_CLKMUX_STAT0);
338 } while (reg & (0x1 << 18));
339
340
341
342
343
344
345 if (!bus_speed_changing)
346 s5pv210_set_refresh(DMC1, 200000);
347 }
348
349
350
351
352
353 if (bus_speed_changing) {
354 reg = __raw_readl(S5P_CLK_DIV6);
355 reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
356 reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
357 __raw_writel(reg, S5P_CLK_DIV6);
358
359 do {
360 reg = __raw_readl(S5P_CLKDIV_STAT1);
361 } while (reg & (1 << 15));
362
363
364 if (index != L4) {
365
366
367
368
369 s5pv210_set_refresh(DMC0, 166000);
370 s5pv210_set_refresh(DMC1, 200000);
371 } else {
372
373
374
375
376 s5pv210_set_refresh(DMC0, 83000);
377 s5pv210_set_refresh(DMC1, 100000);
378 }
379 }
380
381 if (freqs.new < freqs.old) {
382
383 }
384
385 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
386
387 printk(KERN_DEBUG "Perf changed[L%d]\n", index);
388
389 return 0;
390}
391
392#ifdef CONFIG_PM
393static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy)
394{
395 return 0;
396}
397
398static int s5pv210_cpufreq_resume(struct cpufreq_policy *policy)
399{
400 return 0;
401}
402#endif
403
404static int check_mem_type(void __iomem *dmc_reg)
405{
406 unsigned long val;
407
408 val = __raw_readl(dmc_reg + 0x4);
409 val = (val & (0xf << 8));
410
411 return val >> 8;
412}
413
414static int __init s5pv210_cpu_init(struct cpufreq_policy *policy)
415{
416 unsigned long mem_type;
417
418 cpu_clk = clk_get(NULL, "armclk");
419 if (IS_ERR(cpu_clk))
420 return PTR_ERR(cpu_clk);
421
422 dmc0_clk = clk_get(NULL, "sclk_dmc0");
423 if (IS_ERR(dmc0_clk)) {
424 clk_put(cpu_clk);
425 return PTR_ERR(dmc0_clk);
426 }
427
428 dmc1_clk = clk_get(NULL, "hclk_msys");
429 if (IS_ERR(dmc1_clk)) {
430 clk_put(dmc0_clk);
431 clk_put(cpu_clk);
432 return PTR_ERR(dmc1_clk);
433 }
434
435 if (policy->cpu != 0)
436 return -EINVAL;
437
438
439
440
441
442 mem_type = check_mem_type(S5P_VA_DMC0);
443
444 if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
445 printk(KERN_ERR "CPUFreq doesn't support this memory type\n");
446 return -EINVAL;
447 }
448
449
450 s5pv210_dram_conf[0].refresh = (__raw_readl(S5P_VA_DMC0 + 0x30) * 1000);
451 s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk);
452
453 s5pv210_dram_conf[1].refresh = (__raw_readl(S5P_VA_DMC1 + 0x30) * 1000);
454 s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk);
455
456 policy->cur = policy->min = policy->max = s5pv210_getspeed(0);
457
458 cpufreq_frequency_table_get_attr(s5pv210_freq_table, policy->cpu);
459
460 policy->cpuinfo.transition_latency = 40000;
461
462 return cpufreq_frequency_table_cpuinfo(policy, s5pv210_freq_table);
463}
464
465static struct cpufreq_driver s5pv210_driver = {
466 .flags = CPUFREQ_STICKY,
467 .verify = s5pv210_verify_speed,
468 .target = s5pv210_target,
469 .get = s5pv210_getspeed,
470 .init = s5pv210_cpu_init,
471 .name = "s5pv210",
472#ifdef CONFIG_PM
473 .suspend = s5pv210_cpufreq_suspend,
474 .resume = s5pv210_cpufreq_resume,
475#endif
476};
477
478static int __init s5pv210_cpufreq_init(void)
479{
480 return cpufreq_register_driver(&s5pv210_driver);
481}
482
483late_initcall(s5pv210_cpufreq_init);
484