Tizen 2.0 Release
[platform/kernel/u-boot.git] / arch / arm / cpu / armv7 / exynos / clock.c
1 /*
2  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
3  * Minkyu Kang <mk7.kang@samsung.com>
4  * Sanghee Kim <sh0130.kim@samsung.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19  * MA 02111-1307 USA
20  */
21
22 #include <common.h>
23 #include <asm/io.h>
24 #include <asm/arch/clock.h>
25 #include <asm/arch/clk.h>
26
27 #ifndef CONFIG_SYS_CLK_FREQ
28 #define CONFIG_SYS_CLK_FREQ     24000000
29 #endif
30
31 /* get_pll_clk: return pll clock frequency */
32 static unsigned long exynos4_get_pll_clk(int pllreg)
33 {
34         struct exynos4_clock *clk =
35                 (struct exynos4_clock *)samsung_get_base_clock();
36         unsigned long r, m, p, s, k = 0, mask, fout;
37         unsigned int freq;
38
39         switch (pllreg) {
40         case APLL:
41                 r = readl(&clk->apll_con0);
42                 break;
43         case MPLL:
44                 r = readl(&clk->mpll_con0);
45                 break;
46         case EPLL:
47                 r = readl(&clk->epll_con0);
48                 k = readl(&clk->epll_con1);
49                 break;
50         case VPLL:
51                 r = readl(&clk->vpll_con0);
52                 k = readl(&clk->vpll_con1);
53                 break;
54         default:
55                 printf("Unsupported PLL (%d)\n", pllreg);
56                 return 0;
57         }
58
59         /*
60          * APLL_CON: MIDV [25:16]
61          * MPLL_CON: MIDV [25:16]
62          * EPLL_CON: MIDV [24:16]
63          * VPLL_CON: MIDV [24:16]
64          */
65         if (pllreg == APLL || pllreg == MPLL)
66                 mask = 0x3ff;
67         else
68                 mask = 0x1ff;
69
70         m = (r >> 16) & mask;
71
72         /* PDIV [13:8] */
73         p = (r >> 8) & 0x3f;
74         /* SDIV [2:0] */
75         s = r & 0x7;
76
77         freq = CONFIG_SYS_CLK_FREQ;
78
79         if (pllreg == EPLL) {
80                 k = k & 0xffff;
81                 /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
82                 fout = (m + k / 65536) * (freq / (p * (1 << s)));
83         } else if (pllreg == VPLL) {
84                 k = k & 0xfff;
85                 /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
86                 fout = (m + k / 1024) * (freq / (p * (1 << s)));
87         } else {
88                 if (s < 1)
89                         s = 1;
90                 /* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
91                 fout = m * (freq / (p * (1 << (s - 1))));
92         }
93
94         return fout;
95 }
96
97 /* get_arm_clk: return ARM clock frequency */
98 static unsigned long exynos4_get_arm_clk(void)
99 {
100         struct exynos4_clock *clk =
101                 (struct exynos4_clock *)samsung_get_base_clock();
102         unsigned long div;
103         unsigned long dout_apll;
104         unsigned int apll_ratio;
105
106         div = readl(&clk->div_cpu0);
107
108         /* APLL_RATIO: [26:24] */
109         apll_ratio = (div >> 24) & 0x7;
110
111         dout_apll = get_pll_clk(APLL) / (apll_ratio + 1);
112
113         return dout_apll;
114 }
115
116 /* get_pwm_clk: return pwm clock frequency */
117 static unsigned long exynos4_get_pwm_clk(void)
118 {
119         unsigned long pclk, sclk;
120         unsigned int ratio;
121
122         sclk = get_pll_clk(MPLL);
123         ratio = 8;
124
125         pclk = sclk / (ratio + 1);
126
127         return pclk;
128 }
129
130 /* get_uart_clk: return uart clock frequency */
131 static unsigned long exynos4_get_uart_clk(int dev_index)
132 {
133         struct exynos4_clock *clk =
134                 (struct exynos4_clock *)samsung_get_base_clock();
135         unsigned long uclk, sclk;
136         unsigned int sel;
137         unsigned int ratio;
138
139         /*
140          * CLK_SRC_PERIL0
141          * UART0_SEL [3:0]
142          * UART1_SEL [7:4]
143          * UART2_SEL [8:11]
144          * UART3_SEL [12:15]
145          * UART4_SEL [16:19]
146          * UART5_SEL [23:20]
147          */
148         sel = readl(&clk->src_peril0);
149         sel = (sel >> (dev_index << 2)) & 0xf;
150
151         if (sel == 0x6)
152                 sclk = get_pll_clk(MPLL);
153         else if (sel == 0x7)
154                 sclk = get_pll_clk(EPLL);
155         else if (sel == 0x8)
156                 sclk = get_pll_clk(VPLL);
157         else
158                 return 0;
159
160         /*
161          * CLK_DIV_PERIL0
162          * UART0_RATIO [3:0]
163          * UART1_RATIO [7:4]
164          * UART2_RATIO [8:11]
165          * UART3_RATIO [12:15]
166          * UART4_RATIO [16:19]
167          * UART5_RATIO [23:20]
168          */
169         ratio = readl(&clk->div_peril0);
170         ratio = (ratio >> (dev_index << 2)) & 0xf;
171
172         uclk = sclk / (ratio + 1);
173
174         return uclk;
175 }
176
177 /*
178  * set_lcd_clk: set parent clock and ratio for fimdx.
179  * @dev_index: index of fimd using bus0 or bus1.
180  * @div: ratio value for sclk_fimd.
181  */
182 static int exynos4_set_lcd_clk(const unsigned int dev_index,
183                 const unsigned int pclk_name, const unsigned int div)
184 {
185         struct exynos4_clock *clk =
186                 (struct exynos4_clock *)samsung_get_base_clock();
187         unsigned int sel, ratio;
188         unsigned int parent_sel;
189         unsigned int src_addr, div_addr;
190
191 #if defined(CONFIG_EXYNOS4210)
192         if (dev_index == 0) {
193                 src_addr = (unsigned int)&clk->src_lcd0;
194                 div_addr = (unsigned int)&clk->div_lcd0;
195         } else if (dev_index == 1) {
196                 src_addr = (unsigned int)&clk->src_lcd1;
197                 div_addr = (unsigned int)&clk->div_lcd1;
198         } else
199                 return -1;
200 #else
201         if (dev_index > 0) {
202                 printf("%s: not supported for %d\n", __func__, dev_index);
203                 return -1;
204         }
205
206         src_addr = (unsigned int)&clk->src_lcd;
207         div_addr = (unsigned int)&clk->div_lcd;
208 #endif
209
210         /*
211          * CLK_SRC_LCDx
212          * FIMDx_SEL [3:0]
213          */
214         sel = readl(src_addr);
215         sel &= ~0xf;
216
217         switch (pclk_name) {
218         case MPLL:
219                 parent_sel = 0x6;
220                 break;
221         case EPLL:
222                 parent_sel = 0x7;
223                 break;
224         case VPLL:
225                 parent_sel = 0x8;
226                 break;
227         default:
228                 parent_sel = 0x6;
229                 break;
230         };
231
232         sel |= (parent_sel & 0xf);
233         writel(sel, src_addr);
234
235         /*
236          * CLK_DIV_LCDx
237          * FIMDx_RATIO [3:0]
238          */
239         ratio = readl(div_addr);
240         ratio &= ~0xf;
241         ratio |= div;
242         writel(ratio, div_addr);
243
244         return 0;
245 }
246
247 /* get_lcd_clk: return lcd clock frequency */
248 static unsigned long exynos4_get_lcd_clk(void)
249 {
250         struct exynos4_clock *clk =
251                 (struct exynos4_clock *)samsung_get_base_clock();
252         unsigned long pclk, sclk;
253         unsigned int sel;
254         unsigned int ratio;
255
256         /*
257          * CLK_SRC_LCD0
258          * FIMD0_SEL [3:0]
259          */
260 #if defined(CONFIG_EXYNOS4210)
261         sel = readl(&clk->src_lcd0);
262 #else
263         sel = readl(&clk->src_lcd);
264 #endif
265         sel = sel & 0xf;
266
267         if (sel == 0x6)
268                 sclk = get_pll_clk(MPLL);
269         else if (sel == 0x7)
270                 sclk = get_pll_clk(EPLL);
271         else if (sel == 0x8)
272                 sclk = get_pll_clk(VPLL);
273         else
274                 return 0;
275
276         /*
277          * CLK_DIV_LCD0
278          * FIMD0_RATIO [3:0]
279          */
280 #if defined(CONFIG_EXYNOS4210)
281         ratio = readl(&clk->div_lcd0);
282 #else
283         ratio = readl(&clk->div_lcd);
284 #endif
285         ratio = ratio & 0xf;
286
287         pclk = sclk / (ratio + 1);
288
289         return pclk;
290 }
291
292 /* set_mmc_clk: set the mmc clock */
293 static void exynos4_set_mmc_clk(int dev_index, unsigned int div)
294 {
295         struct exynos4_clock *clk =
296                 (struct exynos4_clock *)samsung_get_base_clock();
297         unsigned int addr;
298         unsigned int val;
299
300         /*
301          * CLK_DIV_FSYS1
302          * MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24]
303          * CLK_DIV_FSYS2
304          * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
305          */
306         if (dev_index < 2) {
307                 addr = (unsigned int)&clk->div_fsys1;
308         } else {
309                 addr = (unsigned int)&clk->div_fsys2;
310                 dev_index -= 2;
311         }
312
313         val = readl(addr);
314         val &= ~(0xff << ((dev_index << 4) + 8));
315         val |= (div & 0xff) << ((dev_index << 4) + 8);
316         writel(val, addr);
317 }
318
319 unsigned long get_pll_clk(int pllreg)
320 {
321         return exynos4_get_pll_clk(pllreg);
322 }
323
324 unsigned long get_arm_clk(void)
325 {
326         return exynos4_get_arm_clk();
327 }
328
329 unsigned long get_pwm_clk(void)
330 {
331         return exynos4_get_pwm_clk();
332 }
333
334 unsigned long get_uart_clk(int dev_index)
335 {
336         return exynos4_get_uart_clk(dev_index);
337 }
338
339 unsigned long get_lcd_clk(void)
340 {
341         return exynos4_get_lcd_clk();
342 }
343
344 unsigned long get_systimer_clk(void)
345 {
346         return (24 * 1000 * 1000);      /* XusbXTI */
347 }
348
349 void set_mmc_clk(int dev_index, unsigned int div)
350 {
351         exynos4_set_mmc_clk(dev_index, div);
352 }
353
354 int set_lcd_clk(const unsigned int dev_index,
355                         const unsigned int pclk_name, const unsigned int div)
356 {
357         return exynos4_set_lcd_clk(dev_index, pclk_name, div);
358 }