diff options
author | Jaecheol Lee <jc.lee@samsung.com> | 2012-03-10 06:00:02 -0500 |
---|---|---|
committer | Dave Jones <davej@redhat.com> | 2012-03-14 14:39:50 -0400 |
commit | 562a6cbe1cf2edf0a87a659eb26095400322430e (patch) | |
tree | e13e1e913094aecab5d38e729ad928087da8fd20 /drivers/cpufreq/exynos5250-cpufreq.c | |
parent | a35c50510dd4d20a9275a0d3d16b859d11b35606 (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/exynos5250-cpufreq.c')
-rw-r--r-- | drivers/cpufreq/exynos5250-cpufreq.c | 347 |
1 files changed, 347 insertions, 0 deletions
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 | |||
26 | static int max_support_idx; | ||
27 | static int min_support_idx = (CPUFREQ_LEVEL_END - 1); | ||
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 | struct cpufreq_clkdiv { | ||
34 | unsigned int index; | ||
35 | unsigned int clkdiv; | ||
36 | unsigned int clkdiv1; | ||
37 | }; | ||
38 | |||
39 | static unsigned int exynos5250_volt_table[CPUFREQ_LEVEL_END]; | ||
40 | |||
41 | static 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 | |||
61 | static struct cpufreq_clkdiv exynos5250_clkdiv_table[CPUFREQ_LEVEL_END]; | ||
62 | |||
63 | static 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 | |||
86 | static 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 | |||
108 | static 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 */ | ||
128 | static 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 | |||
134 | static 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 | |||
156 | static 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 | |||
198 | bool 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 | |||
206 | static 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 | |||
247 | static 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 | |||
265 | int 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 | |||
337 | err_mout_apll: | ||
338 | clk_put(mout_mpll); | ||
339 | err_mout_mpll: | ||
340 | clk_put(moutcore); | ||
341 | err_moutcore: | ||
342 | clk_put(cpu_clk); | ||
343 | |||
344 | pr_err("%s: failed initialization\n", __func__); | ||
345 | return -EINVAL; | ||
346 | } | ||
347 | EXPORT_SYMBOL(exynos5250_cpufreq_init); | ||