2f7048b6a33e39540d4a68e38e99522854f981de
[platform/kernel/u-boot.git] / arch / arm / cpu / armv7 / exynos / clock.c
1 /*
2  * Copyright (C) 2010 Samsung Electronics
3  * Minkyu Kang <mk7.kang@samsung.com>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <asm/io.h>
26 #include <asm/arch/clock.h>
27 #include <asm/arch/clk.h>
28
29 /* exynos4: return pll clock frequency */
30 static unsigned long exynos4_get_pll_clk(int pllreg)
31 {
32         struct exynos4_clock *clk =
33                 (struct exynos4_clock *)samsung_get_base_clock();
34         unsigned long r, m, p, s, k = 0, mask, fout;
35         unsigned int freq;
36
37         switch (pllreg) {
38         case APLL:
39                 r = readl(&clk->apll_con0);
40                 break;
41         case MPLL:
42                 r = readl(&clk->mpll_con0);
43                 break;
44         case EPLL:
45                 r = readl(&clk->epll_con0);
46                 k = readl(&clk->epll_con1);
47                 break;
48         case VPLL:
49                 r = readl(&clk->vpll_con0);
50                 k = readl(&clk->vpll_con1);
51                 break;
52         default:
53                 printf("Unsupported PLL (%d)\n", pllreg);
54                 return 0;
55         }
56
57         /*
58          * APLL_CON: MIDV [25:16]
59          * MPLL_CON: MIDV [25:16]
60          * EPLL_CON: MIDV [24:16]
61          * VPLL_CON: MIDV [24:16]
62          */
63         if (pllreg == APLL || pllreg == MPLL)
64                 mask = 0x3ff;
65         else
66                 mask = 0x1ff;
67
68         m = (r >> 16) & mask;
69
70         /* PDIV [13:8] */
71         p = (r >> 8) & 0x3f;
72         /* SDIV [2:0] */
73         s = r & 0x7;
74
75         freq = CONFIG_SYS_CLK_FREQ;
76
77         if (pllreg == EPLL) {
78                 k = k & 0xffff;
79                 /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
80                 fout = (m + k / 65536) * (freq / (p * (1 << s)));
81         } else if (pllreg == VPLL) {
82                 k = k & 0xfff;
83                 /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
84                 fout = (m + k / 1024) * (freq / (p * (1 << s)));
85         } else {
86                 if (s < 1)
87                         s = 1;
88                 /* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
89                 fout = m * (freq / (p * (1 << (s - 1))));
90         }
91
92         return fout;
93 }
94
95 /* exynos5: return pll clock frequency */
96 static unsigned long exynos5_get_pll_clk(int pllreg)
97 {
98         struct exynos5_clock *clk =
99                 (struct exynos5_clock *)samsung_get_base_clock();
100         unsigned long r, m, p, s, k = 0, mask, fout;
101         unsigned int freq;
102
103         switch (pllreg) {
104         case APLL:
105                 r = readl(&clk->apll_con0);
106                 break;
107         case MPLL:
108                 r = readl(&clk->mpll_con0);
109                 break;
110         case EPLL:
111                 r = readl(&clk->epll_con0);
112                 k = readl(&clk->epll_con1);
113                 break;
114         case VPLL:
115                 r = readl(&clk->vpll_con0);
116                 k = readl(&clk->vpll_con1);
117                 break;
118         default:
119                 printf("Unsupported PLL (%d)\n", pllreg);
120                 return 0;
121         }
122
123         /*
124          * APLL_CON: MIDV [25:16]
125          * MPLL_CON: MIDV [25:16]
126          * EPLL_CON: MIDV [24:16]
127          * VPLL_CON: MIDV [24:16]
128          */
129         if (pllreg == APLL || pllreg == MPLL)
130                 mask = 0x3ff;
131         else
132                 mask = 0x1ff;
133
134         m = (r >> 16) & mask;
135
136         /* PDIV [13:8] */
137         p = (r >> 8) & 0x3f;
138         /* SDIV [2:0] */
139         s = r & 0x7;
140
141         freq = CONFIG_SYS_CLK_FREQ;
142
143         if (pllreg == EPLL) {
144                 k = k & 0xffff;
145                 /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
146                 fout = (m + k / 65536) * (freq / (p * (1 << s)));
147         } else if (pllreg == VPLL) {
148                 k = k & 0xfff;
149                 /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
150                 fout = (m + k / 1024) * (freq / (p * (1 << s)));
151         } else {
152                 if (s < 1)
153                         s = 1;
154                 /* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
155                 fout = m * (freq / (p * (1 << (s - 1))));
156         }
157
158         return fout;
159 }
160
161 /* exynos4: return ARM clock frequency */
162 static unsigned long exynos4_get_arm_clk(void)
163 {
164         struct exynos4_clock *clk =
165                 (struct exynos4_clock *)samsung_get_base_clock();
166         unsigned long div;
167         unsigned long armclk;
168         unsigned int core_ratio;
169         unsigned int core2_ratio;
170
171         div = readl(&clk->div_cpu0);
172
173         /* CORE_RATIO: [2:0], CORE2_RATIO: [30:28] */
174         core_ratio = (div >> 0) & 0x7;
175         core2_ratio = (div >> 28) & 0x7;
176
177         armclk = get_pll_clk(APLL) / (core_ratio + 1);
178         armclk /= (core2_ratio + 1);
179
180         return armclk;
181 }
182
183 /* exynos5: return ARM clock frequency */
184 static unsigned long exynos5_get_arm_clk(void)
185 {
186         struct exynos5_clock *clk =
187                 (struct exynos5_clock *)samsung_get_base_clock();
188         unsigned long div;
189         unsigned long armclk;
190         unsigned int arm_ratio;
191         unsigned int arm2_ratio;
192
193         div = readl(&clk->div_cpu0);
194
195         /* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */
196         arm_ratio = (div >> 0) & 0x7;
197         arm2_ratio = (div >> 28) & 0x7;
198
199         armclk = get_pll_clk(APLL) / (arm_ratio + 1);
200         armclk /= (arm2_ratio + 1);
201
202         return armclk;
203 }
204
205 /* exynos4: return pwm clock frequency */
206 static unsigned long exynos4_get_pwm_clk(void)
207 {
208         struct exynos4_clock *clk =
209                 (struct exynos4_clock *)samsung_get_base_clock();
210         unsigned long pclk, sclk;
211         unsigned int sel;
212         unsigned int ratio;
213
214         if (s5p_get_cpu_rev() == 0) {
215                 /*
216                  * CLK_SRC_PERIL0
217                  * PWM_SEL [27:24]
218                  */
219                 sel = readl(&clk->src_peril0);
220                 sel = (sel >> 24) & 0xf;
221
222                 if (sel == 0x6)
223                         sclk = get_pll_clk(MPLL);
224                 else if (sel == 0x7)
225                         sclk = get_pll_clk(EPLL);
226                 else if (sel == 0x8)
227                         sclk = get_pll_clk(VPLL);
228                 else
229                         return 0;
230
231                 /*
232                  * CLK_DIV_PERIL3
233                  * PWM_RATIO [3:0]
234                  */
235                 ratio = readl(&clk->div_peril3);
236                 ratio = ratio & 0xf;
237         } else if (s5p_get_cpu_rev() == 1) {
238                 sclk = get_pll_clk(MPLL);
239                 ratio = 8;
240         } else
241                 return 0;
242
243         pclk = sclk / (ratio + 1);
244
245         return pclk;
246 }
247
248 /* exynos5: return pwm clock frequency */
249 static unsigned long exynos5_get_pwm_clk(void)
250 {
251         struct exynos5_clock *clk =
252                 (struct exynos5_clock *)samsung_get_base_clock();
253         unsigned long pclk, sclk;
254         unsigned int ratio;
255
256         /*
257          * CLK_DIV_PERIC3
258          * PWM_RATIO [3:0]
259          */
260         ratio = readl(&clk->div_peric3);
261         ratio = ratio & 0xf;
262         sclk = get_pll_clk(MPLL);
263
264         pclk = sclk / (ratio + 1);
265
266         return pclk;
267 }
268
269 /* exynos4: return uart clock frequency */
270 static unsigned long exynos4_get_uart_clk(int dev_index)
271 {
272         struct exynos4_clock *clk =
273                 (struct exynos4_clock *)samsung_get_base_clock();
274         unsigned long uclk, sclk;
275         unsigned int sel;
276         unsigned int ratio;
277
278         /*
279          * CLK_SRC_PERIL0
280          * UART0_SEL [3:0]
281          * UART1_SEL [7:4]
282          * UART2_SEL [8:11]
283          * UART3_SEL [12:15]
284          * UART4_SEL [16:19]
285          * UART5_SEL [23:20]
286          */
287         sel = readl(&clk->src_peril0);
288         sel = (sel >> (dev_index << 2)) & 0xf;
289
290         if (sel == 0x6)
291                 sclk = get_pll_clk(MPLL);
292         else if (sel == 0x7)
293                 sclk = get_pll_clk(EPLL);
294         else if (sel == 0x8)
295                 sclk = get_pll_clk(VPLL);
296         else
297                 return 0;
298
299         /*
300          * CLK_DIV_PERIL0
301          * UART0_RATIO [3:0]
302          * UART1_RATIO [7:4]
303          * UART2_RATIO [8:11]
304          * UART3_RATIO [12:15]
305          * UART4_RATIO [16:19]
306          * UART5_RATIO [23:20]
307          */
308         ratio = readl(&clk->div_peril0);
309         ratio = (ratio >> (dev_index << 2)) & 0xf;
310
311         uclk = sclk / (ratio + 1);
312
313         return uclk;
314 }
315
316 /* exynos5: return uart clock frequency */
317 static unsigned long exynos5_get_uart_clk(int dev_index)
318 {
319         struct exynos5_clock *clk =
320                 (struct exynos5_clock *)samsung_get_base_clock();
321         unsigned long uclk, sclk;
322         unsigned int sel;
323         unsigned int ratio;
324
325         /*
326          * CLK_SRC_PERIC0
327          * UART0_SEL [3:0]
328          * UART1_SEL [7:4]
329          * UART2_SEL [8:11]
330          * UART3_SEL [12:15]
331          * UART4_SEL [16:19]
332          * UART5_SEL [23:20]
333          */
334         sel = readl(&clk->src_peric0);
335         sel = (sel >> (dev_index << 2)) & 0xf;
336
337         if (sel == 0x6)
338                 sclk = get_pll_clk(MPLL);
339         else if (sel == 0x7)
340                 sclk = get_pll_clk(EPLL);
341         else if (sel == 0x8)
342                 sclk = get_pll_clk(VPLL);
343         else
344                 return 0;
345
346         /*
347          * CLK_DIV_PERIC0
348          * UART0_RATIO [3:0]
349          * UART1_RATIO [7:4]
350          * UART2_RATIO [8:11]
351          * UART3_RATIO [12:15]
352          * UART4_RATIO [16:19]
353          * UART5_RATIO [23:20]
354          */
355         ratio = readl(&clk->div_peric0);
356         ratio = (ratio >> (dev_index << 2)) & 0xf;
357
358         uclk = sclk / (ratio + 1);
359
360         return uclk;
361 }
362
363 /* exynos4: set the mmc clock */
364 static void exynos4_set_mmc_clk(int dev_index, unsigned int div)
365 {
366         struct exynos4_clock *clk =
367                 (struct exynos4_clock *)samsung_get_base_clock();
368         unsigned int addr;
369         unsigned int val;
370
371         /*
372          * CLK_DIV_FSYS1
373          * MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24]
374          * CLK_DIV_FSYS2
375          * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
376          */
377         if (dev_index < 2) {
378                 addr = (unsigned int)&clk->div_fsys1;
379         } else {
380                 addr = (unsigned int)&clk->div_fsys2;
381                 dev_index -= 2;
382         }
383
384         val = readl(addr);
385         val &= ~(0xff << ((dev_index << 4) + 8));
386         val |= (div & 0xff) << ((dev_index << 4) + 8);
387         writel(val, addr);
388 }
389
390 /* exynos5: set the mmc clock */
391 static void exynos5_set_mmc_clk(int dev_index, unsigned int div)
392 {
393         struct exynos5_clock *clk =
394                 (struct exynos5_clock *)samsung_get_base_clock();
395         unsigned int addr;
396         unsigned int val;
397
398         /*
399          * CLK_DIV_FSYS1
400          * MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24]
401          * CLK_DIV_FSYS2
402          * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
403          */
404         if (dev_index < 2) {
405                 addr = (unsigned int)&clk->div_fsys1;
406         } else {
407                 addr = (unsigned int)&clk->div_fsys2;
408                 dev_index -= 2;
409         }
410
411         val = readl(addr);
412         val &= ~(0xff << ((dev_index << 4) + 8));
413         val |= (div & 0xff) << ((dev_index << 4) + 8);
414         writel(val, addr);
415 }
416
417 unsigned long get_pll_clk(int pllreg)
418 {
419         if (cpu_is_exynos5())
420                 return exynos5_get_pll_clk(pllreg);
421         else
422                 return exynos4_get_pll_clk(pllreg);
423 }
424
425 unsigned long get_arm_clk(void)
426 {
427         if (cpu_is_exynos5())
428                 return exynos5_get_arm_clk();
429         else
430                 return exynos4_get_arm_clk();
431 }
432
433 unsigned long get_pwm_clk(void)
434 {
435         if (cpu_is_exynos5())
436                 return exynos5_get_pwm_clk();
437         else
438                 return exynos4_get_pwm_clk();
439 }
440
441 unsigned long get_uart_clk(int dev_index)
442 {
443         if (cpu_is_exynos5())
444                 return exynos5_get_uart_clk(dev_index);
445         else
446                 return exynos4_get_uart_clk(dev_index);
447 }
448
449 void set_mmc_clk(int dev_index, unsigned int div)
450 {
451         if (cpu_is_exynos5())
452                 exynos5_set_mmc_clk(dev_index, div);
453         else
454                 exynos4_set_mmc_clk(dev_index, div);
455 }