]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - drivers/cpufreq/s5pv210-cpufreq.c
[CPUFREQ] S5PV210: Add arm/int voltage control support
[mirror_ubuntu-zesty-kernel.git] / drivers / cpufreq / s5pv210-cpufreq.c
CommitLineData
f7d77079 1/*
83efc743
JL
2 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com
4 *
5 * CPU frequency scaling for S5PC110/S5PV210
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/init.h>
15#include <linux/err.h>
16#include <linux/clk.h>
17#include <linux/io.h>
18#include <linux/cpufreq.h>
e8b4c198 19#include <linux/regulator/consumer.h>
83efc743
JL
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/* APLL M,P,S values for 1G/800Mhz */
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
90d5d0a1
HK
33/*
34 * relation has an additional symantics other than the standard of cpufreq
35 * DISALBE_FURTHER_CPUFREQ: disable further access to target
36 * ENABLE_FURTUER_CPUFREQ: enable access to target
37 */
38enum cpufreq_access {
39 DISABLE_FURTHER_CPUFREQ = 0x10,
40 ENABLE_FURTHER_CPUFREQ = 0x20,
41};
42
43static bool no_cpufreq_access;
44
83efc743
JL
45/*
46 * DRAM configurations to calculate refresh counter for changing
47 * frequency of memory.
48 */
49struct dram_conf {
50 unsigned long freq; /* HZ */
51 unsigned long refresh; /* DRAM refresh counter * 1000 */
52};
53
54/* DRAM configuration (DMC0 and DMC1) */
55static struct dram_conf s5pv210_dram_conf[2];
56
57enum perf_level {
58 L0, L1, L2, L3, L4,
59};
60
61enum s5pv210_mem_type {
62 LPDDR = 0x1,
63 LPDDR2 = 0x2,
64 DDR2 = 0x4,
65};
66
67enum s5pv210_dmc_port {
68 DMC0 = 0,
69 DMC1,
70};
71
72static struct cpufreq_frequency_table s5pv210_freq_table[] = {
73 {L0, 1000*1000},
74 {L1, 800*1000},
75 {L2, 400*1000},
76 {L3, 200*1000},
77 {L4, 100*1000},
78 {0, CPUFREQ_TABLE_END},
79};
80
e8b4c198
JC
81static struct regulator *arm_regulator;
82static struct regulator *int_regulator;
83
84struct s5pv210_dvs_conf {
85 int arm_volt; /* uV */
86 int int_volt; /* uV */
87};
88
89static const int arm_volt_max = 1350000;
90static const int int_volt_max = 1250000;
91
92static struct s5pv210_dvs_conf dvs_conf[] = {
93 [L0] = {
94 .arm_volt = 1250000,
95 .int_volt = 1100000,
96 },
97 [L1] = {
98 .arm_volt = 1200000,
99 .int_volt = 1100000,
100 },
101 [L2] = {
102 .arm_volt = 1050000,
103 .int_volt = 1100000,
104 },
105 [L3] = {
106 .arm_volt = 950000,
107 .int_volt = 1100000,
108 },
109 [L4] = {
110 .arm_volt = 950000,
111 .int_volt = 1000000,
112 },
113};
114
83efc743
JL
115static u32 clkdiv_val[5][11] = {
116 /*
117 * Clock divider value for following
118 * { APLL, A2M, HCLK_MSYS, PCLK_MSYS,
119 * HCLK_DSYS, PCLK_DSYS, HCLK_PSYS, PCLK_PSYS,
120 * ONEDRAM, MFC, G3D }
121 */
122
123 /* L0 : [1000/200/100][166/83][133/66][200/200] */
124 {0, 4, 4, 1, 3, 1, 4, 1, 3, 0, 0},
125
126 /* L1 : [800/200/100][166/83][133/66][200/200] */
127 {0, 3, 3, 1, 3, 1, 4, 1, 3, 0, 0},
128
129 /* L2 : [400/200/100][166/83][133/66][200/200] */
130 {1, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
131
132 /* L3 : [200/200/100][166/83][133/66][200/200] */
133 {3, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
134
135 /* L4 : [100/100/100][83/83][66/66][100/100] */
136 {7, 7, 0, 0, 7, 0, 9, 0, 7, 0, 0},
137};
138
139/*
140 * This function set DRAM refresh counter
141 * accoriding to operating frequency of DRAM
142 * ch: DMC port number 0 or 1
143 * freq: Operating frequency of DRAM(KHz)
144 */
145static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq)
146{
147 unsigned long tmp, tmp1;
148 void __iomem *reg = NULL;
149
d62fa311 150 if (ch == DMC0) {
83efc743 151 reg = (S5P_VA_DMC0 + 0x30);
d62fa311 152 } else if (ch == DMC1) {
83efc743 153 reg = (S5P_VA_DMC1 + 0x30);
d62fa311 154 } else {
83efc743 155 printk(KERN_ERR "Cannot find DMC port\n");
d62fa311
JC
156 return;
157 }
83efc743
JL
158
159 /* Find current DRAM frequency */
160 tmp = s5pv210_dram_conf[ch].freq;
161
162 do_div(tmp, freq);
163
164 tmp1 = s5pv210_dram_conf[ch].refresh;
165
166 do_div(tmp1, tmp);
167
168 __raw_writel(tmp1, reg);
169}
170
171int s5pv210_verify_speed(struct cpufreq_policy *policy)
172{
173 if (policy->cpu)
174 return -EINVAL;
175
176 return cpufreq_frequency_table_verify(policy, s5pv210_freq_table);
177}
178
179unsigned int s5pv210_getspeed(unsigned int cpu)
180{
181 if (cpu)
182 return 0;
183
184 return clk_get_rate(cpu_clk) / 1000;
185}
186
187static int s5pv210_target(struct cpufreq_policy *policy,
188 unsigned int target_freq,
189 unsigned int relation)
190{
191 unsigned long reg;
192 unsigned int index, priv_index;
193 unsigned int pll_changing = 0;
194 unsigned int bus_speed_changing = 0;
e8b4c198
JC
195 int arm_volt, int_volt;
196 int ret = 0;
83efc743 197
90d5d0a1
HK
198 if (relation & ENABLE_FURTHER_CPUFREQ)
199 no_cpufreq_access = false;
200
201 if (no_cpufreq_access) {
202#ifdef CONFIG_PM_VERBOSE
203 pr_err("%s:%d denied access to %s as it is disabled"
204 "temporarily\n", __FILE__, __LINE__, __func__);
205#endif
206 return -EINVAL;
207 }
208
209 if (relation & DISABLE_FURTHER_CPUFREQ)
210 no_cpufreq_access = true;
211
212 relation &= ~(ENABLE_FURTHER_CPUFREQ | DISABLE_FURTHER_CPUFREQ);
213
83efc743
JL
214 freqs.old = s5pv210_getspeed(0);
215
216 if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
217 target_freq, relation, &index))
218 return -EINVAL;
219
220 freqs.new = s5pv210_freq_table[index].frequency;
221 freqs.cpu = 0;
222
223 if (freqs.new == freqs.old)
224 return 0;
225
226 /* Finding current running level index */
227 if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
228 freqs.old, relation, &priv_index))
229 return -EINVAL;
230
e8b4c198
JC
231 arm_volt = dvs_conf[index].arm_volt;
232 int_volt = dvs_conf[index].int_volt;
83efc743
JL
233
234 if (freqs.new > freqs.old) {
e8b4c198
JC
235 ret = regulator_set_voltage(arm_regulator,
236 arm_volt, arm_volt_max);
237 if (ret)
238 return ret;
239
240 ret = regulator_set_voltage(int_regulator,
241 int_volt, int_volt_max);
242 if (ret)
243 return ret;
83efc743
JL
244 }
245
e8b4c198
JC
246 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
247
83efc743
JL
248 /* Check if there need to change PLL */
249 if ((index == L0) || (priv_index == L0))
250 pll_changing = 1;
251
252 /* Check if there need to change System bus clock */
253 if ((index == L4) || (priv_index == L4))
254 bus_speed_changing = 1;
255
256 if (bus_speed_changing) {
257 /*
258 * Reconfigure DRAM refresh counter value for minimum
259 * temporary clock while changing divider.
260 * expected clock is 83Mhz : 7.8usec/(1/83Mhz) = 0x287
261 */
262 if (pll_changing)
263 s5pv210_set_refresh(DMC1, 83000);
264 else
265 s5pv210_set_refresh(DMC1, 100000);
266
267 s5pv210_set_refresh(DMC0, 83000);
268 }
269
270 /*
271 * APLL should be changed in this level
272 * APLL -> MPLL(for stable transition) -> APLL
273 * Some clock source's clock API are not prepared.
274 * Do not use clock API in below code.
275 */
276 if (pll_changing) {
277 /*
278 * 1. Temporary Change divider for MFC and G3D
279 * SCLKA2M(200/1=200)->(200/4=50)Mhz
280 */
281 reg = __raw_readl(S5P_CLK_DIV2);
282 reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
283 reg |= (3 << S5P_CLKDIV2_G3D_SHIFT) |
284 (3 << S5P_CLKDIV2_MFC_SHIFT);
285 __raw_writel(reg, S5P_CLK_DIV2);
286
287 /* For MFC, G3D dividing */
288 do {
289 reg = __raw_readl(S5P_CLKDIV_STAT0);
290 } while (reg & ((1 << 16) | (1 << 17)));
291
292 /*
293 * 2. Change SCLKA2M(200Mhz)to SCLKMPLL in MFC_MUX, G3D MUX
294 * (200/4=50)->(667/4=166)Mhz
295 */
296 reg = __raw_readl(S5P_CLK_SRC2);
297 reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
298 reg |= (1 << S5P_CLKSRC2_G3D_SHIFT) |
299 (1 << S5P_CLKSRC2_MFC_SHIFT);
300 __raw_writel(reg, S5P_CLK_SRC2);
301
302 do {
303 reg = __raw_readl(S5P_CLKMUX_STAT1);
304 } while (reg & ((1 << 7) | (1 << 3)));
305
306 /*
307 * 3. DMC1 refresh count for 133Mhz if (index == L4) is
308 * true refresh counter is already programed in upper
309 * code. 0x287@83Mhz
310 */
311 if (!bus_speed_changing)
312 s5pv210_set_refresh(DMC1, 133000);
313
314 /* 4. SCLKAPLL -> SCLKMPLL */
315 reg = __raw_readl(S5P_CLK_SRC0);
316 reg &= ~(S5P_CLKSRC0_MUX200_MASK);
317 reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
318 __raw_writel(reg, S5P_CLK_SRC0);
319
320 do {
321 reg = __raw_readl(S5P_CLKMUX_STAT0);
322 } while (reg & (0x1 << 18));
323
324 }
325
326 /* Change divider */
327 reg = __raw_readl(S5P_CLK_DIV0);
328
329 reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK |
330 S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK |
331 S5P_CLKDIV0_HCLK166_MASK | S5P_CLKDIV0_PCLK83_MASK |
332 S5P_CLKDIV0_HCLK133_MASK | S5P_CLKDIV0_PCLK66_MASK);
333
334 reg |= ((clkdiv_val[index][0] << S5P_CLKDIV0_APLL_SHIFT) |
335 (clkdiv_val[index][1] << S5P_CLKDIV0_A2M_SHIFT) |
336 (clkdiv_val[index][2] << S5P_CLKDIV0_HCLK200_SHIFT) |
337 (clkdiv_val[index][3] << S5P_CLKDIV0_PCLK100_SHIFT) |
338 (clkdiv_val[index][4] << S5P_CLKDIV0_HCLK166_SHIFT) |
339 (clkdiv_val[index][5] << S5P_CLKDIV0_PCLK83_SHIFT) |
340 (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT) |
341 (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
342
343 __raw_writel(reg, S5P_CLK_DIV0);
344
345 do {
346 reg = __raw_readl(S5P_CLKDIV_STAT0);
347 } while (reg & 0xff);
348
349 /* ARM MCS value changed */
350 reg = __raw_readl(S5P_ARM_MCS_CON);
351 reg &= ~0x3;
352 if (index >= L3)
353 reg |= 0x3;
354 else
355 reg |= 0x1;
356
357 __raw_writel(reg, S5P_ARM_MCS_CON);
358
359 if (pll_changing) {
360 /* 5. Set Lock time = 30us*24Mhz = 0x2cf */
361 __raw_writel(0x2cf, S5P_APLL_LOCK);
362
363 /*
364 * 6. Turn on APLL
365 * 6-1. Set PMS values
366 * 6-2. Wait untile the PLL is locked
367 */
368 if (index == L0)
369 __raw_writel(APLL_VAL_1000, S5P_APLL_CON);
370 else
371 __raw_writel(APLL_VAL_800, S5P_APLL_CON);
372
373 do {
374 reg = __raw_readl(S5P_APLL_CON);
375 } while (!(reg & (0x1 << 29)));
376
377 /*
378 * 7. Change souce clock from SCLKMPLL(667Mhz)
379 * to SCLKA2M(200Mhz) in MFC_MUX and G3D MUX
380 * (667/4=166)->(200/4=50)Mhz
381 */
382 reg = __raw_readl(S5P_CLK_SRC2);
383 reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
384 reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) |
385 (0 << S5P_CLKSRC2_MFC_SHIFT);
386 __raw_writel(reg, S5P_CLK_SRC2);
387
388 do {
389 reg = __raw_readl(S5P_CLKMUX_STAT1);
390 } while (reg & ((1 << 7) | (1 << 3)));
391
392 /*
393 * 8. Change divider for MFC and G3D
394 * (200/4=50)->(200/1=200)Mhz
395 */
396 reg = __raw_readl(S5P_CLK_DIV2);
397 reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
398 reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
399 (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
400 __raw_writel(reg, S5P_CLK_DIV2);
401
402 /* For MFC, G3D dividing */
403 do {
404 reg = __raw_readl(S5P_CLKDIV_STAT0);
405 } while (reg & ((1 << 16) | (1 << 17)));
406
407 /* 9. Change MPLL to APLL in MSYS_MUX */
408 reg = __raw_readl(S5P_CLK_SRC0);
409 reg &= ~(S5P_CLKSRC0_MUX200_MASK);
410 reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT);
411 __raw_writel(reg, S5P_CLK_SRC0);
412
413 do {
414 reg = __raw_readl(S5P_CLKMUX_STAT0);
415 } while (reg & (0x1 << 18));
416
417 /*
418 * 10. DMC1 refresh counter
419 * L4 : DMC1 = 100Mhz 7.8us/(1/100) = 0x30c
420 * Others : DMC1 = 200Mhz 7.8us/(1/200) = 0x618
421 */
422 if (!bus_speed_changing)
423 s5pv210_set_refresh(DMC1, 200000);
424 }
425
426 /*
427 * L4 level need to change memory bus speed, hence onedram clock divier
428 * and memory refresh parameter should be changed
429 */
430 if (bus_speed_changing) {
431 reg = __raw_readl(S5P_CLK_DIV6);
432 reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
433 reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
434 __raw_writel(reg, S5P_CLK_DIV6);
435
436 do {
437 reg = __raw_readl(S5P_CLKDIV_STAT1);
438 } while (reg & (1 << 15));
439
440 /* Reconfigure DRAM refresh counter value */
441 if (index != L4) {
442 /*
443 * DMC0 : 166Mhz
444 * DMC1 : 200Mhz
445 */
446 s5pv210_set_refresh(DMC0, 166000);
447 s5pv210_set_refresh(DMC1, 200000);
448 } else {
449 /*
450 * DMC0 : 83Mhz
451 * DMC1 : 100Mhz
452 */
453 s5pv210_set_refresh(DMC0, 83000);
454 s5pv210_set_refresh(DMC1, 100000);
455 }
456 }
457
458 if (freqs.new < freqs.old) {
e8b4c198
JC
459 regulator_set_voltage(int_regulator,
460 int_volt, int_volt_max);
461
462 regulator_set_voltage(arm_regulator,
463 arm_volt, arm_volt_max);
83efc743
JL
464 }
465
466 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
467
468 printk(KERN_DEBUG "Perf changed[L%d]\n", index);
469
470 return 0;
471}
472
473#ifdef CONFIG_PM
7ca64e2d 474static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy)
83efc743
JL
475{
476 return 0;
477}
478
479static int s5pv210_cpufreq_resume(struct cpufreq_policy *policy)
480{
481 return 0;
482}
483#endif
484
485static int check_mem_type(void __iomem *dmc_reg)
486{
487 unsigned long val;
488
489 val = __raw_readl(dmc_reg + 0x4);
490 val = (val & (0xf << 8));
491
492 return val >> 8;
493}
494
495static int __init s5pv210_cpu_init(struct cpufreq_policy *policy)
496{
497 unsigned long mem_type;
4911ca10 498 int ret;
83efc743
JL
499
500 cpu_clk = clk_get(NULL, "armclk");
501 if (IS_ERR(cpu_clk))
502 return PTR_ERR(cpu_clk);
503
504 dmc0_clk = clk_get(NULL, "sclk_dmc0");
505 if (IS_ERR(dmc0_clk)) {
4911ca10
JL
506 ret = PTR_ERR(dmc0_clk);
507 goto out_dmc0;
83efc743
JL
508 }
509
510 dmc1_clk = clk_get(NULL, "hclk_msys");
511 if (IS_ERR(dmc1_clk)) {
4911ca10
JL
512 ret = PTR_ERR(dmc1_clk);
513 goto out_dmc1;
83efc743
JL
514 }
515
4911ca10
JL
516 if (policy->cpu != 0) {
517 ret = -EINVAL;
518 goto out_dmc1;
519 }
83efc743
JL
520
521 /*
522 * check_mem_type : This driver only support LPDDR & LPDDR2.
523 * other memory type is not supported.
524 */
525 mem_type = check_mem_type(S5P_VA_DMC0);
526
527 if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
528 printk(KERN_ERR "CPUFreq doesn't support this memory type\n");
4911ca10
JL
529 ret = -EINVAL;
530 goto out_dmc1;
83efc743
JL
531 }
532
533 /* Find current refresh counter and frequency each DMC */
534 s5pv210_dram_conf[0].refresh = (__raw_readl(S5P_VA_DMC0 + 0x30) * 1000);
535 s5pv210_dram_conf[0].freq = clk_get_rate(dmc0_clk);
536
537 s5pv210_dram_conf[1].refresh = (__raw_readl(S5P_VA_DMC1 + 0x30) * 1000);
538 s5pv210_dram_conf[1].freq = clk_get_rate(dmc1_clk);
539
540 policy->cur = policy->min = policy->max = s5pv210_getspeed(0);
541
542 cpufreq_frequency_table_get_attr(s5pv210_freq_table, policy->cpu);
543
544 policy->cpuinfo.transition_latency = 40000;
545
546 return cpufreq_frequency_table_cpuinfo(policy, s5pv210_freq_table);
4911ca10
JL
547
548out_dmc1:
549 clk_put(dmc0_clk);
550out_dmc0:
551 clk_put(cpu_clk);
552 return ret;
83efc743
JL
553}
554
555static struct cpufreq_driver s5pv210_driver = {
556 .flags = CPUFREQ_STICKY,
557 .verify = s5pv210_verify_speed,
558 .target = s5pv210_target,
559 .get = s5pv210_getspeed,
560 .init = s5pv210_cpu_init,
561 .name = "s5pv210",
562#ifdef CONFIG_PM
563 .suspend = s5pv210_cpufreq_suspend,
564 .resume = s5pv210_cpufreq_resume,
565#endif
566};
567
568static int __init s5pv210_cpufreq_init(void)
569{
e8b4c198
JC
570 arm_regulator = regulator_get(NULL, "vddarm");
571 if (IS_ERR(arm_regulator)) {
572 pr_err("failed to get regulator vddarm");
573 return PTR_ERR(arm_regulator);
574 }
575
576 int_regulator = regulator_get(NULL, "vddint");
577 if (IS_ERR(int_regulator)) {
578 pr_err("failed to get regulator vddint");
579 regulator_put(arm_regulator);
580 return PTR_ERR(int_regulator);
581 }
582
83efc743
JL
583 return cpufreq_register_driver(&s5pv210_driver);
584}
585
586late_initcall(s5pv210_cpufreq_init);