aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s3c64xx
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-s3c64xx')
-rw-r--r--arch/arm/mach-s3c64xx/Kconfig2
-rw-r--r--arch/arm/mach-s3c64xx/clock.c507
2 files changed, 507 insertions, 2 deletions
diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig
index 33d82b1c9eff..8c2c89c24fce 100644
--- a/arch/arm/mach-s3c64xx/Kconfig
+++ b/arch/arm/mach-s3c64xx/Kconfig
@@ -7,13 +7,11 @@
7 7
8config CPU_S3C6400 8config CPU_S3C6400
9 bool 9 bool
10 select CPU_S3C6400_CLOCK
11 help 10 help
12 Enable S3C6400 CPU support 11 Enable S3C6400 CPU support
13 12
14config CPU_S3C6410 13config CPU_S3C6410
15 bool 14 bool
16 select CPU_S3C6400_CLOCK
17 help 15 help
18 Enable S3C6410 CPU support 16 Enable S3C6410 CPU support
19 17
diff --git a/arch/arm/mach-s3c64xx/clock.c b/arch/arm/mach-s3c64xx/clock.c
index 229bb3bcc54f..9b587e267422 100644
--- a/arch/arm/mach-s3c64xx/clock.c
+++ b/arch/arm/mach-s3c64xx/clock.c
@@ -16,6 +16,8 @@
16#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/interrupt.h> 17#include <linux/interrupt.h>
18#include <linux/ioport.h> 18#include <linux/ioport.h>
19#include <linux/clk.h>
20#include <linux/err.h>
19#include <linux/io.h> 21#include <linux/io.h>
20 22
21#include <mach/hardware.h> 23#include <mach/hardware.h>
@@ -27,7 +29,25 @@
27 29
28#include <plat/cpu.h> 30#include <plat/cpu.h>
29#include <plat/devs.h> 31#include <plat/devs.h>
32#include <plat/cpu-freq.h>
30#include <plat/clock.h> 33#include <plat/clock.h>
34#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
31 51
32struct clk clk_h2 = { 52struct clk clk_h2 = {
33 .name = "hclk2", 53 .name = "hclk2",
@@ -273,6 +293,493 @@ static struct clk init_clocks[] = {
273 } 293 }
274}; 294};
275 295
296
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
751/**
752 * s3c6400_register_clocks - register clocks for s3c6400 and above
753 * @armclk_divlimit: Divisor mask for ARMCLK
754 *
755 * Register the clocks for the S3C6400 and above SoC range, such
756 * as ARMCLK and the clocks which have divider chains attached.
757 *
758 * This call does not setup the clocks, which is left to the
759 * s3c6400_setup_clocks() call which may be needed by the cpufreq
760 * or resume code to re-set the clocks if the bootloader has changed
761 * them.
762 */
763void __init s3c6400_register_clocks(unsigned armclk_divlimit)
764{
765 struct clk *clkp;
766 int ret;
767 int ptr;
768
769 armclk_mask = armclk_divlimit;
770
771 for (ptr = 0; ptr < ARRAY_SIZE(clks1); ptr++) {
772 clkp = clks1[ptr];
773 ret = s3c24xx_register_clock(clkp);
774 if (ret < 0) {
775 printk(KERN_ERR "Failed to register clock %s (%d)\n",
776 clkp->name, ret);
777 }
778 }
779
780 s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
781}
782
276static struct clk *clks[] __initdata = { 783static struct clk *clks[] __initdata = {
277 &clk_ext, 784 &clk_ext,
278 &clk_epll, 785 &clk_epll,