diff options
Diffstat (limited to 'arch/arm/mach-s3c2443/clock.c')
-rw-r--r-- | arch/arm/mach-s3c2443/clock.c | 1007 |
1 files changed, 1007 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c new file mode 100644 index 000000000000..dd2272fb1131 --- /dev/null +++ b/arch/arm/mach-s3c2443/clock.c | |||
@@ -0,0 +1,1007 @@ | |||
1 | /* linux/arch/arm/mach-s3c2443/clock.c | ||
2 | * | ||
3 | * Copyright (c) 2007 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * S3C2443 Clock control support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (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, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <linux/init.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/list.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/err.h> | ||
29 | #include <linux/sysdev.h> | ||
30 | #include <linux/clk.h> | ||
31 | #include <linux/mutex.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/serial_core.h> | ||
34 | |||
35 | #include <asm/mach/map.h> | ||
36 | |||
37 | #include <asm/hardware.h> | ||
38 | #include <asm/io.h> | ||
39 | |||
40 | #include <asm/arch/regs-s3c2443-clock.h> | ||
41 | |||
42 | #include <asm/plat-s3c24xx/s3c2443.h> | ||
43 | #include <asm/plat-s3c24xx/clock.h> | ||
44 | #include <asm/plat-s3c24xx/cpu.h> | ||
45 | |||
46 | /* We currently have to assume that the system is running | ||
47 | * from the XTPll input, and that all ***REFCLKs are being | ||
48 | * fed from it, as we cannot read the state of OM[4] from | ||
49 | * software. | ||
50 | * | ||
51 | * It would be possible for each board initialisation to | ||
52 | * set the correct muxing at initialisation | ||
53 | */ | ||
54 | |||
55 | static int s3c2443_clkcon_enable_h(struct clk *clk, int enable) | ||
56 | { | ||
57 | unsigned int clocks = clk->ctrlbit; | ||
58 | unsigned long clkcon; | ||
59 | |||
60 | clkcon = __raw_readl(S3C2443_HCLKCON); | ||
61 | |||
62 | if (enable) | ||
63 | clkcon |= clocks; | ||
64 | else | ||
65 | clkcon &= ~clocks; | ||
66 | |||
67 | __raw_writel(clkcon, S3C2443_HCLKCON); | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int s3c2443_clkcon_enable_p(struct clk *clk, int enable) | ||
73 | { | ||
74 | unsigned int clocks = clk->ctrlbit; | ||
75 | unsigned long clkcon; | ||
76 | |||
77 | clkcon = __raw_readl(S3C2443_PCLKCON); | ||
78 | |||
79 | if (enable) | ||
80 | clkcon |= clocks; | ||
81 | else | ||
82 | clkcon &= ~clocks; | ||
83 | |||
84 | __raw_writel(clkcon, S3C2443_HCLKCON); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int s3c2443_clkcon_enable_s(struct clk *clk, int enable) | ||
90 | { | ||
91 | unsigned int clocks = clk->ctrlbit; | ||
92 | unsigned long clkcon; | ||
93 | |||
94 | clkcon = __raw_readl(S3C2443_SCLKCON); | ||
95 | |||
96 | if (enable) | ||
97 | clkcon |= clocks; | ||
98 | else | ||
99 | clkcon &= ~clocks; | ||
100 | |||
101 | __raw_writel(clkcon, S3C2443_SCLKCON); | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static unsigned long s3c2443_roundrate_clksrc(struct clk *clk, | ||
107 | unsigned long rate, | ||
108 | unsigned int max) | ||
109 | { | ||
110 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
111 | int div; | ||
112 | |||
113 | if (rate > parent_rate) | ||
114 | return parent_rate; | ||
115 | |||
116 | /* note, we remove the +/- 1 calculations as they cancel out */ | ||
117 | |||
118 | div = (rate / parent_rate); | ||
119 | |||
120 | if (div < 1) | ||
121 | div = 1; | ||
122 | else if (div > max) | ||
123 | div = max; | ||
124 | |||
125 | return parent_rate / div; | ||
126 | } | ||
127 | |||
128 | static unsigned long s3c2443_roundrate_clksrc4(struct clk *clk, | ||
129 | unsigned long rate) | ||
130 | { | ||
131 | return s3c2443_roundrate_clksrc(clk, rate, 4); | ||
132 | } | ||
133 | |||
134 | static unsigned long s3c2443_roundrate_clksrc16(struct clk *clk, | ||
135 | unsigned long rate) | ||
136 | { | ||
137 | return s3c2443_roundrate_clksrc(clk, rate, 16); | ||
138 | } | ||
139 | |||
140 | static unsigned long s3c2443_roundrate_clksrc256(struct clk *clk, | ||
141 | unsigned long rate) | ||
142 | { | ||
143 | return s3c2443_roundrate_clksrc(clk, rate, 256); | ||
144 | } | ||
145 | |||
146 | /* clock selections */ | ||
147 | |||
148 | /* CPU EXTCLK input */ | ||
149 | static struct clk clk_ext = { | ||
150 | .name = "ext", | ||
151 | .id = -1, | ||
152 | }; | ||
153 | |||
154 | static struct clk clk_mpllref = { | ||
155 | .name = "mpllref", | ||
156 | .parent = &clk_xtal, | ||
157 | .id = -1, | ||
158 | }; | ||
159 | |||
160 | #if 0 | ||
161 | static struct clk clk_mpll = { | ||
162 | .name = "mpll", | ||
163 | .parent = &clk_mpllref, | ||
164 | .id = -1, | ||
165 | }; | ||
166 | #endif | ||
167 | |||
168 | static struct clk clk_epllref; | ||
169 | |||
170 | static struct clk clk_epll = { | ||
171 | .name = "epll", | ||
172 | .parent = &clk_epllref, | ||
173 | .id = -1, | ||
174 | }; | ||
175 | |||
176 | static struct clk clk_i2s_ext = { | ||
177 | .name = "i2s-ext", | ||
178 | .id = -1, | ||
179 | }; | ||
180 | |||
181 | static int s3c2443_setparent_epllref(struct clk *clk, struct clk *parent) | ||
182 | { | ||
183 | unsigned long clksrc = __raw_readl(S3C2443_CLKSRC); | ||
184 | |||
185 | clksrc &= ~S3C2443_CLKSRC_EPLLREF_MASK; | ||
186 | |||
187 | if (parent == &clk_xtal) | ||
188 | clksrc |= S3C2443_CLKSRC_EPLLREF_XTAL; | ||
189 | else if (parent == &clk_ext) | ||
190 | clksrc |= S3C2443_CLKSRC_EPLLREF_EXTCLK; | ||
191 | else if (parent != &clk_mpllref) | ||
192 | return -EINVAL; | ||
193 | |||
194 | __raw_writel(clksrc, S3C2443_CLKSRC); | ||
195 | clk->parent = parent; | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | static struct clk clk_epllref = { | ||
201 | .name = "epllref", | ||
202 | .id = -1, | ||
203 | .set_parent = s3c2443_setparent_epllref, | ||
204 | }; | ||
205 | |||
206 | static unsigned long s3c2443_getrate_mdivclk(struct clk *clk) | ||
207 | { | ||
208 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
209 | unsigned long div = __raw_readl(S3C2443_CLKDIV0); | ||
210 | |||
211 | div &= S3C2443_CLKDIV0_EXTDIV_MASK; | ||
212 | div >>= (S3C2443_CLKDIV0_EXTDIV_SHIFT-1); /* x2 */ | ||
213 | |||
214 | return parent_rate / (div + 1); | ||
215 | } | ||
216 | |||
217 | static struct clk clk_mdivclk = { | ||
218 | .name = "mdivclk", | ||
219 | .parent = &clk_mpllref, | ||
220 | .id = -1, | ||
221 | .get_rate = s3c2443_getrate_mdivclk, | ||
222 | }; | ||
223 | |||
224 | |||
225 | static int s3c2443_setparent_msysclk(struct clk *clk, struct clk *parent) | ||
226 | { | ||
227 | unsigned long clksrc = __raw_readl(S3C2443_CLKSRC); | ||
228 | |||
229 | clksrc &= ~(S3C2443_CLKSRC_MSYSCLK_MPLL | | ||
230 | S3C2443_CLKSRC_EXTCLK_DIV); | ||
231 | |||
232 | if (parent == &clk_mpll) | ||
233 | clksrc |= S3C2443_CLKSRC_MSYSCLK_MPLL; | ||
234 | else if (parent == &clk_mdivclk) | ||
235 | clksrc |= S3C2443_CLKSRC_EXTCLK_DIV; | ||
236 | else if (parent != &clk_mpllref) | ||
237 | return -EINVAL; | ||
238 | |||
239 | __raw_writel(clksrc, S3C2443_CLKSRC); | ||
240 | clk->parent = parent; | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | static struct clk clk_msysclk = { | ||
246 | .name = "msysclk", | ||
247 | .parent = &clk_xtal, | ||
248 | .id = -1, | ||
249 | .set_parent = s3c2443_setparent_msysclk, | ||
250 | }; | ||
251 | |||
252 | |||
253 | /* esysclk | ||
254 | * | ||
255 | * this is sourced from either the EPLL or the EPLLref clock | ||
256 | */ | ||
257 | |||
258 | static int s3c2443_setparent_esysclk(struct clk *clk, struct clk *parent) | ||
259 | { | ||
260 | unsigned long clksrc = __raw_readl(S3C2443_CLKSRC); | ||
261 | |||
262 | if (parent == &clk_epll) | ||
263 | clksrc |= S3C2443_CLKSRC_ESYSCLK_EPLL; | ||
264 | else if (parent == &clk_epllref) | ||
265 | clksrc &= ~S3C2443_CLKSRC_ESYSCLK_EPLL; | ||
266 | else | ||
267 | return -EINVAL; | ||
268 | |||
269 | __raw_writel(clksrc, S3C2443_CLKSRC); | ||
270 | clk->parent = parent; | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static struct clk clk_esysclk = { | ||
276 | .name = "esysclk", | ||
277 | .parent = &clk_epll, | ||
278 | .id = -1, | ||
279 | .set_parent = s3c2443_setparent_esysclk, | ||
280 | }; | ||
281 | |||
282 | /* uartclk | ||
283 | * | ||
284 | * UART baud-rate clock sourced from esysclk via a divisor | ||
285 | */ | ||
286 | |||
287 | static unsigned long s3c2443_getrate_uart(struct clk *clk) | ||
288 | { | ||
289 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
290 | unsigned long div = __raw_readl(S3C2443_CLKDIV1); | ||
291 | |||
292 | div &= S3C2443_CLKDIV1_UARTDIV_MASK; | ||
293 | div >>= S3C2443_CLKDIV1_UARTDIV_SHIFT; | ||
294 | |||
295 | return parent_rate / (div + 1); | ||
296 | } | ||
297 | |||
298 | |||
299 | static int s3c2443_setrate_uart(struct clk *clk, unsigned long rate) | ||
300 | { | ||
301 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
302 | unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1); | ||
303 | |||
304 | rate = s3c2443_roundrate_clksrc16(clk, rate); | ||
305 | rate = parent_rate / rate; | ||
306 | |||
307 | clkdivn &= ~S3C2443_CLKDIV1_UARTDIV_MASK; | ||
308 | clkdivn |= (rate - 1) << S3C2443_CLKDIV1_UARTDIV_SHIFT; | ||
309 | |||
310 | __raw_writel(clkdivn, S3C2443_CLKDIV1); | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static struct clk clk_uart = { | ||
315 | .name = "uartclk", | ||
316 | .id = -1, | ||
317 | .parent = &clk_esysclk, | ||
318 | .get_rate = s3c2443_getrate_uart, | ||
319 | .set_rate = s3c2443_setrate_uart, | ||
320 | .round_rate = s3c2443_roundrate_clksrc16, | ||
321 | }; | ||
322 | |||
323 | /* hsspi | ||
324 | * | ||
325 | * high-speed spi clock, sourced from esysclk | ||
326 | */ | ||
327 | |||
328 | static unsigned long s3c2443_getrate_hsspi(struct clk *clk) | ||
329 | { | ||
330 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
331 | unsigned long div = __raw_readl(S3C2443_CLKDIV1); | ||
332 | |||
333 | div &= S3C2443_CLKDIV1_HSSPIDIV_MASK; | ||
334 | div >>= S3C2443_CLKDIV1_HSSPIDIV_SHIFT; | ||
335 | |||
336 | return parent_rate / (div + 1); | ||
337 | } | ||
338 | |||
339 | |||
340 | static int s3c2443_setrate_hsspi(struct clk *clk, unsigned long rate) | ||
341 | { | ||
342 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
343 | unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1); | ||
344 | |||
345 | rate = s3c2443_roundrate_clksrc4(clk, rate); | ||
346 | rate = parent_rate / rate; | ||
347 | |||
348 | clkdivn &= ~S3C2443_CLKDIV1_HSSPIDIV_MASK; | ||
349 | clkdivn |= (rate - 1) << S3C2443_CLKDIV1_HSSPIDIV_SHIFT; | ||
350 | |||
351 | __raw_writel(clkdivn, S3C2443_CLKDIV1); | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static struct clk clk_hsspi = { | ||
356 | .name = "hsspi", | ||
357 | .id = -1, | ||
358 | .parent = &clk_esysclk, | ||
359 | .ctrlbit = S3C2443_SCLKCON_HSSPICLK, | ||
360 | .enable = s3c2443_clkcon_enable_s, | ||
361 | .get_rate = s3c2443_getrate_hsspi, | ||
362 | .set_rate = s3c2443_setrate_hsspi, | ||
363 | .round_rate = s3c2443_roundrate_clksrc4, | ||
364 | }; | ||
365 | |||
366 | /* usbhost | ||
367 | * | ||
368 | * usb host bus-clock, usually 48MHz to provide USB bus clock timing | ||
369 | */ | ||
370 | |||
371 | static unsigned long s3c2443_getrate_usbhost(struct clk *clk) | ||
372 | { | ||
373 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
374 | unsigned long div = __raw_readl(S3C2443_CLKDIV1); | ||
375 | |||
376 | div &= S3C2443_CLKDIV1_USBHOSTDIV_MASK; | ||
377 | div >>= S3C2443_CLKDIV1_USBHOSTDIV_SHIFT; | ||
378 | |||
379 | return parent_rate / (div + 1); | ||
380 | } | ||
381 | |||
382 | static int s3c2443_setrate_usbhost(struct clk *clk, unsigned long rate) | ||
383 | { | ||
384 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
385 | unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1); | ||
386 | |||
387 | rate = s3c2443_roundrate_clksrc4(clk, rate); | ||
388 | rate = parent_rate / rate; | ||
389 | |||
390 | clkdivn &= ~S3C2443_CLKDIV1_USBHOSTDIV_MASK; | ||
391 | clkdivn |= (rate - 1) << S3C2443_CLKDIV1_USBHOSTDIV_SHIFT; | ||
392 | |||
393 | __raw_writel(clkdivn, S3C2443_CLKDIV1); | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | struct clk clk_usb_bus_host = { | ||
398 | .name = "usb-bus-host-parent", | ||
399 | .id = -1, | ||
400 | .parent = &clk_esysclk, | ||
401 | .ctrlbit = S3C2443_SCLKCON_USBHOST, | ||
402 | .enable = s3c2443_clkcon_enable_s, | ||
403 | .get_rate = s3c2443_getrate_usbhost, | ||
404 | .set_rate = s3c2443_setrate_usbhost, | ||
405 | .round_rate = s3c2443_roundrate_clksrc4, | ||
406 | }; | ||
407 | |||
408 | /* clk_hsmcc_div | ||
409 | * | ||
410 | * this clock is sourced from epll, and is fed through a divider, | ||
411 | * to a mux controlled by sclkcon where either it or a extclk can | ||
412 | * be fed to the hsmmc block | ||
413 | */ | ||
414 | |||
415 | static unsigned long s3c2443_getrate_hsmmc_div(struct clk *clk) | ||
416 | { | ||
417 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
418 | unsigned long div = __raw_readl(S3C2443_CLKDIV1); | ||
419 | |||
420 | div &= S3C2443_CLKDIV1_HSMMCDIV_MASK; | ||
421 | div >>= S3C2443_CLKDIV1_HSMMCDIV_SHIFT; | ||
422 | |||
423 | return parent_rate / (div + 1); | ||
424 | } | ||
425 | |||
426 | static int s3c2443_setrate_hsmmc_div(struct clk *clk, unsigned long rate) | ||
427 | { | ||
428 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
429 | unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1); | ||
430 | |||
431 | rate = s3c2443_roundrate_clksrc4(clk, rate); | ||
432 | rate = parent_rate / rate; | ||
433 | |||
434 | clkdivn &= ~S3C2443_CLKDIV1_HSMMCDIV_MASK; | ||
435 | clkdivn |= (rate - 1) << S3C2443_CLKDIV1_HSMMCDIV_SHIFT; | ||
436 | |||
437 | __raw_writel(clkdivn, S3C2443_CLKDIV1); | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static struct clk clk_hsmmc_div = { | ||
442 | .name = "hsmmc-div", | ||
443 | .id = -1, | ||
444 | .parent = &clk_esysclk, | ||
445 | .get_rate = s3c2443_getrate_hsmmc_div, | ||
446 | .set_rate = s3c2443_setrate_hsmmc_div, | ||
447 | .round_rate = s3c2443_roundrate_clksrc4, | ||
448 | }; | ||
449 | |||
450 | static int s3c2443_setparent_hsmmc(struct clk *clk, struct clk *parent) | ||
451 | { | ||
452 | unsigned long clksrc = __raw_readl(S3C2443_SCLKCON); | ||
453 | |||
454 | clksrc &= ~(S3C2443_SCLKCON_HSMMCCLK_EXT | | ||
455 | S3C2443_SCLKCON_HSMMCCLK_EPLL); | ||
456 | |||
457 | if (parent == &clk_epll) | ||
458 | clksrc |= S3C2443_SCLKCON_HSMMCCLK_EPLL; | ||
459 | else if (parent == &clk_ext) | ||
460 | clksrc |= S3C2443_SCLKCON_HSMMCCLK_EXT; | ||
461 | else | ||
462 | return -EINVAL; | ||
463 | |||
464 | if (clk->usage > 0) { | ||
465 | __raw_writel(clksrc, S3C2443_SCLKCON); | ||
466 | } | ||
467 | |||
468 | clk->parent = parent; | ||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | static int s3c2443_enable_hsmmc(struct clk *clk, int enable) | ||
473 | { | ||
474 | return s3c2443_setparent_hsmmc(clk, clk->parent); | ||
475 | } | ||
476 | |||
477 | static struct clk clk_hsmmc = { | ||
478 | .name = "hsmmc-if", | ||
479 | .id = -1, | ||
480 | .parent = &clk_hsmmc_div, | ||
481 | .enable = s3c2443_enable_hsmmc, | ||
482 | .set_parent = s3c2443_setparent_hsmmc, | ||
483 | }; | ||
484 | |||
485 | /* i2s_eplldiv | ||
486 | * | ||
487 | * this clock is the output from the i2s divisor of esysclk | ||
488 | */ | ||
489 | |||
490 | static unsigned long s3c2443_getrate_i2s_eplldiv(struct clk *clk) | ||
491 | { | ||
492 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
493 | unsigned long div = __raw_readl(S3C2443_CLKDIV1); | ||
494 | |||
495 | div &= S3C2443_CLKDIV1_I2SDIV_MASK; | ||
496 | div >>= S3C2443_CLKDIV1_I2SDIV_SHIFT; | ||
497 | |||
498 | return parent_rate / (div + 1); | ||
499 | } | ||
500 | |||
501 | static int s3c2443_setrate_i2s_eplldiv(struct clk *clk, unsigned long rate) | ||
502 | { | ||
503 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
504 | unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1); | ||
505 | |||
506 | rate = s3c2443_roundrate_clksrc16(clk, rate); | ||
507 | rate = parent_rate / rate; | ||
508 | |||
509 | clkdivn &= ~S3C2443_CLKDIV1_I2SDIV_MASK; | ||
510 | clkdivn |= (rate - 1) << S3C2443_CLKDIV1_I2SDIV_SHIFT; | ||
511 | |||
512 | __raw_writel(clkdivn, S3C2443_CLKDIV1); | ||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | static struct clk clk_i2s_eplldiv = { | ||
517 | .name = "i2s-eplldiv", | ||
518 | .id = -1, | ||
519 | .parent = &clk_esysclk, | ||
520 | .get_rate = s3c2443_getrate_i2s_eplldiv, | ||
521 | .set_rate = s3c2443_setrate_i2s_eplldiv, | ||
522 | .round_rate = s3c2443_roundrate_clksrc16, | ||
523 | }; | ||
524 | |||
525 | /* i2s-ref | ||
526 | * | ||
527 | * i2s bus reference clock, selectable from external, esysclk or epllref | ||
528 | */ | ||
529 | |||
530 | static int s3c2443_setparent_i2s(struct clk *clk, struct clk *parent) | ||
531 | { | ||
532 | unsigned long clksrc = __raw_readl(S3C2443_CLKSRC); | ||
533 | |||
534 | clksrc &= ~S3C2443_CLKSRC_I2S_MASK; | ||
535 | |||
536 | if (parent == &clk_epllref) | ||
537 | clksrc |= S3C2443_CLKSRC_I2S_EPLLREF; | ||
538 | else if (parent == &clk_i2s_ext) | ||
539 | clksrc |= S3C2443_CLKSRC_I2S_EXT; | ||
540 | else if (parent != &clk_i2s_eplldiv) | ||
541 | return -EINVAL; | ||
542 | |||
543 | clk->parent = parent; | ||
544 | __raw_writel(clksrc, S3C2443_CLKSRC); | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static struct clk clk_i2s = { | ||
550 | .name = "i2s-if", | ||
551 | .id = -1, | ||
552 | .parent = &clk_i2s_eplldiv, | ||
553 | .ctrlbit = S3C2443_SCLKCON_I2SCLK, | ||
554 | .enable = s3c2443_clkcon_enable_s, | ||
555 | .set_parent = s3c2443_setparent_i2s, | ||
556 | }; | ||
557 | |||
558 | /* cam-if | ||
559 | * | ||
560 | * camera interface bus-clock, divided down from esysclk | ||
561 | */ | ||
562 | |||
563 | static unsigned long s3c2443_getrate_cam(struct clk *clk) | ||
564 | { | ||
565 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
566 | unsigned long div = __raw_readl(S3C2443_CLKDIV1); | ||
567 | |||
568 | div &= S3C2443_CLKDIV1_CAMDIV_MASK; | ||
569 | div >>= S3C2443_CLKDIV1_CAMDIV_SHIFT; | ||
570 | |||
571 | return parent_rate / (div + 1); | ||
572 | } | ||
573 | |||
574 | static int s3c2443_setrate_cam(struct clk *clk, unsigned long rate) | ||
575 | { | ||
576 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
577 | unsigned long clkdiv1 = __raw_readl(S3C2443_CLKDIV1); | ||
578 | |||
579 | rate = s3c2443_roundrate_clksrc16(clk, rate); | ||
580 | rate = parent_rate / rate; | ||
581 | |||
582 | clkdiv1 &= ~S3C2443_CLKDIV1_CAMDIV_MASK; | ||
583 | clkdiv1 |= (rate - 1) << S3C2443_CLKDIV1_CAMDIV_SHIFT; | ||
584 | |||
585 | __raw_writel(clkdiv1, S3C2443_CLKDIV1); | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static struct clk clk_cam = { | ||
590 | .name = "camif-upll", /* same as 2440 name */ | ||
591 | .id = -1, | ||
592 | .parent = &clk_esysclk, | ||
593 | .ctrlbit = S3C2443_SCLKCON_CAMCLK, | ||
594 | .enable = s3c2443_clkcon_enable_s, | ||
595 | .get_rate = s3c2443_getrate_cam, | ||
596 | .set_rate = s3c2443_setrate_cam, | ||
597 | .round_rate = s3c2443_roundrate_clksrc16, | ||
598 | }; | ||
599 | |||
600 | /* display-if | ||
601 | * | ||
602 | * display interface clock, divided from esysclk | ||
603 | */ | ||
604 | |||
605 | static unsigned long s3c2443_getrate_display(struct clk *clk) | ||
606 | { | ||
607 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
608 | unsigned long div = __raw_readl(S3C2443_CLKDIV1); | ||
609 | |||
610 | div &= S3C2443_CLKDIV1_DISPDIV_MASK; | ||
611 | div >>= S3C2443_CLKDIV1_DISPDIV_SHIFT; | ||
612 | |||
613 | return parent_rate / (div + 1); | ||
614 | } | ||
615 | |||
616 | static int s3c2443_setrate_display(struct clk *clk, unsigned long rate) | ||
617 | { | ||
618 | unsigned long parent_rate = clk_get_rate(clk->parent); | ||
619 | unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1); | ||
620 | |||
621 | rate = s3c2443_roundrate_clksrc256(clk, rate); | ||
622 | rate = parent_rate / rate; | ||
623 | |||
624 | clkdivn &= ~S3C2443_CLKDIV1_UARTDIV_MASK; | ||
625 | clkdivn |= (rate - 1) << S3C2443_CLKDIV1_UARTDIV_SHIFT; | ||
626 | |||
627 | __raw_writel(clkdivn, S3C2443_CLKDIV1); | ||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | static struct clk clk_display = { | ||
632 | .name = "display-if", | ||
633 | .id = -1, | ||
634 | .parent = &clk_esysclk, | ||
635 | .ctrlbit = S3C2443_SCLKCON_DISPCLK, | ||
636 | .enable = s3c2443_clkcon_enable_s, | ||
637 | .get_rate = s3c2443_getrate_display, | ||
638 | .set_rate = s3c2443_setrate_display, | ||
639 | .round_rate = s3c2443_roundrate_clksrc256, | ||
640 | }; | ||
641 | |||
642 | /* standard clock definitions */ | ||
643 | |||
644 | static struct clk init_clocks_disable[] = { | ||
645 | { | ||
646 | .name = "nand", | ||
647 | .id = -1, | ||
648 | .parent = &clk_h, | ||
649 | }, { | ||
650 | .name = "sdi", | ||
651 | .id = -1, | ||
652 | .parent = &clk_p, | ||
653 | .enable = s3c2443_clkcon_enable_p, | ||
654 | .ctrlbit = S3C2443_PCLKCON_SDI, | ||
655 | }, { | ||
656 | .name = "adc", | ||
657 | .id = -1, | ||
658 | .parent = &clk_p, | ||
659 | .enable = s3c2443_clkcon_enable_p, | ||
660 | .ctrlbit = S3C2443_PCLKCON_ADC, | ||
661 | }, { | ||
662 | .name = "i2c", | ||
663 | .id = -1, | ||
664 | .parent = &clk_p, | ||
665 | .enable = s3c2443_clkcon_enable_p, | ||
666 | .ctrlbit = S3C2443_PCLKCON_IIC, | ||
667 | }, { | ||
668 | .name = "iis", | ||
669 | .id = -1, | ||
670 | .parent = &clk_p, | ||
671 | .enable = s3c2443_clkcon_enable_p, | ||
672 | .ctrlbit = S3C2443_PCLKCON_IIS, | ||
673 | }, { | ||
674 | .name = "spi", | ||
675 | .id = 0, | ||
676 | .parent = &clk_p, | ||
677 | .enable = s3c2443_clkcon_enable_p, | ||
678 | .ctrlbit = S3C2443_PCLKCON_SPI0, | ||
679 | }, { | ||
680 | .name = "spi", | ||
681 | .id = 1, | ||
682 | .parent = &clk_p, | ||
683 | .enable = s3c2443_clkcon_enable_p, | ||
684 | .ctrlbit = S3C2443_PCLKCON_SPI1, | ||
685 | } | ||
686 | }; | ||
687 | |||
688 | static struct clk init_clocks[] = { | ||
689 | { | ||
690 | .name = "dma", | ||
691 | .id = 0, | ||
692 | .parent = &clk_h, | ||
693 | .enable = s3c2443_clkcon_enable_h, | ||
694 | .ctrlbit = S3C2443_HCLKCON_DMA0, | ||
695 | }, { | ||
696 | .name = "dma", | ||
697 | .id = 1, | ||
698 | .parent = &clk_h, | ||
699 | .enable = s3c2443_clkcon_enable_h, | ||
700 | .ctrlbit = S3C2443_HCLKCON_DMA1, | ||
701 | }, { | ||
702 | .name = "dma", | ||
703 | .id = 2, | ||
704 | .parent = &clk_h, | ||
705 | .enable = s3c2443_clkcon_enable_h, | ||
706 | .ctrlbit = S3C2443_HCLKCON_DMA2, | ||
707 | }, { | ||
708 | .name = "dma", | ||
709 | .id = 3, | ||
710 | .parent = &clk_h, | ||
711 | .enable = s3c2443_clkcon_enable_h, | ||
712 | .ctrlbit = S3C2443_HCLKCON_DMA3, | ||
713 | }, { | ||
714 | .name = "dma", | ||
715 | .id = 4, | ||
716 | .parent = &clk_h, | ||
717 | .enable = s3c2443_clkcon_enable_h, | ||
718 | .ctrlbit = S3C2443_HCLKCON_DMA4, | ||
719 | }, { | ||
720 | .name = "dma", | ||
721 | .id = 5, | ||
722 | .parent = &clk_h, | ||
723 | .enable = s3c2443_clkcon_enable_h, | ||
724 | .ctrlbit = S3C2443_HCLKCON_DMA5, | ||
725 | }, { | ||
726 | .name = "lcd", | ||
727 | .id = -1, | ||
728 | .parent = &clk_h, | ||
729 | .enable = s3c2443_clkcon_enable_h, | ||
730 | .ctrlbit = S3C2443_HCLKCON_LCDC, | ||
731 | }, { | ||
732 | .name = "gpio", | ||
733 | .id = -1, | ||
734 | .parent = &clk_p, | ||
735 | .enable = s3c2443_clkcon_enable_p, | ||
736 | .ctrlbit = S3C2443_PCLKCON_GPIO, | ||
737 | }, { | ||
738 | .name = "usb-host", | ||
739 | .id = -1, | ||
740 | .parent = &clk_h, | ||
741 | .enable = s3c2443_clkcon_enable_h, | ||
742 | .ctrlbit = S3C2443_HCLKCON_USBH, | ||
743 | }, { | ||
744 | .name = "usb-device", | ||
745 | .id = -1, | ||
746 | .parent = &clk_h, | ||
747 | .enable = s3c2443_clkcon_enable_h, | ||
748 | .ctrlbit = S3C2443_HCLKCON_USBD, | ||
749 | }, { | ||
750 | .name = "timers", | ||
751 | .id = -1, | ||
752 | .parent = &clk_p, | ||
753 | .enable = s3c2443_clkcon_enable_p, | ||
754 | .ctrlbit = S3C2443_PCLKCON_PWMT, | ||
755 | }, { | ||
756 | .name = "uart", | ||
757 | .id = 0, | ||
758 | .parent = &clk_p, | ||
759 | .enable = s3c2443_clkcon_enable_p, | ||
760 | .ctrlbit = S3C2443_PCLKCON_UART0, | ||
761 | }, { | ||
762 | .name = "uart", | ||
763 | .id = 1, | ||
764 | .parent = &clk_p, | ||
765 | .enable = s3c2443_clkcon_enable_p, | ||
766 | .ctrlbit = S3C2443_PCLKCON_UART1, | ||
767 | }, { | ||
768 | .name = "uart", | ||
769 | .id = 2, | ||
770 | .parent = &clk_p, | ||
771 | .enable = s3c2443_clkcon_enable_p, | ||
772 | .ctrlbit = S3C2443_PCLKCON_UART2, | ||
773 | }, { | ||
774 | .name = "uart", | ||
775 | .id = 3, | ||
776 | .parent = &clk_p, | ||
777 | .enable = s3c2443_clkcon_enable_p, | ||
778 | .ctrlbit = S3C2443_PCLKCON_UART3, | ||
779 | }, { | ||
780 | .name = "rtc", | ||
781 | .id = -1, | ||
782 | .parent = &clk_p, | ||
783 | .enable = s3c2443_clkcon_enable_p, | ||
784 | .ctrlbit = S3C2443_PCLKCON_RTC, | ||
785 | }, { | ||
786 | .name = "watchdog", | ||
787 | .id = -1, | ||
788 | .parent = &clk_p, | ||
789 | .ctrlbit = S3C2443_PCLKCON_WDT, | ||
790 | }, { | ||
791 | .name = "usb-bus-host", | ||
792 | .id = -1, | ||
793 | .parent = &clk_usb_bus_host, | ||
794 | } | ||
795 | }; | ||
796 | |||
797 | /* clocks to add where we need to check their parentage */ | ||
798 | |||
799 | /* s3c2443_clk_initparents | ||
800 | * | ||
801 | * Initialise the parents for the clocks that we get at start-time | ||
802 | */ | ||
803 | |||
804 | static int __init clk_init_set_parent(struct clk *clk, struct clk *parent) | ||
805 | { | ||
806 | printk(KERN_DEBUG "clock %s: parent %s\n", clk->name, parent->name); | ||
807 | return clk_set_parent(clk, parent); | ||
808 | } | ||
809 | |||
810 | static void __init s3c2443_clk_initparents(void) | ||
811 | { | ||
812 | unsigned long clksrc = __raw_readl(S3C2443_CLKSRC); | ||
813 | struct clk *parent; | ||
814 | |||
815 | switch (clksrc & S3C2443_CLKSRC_EPLLREF_MASK) { | ||
816 | case S3C2443_CLKSRC_EPLLREF_EXTCLK: | ||
817 | parent = &clk_ext; | ||
818 | break; | ||
819 | |||
820 | case S3C2443_CLKSRC_EPLLREF_XTAL: | ||
821 | default: | ||
822 | parent = &clk_xtal; | ||
823 | break; | ||
824 | |||
825 | case S3C2443_CLKSRC_EPLLREF_MPLLREF: | ||
826 | case S3C2443_CLKSRC_EPLLREF_MPLLREF2: | ||
827 | parent = &clk_mpllref; | ||
828 | break; | ||
829 | } | ||
830 | |||
831 | clk_init_set_parent(&clk_epllref, parent); | ||
832 | |||
833 | switch (clksrc & S3C2443_CLKSRC_I2S_MASK) { | ||
834 | case S3C2443_CLKSRC_I2S_EXT: | ||
835 | parent = &clk_i2s_ext; | ||
836 | break; | ||
837 | |||
838 | case S3C2443_CLKSRC_I2S_EPLLDIV: | ||
839 | default: | ||
840 | parent = &clk_i2s_eplldiv; | ||
841 | break; | ||
842 | |||
843 | case S3C2443_CLKSRC_I2S_EPLLREF: | ||
844 | case S3C2443_CLKSRC_I2S_EPLLREF3: | ||
845 | parent = &clk_epllref; | ||
846 | } | ||
847 | |||
848 | clk_init_set_parent(&clk_i2s, &clk_epllref); | ||
849 | |||
850 | /* esysclk source */ | ||
851 | |||
852 | parent = (clksrc & S3C2443_CLKSRC_ESYSCLK_EPLL) ? | ||
853 | &clk_epll : &clk_epllref; | ||
854 | |||
855 | clk_init_set_parent(&clk_esysclk, parent); | ||
856 | |||
857 | /* msysclk source */ | ||
858 | |||
859 | if (clksrc & S3C2443_CLKSRC_MSYSCLK_MPLL) { | ||
860 | parent = &clk_mpll; | ||
861 | } else { | ||
862 | parent = (clksrc & S3C2443_CLKSRC_EXTCLK_DIV) ? | ||
863 | &clk_mdivclk : &clk_mpllref; | ||
864 | } | ||
865 | |||
866 | clk_init_set_parent(&clk_msysclk, parent); | ||
867 | } | ||
868 | |||
869 | /* armdiv divisor table */ | ||
870 | |||
871 | static unsigned int armdiv[16] = { | ||
872 | [S3C2443_CLKDIV0_ARMDIV_1 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 1, | ||
873 | [S3C2443_CLKDIV0_ARMDIV_2 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 2, | ||
874 | [S3C2443_CLKDIV0_ARMDIV_3 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 3, | ||
875 | [S3C2443_CLKDIV0_ARMDIV_4 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 4, | ||
876 | [S3C2443_CLKDIV0_ARMDIV_6 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 6, | ||
877 | [S3C2443_CLKDIV0_ARMDIV_8 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 8, | ||
878 | [S3C2443_CLKDIV0_ARMDIV_12 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 12, | ||
879 | [S3C2443_CLKDIV0_ARMDIV_16 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 16, | ||
880 | }; | ||
881 | |||
882 | static inline unsigned int s3c2443_fclk_div(unsigned long clkcon0) | ||
883 | { | ||
884 | clkcon0 &= S3C2443_CLKDIV0_ARMDIV_MASK; | ||
885 | |||
886 | return armdiv[clkcon0 >> S3C2443_CLKDIV0_ARMDIV_SHIFT]; | ||
887 | } | ||
888 | |||
889 | static inline unsigned long s3c2443_get_prediv(unsigned long clkcon0) | ||
890 | { | ||
891 | clkcon0 &= S3C2443_CLKDIV0_PREDIV_MASK; | ||
892 | clkcon0 >>= S3C2443_CLKDIV0_PREDIV_SHIFT; | ||
893 | |||
894 | return clkcon0 + 1; | ||
895 | } | ||
896 | |||
897 | /* clocks to add straight away */ | ||
898 | |||
899 | static struct clk *clks[] __initdata = { | ||
900 | &clk_ext, | ||
901 | &clk_epll, | ||
902 | &clk_usb_bus_host, | ||
903 | &clk_usb_bus, | ||
904 | &clk_esysclk, | ||
905 | &clk_epllref, | ||
906 | &clk_mpllref, | ||
907 | &clk_msysclk, | ||
908 | &clk_uart, | ||
909 | &clk_display, | ||
910 | &clk_cam, | ||
911 | &clk_i2s_eplldiv, | ||
912 | &clk_i2s, | ||
913 | &clk_hsspi, | ||
914 | &clk_hsmmc_div, | ||
915 | &clk_hsmmc, | ||
916 | }; | ||
917 | |||
918 | void __init s3c2443_init_clocks(int xtal) | ||
919 | { | ||
920 | unsigned long epllcon = __raw_readl(S3C2443_EPLLCON); | ||
921 | unsigned long mpllcon = __raw_readl(S3C2443_MPLLCON); | ||
922 | unsigned long clkdiv0 = __raw_readl(S3C2443_CLKDIV0); | ||
923 | unsigned long pll; | ||
924 | unsigned long fclk; | ||
925 | unsigned long hclk; | ||
926 | unsigned long pclk; | ||
927 | struct clk *clkp; | ||
928 | int ret; | ||
929 | int ptr; | ||
930 | |||
931 | pll = s3c2443_get_mpll(mpllcon, xtal); | ||
932 | |||
933 | fclk = pll / s3c2443_fclk_div(clkdiv0); | ||
934 | hclk = fclk / s3c2443_get_prediv(clkdiv0); | ||
935 | hclk = hclk / ((clkdiv0 & S3C2443_CLKDIV0_HALF_HCLK) ? 2 : 1); | ||
936 | pclk = hclk / ((clkdiv0 & S3C2443_CLKDIV0_HALF_PCLK) ? 2 : 1); | ||
937 | |||
938 | s3c24xx_setup_clocks(xtal, fclk, hclk, pclk); | ||
939 | |||
940 | printk("S3C2443: mpll %s %ld.%03ld MHz, cpu %ld.%03ld MHz, mem %ld.%03ld MHz, pclk %ld.%03ld MHz\n", | ||
941 | (mpllcon & S3C2443_PLLCON_OFF) ? "off":"on", | ||
942 | print_mhz(pll), print_mhz(fclk), | ||
943 | print_mhz(hclk), print_mhz(pclk)); | ||
944 | |||
945 | s3c2443_clk_initparents(); | ||
946 | |||
947 | for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) { | ||
948 | clkp = clks[ptr]; | ||
949 | |||
950 | ret = s3c24xx_register_clock(clkp); | ||
951 | if (ret < 0) { | ||
952 | printk(KERN_ERR "Failed to register clock %s (%d)\n", | ||
953 | clkp->name, ret); | ||
954 | } | ||
955 | } | ||
956 | |||
957 | clk_epll.rate = s3c2443_get_epll(epllcon, xtal); | ||
958 | |||
959 | clk_usb_bus.parent = &clk_usb_bus_host; | ||
960 | |||
961 | /* ensure usb bus clock is within correct rate of 48MHz */ | ||
962 | |||
963 | if (clk_get_rate(&clk_usb_bus_host) != (48 * 1000 * 1000)) { | ||
964 | printk(KERN_INFO "Warning: USB host bus not at 48MHz\n"); | ||
965 | clk_set_rate(&clk_usb_bus_host, 48*1000*1000); | ||
966 | } | ||
967 | |||
968 | printk("S3C2443: epll %s %ld.%03ld MHz, usb-bus %ld.%03ld MHz\n", | ||
969 | (epllcon & S3C2443_PLLCON_OFF) ? "off":"on", | ||
970 | print_mhz(clk_get_rate(&clk_epll)), | ||
971 | print_mhz(clk_get_rate(&clk_usb_bus))); | ||
972 | |||
973 | /* register clocks from clock array */ | ||
974 | |||
975 | clkp = init_clocks; | ||
976 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { | ||
977 | ret = s3c24xx_register_clock(clkp); | ||
978 | if (ret < 0) { | ||
979 | printk(KERN_ERR "Failed to register clock %s (%d)\n", | ||
980 | clkp->name, ret); | ||
981 | } | ||
982 | } | ||
983 | |||
984 | /* We must be careful disabling the clocks we are not intending to | ||
985 | * be using at boot time, as subsytems such as the LCD which do | ||
986 | * their own DMA requests to the bus can cause the system to lockup | ||
987 | * if they where in the middle of requesting bus access. | ||
988 | * | ||
989 | * Disabling the LCD clock if the LCD is active is very dangerous, | ||
990 | * and therefore the bootloader should be careful to not enable | ||
991 | * the LCD clock if it is not needed. | ||
992 | */ | ||
993 | |||
994 | /* install (and disable) the clocks we do not need immediately */ | ||
995 | |||
996 | clkp = init_clocks_disable; | ||
997 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) { | ||
998 | |||
999 | ret = s3c24xx_register_clock(clkp); | ||
1000 | if (ret < 0) { | ||
1001 | printk(KERN_ERR "Failed to register clock %s (%d)\n", | ||
1002 | clkp->name, ret); | ||
1003 | } | ||
1004 | |||
1005 | (clkp->enable)(clkp, 0); | ||
1006 | } | ||
1007 | } | ||