Exynos: Fix ARM Clock frequency calculation
[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 #ifndef CONFIG_SYS_CLK_FREQ_C210
30 #define CONFIG_SYS_CLK_FREQ_C210        24000000
31 #endif
32
33 /* exynos4: return pll clock frequency */
34 static unsigned long exynos4_get_pll_clk(int pllreg)
35 {
36         struct exynos4_clock *clk =
37                 (struct exynos4_clock *)samsung_get_base_clock();
38         unsigned long r, m, p, s, k = 0, mask, fout;
39         unsigned int freq;
40
41         switch (pllreg) {
42         case APLL:
43                 r = readl(&clk->apll_con0);
44                 break;
45         case MPLL:
46                 r = readl(&clk->mpll_con0);
47                 break;
48         case EPLL:
49                 r = readl(&clk->epll_con0);
50                 k = readl(&clk->epll_con1);
51                 break;
52         case VPLL:
53                 r = readl(&clk->vpll_con0);
54                 k = readl(&clk->vpll_con1);
55                 break;
56         default:
57                 printf("Unsupported PLL (%d)\n", pllreg);
58                 return 0;
59         }
60
61         /*
62          * APLL_CON: MIDV [25:16]
63          * MPLL_CON: MIDV [25:16]
64          * EPLL_CON: MIDV [24:16]
65          * VPLL_CON: MIDV [24:16]
66          */
67         if (pllreg == APLL || pllreg == MPLL)
68                 mask = 0x3ff;
69         else
70                 mask = 0x1ff;
71
72         m = (r >> 16) & mask;
73
74         /* PDIV [13:8] */
75         p = (r >> 8) & 0x3f;
76         /* SDIV [2:0] */
77         s = r & 0x7;
78
79         freq = CONFIG_SYS_CLK_FREQ_C210;
80
81         if (pllreg == EPLL) {
82                 k = k & 0xffff;
83                 /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
84                 fout = (m + k / 65536) * (freq / (p * (1 << s)));
85         } else if (pllreg == VPLL) {
86                 k = k & 0xfff;
87                 /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
88                 fout = (m + k / 1024) * (freq / (p * (1 << s)));
89         } else {
90                 if (s < 1)
91                         s = 1;
92                 /* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
93                 fout = m * (freq / (p * (1 << (s - 1))));
94         }
95
96         return fout;
97 }
98
99 /* exynos4: return ARM clock frequency */
100 static unsigned long exynos4_get_arm_clk(void)
101 {
102         struct exynos4_clock *clk =
103                 (struct exynos4_clock *)samsung_get_base_clock();
104         unsigned long div;
105         unsigned long armclk;
106         unsigned int core_ratio;
107         unsigned int core2_ratio;
108
109         div = readl(&clk->div_cpu0);
110
111         /* CORE_RATIO: [2:0], CORE2_RATIO: [30:28] */
112         core_ratio = (div >> 0) & 0x7;
113         core2_ratio = (div >> 28) & 0x7;
114
115         armclk = get_pll_clk(APLL) / (core_ratio + 1);
116         armclk /= (core2_ratio + 1);
117
118         return armclk;
119 }
120
121 /* exynos4: return pwm clock frequency */
122 static unsigned long exynos4_get_pwm_clk(void)
123 {
124         struct exynos4_clock *clk =
125                 (struct exynos4_clock *)samsung_get_base_clock();
126         unsigned long pclk, sclk;
127         unsigned int sel;
128         unsigned int ratio;
129
130         if (s5p_get_cpu_rev() == 0) {
131                 /*
132                  * CLK_SRC_PERIL0
133                  * PWM_SEL [27:24]
134                  */
135                 sel = readl(&clk->src_peril0);
136                 sel = (sel >> 24) & 0xf;
137
138                 if (sel == 0x6)
139                         sclk = get_pll_clk(MPLL);
140                 else if (sel == 0x7)
141                         sclk = get_pll_clk(EPLL);
142                 else if (sel == 0x8)
143                         sclk = get_pll_clk(VPLL);
144                 else
145                         return 0;
146
147                 /*
148                  * CLK_DIV_PERIL3
149                  * PWM_RATIO [3:0]
150                  */
151                 ratio = readl(&clk->div_peril3);
152                 ratio = ratio & 0xf;
153         } else if (s5p_get_cpu_rev() == 1) {
154                 sclk = get_pll_clk(MPLL);
155                 ratio = 8;
156         } else
157                 return 0;
158
159         pclk = sclk / (ratio + 1);
160
161         return pclk;
162 }
163
164 /* exynos4: return uart clock frequency */
165 static unsigned long exynos4_get_uart_clk(int dev_index)
166 {
167         struct exynos4_clock *clk =
168                 (struct exynos4_clock *)samsung_get_base_clock();
169         unsigned long uclk, sclk;
170         unsigned int sel;
171         unsigned int ratio;
172
173         /*
174          * CLK_SRC_PERIL0
175          * UART0_SEL [3:0]
176          * UART1_SEL [7:4]
177          * UART2_SEL [8:11]
178          * UART3_SEL [12:15]
179          * UART4_SEL [16:19]
180          * UART5_SEL [23:20]
181          */
182         sel = readl(&clk->src_peril0);
183         sel = (sel >> (dev_index << 2)) & 0xf;
184
185         if (sel == 0x6)
186                 sclk = get_pll_clk(MPLL);
187         else if (sel == 0x7)
188                 sclk = get_pll_clk(EPLL);
189         else if (sel == 0x8)
190                 sclk = get_pll_clk(VPLL);
191         else
192                 return 0;
193
194         /*
195          * CLK_DIV_PERIL0
196          * UART0_RATIO [3:0]
197          * UART1_RATIO [7:4]
198          * UART2_RATIO [8:11]
199          * UART3_RATIO [12:15]
200          * UART4_RATIO [16:19]
201          * UART5_RATIO [23:20]
202          */
203         ratio = readl(&clk->div_peril0);
204         ratio = (ratio >> (dev_index << 2)) & 0xf;
205
206         uclk = sclk / (ratio + 1);
207
208         return uclk;
209 }
210
211 /* exynos4: set the mmc clock */
212 static void exynos4_set_mmc_clk(int dev_index, unsigned int div)
213 {
214         struct exynos4_clock *clk =
215                 (struct exynos4_clock *)samsung_get_base_clock();
216         unsigned int addr;
217         unsigned int val;
218
219         /*
220          * CLK_DIV_FSYS1
221          * MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24]
222          * CLK_DIV_FSYS2
223          * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
224          */
225         if (dev_index < 2) {
226                 addr = (unsigned int)&clk->div_fsys1;
227         } else {
228                 addr = (unsigned int)&clk->div_fsys2;
229                 dev_index -= 2;
230         }
231
232         val = readl(addr);
233         val &= ~(0xff << ((dev_index << 4) + 8));
234         val |= (div & 0xff) << ((dev_index << 4) + 8);
235         writel(val, addr);
236 }
237
238 unsigned long get_pll_clk(int pllreg)
239 {
240         return exynos4_get_pll_clk(pllreg);
241 }
242
243 unsigned long get_arm_clk(void)
244 {
245         return exynos4_get_arm_clk();
246 }
247
248 unsigned long get_pwm_clk(void)
249 {
250         return exynos4_get_pwm_clk();
251 }
252
253 unsigned long get_uart_clk(int dev_index)
254 {
255         return exynos4_get_uart_clk(dev_index);
256 }
257
258 void set_mmc_clk(int dev_index, unsigned int div)
259 {
260         exynos4_set_mmc_clk(dev_index, div);
261 }