Commit | Line | Data |
---|---|---|
3c2a0909 S |
1 | /* |
2 | * Copyright (c) 2013 Samsung Electronics Co., Ltd. | |
3 | * http://www.samsung.com | |
4 | * | |
5 | * EXYNOS5430 - KFC Core 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/kernel.h> | |
13 | #include <linux/err.h> | |
14 | #include <linux/clk.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/cpufreq.h> | |
18 | #include <linux/clk-private.h> | |
19 | ||
20 | #include <mach/map.h> | |
21 | #include <mach/regs-clock.h> | |
22 | #include <mach/regs-clock-exynos5430.h> | |
23 | #include <mach/regs-pmu.h> | |
24 | #include <mach/cpufreq.h> | |
25 | #include <mach/asv-exynos.h> | |
26 | ||
27 | #define CPUFREQ_LEVEL_END_CA7 (L18 + 1) | |
28 | #define L2_LOCAL_PWR_EN 0x7 | |
29 | ||
30 | #undef PRINT_DIV_VAL | |
31 | #undef ENABLE_CLKOUT | |
32 | ||
33 | static int max_support_idx_CA7; | |
34 | static int min_support_idx_CA7 = (CPUFREQ_LEVEL_END_CA7 - 1); | |
35 | ||
36 | static struct clk *mout_kfc; | |
37 | static struct clk *mout_kfc_pll; | |
38 | static struct clk *sclk_bus_pll; | |
39 | static struct clk *mout_bus_pll_user; | |
40 | static struct clk *fout_kfc_pll; | |
41 | ||
42 | static unsigned int exynos5430_volt_table_CA7[CPUFREQ_LEVEL_END_CA7]; | |
43 | static unsigned int exynos5430_abb_table_CA7[CPUFREQ_LEVEL_END_CA7]; | |
44 | ||
45 | static struct cpufreq_frequency_table exynos5430_freq_table_CA7[] = { | |
46 | {L0, 2000 * 1000}, | |
47 | {L1, 1900 * 1000}, | |
48 | {L2, 1800 * 1000}, | |
49 | {L3, 1700 * 1000}, | |
50 | {L4, 1600 * 1000}, | |
51 | {L5, 1500 * 1000}, | |
52 | {L6, 1400 * 1000}, | |
53 | {L7, 1300 * 1000}, | |
54 | {L8, 1200 * 1000}, | |
55 | {L9, 1100 * 1000}, | |
56 | {L10, 1000 * 1000}, | |
57 | {L11, 900 * 1000}, | |
58 | {L12, 800 * 1000}, | |
59 | {L13, 700 * 1000}, | |
60 | {L14, 600 * 1000}, | |
61 | {L15, 500 * 1000}, | |
62 | {L16, 400 * 1000}, | |
63 | {L17, 300 * 1000}, | |
64 | {L18, 200 * 1000}, | |
65 | {0, CPUFREQ_TABLE_END}, | |
66 | }; | |
67 | ||
68 | static struct cpufreq_clkdiv exynos5430_clkdiv_table_CA7[CPUFREQ_LEVEL_END_CA7]; | |
69 | ||
70 | static unsigned int clkdiv_cpu0_5430_CA7[CPUFREQ_LEVEL_END_CA7][7] = { | |
71 | /* | |
72 | * Clock divider value for following | |
73 | * { KFC1, KFC2, ACLK_KFC, PCLK_KFC, | |
74 | * ATCLK, PCLK_DBG_KFC, SCLK_CNTCLK } | |
75 | */ | |
76 | ||
77 | /* ARM L0: 2.0GHz */ | |
78 | { 0, 0, 2, 7, 7, 7, 3 }, | |
79 | ||
80 | /* ARM L1: 1.9GMHz */ | |
81 | { 0, 0, 2, 7, 7, 7, 3 }, | |
82 | ||
83 | /* ARM L2: 1.8GMHz */ | |
84 | { 0, 0, 2, 7, 7, 7, 3 }, | |
85 | ||
86 | /* ARM L3: 1.7GHz */ | |
87 | { 0, 0, 2, 7, 7, 7, 3 }, | |
88 | ||
89 | /* ARM L4: 1.6GHz */ | |
90 | { 0, 0, 2, 7, 7, 7, 3 }, | |
91 | ||
92 | /* ARM L5: 1.5GMHz */ | |
93 | { 0, 0, 2, 7, 7, 7, 3 }, | |
94 | ||
95 | /* ARM L6: 1.4GMHz */ | |
96 | { 0, 0, 2, 7, 7, 7, 3 }, | |
97 | ||
98 | /* ARM L7: 1.3GHz */ | |
99 | { 0, 0, 2, 7, 7, 7, 3 }, | |
100 | ||
101 | /* ARM L8: 1.2GHz */ | |
102 | { 0, 0, 2, 7, 7, 7, 3 }, | |
103 | ||
104 | /* ARM L9: 1.1GHz */ | |
105 | { 0, 0, 2, 7, 7, 7, 3 }, | |
106 | ||
107 | /* ARM L10: 1000MHz */ | |
108 | { 0, 0, 2, 7, 7, 7, 3 }, | |
109 | ||
110 | /* ARM L11: 900MHz */ | |
111 | { 0, 0, 2, 7, 7, 7, 3 }, | |
112 | ||
113 | /* ARM L12: 800MHz */ | |
114 | { 0, 0, 2, 7, 7, 7, 3 }, | |
115 | ||
116 | /* ARM L13: 700MHz */ | |
117 | { 0, 0, 2, 7, 7, 7, 3 }, | |
118 | ||
119 | /* ARM L14: 600MHz */ | |
120 | { 0, 0, 1, 7, 7, 7, 3 }, | |
121 | ||
122 | /* ARM L15: 500MHz */ | |
123 | { 0, 0, 1, 7, 7, 7, 3 }, | |
124 | ||
125 | /* ARM L16: 400MHz */ | |
126 | { 0, 0, 1, 7, 7, 7, 3 }, | |
127 | ||
128 | /* ARM L17: 300MHz */ | |
129 | { 0, 0, 1, 7, 7, 7, 3 }, | |
130 | ||
131 | /* ARM L18: 200MHz */ | |
132 | { 0, 0, 1, 7, 7, 7, 3 }, | |
133 | }; | |
134 | ||
135 | static unsigned int clkdiv_cpu1_5430_CA7[CPUFREQ_LEVEL_END_CA7][2] = { | |
136 | /* | |
137 | * Clock divider value for following | |
138 | * { SCLK_KFC_PLL, SCLK_HPM_KFC } | |
139 | */ | |
140 | ||
141 | /* ARM L0: 2.0GHz */ | |
142 | { 2, 7 }, | |
143 | ||
144 | /* ARM L1: 1.9GMHz */ | |
145 | { 2, 7 }, | |
146 | ||
147 | /* ARM L2: 1.8GMHz */ | |
148 | { 2, 7 }, | |
149 | ||
150 | /* ARM L3: 1.7GHz */ | |
151 | { 2, 7 }, | |
152 | ||
153 | /* ARM L4: 1.6GHz */ | |
154 | { 2, 7 }, | |
155 | ||
156 | /* ARM L5: 1.5GMHz */ | |
157 | { 2, 7 }, | |
158 | ||
159 | /* ARM L6: 1.4GMHz */ | |
160 | { 2, 7 }, | |
161 | ||
162 | /* ARM L7: 1.3GHz */ | |
163 | { 2, 7 }, | |
164 | ||
165 | /* ARM L8: 1.2GHz */ | |
166 | { 2, 7 }, | |
167 | ||
168 | /* ARM L9: 1.1GHz */ | |
169 | { 2, 7 }, | |
170 | ||
171 | /* ARM L10: 1000MHz */ | |
172 | { 2, 7 }, | |
173 | ||
174 | /* ARM L11: 900MHz */ | |
175 | { 2, 7 }, | |
176 | ||
177 | /* ARM L12: 800MHz */ | |
178 | { 2, 7 }, | |
179 | ||
180 | /* ARM L13: 700MHz */ | |
181 | { 2, 7 }, | |
182 | ||
183 | /* ARM L14: 600MHz */ | |
184 | { 2, 7 }, | |
185 | ||
186 | /* ARM L15: 500MHz */ | |
187 | { 2, 7 }, | |
188 | ||
189 | /* ARM L16: 400MHz */ | |
190 | { 2, 7 }, | |
191 | ||
192 | /* ARM L17: 300MHz */ | |
193 | { 2, 7 }, | |
194 | ||
195 | /* ARM L18: 200MHz */ | |
196 | { 2, 7 }, | |
197 | }; | |
198 | ||
199 | static unsigned int exynos5430_kfc_pll_pms_table_CA7[CPUFREQ_LEVEL_END_CA7] = { | |
200 | /* MDIV | PDIV | SDIV */ | |
201 | /* KPLL FOUT L0: 2.0GHz */ | |
202 | PLL2450X_PMS(500, 6, 0), | |
203 | ||
204 | /* KPLL FOUT L1: 1.9GHz */ | |
205 | PLL2450X_PMS(475, 6, 0), | |
206 | ||
207 | /* KPLL FOUT L2: 1.8GHz */ | |
208 | PLL2450X_PMS(375, 5, 0), | |
209 | ||
210 | /* KPLL FOUT L3: 1.7GHz */ | |
211 | PLL2450X_PMS(425, 6, 0), | |
212 | ||
213 | /* KPLL FOUT L4: 1.6GHz */ | |
214 | PLL2450X_PMS(400, 6, 0), | |
215 | ||
216 | /* KPLL FOUT L5: 1.5GHz */ | |
217 | PLL2450X_PMS(250, 4, 0), | |
218 | ||
219 | /* KPLL FOUT L6: 1.4GHz */ | |
220 | PLL2450X_PMS(350, 6, 0), | |
221 | ||
222 | /* KPLL FOUT L7: 1.3GHz */ | |
223 | PLL2450X_PMS(325, 6, 0), | |
224 | ||
225 | /* KPLL FOUT L8: 1.2GHz */ | |
226 | PLL2450X_PMS(500, 5, 1), | |
227 | ||
228 | /* KPLL FOUT L9: 1.1GHz */ | |
229 | PLL2450X_PMS(550, 6, 1), | |
230 | ||
231 | /* KPLL FOUT L10: 1000MHz */ | |
232 | PLL2450X_PMS(500, 6, 1), | |
233 | ||
234 | /* KPLL FOUT L11: 900MHz */ | |
235 | PLL2450X_PMS(375, 5, 1), | |
236 | ||
237 | /* KPLL FOUT L12: 800MHz */ | |
238 | PLL2450X_PMS(400, 6, 1), | |
239 | ||
240 | /* KPLL FOUT L13: 700MHz */ | |
241 | PLL2450X_PMS(350, 6, 1), | |
242 | ||
243 | /* KPLL FOUT L14: 600MHz */ | |
244 | PLL2450X_PMS(500, 5, 2), | |
245 | ||
246 | /* KPLL FOUT L15: 500MHz */ | |
247 | PLL2450X_PMS(500, 6, 2), | |
248 | ||
249 | /* KPLL FOUT L16: 400MHz */ | |
250 | PLL2450X_PMS(400, 6, 2), | |
251 | ||
252 | /* KPLL FOUT L17: 300MHz */ | |
253 | PLL2450X_PMS(500, 5, 3), | |
254 | ||
255 | /* KPLL FOUT L18: 200MHz */ | |
256 | PLL2450X_PMS(400, 6, 3), | |
257 | }; | |
258 | ||
259 | /* | |
260 | * ASV group voltage table | |
261 | */ | |
262 | static const unsigned int asv_voltage_5430_CA7[CPUFREQ_LEVEL_END_CA7] = { | |
263 | 1225000, /* L0 2000 */ | |
264 | 1225000, /* L1 1900 */ | |
265 | 1225000, /* L2 1800 */ | |
266 | 1225000, /* L3 1700 */ | |
267 | 1225000, /* L4 1600 */ | |
268 | 1225000, /* L5 1500 */ | |
269 | 1187500, /* L6 1400 */ | |
270 | 1150000, /* L7 1300 */ | |
271 | 1112500, /* L8 1200 */ | |
272 | 1075000, /* L9 1100 */ | |
273 | 1037500, /* L10 1000 */ | |
274 | 1000000, /* L11 900 */ | |
275 | 975000, /* L12 800 */ | |
276 | 950000, /* L13 700 */ | |
277 | 925000, /* L14 600 */ | |
278 | 925000, /* L15 500 */ | |
279 | 925000, /* L16 400 */ | |
280 | 925000, /* L17 300 */ | |
281 | 925000, /* L18 200 */ | |
282 | }; | |
283 | ||
284 | /* Minimum memory throughput in megabytes per second */ | |
285 | static int exynos5430_bus_table_CA7[CPUFREQ_LEVEL_END_CA7] = { | |
286 | #if defined(CONFIG_EXYNOS5430_HD) | |
287 | 633000, /* 2.0 GHz */ | |
288 | 633000, /* 1.9 GHz */ | |
289 | 633000, /* 1.8 GHz */ | |
290 | 633000, /* 1.7 GHz */ | |
291 | 633000, /* 1.6 GHz */ | |
292 | 633000, /* 1.5 GHz */ | |
293 | 633000, /* 1.4 GHz */ | |
294 | 633000, /* 1.3 GHz */ | |
295 | 633000, /* 1.2 GHz */ | |
296 | 413000, /* 1.1 GHz */ | |
297 | 413000, /* 1.0 GHz */ | |
298 | 158000, /* 900 MHz */ | |
299 | 158000, /* 800 MHz */ | |
300 | 158000, /* 700 MHz */ | |
301 | 158000, /* 600 MHz */ | |
302 | 0, /* 500 MHz */ | |
303 | 0, /* 400 MHz */ | |
304 | 0, /* 300 MHz */ | |
305 | 0, /* 200 MHz */ | |
306 | #else | |
307 | 633000, /* 2.0 GHz */ | |
308 | 633000, /* 1.9 GHz */ | |
309 | 633000, /* 1.8 GHz */ | |
310 | 633000, /* 1.7 GHz */ | |
311 | 633000, /* 1.6 GHz */ | |
312 | 633000, /* 1.5 GHz */ | |
313 | 633000, /* 1.4 GHz */ | |
314 | 633000, /* 1.3 GHz */ | |
315 | 633000, /* 1.2 GHz */ | |
316 | 633000, /* 1.1 GHz */ | |
317 | 543000, /* 1.0 GHz */ | |
318 | 158000, /* 900 MHz */ | |
319 | 158000, /* 800 MHz */ | |
320 | 136000, /* 700 MHz */ | |
321 | 136000, /* 600 MHz */ | |
322 | 0, /* 500 MHz */ | |
323 | 0, /* 400 MHz */ | |
324 | 0, /* 300 MHz */ | |
325 | 0, /* 200 MHz */ | |
326 | #endif | |
327 | }; | |
328 | ||
329 | static void exynos5430_set_clkdiv_CA7(unsigned int div_index) | |
330 | { | |
331 | unsigned int tmp, tmp1; | |
332 | ||
333 | /* Change Divider - KFC0 */ | |
334 | tmp = exynos5430_clkdiv_table_CA7[div_index].clkdiv0; | |
335 | ||
336 | __raw_writel(tmp, EXYNOS5430_DIV_KFC0); | |
337 | ||
338 | while (__raw_readl(EXYNOS5430_DIV_STAT_KFC0) & 0x1111111) | |
339 | cpu_relax(); | |
340 | ||
341 | /* Change Divider - KFC1 */ | |
342 | tmp1 = exynos5430_clkdiv_table_CA7[div_index].clkdiv1; | |
343 | ||
344 | __raw_writel(tmp1, EXYNOS5430_DIV_KFC1); | |
345 | ||
346 | while (__raw_readl(EXYNOS5430_DIV_STAT_KFC1) & 0x11) | |
347 | cpu_relax(); | |
348 | ||
349 | #ifdef PRINT_DIV_VAL | |
350 | tmp = __raw_readl(EXYNOS5430_DIV_KFC0); | |
351 | tmp1 = __raw_readl(EXYNOS5430_DIV_KFC1); | |
352 | pr_info("%s: DIV_KFC0[0x%08x], DIV_KFC1[0x%08x]\n", | |
353 | __func__, tmp, tmp1); | |
354 | #endif | |
355 | } | |
356 | ||
357 | static void exynos5430_set_kfc_pll_CA7(unsigned int new_index, unsigned int old_index) | |
358 | { | |
359 | unsigned int tmp, pdiv; | |
360 | ||
361 | /* 1. before change to BUS_PLL, set div for BUS_PLL output */ | |
362 | if ((new_index < L12) && (old_index < L12)) | |
363 | exynos5430_set_clkdiv_CA7(L12); /* pll_safe_idx of CA7 */ | |
364 | ||
365 | /* 2. CLKMUX_SEL_KFC2 = MOUT_BUS_PLL_USER, KFCCLK uses BUS_PLL_USER for lock time */ | |
366 | if (clk_set_parent(mout_kfc, mout_bus_pll_user)) | |
367 | pr_err("Unable to set parent %s of clock %s.\n", | |
368 | mout_bus_pll_user->name, mout_kfc->name); | |
369 | do { | |
370 | cpu_relax(); | |
371 | tmp = __raw_readl(EXYNOS5430_SRC_STAT_KFC2); | |
372 | tmp &= EXYNOS5430_SRC_STAT_KFC2_KFC_MASK; | |
373 | } while (tmp != 0x2); | |
374 | ||
375 | /* 3. Set KFC_PLL Lock time */ | |
376 | pdiv = ((exynos5430_kfc_pll_pms_table_CA7[new_index] & | |
377 | EXYNOS5430_PLL_PDIV_MASK) >> EXYNOS5430_PLL_PDIV_SHIFT); | |
378 | ||
379 | __raw_writel((pdiv * 150), EXYNOS5430_KFC_PLL_LOCK); | |
380 | ||
381 | /* 4. Change PLL PMS values */ | |
382 | tmp = __raw_readl(EXYNOS5430_KFC_PLL_CON0); | |
383 | tmp &= ~(EXYNOS5430_PLL_MDIV_MASK | | |
384 | EXYNOS5430_PLL_PDIV_MASK | | |
385 | EXYNOS5430_PLL_SDIV_MASK); | |
386 | tmp |= exynos5430_kfc_pll_pms_table_CA7[new_index]; | |
387 | __raw_writel(tmp, EXYNOS5430_KFC_PLL_CON0); | |
388 | ||
389 | /* 5. wait_lock_time */ | |
390 | do { | |
391 | cpu_relax(); | |
392 | tmp = __raw_readl(EXYNOS5430_KFC_PLL_CON0); | |
393 | } while (!(tmp & (0x1 << EXYNOS5430_KFC_PLL_CON0_LOCKED_SHIFT))); | |
394 | ||
395 | /* 6. CLKMUX_SEL_KFC2 = MOUT_KFC_PLL */ | |
396 | if (clk_set_parent(mout_kfc, mout_kfc_pll)) | |
397 | pr_err("Unable to set parent %s of clock %s.\n", | |
398 | mout_kfc_pll->name, mout_kfc->name); | |
399 | do { | |
400 | cpu_relax(); | |
401 | tmp = __raw_readl(EXYNOS5430_SRC_STAT_KFC2); | |
402 | tmp &= EXYNOS5430_SRC_STAT_KFC2_KFC_MASK; | |
403 | } while (tmp != 0x1); | |
404 | ||
405 | /* 7. restore original div value */ | |
406 | if ((new_index < L12) && (old_index < L12)) | |
407 | exynos5430_set_clkdiv_CA7(new_index); | |
408 | } | |
409 | ||
410 | static bool exynos5430_pms_change_CA7(unsigned int old_index, | |
411 | unsigned int new_index) | |
412 | { | |
413 | unsigned int old_pm = (exynos5430_kfc_pll_pms_table_CA7[old_index] >> | |
414 | EXYNOS5430_PLL_PDIV_SHIFT); | |
415 | unsigned int new_pm = (exynos5430_kfc_pll_pms_table_CA7[new_index] >> | |
416 | EXYNOS5430_PLL_PDIV_SHIFT); | |
417 | ||
418 | return (old_pm == new_pm) ? 0 : 1; | |
419 | } | |
420 | ||
421 | static void exynos5430_set_frequency_CA7(unsigned int old_index, | |
422 | unsigned int new_index) | |
423 | { | |
424 | unsigned int tmp; | |
425 | ||
426 | if (old_index > new_index) { | |
427 | if (!exynos5430_pms_change_CA7(old_index, new_index)) { | |
428 | /* 1. Change the system clock divider values */ | |
429 | exynos5430_set_clkdiv_CA7(new_index); | |
430 | /* 2. Change just s value in kfc_pll m,p,s value */ | |
431 | tmp = __raw_readl(EXYNOS5430_KFC_PLL_CON0); | |
432 | tmp &= ~EXYNOS5430_PLL_SDIV_MASK; | |
433 | tmp |= (exynos5430_kfc_pll_pms_table_CA7[new_index] & EXYNOS5430_PLL_SDIV_MASK); | |
434 | __raw_writel(tmp, EXYNOS5430_KFC_PLL_CON0); | |
435 | } else { | |
436 | /* Clock Configuration Procedure */ | |
437 | /* 1. Change the system clock divider values */ | |
438 | exynos5430_set_clkdiv_CA7(new_index); | |
439 | /* 2. Change the kfc_pll m,p,s value */ | |
440 | exynos5430_set_kfc_pll_CA7(new_index, old_index); | |
441 | } | |
442 | } else if (old_index < new_index) { | |
443 | if (!exynos5430_pms_change_CA7(old_index, new_index)) { | |
444 | /* 1. Change just s value in kfc_pll m,p,s value */ | |
445 | tmp = __raw_readl(EXYNOS5430_KFC_PLL_CON0); | |
446 | tmp &= ~EXYNOS5430_PLL_SDIV_MASK; | |
447 | tmp |= (exynos5430_kfc_pll_pms_table_CA7[new_index] & EXYNOS5430_PLL_SDIV_MASK); | |
448 | __raw_writel(tmp, EXYNOS5430_KFC_PLL_CON0); | |
449 | /* 2. Change the system clock divider values */ | |
450 | exynos5430_set_clkdiv_CA7(new_index); | |
451 | } else { | |
452 | /* Clock Configuration Procedure */ | |
453 | /* 1. Change the kfc_pll m,p,s value */ | |
454 | exynos5430_set_kfc_pll_CA7(new_index, old_index); | |
455 | /* 2. Change the system clock divider values */ | |
456 | exynos5430_set_clkdiv_CA7(new_index); | |
457 | } | |
458 | } | |
459 | ||
460 | clk_set_rate(fout_kfc_pll, exynos5430_freq_table_CA7[new_index].frequency * 1000); | |
461 | } | |
462 | ||
463 | static void __init set_volt_table_CA7(void) | |
464 | { | |
465 | unsigned int i; | |
466 | unsigned int asv_volt = 0; | |
467 | ||
468 | for (i = 0; i < CPUFREQ_LEVEL_END_CA7; i++) { | |
469 | asv_volt = get_match_volt(ID_KFC, exynos5430_freq_table_CA7[i].frequency); | |
470 | if (!asv_volt) | |
471 | exynos5430_volt_table_CA7[i] = asv_voltage_5430_CA7[i]; | |
472 | else | |
473 | exynos5430_volt_table_CA7[i] = asv_volt; | |
474 | ||
475 | pr_info("CPUFREQ of CA7 L%d : %d uV\n", i, | |
476 | exynos5430_volt_table_CA7[i]); | |
477 | ||
478 | exynos5430_abb_table_CA7[i] = | |
479 | get_match_abb(ID_KFC, exynos5430_freq_table_CA7[i].frequency); | |
480 | ||
481 | pr_info("CPUFREQ of CA7 L%d : ABB %d\n", i, | |
482 | exynos5430_abb_table_CA7[i]); | |
483 | } | |
484 | ||
485 | #if defined(CONFIG_SOC_EXYNOS5430_L) | |
486 | max_support_idx_CA7 = L7; /* 1.3GHz */ | |
487 | #else | |
488 | max_support_idx_CA7 = L5; /* 1.5GHz */ | |
489 | #endif | |
490 | min_support_idx_CA7 = L15; /* 500MHz */ | |
491 | ||
492 | pr_info("CPUFREQ of CA7 max_freq : L%d %u khz\n", max_support_idx_CA7, | |
493 | exynos5430_freq_table_CA7[max_support_idx_CA7].frequency); | |
494 | pr_info("CPUFREQ of CA7 min_freq : L%d %u khz\n", min_support_idx_CA7, | |
495 | exynos5430_freq_table_CA7[min_support_idx_CA7].frequency); | |
496 | } | |
497 | ||
498 | static bool exynos5430_is_alive_CA7(void) | |
499 | { | |
500 | unsigned int tmp; | |
501 | ||
502 | tmp = __raw_readl(EXYNOS5430_KFC_PLL_CON1); | |
503 | tmp &= EXYNOS5430_PLL_BYPASS_MASK; | |
504 | tmp >>= EXYNOS5430_PLL_BYPASS_SHIFT; | |
505 | ||
506 | return !tmp ? true : false; | |
507 | } | |
508 | ||
509 | int __init exynos5_cpufreq_CA7_init(struct exynos_dvfs_info *info) | |
510 | { | |
511 | int i; | |
512 | unsigned int tmp; | |
513 | unsigned long rate; | |
514 | ||
515 | set_volt_table_CA7(); | |
516 | ||
517 | mout_kfc = __clk_lookup("mout_kfc"); | |
518 | if (!mout_kfc) { | |
519 | pr_err("failed get mout_kfc clk\n"); | |
520 | return -EINVAL; | |
521 | } | |
522 | ||
523 | mout_kfc_pll = __clk_lookup("mout_kfc_pll"); | |
524 | if (!mout_kfc_pll) { | |
525 | pr_err("failed get mout_kfc_pll clk\n"); | |
526 | goto err_mout_kfc_pll; | |
527 | } | |
528 | ||
529 | sclk_bus_pll = __clk_lookup("sclk_bus_pll"); | |
530 | if (!sclk_bus_pll) { | |
531 | pr_err("failed get sclk_bus_pll clk\n"); | |
532 | goto err_sclk_bus_pll; | |
533 | } | |
534 | ||
535 | mout_bus_pll_user = __clk_lookup("mout_bus_pll_kfc_user"); | |
536 | if (!mout_bus_pll_user) { | |
537 | pr_err("failed get mout_bus_pll_kfc_user clk\n"); | |
538 | goto err_mout_bus_pll_user; | |
539 | } | |
540 | ||
541 | if (clk_set_parent(mout_bus_pll_user, sclk_bus_pll)) { | |
542 | pr_err("Unable to set parent %s of clock %s.\n", | |
543 | sclk_bus_pll->name, mout_bus_pll_user->name); | |
544 | goto err_clk_set_parent; | |
545 | } | |
546 | ||
547 | rate = clk_get_rate(mout_bus_pll_user) / 1000; | |
548 | ||
549 | fout_kfc_pll = __clk_lookup("fout_kfc_pll"); | |
550 | if (!fout_kfc_pll) { | |
551 | pr_err("failed get fout_kfc_pll clk\n"); | |
552 | goto err_fout_kfc_pll; | |
553 | } | |
554 | ||
555 | clk_put(sclk_bus_pll); | |
556 | ||
557 | for (i = L0; i < CPUFREQ_LEVEL_END_CA7; i++) { | |
558 | exynos5430_clkdiv_table_CA7[i].index = i; | |
559 | ||
560 | /* CLK_DIV_KFC0 */ | |
561 | tmp = __raw_readl(EXYNOS5430_DIV_KFC0); | |
562 | ||
563 | tmp &= ~(EXYNOS5430_DIV_KFC0_KFC1_MASK | | |
564 | EXYNOS5430_DIV_KFC0_KFC2_MASK | | |
565 | EXYNOS5430_DIV_KFC0_ACLK_KFC_MASK | | |
566 | EXYNOS5430_DIV_KFC0_PCLK_KFC_MASK | | |
567 | EXYNOS5430_DIV_KFC0_ATCLK_KFC_MASK | | |
568 | EXYNOS5430_DIV_KFC0_PCLK_DBG_KFC_MASK | | |
569 | EXYNOS5430_DIV_KFC0_CNTCLK_KFC_MASK); | |
570 | ||
571 | tmp |= ((clkdiv_cpu0_5430_CA7[i][0] << EXYNOS5430_DIV_KFC0_KFC1_SHIFT) | | |
572 | (clkdiv_cpu0_5430_CA7[i][1] << EXYNOS5430_DIV_KFC0_KFC2_SHIFT) | | |
573 | (clkdiv_cpu0_5430_CA7[i][2] << EXYNOS5430_DIV_KFC0_ACLK_KFC_SHIFT) | | |
574 | (clkdiv_cpu0_5430_CA7[i][3] << EXYNOS5430_DIV_KFC0_PCLK_KFC_SHIFT) | | |
575 | (clkdiv_cpu0_5430_CA7[i][4] << EXYNOS5430_DIV_KFC0_ATCLK_KFC_SHIFT) | | |
576 | (clkdiv_cpu0_5430_CA7[i][5] << EXYNOS5430_DIV_KFC0_PCLK_DBG_KFC_SHIFT) | | |
577 | (clkdiv_cpu0_5430_CA7[i][6] << EXYNOS5430_DIV_KFC0_CNTCLK_KFC_SHIFT)); | |
578 | ||
579 | exynos5430_clkdiv_table_CA7[i].clkdiv0 = tmp; | |
580 | ||
581 | /* CLK_DIV_KFC1 */ | |
582 | tmp = __raw_readl(EXYNOS5430_DIV_KFC1); | |
583 | ||
584 | tmp &= ~(EXYNOS5430_DIV_KFC1_KFC_PLL_MASK | | |
585 | EXYNOS5430_DIV_KFC1_SCLK_HPM_KFC_MASK); | |
586 | ||
587 | tmp |= ((clkdiv_cpu1_5430_CA7[i][0] << EXYNOS5430_DIV_KFC1_KFC_PLL_SHIFT) | | |
588 | (clkdiv_cpu1_5430_CA7[i][1] << EXYNOS5430_DIV_KFC1_SCLK_HPM_KFC_SHIFT)); | |
589 | ||
590 | exynos5430_clkdiv_table_CA7[i].clkdiv1 = tmp; | |
591 | } | |
592 | ||
593 | info->mpll_freq_khz = rate; | |
594 | info->pll_safe_idx = L12; | |
595 | info->max_support_idx = max_support_idx_CA7; | |
596 | info->min_support_idx = min_support_idx_CA7; | |
597 | info->boost_freq = exynos5430_freq_table_CA7[L10].frequency; | |
598 | /* booting frequency is 1.3GHz */ | |
599 | info->boot_cpu_min_qos = exynos5430_freq_table_CA7[L7].frequency; | |
600 | info->boot_cpu_max_qos = exynos5430_freq_table_CA7[L7].frequency; | |
601 | info->bus_table = exynos5430_bus_table_CA7; | |
602 | info->cpu_clk = fout_kfc_pll; | |
603 | ||
604 | info->volt_table = exynos5430_volt_table_CA7; | |
605 | info->abb_table = exynos5430_abb_table_CA7; | |
606 | info->freq_table = exynos5430_freq_table_CA7; | |
607 | info->set_freq = exynos5430_set_frequency_CA7; | |
608 | info->need_apll_change = exynos5430_pms_change_CA7; | |
609 | info->is_alive = exynos5430_is_alive_CA7; | |
610 | ||
611 | #ifdef ENABLE_CLKOUT | |
612 | /* dividing KFC_CLK to 1/16 */ | |
613 | tmp = __raw_readl(EXYNOS5430_CLKOUT_CMU_KFC); | |
614 | tmp &= ~0xfff; | |
615 | tmp |= 0xf01; | |
616 | __raw_writel(tmp, EXYNOS5430_CLKOUT_CMU_KFC); | |
617 | #endif | |
618 | ||
619 | return 0; | |
620 | ||
621 | err_fout_kfc_pll: | |
622 | err_clk_set_parent: | |
623 | clk_put(mout_bus_pll_user); | |
624 | err_mout_bus_pll_user: | |
625 | clk_put(sclk_bus_pll); | |
626 | err_sclk_bus_pll: | |
627 | clk_put(mout_kfc_pll); | |
628 | err_mout_kfc_pll: | |
629 | clk_put(mout_kfc); | |
630 | ||
631 | pr_debug("%s: failed initialization\n", __func__); | |
632 | return -EINVAL; | |
633 | } |