blob: 7f5e1aa99ce1259816c69b600cbc98e92b9fadc8 [file] [log] [blame]
Ben Dooks4b31d8b2008-10-21 14:07:00 +01001/* linux/arch/arm/plat-s3c64xx/clock.c
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C64XX Base clock support
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/interrupt.h>
18#include <linux/ioport.h>
Ben Dooks62acb2f82010-01-26 14:53:19 +090019#include <linux/clk.h>
20#include <linux/err.h>
Ben Dooks4b31d8b2008-10-21 14:07:00 +010021#include <linux/io.h>
22
23#include <mach/hardware.h>
24#include <mach/map.h>
25
Ben Dooks3501c9a2010-01-26 10:45:40 +090026#include <mach/regs-sys.h>
27#include <mach/regs-clock.h>
Ben Dooksf7be9ab2010-01-26 13:41:30 +090028#include <mach/pll.h>
29
Ben Dooks4b31d8b2008-10-21 14:07:00 +010030#include <plat/cpu.h>
31#include <plat/devs.h>
Ben Dooks62acb2f82010-01-26 14:53:19 +090032#include <plat/cpu-freq.h>
Ben Dooks4b31d8b2008-10-21 14:07:00 +010033#include <plat/clock.h>
Ben Dooks62acb2f82010-01-26 14:53:19 +090034#include <plat/clock-clksrc.h>
35
36/* fin_apll, fin_mpll and fin_epll are all the same clock, which we call
37 * ext_xtal_mux for want of an actual name from the manual.
38*/
39
40static struct clk clk_ext_xtal_mux = {
41 .name = "ext_xtal",
42 .id = -1,
43};
44
45#define clk_fin_apll clk_ext_xtal_mux
46#define clk_fin_mpll clk_ext_xtal_mux
47#define clk_fin_epll clk_ext_xtal_mux
48
49#define clk_fout_mpll clk_mpll
50#define clk_fout_epll clk_epll
Ben Dooks4b31d8b2008-10-21 14:07:00 +010051
Werner Almesbergera03f7da2009-03-05 11:43:13 +080052struct clk clk_h2 = {
53 .name = "hclk2",
54 .id = -1,
55 .rate = 0,
56};
57
Ben Dooks4b31d8b2008-10-21 14:07:00 +010058struct clk clk_27m = {
59 .name = "clk_27m",
60 .id = -1,
61 .rate = 27000000,
62};
63
Ben Dooks3627379f2008-10-31 16:14:36 +000064static int clk_48m_ctrl(struct clk *clk, int enable)
65{
66 unsigned long flags;
67 u32 val;
68
69 /* can't rely on clock lock, this register has other usages */
70 local_irq_save(flags);
71
72 val = __raw_readl(S3C64XX_OTHERS);
73 if (enable)
74 val |= S3C64XX_OTHERS_USBMASK;
75 else
76 val &= ~S3C64XX_OTHERS_USBMASK;
77
78 __raw_writel(val, S3C64XX_OTHERS);
79 local_irq_restore(flags);
80
81 return 0;
82}
83
Ben Dooks4b31d8b2008-10-21 14:07:00 +010084struct clk clk_48m = {
85 .name = "clk_48m",
86 .id = -1,
87 .rate = 48000000,
Ben Dooks3627379f2008-10-31 16:14:36 +000088 .enable = clk_48m_ctrl,
Ben Dooks4b31d8b2008-10-21 14:07:00 +010089};
90
91static int inline s3c64xx_gate(void __iomem *reg,
92 struct clk *clk,
93 int enable)
94{
95 unsigned int ctrlbit = clk->ctrlbit;
96 u32 con;
97
98 con = __raw_readl(reg);
99
100 if (enable)
101 con |= ctrlbit;
102 else
103 con &= ~ctrlbit;
104
105 __raw_writel(con, reg);
106 return 0;
107}
108
109static int s3c64xx_pclk_ctrl(struct clk *clk, int enable)
110{
111 return s3c64xx_gate(S3C_PCLK_GATE, clk, enable);
112}
113
114static int s3c64xx_hclk_ctrl(struct clk *clk, int enable)
115{
116 return s3c64xx_gate(S3C_HCLK_GATE, clk, enable);
117}
118
Ben Dookscf18acf2008-10-21 14:07:02 +0100119int s3c64xx_sclk_ctrl(struct clk *clk, int enable)
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100120{
121 return s3c64xx_gate(S3C_SCLK_GATE, clk, enable);
122}
123
124static struct clk init_clocks_disable[] = {
125 {
126 .name = "nand",
127 .id = -1,
128 .parent = &clk_h,
129 }, {
130 .name = "adc",
131 .id = -1,
132 .parent = &clk_p,
133 .enable = s3c64xx_pclk_ctrl,
134 .ctrlbit = S3C_CLKCON_PCLK_TSADC,
135 }, {
136 .name = "i2c",
137 .id = -1,
138 .parent = &clk_p,
139 .enable = s3c64xx_pclk_ctrl,
140 .ctrlbit = S3C_CLKCON_PCLK_IIC,
141 }, {
142 .name = "iis",
143 .id = 0,
144 .parent = &clk_p,
145 .enable = s3c64xx_pclk_ctrl,
146 .ctrlbit = S3C_CLKCON_PCLK_IIS0,
147 }, {
148 .name = "iis",
149 .id = 1,
150 .parent = &clk_p,
151 .enable = s3c64xx_pclk_ctrl,
152 .ctrlbit = S3C_CLKCON_PCLK_IIS1,
153 }, {
154 .name = "spi",
155 .id = 0,
156 .parent = &clk_p,
157 .enable = s3c64xx_pclk_ctrl,
158 .ctrlbit = S3C_CLKCON_PCLK_SPI0,
159 }, {
160 .name = "spi",
161 .id = 1,
162 .parent = &clk_p,
163 .enable = s3c64xx_pclk_ctrl,
164 .ctrlbit = S3C_CLKCON_PCLK_SPI1,
165 }, {
Jassi Brar87315a82010-01-18 16:15:08 +0900166 .name = "spi_48m",
167 .id = 0,
168 .parent = &clk_48m,
169 .enable = s3c64xx_sclk_ctrl,
170 .ctrlbit = S3C_CLKCON_SCLK_SPI0_48,
171 }, {
172 .name = "spi_48m",
173 .id = 1,
174 .parent = &clk_48m,
175 .enable = s3c64xx_sclk_ctrl,
176 .ctrlbit = S3C_CLKCON_SCLK_SPI1_48,
177 }, {
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100178 .name = "48m",
179 .id = 0,
180 .parent = &clk_48m,
181 .enable = s3c64xx_sclk_ctrl,
182 .ctrlbit = S3C_CLKCON_SCLK_MMC0_48,
183 }, {
184 .name = "48m",
185 .id = 1,
186 .parent = &clk_48m,
187 .enable = s3c64xx_sclk_ctrl,
188 .ctrlbit = S3C_CLKCON_SCLK_MMC1_48,
189 }, {
190 .name = "48m",
191 .id = 2,
192 .parent = &clk_48m,
193 .enable = s3c64xx_sclk_ctrl,
194 .ctrlbit = S3C_CLKCON_SCLK_MMC2_48,
Mark Brown8f1ecf12009-04-28 16:06:24 +0100195 }, {
196 .name = "dma0",
197 .id = -1,
198 .parent = &clk_h,
199 .enable = s3c64xx_hclk_ctrl,
200 .ctrlbit = S3C_CLKCON_HCLK_DMA0,
201 }, {
202 .name = "dma1",
203 .id = -1,
204 .parent = &clk_h,
205 .enable = s3c64xx_hclk_ctrl,
206 .ctrlbit = S3C_CLKCON_HCLK_DMA1,
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100207 },
208};
209
210static struct clk init_clocks[] = {
211 {
212 .name = "lcd",
213 .id = -1,
214 .parent = &clk_h,
215 .enable = s3c64xx_hclk_ctrl,
216 .ctrlbit = S3C_CLKCON_HCLK_LCD,
217 }, {
218 .name = "gpio",
219 .id = -1,
220 .parent = &clk_p,
221 .enable = s3c64xx_pclk_ctrl,
222 .ctrlbit = S3C_CLKCON_PCLK_GPIO,
223 }, {
224 .name = "usb-host",
225 .id = -1,
226 .parent = &clk_h,
227 .enable = s3c64xx_hclk_ctrl,
Peter Korsgaard386f4352009-06-18 23:54:44 +0200228 .ctrlbit = S3C_CLKCON_HCLK_UHOST,
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100229 }, {
230 .name = "hsmmc",
231 .id = 0,
232 .parent = &clk_h,
233 .enable = s3c64xx_hclk_ctrl,
234 .ctrlbit = S3C_CLKCON_HCLK_HSMMC0,
235 }, {
236 .name = "hsmmc",
237 .id = 1,
238 .parent = &clk_h,
239 .enable = s3c64xx_hclk_ctrl,
240 .ctrlbit = S3C_CLKCON_HCLK_HSMMC1,
241 }, {
242 .name = "hsmmc",
243 .id = 2,
244 .parent = &clk_h,
245 .enable = s3c64xx_hclk_ctrl,
246 .ctrlbit = S3C_CLKCON_HCLK_HSMMC2,
247 }, {
248 .name = "timers",
249 .id = -1,
250 .parent = &clk_p,
251 .enable = s3c64xx_pclk_ctrl,
252 .ctrlbit = S3C_CLKCON_PCLK_PWM,
253 }, {
254 .name = "uart",
255 .id = 0,
256 .parent = &clk_p,
257 .enable = s3c64xx_pclk_ctrl,
258 .ctrlbit = S3C_CLKCON_PCLK_UART0,
259 }, {
260 .name = "uart",
261 .id = 1,
262 .parent = &clk_p,
263 .enable = s3c64xx_pclk_ctrl,
264 .ctrlbit = S3C_CLKCON_PCLK_UART1,
265 }, {
266 .name = "uart",
267 .id = 2,
268 .parent = &clk_p,
269 .enable = s3c64xx_pclk_ctrl,
270 .ctrlbit = S3C_CLKCON_PCLK_UART2,
271 }, {
272 .name = "uart",
273 .id = 3,
274 .parent = &clk_p,
275 .enable = s3c64xx_pclk_ctrl,
276 .ctrlbit = S3C_CLKCON_PCLK_UART3,
277 }, {
278 .name = "rtc",
279 .id = -1,
280 .parent = &clk_p,
281 .enable = s3c64xx_pclk_ctrl,
282 .ctrlbit = S3C_CLKCON_PCLK_RTC,
283 }, {
284 .name = "watchdog",
285 .id = -1,
286 .parent = &clk_p,
287 .ctrlbit = S3C_CLKCON_PCLK_WDT,
288 }, {
289 .name = "ac97",
290 .id = -1,
291 .parent = &clk_p,
292 .ctrlbit = S3C_CLKCON_PCLK_AC97,
293 }
294};
295
Ben Dooks62acb2f82010-01-26 14:53:19 +0900296
297static struct clk clk_fout_apll = {
298 .name = "fout_apll",
299 .id = -1,
300};
301
302static struct clk *clk_src_apll_list[] = {
303 [0] = &clk_fin_apll,
304 [1] = &clk_fout_apll,
305};
306
307static struct clksrc_sources clk_src_apll = {
308 .sources = clk_src_apll_list,
309 .nr_sources = ARRAY_SIZE(clk_src_apll_list),
310};
311
312static struct clksrc_clk clk_mout_apll = {
313 .clk = {
314 .name = "mout_apll",
315 .id = -1,
316 },
317 .reg_src = { .reg = S3C_CLK_SRC, .shift = 0, .size = 1 },
318 .sources = &clk_src_apll,
319};
320
321static struct clk *clk_src_epll_list[] = {
322 [0] = &clk_fin_epll,
323 [1] = &clk_fout_epll,
324};
325
326static struct clksrc_sources clk_src_epll = {
327 .sources = clk_src_epll_list,
328 .nr_sources = ARRAY_SIZE(clk_src_epll_list),
329};
330
331static struct clksrc_clk clk_mout_epll = {
332 .clk = {
333 .name = "mout_epll",
334 .id = -1,
335 },
336 .reg_src = { .reg = S3C_CLK_SRC, .shift = 2, .size = 1 },
337 .sources = &clk_src_epll,
338};
339
340static struct clk *clk_src_mpll_list[] = {
341 [0] = &clk_fin_mpll,
342 [1] = &clk_fout_mpll,
343};
344
345static struct clksrc_sources clk_src_mpll = {
346 .sources = clk_src_mpll_list,
347 .nr_sources = ARRAY_SIZE(clk_src_mpll_list),
348};
349
350static struct clksrc_clk clk_mout_mpll = {
351 .clk = {
352 .name = "mout_mpll",
353 .id = -1,
354 },
355 .reg_src = { .reg = S3C_CLK_SRC, .shift = 1, .size = 1 },
356 .sources = &clk_src_mpll,
357};
358
359static unsigned int armclk_mask;
360
361static unsigned long s3c64xx_clk_arm_get_rate(struct clk *clk)
362{
363 unsigned long rate = clk_get_rate(clk->parent);
364 u32 clkdiv;
365
366 /* divisor mask starts at bit0, so no need to shift */
367 clkdiv = __raw_readl(S3C_CLK_DIV0) & armclk_mask;
368
369 return rate / (clkdiv + 1);
370}
371
372static unsigned long s3c64xx_clk_arm_round_rate(struct clk *clk,
373 unsigned long rate)
374{
375 unsigned long parent = clk_get_rate(clk->parent);
376 u32 div;
377
378 if (parent < rate)
379 return parent;
380
381 div = (parent / rate) - 1;
382 if (div > armclk_mask)
383 div = armclk_mask;
384
385 return parent / (div + 1);
386}
387
388static int s3c64xx_clk_arm_set_rate(struct clk *clk, unsigned long rate)
389{
390 unsigned long parent = clk_get_rate(clk->parent);
391 u32 div;
392 u32 val;
393
394 if (rate < parent / (armclk_mask + 1))
395 return -EINVAL;
396
397 rate = clk_round_rate(clk, rate);
398 div = clk_get_rate(clk->parent) / rate;
399
400 val = __raw_readl(S3C_CLK_DIV0);
401 val &= ~armclk_mask;
402 val |= (div - 1);
403 __raw_writel(val, S3C_CLK_DIV0);
404
405 return 0;
406
407}
408
409static struct clk clk_arm = {
410 .name = "armclk",
411 .id = -1,
412 .parent = &clk_mout_apll.clk,
413 .ops = &(struct clk_ops) {
414 .get_rate = s3c64xx_clk_arm_get_rate,
415 .set_rate = s3c64xx_clk_arm_set_rate,
416 .round_rate = s3c64xx_clk_arm_round_rate,
417 },
418};
419
420static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk)
421{
422 unsigned long rate = clk_get_rate(clk->parent);
423
424 printk(KERN_DEBUG "%s: parent is %ld\n", __func__, rate);
425
426 if (__raw_readl(S3C_CLK_DIV0) & S3C6400_CLKDIV0_MPLL_MASK)
427 rate /= 2;
428
429 return rate;
430}
431
432static struct clk_ops clk_dout_ops = {
433 .get_rate = s3c64xx_clk_doutmpll_get_rate,
434};
435
436static struct clk clk_dout_mpll = {
437 .name = "dout_mpll",
438 .id = -1,
439 .parent = &clk_mout_mpll.clk,
440 .ops = &clk_dout_ops,
441};
442
443static struct clk *clkset_spi_mmc_list[] = {
444 &clk_mout_epll.clk,
445 &clk_dout_mpll,
446 &clk_fin_epll,
447 &clk_27m,
448};
449
450static struct clksrc_sources clkset_spi_mmc = {
451 .sources = clkset_spi_mmc_list,
452 .nr_sources = ARRAY_SIZE(clkset_spi_mmc_list),
453};
454
455static struct clk *clkset_irda_list[] = {
456 &clk_mout_epll.clk,
457 &clk_dout_mpll,
458 NULL,
459 &clk_27m,
460};
461
462static struct clksrc_sources clkset_irda = {
463 .sources = clkset_irda_list,
464 .nr_sources = ARRAY_SIZE(clkset_irda_list),
465};
466
467static struct clk *clkset_uart_list[] = {
468 &clk_mout_epll.clk,
469 &clk_dout_mpll,
470 NULL,
471 NULL
472};
473
474static struct clksrc_sources clkset_uart = {
475 .sources = clkset_uart_list,
476 .nr_sources = ARRAY_SIZE(clkset_uart_list),
477};
478
479static struct clk *clkset_uhost_list[] = {
480 &clk_48m,
481 &clk_mout_epll.clk,
482 &clk_dout_mpll,
483 &clk_fin_epll,
484};
485
486static struct clksrc_sources clkset_uhost = {
487 .sources = clkset_uhost_list,
488 .nr_sources = ARRAY_SIZE(clkset_uhost_list),
489};
490
491/* The peripheral clocks are all controlled via clocksource followed
492 * by an optional divider and gate stage. We currently roll this into
493 * one clock which hides the intermediate clock from the mux.
494 *
495 * Note, the JPEG clock can only be an even divider...
496 *
497 * The scaler and LCD clocks depend on the S3C64XX version, and also
498 * have a common parent divisor so are not included here.
499 */
500
501/* clocks that feed other parts of the clock source tree */
502
503static struct clk clk_iis_cd0 = {
504 .name = "iis_cdclk0",
505 .id = -1,
506};
507
508static struct clk clk_iis_cd1 = {
509 .name = "iis_cdclk1",
510 .id = -1,
511};
512
513static struct clk clk_pcm_cd = {
514 .name = "pcm_cdclk",
515 .id = -1,
516};
517
518static struct clk *clkset_audio0_list[] = {
519 [0] = &clk_mout_epll.clk,
520 [1] = &clk_dout_mpll,
521 [2] = &clk_fin_epll,
522 [3] = &clk_iis_cd0,
523 [4] = &clk_pcm_cd,
524};
525
526static struct clksrc_sources clkset_audio0 = {
527 .sources = clkset_audio0_list,
528 .nr_sources = ARRAY_SIZE(clkset_audio0_list),
529};
530
531static struct clk *clkset_audio1_list[] = {
532 [0] = &clk_mout_epll.clk,
533 [1] = &clk_dout_mpll,
534 [2] = &clk_fin_epll,
535 [3] = &clk_iis_cd1,
536 [4] = &clk_pcm_cd,
537};
538
539static struct clksrc_sources clkset_audio1 = {
540 .sources = clkset_audio1_list,
541 .nr_sources = ARRAY_SIZE(clkset_audio1_list),
542};
543
544static struct clk *clkset_camif_list[] = {
545 &clk_h2,
546};
547
548static struct clksrc_sources clkset_camif = {
549 .sources = clkset_camif_list,
550 .nr_sources = ARRAY_SIZE(clkset_camif_list),
551};
552
553static struct clksrc_clk clksrcs[] = {
554 {
555 .clk = {
556 .name = "mmc_bus",
557 .id = 0,
558 .ctrlbit = S3C_CLKCON_SCLK_MMC0,
559 .enable = s3c64xx_sclk_ctrl,
560 },
561 .reg_src = { .reg = S3C_CLK_SRC, .shift = 18, .size = 2 },
562 .reg_div = { .reg = S3C_CLK_DIV1, .shift = 0, .size = 4 },
563 .sources = &clkset_spi_mmc,
564 }, {
565 .clk = {
566 .name = "mmc_bus",
567 .id = 1,
568 .ctrlbit = S3C_CLKCON_SCLK_MMC1,
569 .enable = s3c64xx_sclk_ctrl,
570 },
571 .reg_src = { .reg = S3C_CLK_SRC, .shift = 20, .size = 2 },
572 .reg_div = { .reg = S3C_CLK_DIV1, .shift = 4, .size = 4 },
573 .sources = &clkset_spi_mmc,
574 }, {
575 .clk = {
576 .name = "mmc_bus",
577 .id = 2,
578 .ctrlbit = S3C_CLKCON_SCLK_MMC2,
579 .enable = s3c64xx_sclk_ctrl,
580 },
581 .reg_src = { .reg = S3C_CLK_SRC, .shift = 22, .size = 2 },
582 .reg_div = { .reg = S3C_CLK_DIV1, .shift = 8, .size = 4 },
583 .sources = &clkset_spi_mmc,
584 }, {
585 .clk = {
586 .name = "usb-bus-host",
587 .id = -1,
588 .ctrlbit = S3C_CLKCON_SCLK_UHOST,
589 .enable = s3c64xx_sclk_ctrl,
590 },
591 .reg_src = { .reg = S3C_CLK_SRC, .shift = 5, .size = 2 },
592 .reg_div = { .reg = S3C_CLK_DIV1, .shift = 20, .size = 4 },
593 .sources = &clkset_uhost,
594 }, {
595 .clk = {
596 .name = "uclk1",
597 .id = -1,
598 .ctrlbit = S3C_CLKCON_SCLK_UART,
599 .enable = s3c64xx_sclk_ctrl,
600 },
601 .reg_src = { .reg = S3C_CLK_SRC, .shift = 13, .size = 1 },
602 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 16, .size = 4 },
603 .sources = &clkset_uart,
604 }, {
605/* Where does UCLK0 come from? */
606 .clk = {
607 .name = "spi-bus",
608 .id = 0,
609 .ctrlbit = S3C_CLKCON_SCLK_SPI0,
610 .enable = s3c64xx_sclk_ctrl,
611 },
612 .reg_src = { .reg = S3C_CLK_SRC, .shift = 14, .size = 2 },
613 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 0, .size = 4 },
614 .sources = &clkset_spi_mmc,
615 }, {
616 .clk = {
617 .name = "spi-bus",
618 .id = 1,
619 .ctrlbit = S3C_CLKCON_SCLK_SPI1,
620 .enable = s3c64xx_sclk_ctrl,
621 },
622 .reg_src = { .reg = S3C_CLK_SRC, .shift = 16, .size = 2 },
623 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 4, .size = 4 },
624 .sources = &clkset_spi_mmc,
625 }, {
626 .clk = {
627 .name = "audio-bus",
628 .id = 0,
629 .ctrlbit = S3C_CLKCON_SCLK_AUDIO0,
630 .enable = s3c64xx_sclk_ctrl,
631 },
632 .reg_src = { .reg = S3C_CLK_SRC, .shift = 7, .size = 3 },
633 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 8, .size = 4 },
634 .sources = &clkset_audio0,
635 }, {
636 .clk = {
637 .name = "audio-bus",
638 .id = 1,
639 .ctrlbit = S3C_CLKCON_SCLK_AUDIO1,
640 .enable = s3c64xx_sclk_ctrl,
641 },
642 .reg_src = { .reg = S3C_CLK_SRC, .shift = 10, .size = 3 },
643 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 12, .size = 4 },
644 .sources = &clkset_audio1,
645 }, {
646 .clk = {
647 .name = "irda-bus",
648 .id = 0,
649 .ctrlbit = S3C_CLKCON_SCLK_IRDA,
650 .enable = s3c64xx_sclk_ctrl,
651 },
652 .reg_src = { .reg = S3C_CLK_SRC, .shift = 24, .size = 2 },
653 .reg_div = { .reg = S3C_CLK_DIV2, .shift = 20, .size = 4 },
654 .sources = &clkset_irda,
655 }, {
656 .clk = {
657 .name = "camera",
658 .id = -1,
659 .ctrlbit = S3C_CLKCON_SCLK_CAM,
660 .enable = s3c64xx_sclk_ctrl,
661 },
662 .reg_div = { .reg = S3C_CLK_DIV0, .shift = 20, .size = 4 },
663 .reg_src = { .reg = NULL, .shift = 0, .size = 0 },
664 .sources = &clkset_camif,
665 },
666};
667
668/* Clock initialisation code */
669
670static struct clksrc_clk *init_parents[] = {
671 &clk_mout_apll,
672 &clk_mout_epll,
673 &clk_mout_mpll,
674};
675
676#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
677
678void __init_or_cpufreq s3c6400_setup_clocks(void)
679{
680 struct clk *xtal_clk;
681 unsigned long xtal;
682 unsigned long fclk;
683 unsigned long hclk;
684 unsigned long hclk2;
685 unsigned long pclk;
686 unsigned long epll;
687 unsigned long apll;
688 unsigned long mpll;
689 unsigned int ptr;
690 u32 clkdiv0;
691
692 printk(KERN_DEBUG "%s: registering clocks\n", __func__);
693
694 clkdiv0 = __raw_readl(S3C_CLK_DIV0);
695 printk(KERN_DEBUG "%s: clkdiv0 = %08x\n", __func__, clkdiv0);
696
697 xtal_clk = clk_get(NULL, "xtal");
698 BUG_ON(IS_ERR(xtal_clk));
699
700 xtal = clk_get_rate(xtal_clk);
701 clk_put(xtal_clk);
702
703 printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
704
705 /* For now assume the mux always selects the crystal */
706 clk_ext_xtal_mux.parent = xtal_clk;
707
708 epll = s3c6400_get_epll(xtal);
709 mpll = s3c6400_get_pll(xtal, __raw_readl(S3C_MPLL_CON));
710 apll = s3c6400_get_pll(xtal, __raw_readl(S3C_APLL_CON));
711
712 fclk = mpll;
713
714 printk(KERN_INFO "S3C64XX: PLL settings, A=%ld, M=%ld, E=%ld\n",
715 apll, mpll, epll);
716
717 hclk2 = mpll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2);
718 hclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK);
719 pclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_PCLK);
720
721 printk(KERN_INFO "S3C64XX: HCLK2=%ld, HCLK=%ld, PCLK=%ld\n",
722 hclk2, hclk, pclk);
723
724 clk_fout_mpll.rate = mpll;
725 clk_fout_epll.rate = epll;
726 clk_fout_apll.rate = apll;
727
728 clk_h2.rate = hclk2;
729 clk_h.rate = hclk;
730 clk_p.rate = pclk;
731 clk_f.rate = fclk;
732
733 for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++)
734 s3c_set_clksrc(init_parents[ptr], true);
735
736 for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++)
737 s3c_set_clksrc(&clksrcs[ptr], true);
738}
739
740static struct clk *clks1[] __initdata = {
741 &clk_ext_xtal_mux,
742 &clk_iis_cd0,
743 &clk_iis_cd1,
744 &clk_pcm_cd,
745 &clk_mout_epll.clk,
746 &clk_mout_mpll.clk,
747 &clk_dout_mpll,
748 &clk_arm,
749};
750
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100751static struct clk *clks[] __initdata = {
752 &clk_ext,
753 &clk_epll,
754 &clk_27m,
755 &clk_48m,
Werner Almesbergera03f7da2009-03-05 11:43:13 +0800756 &clk_h2,
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100757};
758
Ben Dooks55bf9262010-01-26 15:10:38 +0900759/**
760 * s3c64xx_register_clocks - register clocks for s3c6400 and s3c6410
761 * @xtal: The rate for the clock crystal feeding the PLLs.
762 * @armclk_divlimit: Divisor mask for ARMCLK.
763 *
764 * Register the clocks for the S3C6400 and S3C6410 SoC range, such
765 * as ARMCLK as well as the necessary parent clocks.
766 *
767 * This call does not setup the clocks, which is left to the
768 * s3c6400_setup_clocks() call which may be needed by the cpufreq
769 * or resume code to re-set the clocks if the bootloader has changed
770 * them.
771 */
772void __init s3c64xx_register_clocks(unsigned long xtal,
773 unsigned armclk_divlimit)
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100774{
775 struct clk *clkp;
776 int ret;
777 int ptr;
778
Ben Dooks55bf9262010-01-26 15:10:38 +0900779 armclk_mask = armclk_divlimit;
780
781 s3c24xx_register_baseclocks(xtal);
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100782 s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
Ben Dooks55bf9262010-01-26 15:10:38 +0900783
Ben Dooks1d9f13c2010-01-06 01:21:38 +0900784 s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100785
786 clkp = init_clocks_disable;
787 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
788
789 ret = s3c24xx_register_clock(clkp);
790 if (ret < 0) {
791 printk(KERN_ERR "Failed to register clock %s (%d)\n",
792 clkp->name, ret);
793 }
794
795 (clkp->enable)(clkp, 0);
796 }
Ben Dooks9d325f22008-11-21 10:36:05 +0000797
Ben Dooks55bf9262010-01-26 15:10:38 +0900798 s3c24xx_register_clocks(clks1, ARRAY_SIZE(clks1));
799 s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
Ben Dooks9d325f22008-11-21 10:36:05 +0000800 s3c_pwmclk_init();
Ben Dooks4b31d8b2008-10-21 14:07:00 +0100801}