aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorJaecheol Lee <jc.lee@samsung.com>2012-03-10 06:00:02 -0500
committerDave Jones <davej@redhat.com>2012-03-14 14:39:50 -0400
commit562a6cbe1cf2edf0a87a659eb26095400322430e (patch)
treee13e1e913094aecab5d38e729ad928087da8fd20 /drivers/cpufreq
parenta35c50510dd4d20a9275a0d3d16b859d11b35606 (diff)
EXYNOS5250: Add support cpufreq for EXYNOS5250
This patch adds support cpufreq for EXYNOS5250 SoC. Basically, the exynos-cpufreq.c is used commonly and exynos5250-cpufreq.c is used for EXYNOS5250(two Cortex-A15 cores) SoC. Signed-off-by: Jaecheol Lee <jc.lee@samsung.com> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com> Signed-off-by: Dave Jones <davej@redhat.com>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/Kconfig.arm7
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/exynos-cpufreq.c2
-rw-r--r--drivers/cpufreq/exynos5250-cpufreq.c347
4 files changed, 357 insertions, 0 deletions
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 9b99a9672a57..2e3351526ffa 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -48,6 +48,7 @@ config ARM_EXYNOS_CPUFREQ
48 depends on ARCH_EXYNOS 48 depends on ARCH_EXYNOS
49 select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210 49 select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210
50 select ARM_EXYNOS4X12_CPUFREQ if (SOC_EXYNOS4212 || SOC_EXYNOS4412) 50 select ARM_EXYNOS4X12_CPUFREQ if (SOC_EXYNOS4212 || SOC_EXYNOS4412)
51 select ARM_EXYNOS5250_CPUFREQ if SOC_EXYNOS5250
51 default y 52 default y
52 help 53 help
53 This adds the CPUFreq driver common part for Samsung 54 This adds the CPUFreq driver common part for Samsung
@@ -67,3 +68,9 @@ config ARM_EXYNOS4X12_CPUFREQ
67 help 68 help
68 This adds the CPUFreq driver for Samsung EXYNOS4X12 69 This adds the CPUFreq driver for Samsung EXYNOS4X12
69 SoC (EXYNOS4212 or EXYNOS4412). 70 SoC (EXYNOS4212 or EXYNOS4412).
71
72config ARM_EXYNOS5250_CPUFREQ
73 bool "Samsung EXYNOS5250"
74 help
75 This adds the CPUFreq driver for Samsung EXYNOS5250
76 SoC.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index f37bbccbc879..3cbbcd09c650 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
46obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o 46obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o
47obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o 47obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o
48obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o 48obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
49obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
49obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o 50obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o
50 51
51################################################################################## 52##################################################################################
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c
index 2c63b68d5b23..b243a7ee01f6 100644
--- a/drivers/cpufreq/exynos-cpufreq.c
+++ b/drivers/cpufreq/exynos-cpufreq.c
@@ -256,6 +256,8 @@ static int __init exynos_cpufreq_init(void)
256 ret = exynos4210_cpufreq_init(exynos_info); 256 ret = exynos4210_cpufreq_init(exynos_info);
257 else if (soc_is_exynos4212() || soc_is_exynos4412()) 257 else if (soc_is_exynos4212() || soc_is_exynos4412())
258 ret = exynos4x12_cpufreq_init(exynos_info); 258 ret = exynos4x12_cpufreq_init(exynos_info);
259 else if (soc_is_exynos5250())
260 ret = exynos5250_cpufreq_init(exynos_info);
259 else 261 else
260 pr_err("%s: CPU type not found\n", __func__); 262 pr_err("%s: CPU type not found\n", __func__);
261 263
diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c
new file mode 100644
index 000000000000..a88331644ebf
--- /dev/null
+++ b/drivers/cpufreq/exynos5250-cpufreq.c
@@ -0,0 +1,347 @@
1/*
2 * Copyright (c) 2010-20122Samsung Electronics Co., Ltd.
3 * http://www.samsung.com
4 *
5 * EXYNOS5250 - 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/module.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/cpufreq.h>
19
20#include <mach/map.h>
21#include <mach/regs-clock.h>
22#include <mach/cpufreq.h>
23
24#define CPUFREQ_LEVEL_END (L15 + 1)
25
26static int max_support_idx;
27static int min_support_idx = (CPUFREQ_LEVEL_END - 1);
28static struct clk *cpu_clk;
29static struct clk *moutcore;
30static struct clk *mout_mpll;
31static struct clk *mout_apll;
32
33struct cpufreq_clkdiv {
34 unsigned int index;
35 unsigned int clkdiv;
36 unsigned int clkdiv1;
37};
38
39static unsigned int exynos5250_volt_table[CPUFREQ_LEVEL_END];
40
41static struct cpufreq_frequency_table exynos5250_freq_table[] = {
42 {L0, 1700 * 1000},
43 {L1, 1600 * 1000},
44 {L2, 1500 * 1000},
45 {L3, 1400 * 1000},
46 {L4, 1300 * 1000},
47 {L5, 1200 * 1000},
48 {L6, 1100 * 1000},
49 {L7, 1000 * 1000},
50 {L8, 900 * 1000},
51 {L9, 800 * 1000},
52 {L10, 700 * 1000},
53 {L11, 600 * 1000},
54 {L12, 500 * 1000},
55 {L13, 400 * 1000},
56 {L14, 300 * 1000},
57 {L15, 200 * 1000},
58 {0, CPUFREQ_TABLE_END},
59};
60
61static struct cpufreq_clkdiv exynos5250_clkdiv_table[CPUFREQ_LEVEL_END];
62
63static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = {
64 /*
65 * Clock divider value for following
66 * { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 }
67 */
68 { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1700 MHz - N/A */
69 { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1600 MHz - N/A */
70 { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1500 MHz - N/A */
71 { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1400 MHz */
72 { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1300 MHz */
73 { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1200 MHz */
74 { 0, 2, 7, 7, 5, 1, 2, 0 }, /* 1100 MHz */
75 { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 1000 MHz */
76 { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 900 MHz */
77 { 0, 2, 7, 7, 3, 1, 1, 0 }, /* 800 MHz */
78 { 0, 1, 7, 7, 3, 1, 1, 0 }, /* 700 MHz */
79 { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 600 MHz */
80 { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 500 MHz */
81 { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 400 MHz */
82 { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 300 MHz */
83 { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 200 MHz */
84};
85
86static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
87 /* Clock divider value for following
88 * { COPY, HPM }
89 */
90 { 0, 2 }, /* 1700 MHz - N/A */
91 { 0, 2 }, /* 1600 MHz - N/A */
92 { 0, 2 }, /* 1500 MHz - N/A */
93 { 0, 2 }, /* 1400 MHz */
94 { 0, 2 }, /* 1300 MHz */
95 { 0, 2 }, /* 1200 MHz */
96 { 0, 2 }, /* 1100 MHz */
97 { 0, 2 }, /* 1000 MHz */
98 { 0, 2 }, /* 900 MHz */
99 { 0, 2 }, /* 800 MHz */
100 { 0, 2 }, /* 700 MHz */
101 { 0, 2 }, /* 600 MHz */
102 { 0, 2 }, /* 500 MHz */
103 { 0, 2 }, /* 400 MHz */
104 { 0, 2 }, /* 300 MHz */
105 { 0, 2 }, /* 200 MHz */
106};
107
108static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
109 (0), /* 1700 MHz - N/A */
110 (0), /* 1600 MHz - N/A */
111 (0), /* 1500 MHz - N/A */
112 (0), /* 1400 MHz */
113 ((325 << 16) | (6 << 8) | 0), /* 1300 MHz */
114 ((200 << 16) | (4 << 8) | 0), /* 1200 MHz */
115 ((275 << 16) | (6 << 8) | 0), /* 1100 MHz */
116 ((125 << 16) | (3 << 8) | 0), /* 1000 MHz */
117 ((150 << 16) | (4 << 8) | 0), /* 900 MHz */
118 ((100 << 16) | (3 << 8) | 0), /* 800 MHz */
119 ((175 << 16) | (3 << 8) | 1), /* 700 MHz */
120 ((200 << 16) | (4 << 8) | 1), /* 600 MHz */
121 ((125 << 16) | (3 << 8) | 1), /* 500 MHz */
122 ((100 << 16) | (3 << 8) | 1), /* 400 MHz */
123 ((200 << 16) | (4 << 8) | 2), /* 300 MHz */
124 ((100 << 16) | (3 << 8) | 2), /* 200 MHz */
125};
126
127/* ASV group voltage table */
128static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = {
129 0, 0, 0, 0, 0, 0, 0, /* 1700 MHz ~ 1100 MHz Not supported */
130 1175000, 1125000, 1075000, 1050000, 1000000,
131 950000, 925000, 925000, 900000
132};
133
134static void set_clkdiv(unsigned int div_index)
135{
136 unsigned int tmp;
137
138 /* Change Divider - CPU0 */
139
140 tmp = exynos5250_clkdiv_table[div_index].clkdiv;
141
142 __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0);
143
144 while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111)
145 cpu_relax();
146
147 /* Change Divider - CPU1 */
148 tmp = exynos5250_clkdiv_table[div_index].clkdiv1;
149
150 __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1);
151
152 while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11)
153 cpu_relax();
154}
155
156static void set_apll(unsigned int new_index,
157 unsigned int old_index)
158{
159 unsigned int tmp, pdiv;
160
161 /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
162 clk_set_parent(moutcore, mout_mpll);
163
164 do {
165 cpu_relax();
166 tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16);
167 tmp &= 0x7;
168 } while (tmp != 0x2);
169
170 /* 2. Set APLL Lock time */
171 pdiv = ((exynos5_apll_pms_table[new_index] >> 8) & 0x3f);
172
173 __raw_writel((pdiv * 250), EXYNOS5_APLL_LOCK);
174
175 /* 3. Change PLL PMS values */
176 tmp = __raw_readl(EXYNOS5_APLL_CON0);
177 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
178 tmp |= exynos5_apll_pms_table[new_index];
179 __raw_writel(tmp, EXYNOS5_APLL_CON0);
180
181 /* 4. wait_lock_time */
182 do {
183 cpu_relax();
184 tmp = __raw_readl(EXYNOS5_APLL_CON0);
185 } while (!(tmp & (0x1 << 29)));
186
187 /* 5. MUX_CORE_SEL = APLL */
188 clk_set_parent(moutcore, mout_apll);
189
190 do {
191 cpu_relax();
192 tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
193 tmp &= (0x7 << 16);
194 } while (tmp != (0x1 << 16));
195
196}
197
198bool exynos5250_pms_change(unsigned int old_index, unsigned int new_index)
199{
200 unsigned int old_pm = (exynos5_apll_pms_table[old_index] >> 8);
201 unsigned int new_pm = (exynos5_apll_pms_table[new_index] >> 8);
202
203 return (old_pm == new_pm) ? 0 : 1;
204}
205
206static void exynos5250_set_frequency(unsigned int old_index,
207 unsigned int new_index)
208{
209 unsigned int tmp;
210
211 if (old_index > new_index) {
212 if (!exynos5250_pms_change(old_index, new_index)) {
213 /* 1. Change the system clock divider values */
214 set_clkdiv(new_index);
215 /* 2. Change just s value in apll m,p,s value */
216 tmp = __raw_readl(EXYNOS5_APLL_CON0);
217 tmp &= ~(0x7 << 0);
218 tmp |= (exynos5_apll_pms_table[new_index] & 0x7);
219 __raw_writel(tmp, EXYNOS5_APLL_CON0);
220
221 } else {
222 /* Clock Configuration Procedure */
223 /* 1. Change the system clock divider values */
224 set_clkdiv(new_index);
225 /* 2. Change the apll m,p,s value */
226 set_apll(new_index, old_index);
227 }
228 } else if (old_index < new_index) {
229 if (!exynos5250_pms_change(old_index, new_index)) {
230 /* 1. Change just s value in apll m,p,s value */
231 tmp = __raw_readl(EXYNOS5_APLL_CON0);
232 tmp &= ~(0x7 << 0);
233 tmp |= (exynos5_apll_pms_table[new_index] & 0x7);
234 __raw_writel(tmp, EXYNOS5_APLL_CON0);
235 /* 2. Change the system clock divider values */
236 set_clkdiv(new_index);
237 } else {
238 /* Clock Configuration Procedure */
239 /* 1. Change the apll m,p,s value */
240 set_apll(new_index, old_index);
241 /* 2. Change the system clock divider values */
242 set_clkdiv(new_index);
243 }
244 }
245}
246
247static void __init set_volt_table(void)
248{
249 unsigned int i;
250
251 exynos5250_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
252 exynos5250_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID;
253 exynos5250_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID;
254 exynos5250_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID;
255 exynos5250_freq_table[L4].frequency = CPUFREQ_ENTRY_INVALID;
256 exynos5250_freq_table[L5].frequency = CPUFREQ_ENTRY_INVALID;
257 exynos5250_freq_table[L6].frequency = CPUFREQ_ENTRY_INVALID;
258
259 max_support_idx = L7;
260
261 for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
262 exynos5250_volt_table[i] = asv_voltage_5250[i];
263}
264
265int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
266{
267 int i;
268 unsigned int tmp;
269 unsigned long rate;
270
271 set_volt_table();
272
273 cpu_clk = clk_get(NULL, "armclk");
274 if (IS_ERR(cpu_clk))
275 return PTR_ERR(cpu_clk);
276
277 moutcore = clk_get(NULL, "mout_cpu");
278 if (IS_ERR(moutcore))
279 goto err_moutcore;
280
281 mout_mpll = clk_get(NULL, "mout_mpll");
282 if (IS_ERR(mout_mpll))
283 goto err_mout_mpll;
284
285 rate = clk_get_rate(mout_mpll) / 1000;
286
287 mout_apll = clk_get(NULL, "mout_apll");
288 if (IS_ERR(mout_apll))
289 goto err_mout_apll;
290
291 for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
292
293 exynos5250_clkdiv_table[i].index = i;
294
295 tmp = __raw_readl(EXYNOS5_CLKDIV_CPU0);
296
297 tmp &= ~((0x7 << 0) | (0x7 << 4) | (0x7 << 8) |
298 (0x7 << 12) | (0x7 << 16) | (0x7 << 20) |
299 (0x7 << 24) | (0x7 << 28));
300
301 tmp |= ((clkdiv_cpu0_5250[i][0] << 0) |
302 (clkdiv_cpu0_5250[i][1] << 4) |
303 (clkdiv_cpu0_5250[i][2] << 8) |
304 (clkdiv_cpu0_5250[i][3] << 12) |
305 (clkdiv_cpu0_5250[i][4] << 16) |
306 (clkdiv_cpu0_5250[i][5] << 20) |
307 (clkdiv_cpu0_5250[i][6] << 24) |
308 (clkdiv_cpu0_5250[i][7] << 28));
309
310 exynos5250_clkdiv_table[i].clkdiv = tmp;
311
312 tmp = __raw_readl(EXYNOS5_CLKDIV_CPU1);
313
314 tmp &= ~((0x7 << 0) | (0x7 << 4));
315
316 tmp |= ((clkdiv_cpu1_5250[i][0] << 0) |
317 (clkdiv_cpu1_5250[i][1] << 4));
318
319 exynos5250_clkdiv_table[i].clkdiv1 = tmp;
320 }
321
322 info->mpll_freq_khz = rate;
323 /* 1000Mhz */
324 info->pm_lock_idx = L7;
325 /* 800Mhz */
326 info->pll_safe_idx = L9;
327 info->max_support_idx = max_support_idx;
328 info->min_support_idx = min_support_idx;
329 info->cpu_clk = cpu_clk;
330 info->volt_table = exynos5250_volt_table;
331 info->freq_table = exynos5250_freq_table;
332 info->set_freq = exynos5250_set_frequency;
333 info->need_apll_change = exynos5250_pms_change;
334
335 return 0;
336
337err_mout_apll:
338 clk_put(mout_mpll);
339err_mout_mpll:
340 clk_put(moutcore);
341err_moutcore:
342 clk_put(cpu_clk);
343
344 pr_err("%s: failed initialization\n", __func__);
345 return -EINVAL;
346}
347EXPORT_SYMBOL(exynos5250_cpufreq_init);