blob: 98a27e551dba5a5464e4663c2ead7112c803e5a0 [file] [log] [blame]
Minkyu Kang399e5ae2009-10-01 17:20:01 +09001/*
2 * Copyright (C) 2009 Samsung Electronics
3 * Minkyu Kang <mk7.kang@samsung.com>
4 * Heungjun Kim <riverful.kim@samsung.com>
5 *
6 * See file CREDITS for list of people who contributed to this
7 * project.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22 * MA 02111-1307 USA
23 */
24
25#include <common.h>
26#include <asm/io.h>
27#include <asm/arch/clock.h>
Naveen Krishna CH6c71a8f2010-02-04 14:17:38 +090028#include <asm/arch/clk.h>
Minkyu Kang399e5ae2009-10-01 17:20:01 +090029
30#define CLK_M 0
31#define CLK_D 1
32#define CLK_P 2
33
34#ifndef CONFIG_SYS_CLK_FREQ_C100
35#define CONFIG_SYS_CLK_FREQ_C100 12000000
36#endif
37#ifndef CONFIG_SYS_CLK_FREQ_C110
38#define CONFIG_SYS_CLK_FREQ_C110 24000000
39#endif
40
Minkyu Kangf70409a2010-08-24 15:51:55 +090041unsigned long (*get_uart_clk)(int dev_index);
42unsigned long (*get_pwm_clk)(void);
Minkyu Kang399e5ae2009-10-01 17:20:01 +090043unsigned long (*get_arm_clk)(void);
44unsigned long (*get_pll_clk)(int);
45
46/* s5pc110: return pll clock frequency */
47static unsigned long s5pc100_get_pll_clk(int pllreg)
48{
Minkyu Kangd93d0f02010-08-13 16:07:35 +090049 struct s5pc100_clock *clk =
50 (struct s5pc100_clock *)samsung_get_base_clock();
Minkyu Kang399e5ae2009-10-01 17:20:01 +090051 unsigned long r, m, p, s, mask, fout;
52 unsigned int freq;
53
54 switch (pllreg) {
55 case APLL:
56 r = readl(&clk->apll_con);
57 break;
58 case MPLL:
59 r = readl(&clk->mpll_con);
60 break;
61 case EPLL:
62 r = readl(&clk->epll_con);
63 break;
64 case HPLL:
65 r = readl(&clk->hpll_con);
66 break;
67 default:
68 printf("Unsupported PLL (%d)\n", pllreg);
69 return 0;
70 }
71
72 /*
73 * APLL_CON: MIDV [25:16]
74 * MPLL_CON: MIDV [23:16]
75 * EPLL_CON: MIDV [23:16]
76 * HPLL_CON: MIDV [23:16]
77 */
78 if (pllreg == APLL)
79 mask = 0x3ff;
80 else
81 mask = 0x0ff;
82
83 m = (r >> 16) & mask;
84
85 /* PDIV [13:8] */
86 p = (r >> 8) & 0x3f;
87 /* SDIV [2:0] */
88 s = r & 0x7;
89
90 /* FOUT = MDIV * FIN / (PDIV * 2^SDIV) */
91 freq = CONFIG_SYS_CLK_FREQ_C100;
92 fout = m * (freq / (p * (1 << s)));
93
94 return fout;
95}
96
97/* s5pc100: return pll clock frequency */
98static unsigned long s5pc110_get_pll_clk(int pllreg)
99{
Minkyu Kangd93d0f02010-08-13 16:07:35 +0900100 struct s5pc110_clock *clk =
101 (struct s5pc110_clock *)samsung_get_base_clock();
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900102 unsigned long r, m, p, s, mask, fout;
103 unsigned int freq;
104
105 switch (pllreg) {
106 case APLL:
107 r = readl(&clk->apll_con);
108 break;
109 case MPLL:
110 r = readl(&clk->mpll_con);
111 break;
112 case EPLL:
113 r = readl(&clk->epll_con);
114 break;
115 case VPLL:
116 r = readl(&clk->vpll_con);
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_C110;
142 if (pllreg == APLL) {
143 if (s < 1)
144 s = 1;
145 /* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
146 fout = m * (freq / (p * (1 << (s - 1))));
147 } else
148 /* FOUT = MDIV * FIN / (PDIV * 2^SDIV) */
149 fout = m * (freq / (p * (1 << s)));
150
151 return fout;
152}
153
154/* s5pc110: return ARM clock frequency */
155static unsigned long s5pc110_get_arm_clk(void)
156{
Minkyu Kangd93d0f02010-08-13 16:07:35 +0900157 struct s5pc110_clock *clk =
158 (struct s5pc110_clock *)samsung_get_base_clock();
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900159 unsigned long div;
160 unsigned long dout_apll, armclk;
161 unsigned int apll_ratio;
162
163 div = readl(&clk->div0);
164
165 /* APLL_RATIO: [2:0] */
166 apll_ratio = div & 0x7;
167
168 dout_apll = get_pll_clk(APLL) / (apll_ratio + 1);
169 armclk = dout_apll;
170
171 return armclk;
172}
173
174/* s5pc100: return ARM clock frequency */
175static unsigned long s5pc100_get_arm_clk(void)
176{
Minkyu Kangd93d0f02010-08-13 16:07:35 +0900177 struct s5pc100_clock *clk =
178 (struct s5pc100_clock *)samsung_get_base_clock();
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900179 unsigned long div;
180 unsigned long dout_apll, armclk;
181 unsigned int apll_ratio, arm_ratio;
182
183 div = readl(&clk->div0);
184
185 /* ARM_RATIO: [6:4] */
186 arm_ratio = (div >> 4) & 0x7;
187 /* APLL_RATIO: [0] */
188 apll_ratio = div & 0x1;
189
190 dout_apll = get_pll_clk(APLL) / (apll_ratio + 1);
191 armclk = dout_apll / (arm_ratio + 1);
192
193 return armclk;
194}
195
196/* s5pc100: return HCLKD0 frequency */
197static unsigned long get_hclk(void)
198{
Minkyu Kangd93d0f02010-08-13 16:07:35 +0900199 struct s5pc100_clock *clk =
200 (struct s5pc100_clock *)samsung_get_base_clock();
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900201 unsigned long hclkd0;
202 uint div, d0_bus_ratio;
203
204 div = readl(&clk->div0);
205 /* D0_BUS_RATIO: [10:8] */
206 d0_bus_ratio = (div >> 8) & 0x7;
207
208 hclkd0 = get_arm_clk() / (d0_bus_ratio + 1);
209
210 return hclkd0;
211}
212
213/* s5pc100: return PCLKD1 frequency */
214static unsigned long get_pclkd1(void)
215{
Minkyu Kangd93d0f02010-08-13 16:07:35 +0900216 struct s5pc100_clock *clk =
217 (struct s5pc100_clock *)samsung_get_base_clock();
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900218 unsigned long d1_bus, pclkd1;
219 uint div, d1_bus_ratio, pclkd1_ratio;
220
221 div = readl(&clk->div0);
222 /* D1_BUS_RATIO: [14:12] */
223 d1_bus_ratio = (div >> 12) & 0x7;
224 /* PCLKD1_RATIO: [18:16] */
225 pclkd1_ratio = (div >> 16) & 0x7;
226
227 /* ASYNC Mode */
228 d1_bus = get_pll_clk(MPLL) / (d1_bus_ratio + 1);
229 pclkd1 = d1_bus / (pclkd1_ratio + 1);
230
231 return pclkd1;
232}
233
234/* s5pc110: return HCLKs frequency */
235static unsigned long get_hclk_sys(int dom)
236{
Minkyu Kangd93d0f02010-08-13 16:07:35 +0900237 struct s5pc110_clock *clk =
238 (struct s5pc110_clock *)samsung_get_base_clock();
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900239 unsigned long hclk;
240 unsigned int div;
241 unsigned int offset;
242 unsigned int hclk_sys_ratio;
243
244 if (dom == CLK_M)
245 return get_hclk();
246
247 div = readl(&clk->div0);
248
249 /*
250 * HCLK_MSYS_RATIO: [10:8]
251 * HCLK_DSYS_RATIO: [19:16]
252 * HCLK_PSYS_RATIO: [27:24]
253 */
254 offset = 8 + (dom << 0x3);
255
256 hclk_sys_ratio = (div >> offset) & 0xf;
257
258 hclk = get_pll_clk(MPLL) / (hclk_sys_ratio + 1);
259
260 return hclk;
261}
262
263/* s5pc110: return PCLKs frequency */
264static unsigned long get_pclk_sys(int dom)
265{
Minkyu Kangd93d0f02010-08-13 16:07:35 +0900266 struct s5pc110_clock *clk =
267 (struct s5pc110_clock *)samsung_get_base_clock();
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900268 unsigned long pclk;
269 unsigned int div;
270 unsigned int offset;
271 unsigned int pclk_sys_ratio;
272
273 div = readl(&clk->div0);
274
275 /*
276 * PCLK_MSYS_RATIO: [14:12]
277 * PCLK_DSYS_RATIO: [22:20]
278 * PCLK_PSYS_RATIO: [30:28]
279 */
280 offset = 12 + (dom << 0x3);
281
282 pclk_sys_ratio = (div >> offset) & 0x7;
283
284 pclk = get_hclk_sys(dom) / (pclk_sys_ratio + 1);
285
286 return pclk;
287}
288
289/* s5pc110: return peripheral clock frequency */
290static unsigned long s5pc110_get_pclk(void)
291{
292 return get_pclk_sys(CLK_P);
293}
294
295/* s5pc100: return peripheral clock frequency */
296static unsigned long s5pc100_get_pclk(void)
297{
298 return get_pclkd1();
299}
300
Minkyu Kangf70409a2010-08-24 15:51:55 +0900301/* s5pc1xx: return uart clock frequency */
302static unsigned long s5pc1xx_get_uart_clk(int dev_index)
303{
304 if (cpu_is_s5pc110())
305 return s5pc110_get_pclk();
306 else
307 return s5pc100_get_pclk();
308}
309
310/* s5pc1xx: return pwm clock frequency */
311static unsigned long s5pc1xx_get_pwm_clk(void)
312{
313 if (cpu_is_s5pc110())
314 return s5pc110_get_pclk();
315 else
316 return s5pc100_get_pclk();
317}
318
Minkyu Kang37168da2010-08-19 20:41:50 +0900319void s5p_clock_init(void)
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900320{
321 if (cpu_is_s5pc110()) {
322 get_pll_clk = s5pc110_get_pll_clk;
323 get_arm_clk = s5pc110_get_arm_clk;
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900324 } else {
325 get_pll_clk = s5pc100_get_pll_clk;
326 get_arm_clk = s5pc100_get_arm_clk;
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900327 }
Minkyu Kangf70409a2010-08-24 15:51:55 +0900328 get_uart_clk = s5pc1xx_get_uart_clk;
329 get_pwm_clk = s5pc1xx_get_pwm_clk;
Minkyu Kang399e5ae2009-10-01 17:20:01 +0900330}