]> git.sur5r.net Git - u-boot/blob - arch/arm/cpu/armv7/s5pc1xx/clock.c
ARMV7: S5P: separate the peripheral clocks
[u-boot] / arch / arm / cpu / armv7 / s5pc1xx / clock.c
1 /*
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>
28 #include <asm/arch/clk.h>
29
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
41 unsigned long (*get_uart_clk)(int dev_index);
42 unsigned long (*get_pwm_clk)(void);
43 unsigned long (*get_arm_clk)(void);
44 unsigned long (*get_pll_clk)(int);
45
46 /* s5pc110: return pll clock frequency */
47 static unsigned long s5pc100_get_pll_clk(int pllreg)
48 {
49         struct s5pc100_clock *clk =
50                 (struct s5pc100_clock *)samsung_get_base_clock();
51         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 */
98 static unsigned long s5pc110_get_pll_clk(int pllreg)
99 {
100         struct s5pc110_clock *clk =
101                 (struct s5pc110_clock *)samsung_get_base_clock();
102         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 */
155 static unsigned long s5pc110_get_arm_clk(void)
156 {
157         struct s5pc110_clock *clk =
158                 (struct s5pc110_clock *)samsung_get_base_clock();
159         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 */
175 static unsigned long s5pc100_get_arm_clk(void)
176 {
177         struct s5pc100_clock *clk =
178                 (struct s5pc100_clock *)samsung_get_base_clock();
179         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 */
197 static unsigned long get_hclk(void)
198 {
199         struct s5pc100_clock *clk =
200                 (struct s5pc100_clock *)samsung_get_base_clock();
201         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 */
214 static unsigned long get_pclkd1(void)
215 {
216         struct s5pc100_clock *clk =
217                 (struct s5pc100_clock *)samsung_get_base_clock();
218         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 */
235 static unsigned long get_hclk_sys(int dom)
236 {
237         struct s5pc110_clock *clk =
238                 (struct s5pc110_clock *)samsung_get_base_clock();
239         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 */
264 static unsigned long get_pclk_sys(int dom)
265 {
266         struct s5pc110_clock *clk =
267                 (struct s5pc110_clock *)samsung_get_base_clock();
268         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 */
290 static unsigned long s5pc110_get_pclk(void)
291 {
292         return get_pclk_sys(CLK_P);
293 }
294
295 /* s5pc100: return peripheral clock frequency */
296 static unsigned long s5pc100_get_pclk(void)
297 {
298         return get_pclkd1();
299 }
300
301 /* s5pc1xx: return uart clock frequency */
302 static 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 */
311 static 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
319 void s5p_clock_init(void)
320 {
321         if (cpu_is_s5pc110()) {
322                 get_pll_clk = s5pc110_get_pll_clk;
323                 get_arm_clk = s5pc110_get_arm_clk;
324         } else {
325                 get_pll_clk = s5pc100_get_pll_clk;
326                 get_arm_clk = s5pc100_get_arm_clk;
327         }
328         get_uart_clk = s5pc1xx_get_uart_clk;
329         get_pwm_clk = s5pc1xx_get_pwm_clk;
330 }