diff options
author | Kukjin Kim <kgene.kim@samsung.com> | 2014-05-25 15:04:47 -0400 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2014-05-25 15:04:47 -0400 |
commit | b5783dcaed065ae6622a9795a19a4eda748b64f8 (patch) | |
tree | 7a1dc27919e217182fc1a7d3105e10f426b993ec /drivers/clk | |
parent | 702b691e4a711e699cf3cccba879c1d945665c0d (diff) | |
parent | 34c453ce16633539a94a2e876faeb731ac1be899 (diff) |
Merge branch 'v3.16-next/clk-s3c24xx-3' into v3.16-next/cleanup-samsung
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/samsung/Makefile | 4 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-pll.c | 266 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-pll.h | 6 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-s3c2410-dclk.c | 440 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-s3c2410.c | 477 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-s3c2412.c | 269 | ||||
-rw-r--r-- | drivers/clk/samsung/clk-s3c2443.c | 462 |
7 files changed, 1922 insertions, 2 deletions
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 8eb4799237f0..2cb62f87e068 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile | |||
@@ -8,4 +8,8 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o | |||
8 | obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o | 8 | obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o |
9 | obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o | 9 | obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o |
10 | obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o | 10 | obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o |
11 | obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o | ||
12 | obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o | ||
13 | obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o | ||
14 | obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o | ||
11 | obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o | 15 | obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o |
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 81e6d2f49aa0..7fb0a28e65d5 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | #include <linux/errno.h> | 12 | #include <linux/errno.h> |
13 | #include <linux/hrtimer.h> | 13 | #include <linux/hrtimer.h> |
14 | #include <linux/delay.h> | ||
14 | #include "clk.h" | 15 | #include "clk.h" |
15 | #include "clk-pll.h" | 16 | #include "clk-pll.h" |
16 | 17 | ||
@@ -59,6 +60,72 @@ static long samsung_pll_round_rate(struct clk_hw *hw, | |||
59 | } | 60 | } |
60 | 61 | ||
61 | /* | 62 | /* |
63 | * PLL2126 Clock Type | ||
64 | */ | ||
65 | |||
66 | #define PLL2126_MDIV_MASK (0xff) | ||
67 | #define PLL2126_PDIV_MASK (0x3f) | ||
68 | #define PLL2126_SDIV_MASK (0x3) | ||
69 | #define PLL2126_MDIV_SHIFT (16) | ||
70 | #define PLL2126_PDIV_SHIFT (8) | ||
71 | #define PLL2126_SDIV_SHIFT (0) | ||
72 | |||
73 | static unsigned long samsung_pll2126_recalc_rate(struct clk_hw *hw, | ||
74 | unsigned long parent_rate) | ||
75 | { | ||
76 | struct samsung_clk_pll *pll = to_clk_pll(hw); | ||
77 | u32 pll_con, mdiv, pdiv, sdiv; | ||
78 | u64 fvco = parent_rate; | ||
79 | |||
80 | pll_con = __raw_readl(pll->con_reg); | ||
81 | mdiv = (pll_con >> PLL2126_MDIV_SHIFT) & PLL2126_MDIV_MASK; | ||
82 | pdiv = (pll_con >> PLL2126_PDIV_SHIFT) & PLL2126_PDIV_MASK; | ||
83 | sdiv = (pll_con >> PLL2126_SDIV_SHIFT) & PLL2126_SDIV_MASK; | ||
84 | |||
85 | fvco *= (mdiv + 8); | ||
86 | do_div(fvco, (pdiv + 2) << sdiv); | ||
87 | |||
88 | return (unsigned long)fvco; | ||
89 | } | ||
90 | |||
91 | static const struct clk_ops samsung_pll2126_clk_ops = { | ||
92 | .recalc_rate = samsung_pll2126_recalc_rate, | ||
93 | }; | ||
94 | |||
95 | /* | ||
96 | * PLL3000 Clock Type | ||
97 | */ | ||
98 | |||
99 | #define PLL3000_MDIV_MASK (0xff) | ||
100 | #define PLL3000_PDIV_MASK (0x3) | ||
101 | #define PLL3000_SDIV_MASK (0x3) | ||
102 | #define PLL3000_MDIV_SHIFT (16) | ||
103 | #define PLL3000_PDIV_SHIFT (8) | ||
104 | #define PLL3000_SDIV_SHIFT (0) | ||
105 | |||
106 | static unsigned long samsung_pll3000_recalc_rate(struct clk_hw *hw, | ||
107 | unsigned long parent_rate) | ||
108 | { | ||
109 | struct samsung_clk_pll *pll = to_clk_pll(hw); | ||
110 | u32 pll_con, mdiv, pdiv, sdiv; | ||
111 | u64 fvco = parent_rate; | ||
112 | |||
113 | pll_con = __raw_readl(pll->con_reg); | ||
114 | mdiv = (pll_con >> PLL3000_MDIV_SHIFT) & PLL3000_MDIV_MASK; | ||
115 | pdiv = (pll_con >> PLL3000_PDIV_SHIFT) & PLL3000_PDIV_MASK; | ||
116 | sdiv = (pll_con >> PLL3000_SDIV_SHIFT) & PLL3000_SDIV_MASK; | ||
117 | |||
118 | fvco *= (2 * (mdiv + 8)); | ||
119 | do_div(fvco, pdiv << sdiv); | ||
120 | |||
121 | return (unsigned long)fvco; | ||
122 | } | ||
123 | |||
124 | static const struct clk_ops samsung_pll3000_clk_ops = { | ||
125 | .recalc_rate = samsung_pll3000_recalc_rate, | ||
126 | }; | ||
127 | |||
128 | /* | ||
62 | * PLL35xx Clock Type | 129 | * PLL35xx Clock Type |
63 | */ | 130 | */ |
64 | /* Maximum lock time can be 270 * PDIV cycles */ | 131 | /* Maximum lock time can be 270 * PDIV cycles */ |
@@ -564,7 +631,9 @@ static const struct clk_ops samsung_pll46xx_clk_min_ops = { | |||
564 | #define PLL6552_PDIV_MASK 0x3f | 631 | #define PLL6552_PDIV_MASK 0x3f |
565 | #define PLL6552_SDIV_MASK 0x7 | 632 | #define PLL6552_SDIV_MASK 0x7 |
566 | #define PLL6552_MDIV_SHIFT 16 | 633 | #define PLL6552_MDIV_SHIFT 16 |
634 | #define PLL6552_MDIV_SHIFT_2416 14 | ||
567 | #define PLL6552_PDIV_SHIFT 8 | 635 | #define PLL6552_PDIV_SHIFT 8 |
636 | #define PLL6552_PDIV_SHIFT_2416 5 | ||
568 | #define PLL6552_SDIV_SHIFT 0 | 637 | #define PLL6552_SDIV_SHIFT 0 |
569 | 638 | ||
570 | static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw, | 639 | static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw, |
@@ -575,8 +644,13 @@ static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw, | |||
575 | u64 fvco = parent_rate; | 644 | u64 fvco = parent_rate; |
576 | 645 | ||
577 | pll_con = __raw_readl(pll->con_reg); | 646 | pll_con = __raw_readl(pll->con_reg); |
578 | mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK; | 647 | if (pll->type == pll_6552_s3c2416) { |
579 | pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK; | 648 | mdiv = (pll_con >> PLL6552_MDIV_SHIFT_2416) & PLL6552_MDIV_MASK; |
649 | pdiv = (pll_con >> PLL6552_PDIV_SHIFT_2416) & PLL6552_PDIV_MASK; | ||
650 | } else { | ||
651 | mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK; | ||
652 | pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK; | ||
653 | } | ||
580 | sdiv = (pll_con >> PLL6552_SDIV_SHIFT) & PLL6552_SDIV_MASK; | 654 | sdiv = (pll_con >> PLL6552_SDIV_SHIFT) & PLL6552_SDIV_MASK; |
581 | 655 | ||
582 | fvco *= mdiv; | 656 | fvco *= mdiv; |
@@ -628,6 +702,169 @@ static const struct clk_ops samsung_pll6553_clk_ops = { | |||
628 | }; | 702 | }; |
629 | 703 | ||
630 | /* | 704 | /* |
705 | * PLL Clock Type of S3C24XX before S3C2443 | ||
706 | */ | ||
707 | |||
708 | #define PLLS3C2410_MDIV_MASK (0xff) | ||
709 | #define PLLS3C2410_PDIV_MASK (0x1f) | ||
710 | #define PLLS3C2410_SDIV_MASK (0x3) | ||
711 | #define PLLS3C2410_MDIV_SHIFT (12) | ||
712 | #define PLLS3C2410_PDIV_SHIFT (4) | ||
713 | #define PLLS3C2410_SDIV_SHIFT (0) | ||
714 | |||
715 | #define PLLS3C2410_ENABLE_REG_OFFSET 0x10 | ||
716 | |||
717 | static unsigned long samsung_s3c2410_pll_recalc_rate(struct clk_hw *hw, | ||
718 | unsigned long parent_rate) | ||
719 | { | ||
720 | struct samsung_clk_pll *pll = to_clk_pll(hw); | ||
721 | u32 pll_con, mdiv, pdiv, sdiv; | ||
722 | u64 fvco = parent_rate; | ||
723 | |||
724 | pll_con = __raw_readl(pll->con_reg); | ||
725 | mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK; | ||
726 | pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK; | ||
727 | sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK; | ||
728 | |||
729 | fvco *= (mdiv + 8); | ||
730 | do_div(fvco, (pdiv + 2) << sdiv); | ||
731 | |||
732 | return (unsigned int)fvco; | ||
733 | } | ||
734 | |||
735 | static unsigned long samsung_s3c2440_mpll_recalc_rate(struct clk_hw *hw, | ||
736 | unsigned long parent_rate) | ||
737 | { | ||
738 | struct samsung_clk_pll *pll = to_clk_pll(hw); | ||
739 | u32 pll_con, mdiv, pdiv, sdiv; | ||
740 | u64 fvco = parent_rate; | ||
741 | |||
742 | pll_con = __raw_readl(pll->con_reg); | ||
743 | mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK; | ||
744 | pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK; | ||
745 | sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK; | ||
746 | |||
747 | fvco *= (2 * (mdiv + 8)); | ||
748 | do_div(fvco, (pdiv + 2) << sdiv); | ||
749 | |||
750 | return (unsigned int)fvco; | ||
751 | } | ||
752 | |||
753 | static int samsung_s3c2410_pll_set_rate(struct clk_hw *hw, unsigned long drate, | ||
754 | unsigned long prate) | ||
755 | { | ||
756 | struct samsung_clk_pll *pll = to_clk_pll(hw); | ||
757 | const struct samsung_pll_rate_table *rate; | ||
758 | u32 tmp; | ||
759 | |||
760 | /* Get required rate settings from table */ | ||
761 | rate = samsung_get_pll_settings(pll, drate); | ||
762 | if (!rate) { | ||
763 | pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__, | ||
764 | drate, __clk_get_name(hw->clk)); | ||
765 | return -EINVAL; | ||
766 | } | ||
767 | |||
768 | tmp = __raw_readl(pll->con_reg); | ||
769 | |||
770 | /* Change PLL PMS values */ | ||
771 | tmp &= ~((PLLS3C2410_MDIV_MASK << PLLS3C2410_MDIV_SHIFT) | | ||
772 | (PLLS3C2410_PDIV_MASK << PLLS3C2410_PDIV_SHIFT) | | ||
773 | (PLLS3C2410_SDIV_MASK << PLLS3C2410_SDIV_SHIFT)); | ||
774 | tmp |= (rate->mdiv << PLLS3C2410_MDIV_SHIFT) | | ||
775 | (rate->pdiv << PLLS3C2410_PDIV_SHIFT) | | ||
776 | (rate->sdiv << PLLS3C2410_SDIV_SHIFT); | ||
777 | __raw_writel(tmp, pll->con_reg); | ||
778 | |||
779 | /* Time to settle according to the manual */ | ||
780 | udelay(300); | ||
781 | |||
782 | return 0; | ||
783 | } | ||
784 | |||
785 | static int samsung_s3c2410_pll_enable(struct clk_hw *hw, int bit, bool enable) | ||
786 | { | ||
787 | struct samsung_clk_pll *pll = to_clk_pll(hw); | ||
788 | u32 pll_en = __raw_readl(pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET); | ||
789 | u32 pll_en_orig = pll_en; | ||
790 | |||
791 | if (enable) | ||
792 | pll_en &= ~BIT(bit); | ||
793 | else | ||
794 | pll_en |= BIT(bit); | ||
795 | |||
796 | __raw_writel(pll_en, pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET); | ||
797 | |||
798 | /* if we started the UPLL, then allow to settle */ | ||
799 | if (enable && (pll_en_orig & BIT(bit))) | ||
800 | udelay(300); | ||
801 | |||
802 | return 0; | ||
803 | } | ||
804 | |||
805 | static int samsung_s3c2410_mpll_enable(struct clk_hw *hw) | ||
806 | { | ||
807 | return samsung_s3c2410_pll_enable(hw, 5, true); | ||
808 | } | ||
809 | |||
810 | static void samsung_s3c2410_mpll_disable(struct clk_hw *hw) | ||
811 | { | ||
812 | samsung_s3c2410_pll_enable(hw, 5, false); | ||
813 | } | ||
814 | |||
815 | static int samsung_s3c2410_upll_enable(struct clk_hw *hw) | ||
816 | { | ||
817 | return samsung_s3c2410_pll_enable(hw, 7, true); | ||
818 | } | ||
819 | |||
820 | static void samsung_s3c2410_upll_disable(struct clk_hw *hw) | ||
821 | { | ||
822 | samsung_s3c2410_pll_enable(hw, 7, false); | ||
823 | } | ||
824 | |||
825 | static const struct clk_ops samsung_s3c2410_mpll_clk_min_ops = { | ||
826 | .recalc_rate = samsung_s3c2410_pll_recalc_rate, | ||
827 | .enable = samsung_s3c2410_mpll_enable, | ||
828 | .disable = samsung_s3c2410_mpll_disable, | ||
829 | }; | ||
830 | |||
831 | static const struct clk_ops samsung_s3c2410_upll_clk_min_ops = { | ||
832 | .recalc_rate = samsung_s3c2410_pll_recalc_rate, | ||
833 | .enable = samsung_s3c2410_upll_enable, | ||
834 | .disable = samsung_s3c2410_upll_disable, | ||
835 | }; | ||
836 | |||
837 | static const struct clk_ops samsung_s3c2440_mpll_clk_min_ops = { | ||
838 | .recalc_rate = samsung_s3c2440_mpll_recalc_rate, | ||
839 | .enable = samsung_s3c2410_mpll_enable, | ||
840 | .disable = samsung_s3c2410_mpll_disable, | ||
841 | }; | ||
842 | |||
843 | static const struct clk_ops samsung_s3c2410_mpll_clk_ops = { | ||
844 | .recalc_rate = samsung_s3c2410_pll_recalc_rate, | ||
845 | .enable = samsung_s3c2410_mpll_enable, | ||
846 | .disable = samsung_s3c2410_mpll_disable, | ||
847 | .round_rate = samsung_pll_round_rate, | ||
848 | .set_rate = samsung_s3c2410_pll_set_rate, | ||
849 | }; | ||
850 | |||
851 | static const struct clk_ops samsung_s3c2410_upll_clk_ops = { | ||
852 | .recalc_rate = samsung_s3c2410_pll_recalc_rate, | ||
853 | .enable = samsung_s3c2410_upll_enable, | ||
854 | .disable = samsung_s3c2410_upll_disable, | ||
855 | .round_rate = samsung_pll_round_rate, | ||
856 | .set_rate = samsung_s3c2410_pll_set_rate, | ||
857 | }; | ||
858 | |||
859 | static const struct clk_ops samsung_s3c2440_mpll_clk_ops = { | ||
860 | .recalc_rate = samsung_s3c2440_mpll_recalc_rate, | ||
861 | .enable = samsung_s3c2410_mpll_enable, | ||
862 | .disable = samsung_s3c2410_mpll_disable, | ||
863 | .round_rate = samsung_pll_round_rate, | ||
864 | .set_rate = samsung_s3c2410_pll_set_rate, | ||
865 | }; | ||
866 | |||
867 | /* | ||
631 | * PLL2550x Clock Type | 868 | * PLL2550x Clock Type |
632 | */ | 869 | */ |
633 | 870 | ||
@@ -746,6 +983,12 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, | |||
746 | } | 983 | } |
747 | 984 | ||
748 | switch (pll_clk->type) { | 985 | switch (pll_clk->type) { |
986 | case pll_2126: | ||
987 | init.ops = &samsung_pll2126_clk_ops; | ||
988 | break; | ||
989 | case pll_3000: | ||
990 | init.ops = &samsung_pll3000_clk_ops; | ||
991 | break; | ||
749 | /* clk_ops for 35xx and 2550 are similar */ | 992 | /* clk_ops for 35xx and 2550 are similar */ |
750 | case pll_35xx: | 993 | case pll_35xx: |
751 | case pll_2550: | 994 | case pll_2550: |
@@ -773,6 +1016,7 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, | |||
773 | init.ops = &samsung_pll36xx_clk_ops; | 1016 | init.ops = &samsung_pll36xx_clk_ops; |
774 | break; | 1017 | break; |
775 | case pll_6552: | 1018 | case pll_6552: |
1019 | case pll_6552_s3c2416: | ||
776 | init.ops = &samsung_pll6552_clk_ops; | 1020 | init.ops = &samsung_pll6552_clk_ops; |
777 | break; | 1021 | break; |
778 | case pll_6553: | 1022 | case pll_6553: |
@@ -786,6 +1030,24 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk, | |||
786 | else | 1030 | else |
787 | init.ops = &samsung_pll46xx_clk_ops; | 1031 | init.ops = &samsung_pll46xx_clk_ops; |
788 | break; | 1032 | break; |
1033 | case pll_s3c2410_mpll: | ||
1034 | if (!pll->rate_table) | ||
1035 | init.ops = &samsung_s3c2410_mpll_clk_min_ops; | ||
1036 | else | ||
1037 | init.ops = &samsung_s3c2410_mpll_clk_ops; | ||
1038 | break; | ||
1039 | case pll_s3c2410_upll: | ||
1040 | if (!pll->rate_table) | ||
1041 | init.ops = &samsung_s3c2410_upll_clk_min_ops; | ||
1042 | else | ||
1043 | init.ops = &samsung_s3c2410_upll_clk_ops; | ||
1044 | break; | ||
1045 | case pll_s3c2440_mpll: | ||
1046 | if (!pll->rate_table) | ||
1047 | init.ops = &samsung_s3c2440_mpll_clk_min_ops; | ||
1048 | else | ||
1049 | init.ops = &samsung_s3c2440_mpll_clk_ops; | ||
1050 | break; | ||
789 | default: | 1051 | default: |
790 | pr_warn("%s: Unknown pll type for pll clk %s\n", | 1052 | pr_warn("%s: Unknown pll type for pll clk %s\n", |
791 | __func__, pll_clk->name); | 1053 | __func__, pll_clk->name); |
diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h index 6c39030080fb..6428bcc6df6f 100644 --- a/drivers/clk/samsung/clk-pll.h +++ b/drivers/clk/samsung/clk-pll.h | |||
@@ -13,6 +13,8 @@ | |||
13 | #define __SAMSUNG_CLK_PLL_H | 13 | #define __SAMSUNG_CLK_PLL_H |
14 | 14 | ||
15 | enum samsung_pll_type { | 15 | enum samsung_pll_type { |
16 | pll_2126, | ||
17 | pll_3000, | ||
16 | pll_35xx, | 18 | pll_35xx, |
17 | pll_36xx, | 19 | pll_36xx, |
18 | pll_2550, | 20 | pll_2550, |
@@ -24,7 +26,11 @@ enum samsung_pll_type { | |||
24 | pll_4650, | 26 | pll_4650, |
25 | pll_4650c, | 27 | pll_4650c, |
26 | pll_6552, | 28 | pll_6552, |
29 | pll_6552_s3c2416, | ||
27 | pll_6553, | 30 | pll_6553, |
31 | pll_s3c2410_mpll, | ||
32 | pll_s3c2410_upll, | ||
33 | pll_s3c2440_mpll, | ||
28 | }; | 34 | }; |
29 | 35 | ||
30 | #define PLL_35XX_RATE(_rate, _m, _p, _s) \ | 36 | #define PLL_35XX_RATE(_rate, _m, _p, _s) \ |
diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c new file mode 100644 index 000000000000..8d8dff005c10 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c | |||
@@ -0,0 +1,440 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * Common Clock Framework support for s3c24xx external clock output. | ||
9 | */ | ||
10 | |||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/module.h> | ||
13 | #include "clk.h" | ||
14 | |||
15 | /* legacy access to misccr, until dt conversion is finished */ | ||
16 | #include <mach/hardware.h> | ||
17 | #include <mach/regs-gpio.h> | ||
18 | |||
19 | #define MUX_DCLK0 0 | ||
20 | #define MUX_DCLK1 1 | ||
21 | #define DIV_DCLK0 2 | ||
22 | #define DIV_DCLK1 3 | ||
23 | #define GATE_DCLK0 4 | ||
24 | #define GATE_DCLK1 5 | ||
25 | #define MUX_CLKOUT0 6 | ||
26 | #define MUX_CLKOUT1 7 | ||
27 | #define DCLK_MAX_CLKS (MUX_CLKOUT1 + 1) | ||
28 | |||
29 | enum supported_socs { | ||
30 | S3C2410, | ||
31 | S3C2412, | ||
32 | S3C2440, | ||
33 | S3C2443, | ||
34 | }; | ||
35 | |||
36 | struct s3c24xx_dclk_drv_data { | ||
37 | const char **clkout0_parent_names; | ||
38 | int clkout0_num_parents; | ||
39 | const char **clkout1_parent_names; | ||
40 | int clkout1_num_parents; | ||
41 | const char **mux_parent_names; | ||
42 | int mux_num_parents; | ||
43 | }; | ||
44 | |||
45 | /* | ||
46 | * Clock for output-parent selection in misccr | ||
47 | */ | ||
48 | |||
49 | struct s3c24xx_clkout { | ||
50 | struct clk_hw hw; | ||
51 | u32 mask; | ||
52 | u8 shift; | ||
53 | }; | ||
54 | |||
55 | #define to_s3c24xx_clkout(_hw) container_of(_hw, struct s3c24xx_clkout, hw) | ||
56 | |||
57 | static u8 s3c24xx_clkout_get_parent(struct clk_hw *hw) | ||
58 | { | ||
59 | struct s3c24xx_clkout *clkout = to_s3c24xx_clkout(hw); | ||
60 | int num_parents = __clk_get_num_parents(hw->clk); | ||
61 | u32 val; | ||
62 | |||
63 | val = readl_relaxed(S3C24XX_MISCCR) >> clkout->shift; | ||
64 | val >>= clkout->shift; | ||
65 | val &= clkout->mask; | ||
66 | |||
67 | if (val >= num_parents) | ||
68 | return -EINVAL; | ||
69 | |||
70 | return val; | ||
71 | } | ||
72 | |||
73 | static int s3c24xx_clkout_set_parent(struct clk_hw *hw, u8 index) | ||
74 | { | ||
75 | struct s3c24xx_clkout *clkout = to_s3c24xx_clkout(hw); | ||
76 | int ret = 0; | ||
77 | |||
78 | s3c2410_modify_misccr((clkout->mask << clkout->shift), | ||
79 | (index << clkout->shift)); | ||
80 | |||
81 | return ret; | ||
82 | } | ||
83 | |||
84 | const struct clk_ops s3c24xx_clkout_ops = { | ||
85 | .get_parent = s3c24xx_clkout_get_parent, | ||
86 | .set_parent = s3c24xx_clkout_set_parent, | ||
87 | .determine_rate = __clk_mux_determine_rate, | ||
88 | }; | ||
89 | |||
90 | struct clk *s3c24xx_register_clkout(struct device *dev, const char *name, | ||
91 | const char **parent_names, u8 num_parents, | ||
92 | u8 shift, u32 mask) | ||
93 | { | ||
94 | struct s3c24xx_clkout *clkout; | ||
95 | struct clk *clk; | ||
96 | struct clk_init_data init; | ||
97 | |||
98 | /* allocate the clkout */ | ||
99 | clkout = kzalloc(sizeof(*clkout), GFP_KERNEL); | ||
100 | if (!clkout) | ||
101 | return ERR_PTR(-ENOMEM); | ||
102 | |||
103 | init.name = name; | ||
104 | init.ops = &s3c24xx_clkout_ops; | ||
105 | init.flags = CLK_IS_BASIC; | ||
106 | init.parent_names = parent_names; | ||
107 | init.num_parents = num_parents; | ||
108 | |||
109 | clkout->shift = shift; | ||
110 | clkout->mask = mask; | ||
111 | clkout->hw.init = &init; | ||
112 | |||
113 | clk = clk_register(dev, &clkout->hw); | ||
114 | |||
115 | return clk; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * dclk and clkout init | ||
120 | */ | ||
121 | |||
122 | struct s3c24xx_dclk { | ||
123 | struct device *dev; | ||
124 | void __iomem *base; | ||
125 | struct clk_onecell_data clk_data; | ||
126 | struct notifier_block dclk0_div_change_nb; | ||
127 | struct notifier_block dclk1_div_change_nb; | ||
128 | spinlock_t dclk_lock; | ||
129 | unsigned long reg_save; | ||
130 | }; | ||
131 | |||
132 | #define to_s3c24xx_dclk0(x) \ | ||
133 | container_of(x, struct s3c24xx_dclk, dclk0_div_change_nb) | ||
134 | |||
135 | #define to_s3c24xx_dclk1(x) \ | ||
136 | container_of(x, struct s3c24xx_dclk, dclk1_div_change_nb) | ||
137 | |||
138 | PNAME(dclk_s3c2410_p) = { "pclk", "uclk" }; | ||
139 | PNAME(clkout0_s3c2410_p) = { "mpll", "upll", "fclk", "hclk", "pclk", | ||
140 | "gate_dclk0" }; | ||
141 | PNAME(clkout1_s3c2410_p) = { "mpll", "upll", "fclk", "hclk", "pclk", | ||
142 | "gate_dclk1" }; | ||
143 | |||
144 | PNAME(clkout0_s3c2412_p) = { "mpll", "upll", "rtc_clkout", | ||
145 | "hclk", "pclk", "gate_dclk0" }; | ||
146 | PNAME(clkout1_s3c2412_p) = { "xti", "upll", "fclk", "hclk", "pclk", | ||
147 | "gate_dclk1" }; | ||
148 | |||
149 | PNAME(clkout0_s3c2440_p) = { "xti", "upll", "fclk", "hclk", "pclk", | ||
150 | "gate_dclk0" }; | ||
151 | PNAME(clkout1_s3c2440_p) = { "mpll", "upll", "rtc_clkout", | ||
152 | "hclk", "pclk", "gate_dclk1" }; | ||
153 | |||
154 | PNAME(dclk_s3c2443_p) = { "pclk", "epll" }; | ||
155 | PNAME(clkout0_s3c2443_p) = { "xti", "epll", "armclk", "hclk", "pclk", | ||
156 | "gate_dclk0" }; | ||
157 | PNAME(clkout1_s3c2443_p) = { "dummy", "epll", "rtc_clkout", | ||
158 | "hclk", "pclk", "gate_dclk1" }; | ||
159 | |||
160 | #define DCLKCON_DCLK_DIV_MASK 0xf | ||
161 | #define DCLKCON_DCLK0_DIV_SHIFT 4 | ||
162 | #define DCLKCON_DCLK0_CMP_SHIFT 8 | ||
163 | #define DCLKCON_DCLK1_DIV_SHIFT 20 | ||
164 | #define DCLKCON_DCLK1_CMP_SHIFT 24 | ||
165 | |||
166 | static void s3c24xx_dclk_update_cmp(struct s3c24xx_dclk *s3c24xx_dclk, | ||
167 | int div_shift, int cmp_shift) | ||
168 | { | ||
169 | unsigned long flags = 0; | ||
170 | u32 dclk_con, div, cmp; | ||
171 | |||
172 | spin_lock_irqsave(&s3c24xx_dclk->dclk_lock, flags); | ||
173 | |||
174 | dclk_con = readl_relaxed(s3c24xx_dclk->base); | ||
175 | |||
176 | div = ((dclk_con >> div_shift) & DCLKCON_DCLK_DIV_MASK) + 1; | ||
177 | cmp = ((div + 1) / 2) - 1; | ||
178 | |||
179 | dclk_con &= ~(DCLKCON_DCLK_DIV_MASK << cmp_shift); | ||
180 | dclk_con |= (cmp << cmp_shift); | ||
181 | |||
182 | writel_relaxed(dclk_con, s3c24xx_dclk->base); | ||
183 | |||
184 | spin_unlock_irqrestore(&s3c24xx_dclk->dclk_lock, flags); | ||
185 | } | ||
186 | |||
187 | static int s3c24xx_dclk0_div_notify(struct notifier_block *nb, | ||
188 | unsigned long event, void *data) | ||
189 | { | ||
190 | struct s3c24xx_dclk *s3c24xx_dclk = to_s3c24xx_dclk0(nb); | ||
191 | |||
192 | if (event == POST_RATE_CHANGE) { | ||
193 | s3c24xx_dclk_update_cmp(s3c24xx_dclk, | ||
194 | DCLKCON_DCLK0_DIV_SHIFT, DCLKCON_DCLK0_CMP_SHIFT); | ||
195 | } | ||
196 | |||
197 | return NOTIFY_DONE; | ||
198 | } | ||
199 | |||
200 | static int s3c24xx_dclk1_div_notify(struct notifier_block *nb, | ||
201 | unsigned long event, void *data) | ||
202 | { | ||
203 | struct s3c24xx_dclk *s3c24xx_dclk = to_s3c24xx_dclk1(nb); | ||
204 | |||
205 | if (event == POST_RATE_CHANGE) { | ||
206 | s3c24xx_dclk_update_cmp(s3c24xx_dclk, | ||
207 | DCLKCON_DCLK1_DIV_SHIFT, DCLKCON_DCLK1_CMP_SHIFT); | ||
208 | } | ||
209 | |||
210 | return NOTIFY_DONE; | ||
211 | } | ||
212 | |||
213 | #ifdef CONFIG_PM_SLEEP | ||
214 | static int s3c24xx_dclk_suspend(struct device *dev) | ||
215 | { | ||
216 | struct platform_device *pdev = to_platform_device(dev); | ||
217 | struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev); | ||
218 | |||
219 | s3c24xx_dclk->reg_save = readl_relaxed(s3c24xx_dclk->base); | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static int s3c24xx_dclk_resume(struct device *dev) | ||
224 | { | ||
225 | struct platform_device *pdev = to_platform_device(dev); | ||
226 | struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev); | ||
227 | |||
228 | writel_relaxed(s3c24xx_dclk->reg_save, s3c24xx_dclk->base); | ||
229 | return 0; | ||
230 | } | ||
231 | #endif | ||
232 | |||
233 | static SIMPLE_DEV_PM_OPS(s3c24xx_dclk_pm_ops, | ||
234 | s3c24xx_dclk_suspend, s3c24xx_dclk_resume); | ||
235 | |||
236 | static int s3c24xx_dclk_probe(struct platform_device *pdev) | ||
237 | { | ||
238 | struct s3c24xx_dclk *s3c24xx_dclk; | ||
239 | struct resource *mem; | ||
240 | struct clk **clk_table; | ||
241 | struct s3c24xx_dclk_drv_data *dclk_variant; | ||
242 | int ret, i; | ||
243 | |||
244 | s3c24xx_dclk = devm_kzalloc(&pdev->dev, sizeof(*s3c24xx_dclk), | ||
245 | GFP_KERNEL); | ||
246 | if (!s3c24xx_dclk) | ||
247 | return -ENOMEM; | ||
248 | |||
249 | s3c24xx_dclk->dev = &pdev->dev; | ||
250 | platform_set_drvdata(pdev, s3c24xx_dclk); | ||
251 | spin_lock_init(&s3c24xx_dclk->dclk_lock); | ||
252 | |||
253 | clk_table = devm_kzalloc(&pdev->dev, | ||
254 | sizeof(struct clk *) * DCLK_MAX_CLKS, | ||
255 | GFP_KERNEL); | ||
256 | if (!clk_table) | ||
257 | return -ENOMEM; | ||
258 | |||
259 | s3c24xx_dclk->clk_data.clks = clk_table; | ||
260 | s3c24xx_dclk->clk_data.clk_num = DCLK_MAX_CLKS; | ||
261 | |||
262 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
263 | s3c24xx_dclk->base = devm_ioremap_resource(&pdev->dev, mem); | ||
264 | if (IS_ERR(s3c24xx_dclk->base)) | ||
265 | return PTR_ERR(s3c24xx_dclk->base); | ||
266 | |||
267 | dclk_variant = (struct s3c24xx_dclk_drv_data *) | ||
268 | platform_get_device_id(pdev)->driver_data; | ||
269 | |||
270 | |||
271 | clk_table[MUX_DCLK0] = clk_register_mux(&pdev->dev, "mux_dclk0", | ||
272 | dclk_variant->mux_parent_names, | ||
273 | dclk_variant->mux_num_parents, 0, | ||
274 | s3c24xx_dclk->base, 1, 1, 0, | ||
275 | &s3c24xx_dclk->dclk_lock); | ||
276 | clk_table[MUX_DCLK1] = clk_register_mux(&pdev->dev, "mux_dclk1", | ||
277 | dclk_variant->mux_parent_names, | ||
278 | dclk_variant->mux_num_parents, 0, | ||
279 | s3c24xx_dclk->base, 17, 1, 0, | ||
280 | &s3c24xx_dclk->dclk_lock); | ||
281 | |||
282 | clk_table[DIV_DCLK0] = clk_register_divider(&pdev->dev, "div_dclk0", | ||
283 | "mux_dclk0", 0, s3c24xx_dclk->base, | ||
284 | 4, 4, 0, &s3c24xx_dclk->dclk_lock); | ||
285 | clk_table[DIV_DCLK1] = clk_register_divider(&pdev->dev, "div_dclk1", | ||
286 | "mux_dclk1", 0, s3c24xx_dclk->base, | ||
287 | 20, 4, 0, &s3c24xx_dclk->dclk_lock); | ||
288 | |||
289 | clk_table[GATE_DCLK0] = clk_register_gate(&pdev->dev, "gate_dclk0", | ||
290 | "div_dclk0", CLK_SET_RATE_PARENT, | ||
291 | s3c24xx_dclk->base, 0, 0, | ||
292 | &s3c24xx_dclk->dclk_lock); | ||
293 | clk_table[GATE_DCLK1] = clk_register_gate(&pdev->dev, "gate_dclk1", | ||
294 | "div_dclk1", CLK_SET_RATE_PARENT, | ||
295 | s3c24xx_dclk->base, 16, 0, | ||
296 | &s3c24xx_dclk->dclk_lock); | ||
297 | |||
298 | clk_table[MUX_CLKOUT0] = s3c24xx_register_clkout(&pdev->dev, | ||
299 | "clkout0", dclk_variant->clkout0_parent_names, | ||
300 | dclk_variant->clkout0_num_parents, 4, 7); | ||
301 | clk_table[MUX_CLKOUT1] = s3c24xx_register_clkout(&pdev->dev, | ||
302 | "clkout1", dclk_variant->clkout1_parent_names, | ||
303 | dclk_variant->clkout1_num_parents, 8, 7); | ||
304 | |||
305 | for (i = 0; i < DCLK_MAX_CLKS; i++) | ||
306 | if (IS_ERR(clk_table[i])) { | ||
307 | dev_err(&pdev->dev, "clock %d failed to register\n", i); | ||
308 | ret = PTR_ERR(clk_table[i]); | ||
309 | goto err_clk_register; | ||
310 | } | ||
311 | |||
312 | ret = clk_register_clkdev(clk_table[MUX_DCLK0], "dclk0", NULL); | ||
313 | if (!ret) | ||
314 | ret = clk_register_clkdev(clk_table[MUX_DCLK1], "dclk1", NULL); | ||
315 | if (!ret) | ||
316 | ret = clk_register_clkdev(clk_table[MUX_CLKOUT0], | ||
317 | "clkout0", NULL); | ||
318 | if (!ret) | ||
319 | ret = clk_register_clkdev(clk_table[MUX_CLKOUT1], | ||
320 | "clkout1", NULL); | ||
321 | if (ret) { | ||
322 | dev_err(&pdev->dev, "failed to register aliases, %d\n", ret); | ||
323 | goto err_clk_register; | ||
324 | } | ||
325 | |||
326 | s3c24xx_dclk->dclk0_div_change_nb.notifier_call = | ||
327 | s3c24xx_dclk0_div_notify; | ||
328 | |||
329 | s3c24xx_dclk->dclk1_div_change_nb.notifier_call = | ||
330 | s3c24xx_dclk1_div_notify; | ||
331 | |||
332 | ret = clk_notifier_register(clk_table[DIV_DCLK0], | ||
333 | &s3c24xx_dclk->dclk0_div_change_nb); | ||
334 | if (ret) | ||
335 | goto err_clk_register; | ||
336 | |||
337 | ret = clk_notifier_register(clk_table[DIV_DCLK1], | ||
338 | &s3c24xx_dclk->dclk1_div_change_nb); | ||
339 | if (ret) | ||
340 | goto err_dclk_notify; | ||
341 | |||
342 | return 0; | ||
343 | |||
344 | err_dclk_notify: | ||
345 | clk_notifier_unregister(clk_table[DIV_DCLK0], | ||
346 | &s3c24xx_dclk->dclk0_div_change_nb); | ||
347 | err_clk_register: | ||
348 | for (i = 0; i < DCLK_MAX_CLKS; i++) | ||
349 | if (clk_table[i] && !IS_ERR(clk_table[i])) | ||
350 | clk_unregister(clk_table[i]); | ||
351 | |||
352 | return ret; | ||
353 | } | ||
354 | |||
355 | static int s3c24xx_dclk_remove(struct platform_device *pdev) | ||
356 | { | ||
357 | struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev); | ||
358 | struct clk **clk_table = s3c24xx_dclk->clk_data.clks; | ||
359 | int i; | ||
360 | |||
361 | clk_notifier_unregister(clk_table[DIV_DCLK1], | ||
362 | &s3c24xx_dclk->dclk1_div_change_nb); | ||
363 | clk_notifier_unregister(clk_table[DIV_DCLK0], | ||
364 | &s3c24xx_dclk->dclk0_div_change_nb); | ||
365 | |||
366 | for (i = 0; i < DCLK_MAX_CLKS; i++) | ||
367 | clk_unregister(clk_table[i]); | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static struct s3c24xx_dclk_drv_data dclk_variants[] = { | ||
373 | [S3C2410] = { | ||
374 | .clkout0_parent_names = clkout0_s3c2410_p, | ||
375 | .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2410_p), | ||
376 | .clkout1_parent_names = clkout1_s3c2410_p, | ||
377 | .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2410_p), | ||
378 | .mux_parent_names = dclk_s3c2410_p, | ||
379 | .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), | ||
380 | }, | ||
381 | [S3C2412] = { | ||
382 | .clkout0_parent_names = clkout0_s3c2412_p, | ||
383 | .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2412_p), | ||
384 | .clkout1_parent_names = clkout1_s3c2412_p, | ||
385 | .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2412_p), | ||
386 | .mux_parent_names = dclk_s3c2410_p, | ||
387 | .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), | ||
388 | }, | ||
389 | [S3C2440] = { | ||
390 | .clkout0_parent_names = clkout0_s3c2440_p, | ||
391 | .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2440_p), | ||
392 | .clkout1_parent_names = clkout1_s3c2440_p, | ||
393 | .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2440_p), | ||
394 | .mux_parent_names = dclk_s3c2410_p, | ||
395 | .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), | ||
396 | }, | ||
397 | [S3C2443] = { | ||
398 | .clkout0_parent_names = clkout0_s3c2443_p, | ||
399 | .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2443_p), | ||
400 | .clkout1_parent_names = clkout1_s3c2443_p, | ||
401 | .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2443_p), | ||
402 | .mux_parent_names = dclk_s3c2443_p, | ||
403 | .mux_num_parents = ARRAY_SIZE(dclk_s3c2443_p), | ||
404 | }, | ||
405 | }; | ||
406 | |||
407 | static struct platform_device_id s3c24xx_dclk_driver_ids[] = { | ||
408 | { | ||
409 | .name = "s3c2410-dclk", | ||
410 | .driver_data = (kernel_ulong_t)&dclk_variants[S3C2410], | ||
411 | }, { | ||
412 | .name = "s3c2412-dclk", | ||
413 | .driver_data = (kernel_ulong_t)&dclk_variants[S3C2412], | ||
414 | }, { | ||
415 | .name = "s3c2440-dclk", | ||
416 | .driver_data = (kernel_ulong_t)&dclk_variants[S3C2440], | ||
417 | }, { | ||
418 | .name = "s3c2443-dclk", | ||
419 | .driver_data = (kernel_ulong_t)&dclk_variants[S3C2443], | ||
420 | }, | ||
421 | { } | ||
422 | }; | ||
423 | |||
424 | MODULE_DEVICE_TABLE(platform, s3c24xx_dclk_driver_ids); | ||
425 | |||
426 | static struct platform_driver s3c24xx_dclk_driver = { | ||
427 | .driver = { | ||
428 | .name = "s3c24xx-dclk", | ||
429 | .owner = THIS_MODULE, | ||
430 | .pm = &s3c24xx_dclk_pm_ops, | ||
431 | }, | ||
432 | .probe = s3c24xx_dclk_probe, | ||
433 | .remove = s3c24xx_dclk_remove, | ||
434 | .id_table = s3c24xx_dclk_driver_ids, | ||
435 | }; | ||
436 | module_platform_driver(s3c24xx_dclk_driver); | ||
437 | |||
438 | MODULE_LICENSE("GPL v2"); | ||
439 | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); | ||
440 | MODULE_DESCRIPTION("Driver for the S3C24XX external clock outputs"); | ||
diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c new file mode 100644 index 000000000000..7b4182186a30 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2410.c | |||
@@ -0,0 +1,477 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * Common Clock Framework support for S3C2410 and following SoCs. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk.h> | ||
12 | #include <linux/clkdev.h> | ||
13 | #include <linux/clk-provider.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/syscore_ops.h> | ||
17 | |||
18 | #include <dt-bindings/clock/s3c2410.h> | ||
19 | |||
20 | #include "clk.h" | ||
21 | #include "clk-pll.h" | ||
22 | |||
23 | #define LOCKTIME 0x00 | ||
24 | #define MPLLCON 0x04 | ||
25 | #define UPLLCON 0x08 | ||
26 | #define CLKCON 0x0c | ||
27 | #define CLKSLOW 0x10 | ||
28 | #define CLKDIVN 0x14 | ||
29 | #define CAMDIVN 0x18 | ||
30 | |||
31 | /* the soc types */ | ||
32 | enum supported_socs { | ||
33 | S3C2410, | ||
34 | S3C2440, | ||
35 | S3C2442, | ||
36 | }; | ||
37 | |||
38 | /* list of PLLs to be registered */ | ||
39 | enum s3c2410_plls { | ||
40 | mpll, upll, | ||
41 | }; | ||
42 | |||
43 | static void __iomem *reg_base; | ||
44 | |||
45 | #ifdef CONFIG_PM_SLEEP | ||
46 | static struct samsung_clk_reg_dump *s3c2410_save; | ||
47 | |||
48 | /* | ||
49 | * list of controller registers to be saved and restored during a | ||
50 | * suspend/resume cycle. | ||
51 | */ | ||
52 | static unsigned long s3c2410_clk_regs[] __initdata = { | ||
53 | LOCKTIME, | ||
54 | MPLLCON, | ||
55 | UPLLCON, | ||
56 | CLKCON, | ||
57 | CLKSLOW, | ||
58 | CLKDIVN, | ||
59 | CAMDIVN, | ||
60 | }; | ||
61 | |||
62 | static int s3c2410_clk_suspend(void) | ||
63 | { | ||
64 | samsung_clk_save(reg_base, s3c2410_save, | ||
65 | ARRAY_SIZE(s3c2410_clk_regs)); | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static void s3c2410_clk_resume(void) | ||
71 | { | ||
72 | samsung_clk_restore(reg_base, s3c2410_save, | ||
73 | ARRAY_SIZE(s3c2410_clk_regs)); | ||
74 | } | ||
75 | |||
76 | static struct syscore_ops s3c2410_clk_syscore_ops = { | ||
77 | .suspend = s3c2410_clk_suspend, | ||
78 | .resume = s3c2410_clk_resume, | ||
79 | }; | ||
80 | |||
81 | static void s3c2410_clk_sleep_init(void) | ||
82 | { | ||
83 | s3c2410_save = samsung_clk_alloc_reg_dump(s3c2410_clk_regs, | ||
84 | ARRAY_SIZE(s3c2410_clk_regs)); | ||
85 | if (!s3c2410_save) { | ||
86 | pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", | ||
87 | __func__); | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | register_syscore_ops(&s3c2410_clk_syscore_ops); | ||
92 | return; | ||
93 | } | ||
94 | #else | ||
95 | static void s3c2410_clk_sleep_init(void) {} | ||
96 | #endif | ||
97 | |||
98 | PNAME(fclk_p) = { "mpll", "div_slow" }; | ||
99 | |||
100 | struct samsung_mux_clock s3c2410_common_muxes[] __initdata = { | ||
101 | MUX(FCLK, "fclk", fclk_p, CLKSLOW, 4, 1), | ||
102 | }; | ||
103 | |||
104 | static struct clk_div_table divslow_d[] = { | ||
105 | { .val = 0, .div = 1 }, | ||
106 | { .val = 1, .div = 2 }, | ||
107 | { .val = 2, .div = 4 }, | ||
108 | { .val = 3, .div = 6 }, | ||
109 | { .val = 4, .div = 8 }, | ||
110 | { .val = 5, .div = 10 }, | ||
111 | { .val = 6, .div = 12 }, | ||
112 | { .val = 7, .div = 14 }, | ||
113 | { /* sentinel */ }, | ||
114 | }; | ||
115 | |||
116 | struct samsung_div_clock s3c2410_common_dividers[] __initdata = { | ||
117 | DIV_T(0, "div_slow", "xti", CLKSLOW, 0, 3, divslow_d), | ||
118 | DIV(PCLK, "pclk", "hclk", CLKDIVN, 0, 1), | ||
119 | }; | ||
120 | |||
121 | struct samsung_gate_clock s3c2410_common_gates[] __initdata = { | ||
122 | GATE(PCLK_SPI, "spi", "pclk", CLKCON, 18, 0, 0), | ||
123 | GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 17, 0, 0), | ||
124 | GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 16, 0, 0), | ||
125 | GATE(PCLK_ADC, "adc", "pclk", CLKCON, 15, 0, 0), | ||
126 | GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 14, 0, 0), | ||
127 | GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 13, CLK_IGNORE_UNUSED, 0), | ||
128 | GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 12, 0, 0), | ||
129 | GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 11, 0, 0), | ||
130 | GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 10, 0, 0), | ||
131 | GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 9, 0, 0), | ||
132 | GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 8, 0, 0), | ||
133 | GATE(HCLK_USBD, "usb-device", "hclk", CLKCON, 7, 0, 0), | ||
134 | GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0), | ||
135 | GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0), | ||
136 | GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0), | ||
137 | }; | ||
138 | |||
139 | /* should be added _after_ the soc-specific clocks are created */ | ||
140 | struct samsung_clock_alias s3c2410_common_aliases[] __initdata = { | ||
141 | ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"), | ||
142 | ALIAS(PCLK_ADC, NULL, "adc"), | ||
143 | ALIAS(PCLK_RTC, NULL, "rtc"), | ||
144 | ALIAS(PCLK_PWM, NULL, "timers"), | ||
145 | ALIAS(HCLK_LCD, NULL, "lcd"), | ||
146 | ALIAS(HCLK_USBD, NULL, "usb-device"), | ||
147 | ALIAS(HCLK_USBH, NULL, "usb-host"), | ||
148 | ALIAS(UCLK, NULL, "usb-bus-host"), | ||
149 | ALIAS(UCLK, NULL, "usb-bus-gadget"), | ||
150 | ALIAS(ARMCLK, NULL, "armclk"), | ||
151 | ALIAS(UCLK, NULL, "uclk"), | ||
152 | ALIAS(HCLK, NULL, "hclk"), | ||
153 | ALIAS(MPLL, NULL, "mpll"), | ||
154 | ALIAS(FCLK, NULL, "fclk"), | ||
155 | }; | ||
156 | |||
157 | /* S3C2410 specific clocks */ | ||
158 | |||
159 | static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = { | ||
160 | /* sorted in descending order */ | ||
161 | /* 2410A extras */ | ||
162 | PLL_35XX_RATE(270000000, 127, 1, 1), | ||
163 | PLL_35XX_RATE(268000000, 126, 1, 1), | ||
164 | PLL_35XX_RATE(266000000, 125, 1, 1), | ||
165 | PLL_35XX_RATE(226000000, 105, 1, 1), | ||
166 | PLL_35XX_RATE(210000000, 132, 2, 1), | ||
167 | /* 2410 common */ | ||
168 | PLL_35XX_RATE(203000000, 161, 3, 1), | ||
169 | PLL_35XX_RATE(192000000, 88, 1, 1), | ||
170 | PLL_35XX_RATE(186000000, 85, 1, 1), | ||
171 | PLL_35XX_RATE(180000000, 82, 1, 1), | ||
172 | PLL_35XX_RATE(170000000, 77, 1, 1), | ||
173 | PLL_35XX_RATE(158000000, 71, 1, 1), | ||
174 | PLL_35XX_RATE(152000000, 68, 1, 1), | ||
175 | PLL_35XX_RATE(147000000, 90, 2, 1), | ||
176 | PLL_35XX_RATE(135000000, 82, 2, 1), | ||
177 | PLL_35XX_RATE(124000000, 116, 1, 2), | ||
178 | PLL_35XX_RATE(118000000, 150, 2, 2), | ||
179 | PLL_35XX_RATE(113000000, 105, 1, 2), | ||
180 | PLL_35XX_RATE(101000000, 127, 2, 2), | ||
181 | PLL_35XX_RATE(90000000, 112, 2, 2), | ||
182 | PLL_35XX_RATE(85000000, 105, 2, 2), | ||
183 | PLL_35XX_RATE(79000000, 71, 1, 2), | ||
184 | PLL_35XX_RATE(68000000, 82, 2, 2), | ||
185 | PLL_35XX_RATE(56000000, 142, 2, 3), | ||
186 | PLL_35XX_RATE(48000000, 120, 2, 3), | ||
187 | PLL_35XX_RATE(51000000, 161, 3, 3), | ||
188 | PLL_35XX_RATE(45000000, 82, 1, 3), | ||
189 | PLL_35XX_RATE(34000000, 82, 2, 3), | ||
190 | { /* sentinel */ }, | ||
191 | }; | ||
192 | |||
193 | static struct samsung_pll_clock s3c2410_plls[] __initdata = { | ||
194 | [mpll] = PLL(pll_s3c2410_mpll, MPLL, "mpll", "xti", | ||
195 | LOCKTIME, MPLLCON, NULL), | ||
196 | [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti", | ||
197 | LOCKTIME, UPLLCON, NULL), | ||
198 | }; | ||
199 | |||
200 | struct samsung_div_clock s3c2410_dividers[] __initdata = { | ||
201 | DIV(HCLK, "hclk", "mpll", CLKDIVN, 1, 1), | ||
202 | }; | ||
203 | |||
204 | struct samsung_fixed_factor_clock s3c2410_ffactor[] __initdata = { | ||
205 | /* | ||
206 | * armclk is directly supplied by the fclk, without | ||
207 | * switching possibility like on the s3c244x below. | ||
208 | */ | ||
209 | FFACTOR(ARMCLK, "armclk", "fclk", 1, 1, 0), | ||
210 | |||
211 | /* uclk is fed from the unmodified upll */ | ||
212 | FFACTOR(UCLK, "uclk", "upll", 1, 1, 0), | ||
213 | }; | ||
214 | |||
215 | struct samsung_clock_alias s3c2410_aliases[] __initdata = { | ||
216 | ALIAS(PCLK_UART0, "s3c2410-uart.0", "uart"), | ||
217 | ALIAS(PCLK_UART1, "s3c2410-uart.1", "uart"), | ||
218 | ALIAS(PCLK_UART2, "s3c2410-uart.2", "uart"), | ||
219 | ALIAS(PCLK_UART0, "s3c2410-uart.0", "clk_uart_baud0"), | ||
220 | ALIAS(PCLK_UART1, "s3c2410-uart.1", "clk_uart_baud0"), | ||
221 | ALIAS(PCLK_UART2, "s3c2410-uart.2", "clk_uart_baud0"), | ||
222 | ALIAS(UCLK, NULL, "clk_uart_baud1"), | ||
223 | }; | ||
224 | |||
225 | /* S3C244x specific clocks */ | ||
226 | |||
227 | static struct samsung_pll_rate_table pll_s3c244x_12mhz_tbl[] __initdata = { | ||
228 | /* sorted in descending order */ | ||
229 | PLL_35XX_RATE(400000000, 0x5c, 1, 1), | ||
230 | PLL_35XX_RATE(390000000, 0x7a, 2, 1), | ||
231 | PLL_35XX_RATE(380000000, 0x57, 1, 1), | ||
232 | PLL_35XX_RATE(370000000, 0xb1, 4, 1), | ||
233 | PLL_35XX_RATE(360000000, 0x70, 2, 1), | ||
234 | PLL_35XX_RATE(350000000, 0xa7, 4, 1), | ||
235 | PLL_35XX_RATE(340000000, 0x4d, 1, 1), | ||
236 | PLL_35XX_RATE(330000000, 0x66, 2, 1), | ||
237 | PLL_35XX_RATE(320000000, 0x98, 4, 1), | ||
238 | PLL_35XX_RATE(310000000, 0x93, 4, 1), | ||
239 | PLL_35XX_RATE(300000000, 0x75, 3, 1), | ||
240 | PLL_35XX_RATE(240000000, 0x70, 1, 2), | ||
241 | PLL_35XX_RATE(230000000, 0x6b, 1, 2), | ||
242 | PLL_35XX_RATE(220000000, 0x66, 1, 2), | ||
243 | PLL_35XX_RATE(210000000, 0x84, 2, 2), | ||
244 | PLL_35XX_RATE(200000000, 0x5c, 1, 2), | ||
245 | PLL_35XX_RATE(190000000, 0x57, 1, 2), | ||
246 | PLL_35XX_RATE(180000000, 0x70, 2, 2), | ||
247 | PLL_35XX_RATE(170000000, 0x4d, 1, 2), | ||
248 | PLL_35XX_RATE(160000000, 0x98, 4, 2), | ||
249 | PLL_35XX_RATE(150000000, 0x75, 3, 2), | ||
250 | PLL_35XX_RATE(120000000, 0x70, 1, 3), | ||
251 | PLL_35XX_RATE(110000000, 0x66, 1, 3), | ||
252 | PLL_35XX_RATE(100000000, 0x5c, 1, 3), | ||
253 | PLL_35XX_RATE(90000000, 0x70, 2, 3), | ||
254 | PLL_35XX_RATE(80000000, 0x98, 4, 3), | ||
255 | PLL_35XX_RATE(75000000, 0x75, 3, 3), | ||
256 | { /* sentinel */ }, | ||
257 | }; | ||
258 | |||
259 | static struct samsung_pll_clock s3c244x_common_plls[] __initdata = { | ||
260 | [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", | ||
261 | LOCKTIME, MPLLCON, NULL), | ||
262 | [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti", | ||
263 | LOCKTIME, UPLLCON, NULL), | ||
264 | }; | ||
265 | |||
266 | PNAME(hclk_p) = { "fclk", "div_hclk_2", "div_hclk_4", "div_hclk_3" }; | ||
267 | PNAME(armclk_p) = { "fclk", "hclk" }; | ||
268 | |||
269 | struct samsung_mux_clock s3c244x_common_muxes[] __initdata = { | ||
270 | MUX(HCLK, "hclk", hclk_p, CLKDIVN, 1, 2), | ||
271 | MUX(ARMCLK, "armclk", armclk_p, CAMDIVN, 12, 1), | ||
272 | }; | ||
273 | |||
274 | struct samsung_fixed_factor_clock s3c244x_common_ffactor[] __initdata = { | ||
275 | FFACTOR(0, "div_hclk_2", "fclk", 1, 2, 0), | ||
276 | FFACTOR(0, "ff_cam", "div_cam", 2, 1, CLK_SET_RATE_PARENT), | ||
277 | }; | ||
278 | |||
279 | static struct clk_div_table div_hclk_4_d[] = { | ||
280 | { .val = 0, .div = 4 }, | ||
281 | { .val = 1, .div = 8 }, | ||
282 | { /* sentinel */ }, | ||
283 | }; | ||
284 | |||
285 | static struct clk_div_table div_hclk_3_d[] = { | ||
286 | { .val = 0, .div = 3 }, | ||
287 | { .val = 1, .div = 6 }, | ||
288 | { /* sentinel */ }, | ||
289 | }; | ||
290 | |||
291 | struct samsung_div_clock s3c244x_common_dividers[] __initdata = { | ||
292 | DIV(UCLK, "uclk", "upll", CLKDIVN, 3, 1), | ||
293 | DIV(0, "div_hclk", "fclk", CLKDIVN, 1, 1), | ||
294 | DIV_T(0, "div_hclk_4", "fclk", CAMDIVN, 9, 1, div_hclk_4_d), | ||
295 | DIV_T(0, "div_hclk_3", "fclk", CAMDIVN, 8, 1, div_hclk_3_d), | ||
296 | DIV(0, "div_cam", "upll", CAMDIVN, 0, 3), | ||
297 | }; | ||
298 | |||
299 | struct samsung_gate_clock s3c244x_common_gates[] __initdata = { | ||
300 | GATE(HCLK_CAM, "cam", "hclk", CLKCON, 19, 0, 0), | ||
301 | }; | ||
302 | |||
303 | struct samsung_clock_alias s3c244x_common_aliases[] __initdata = { | ||
304 | ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"), | ||
305 | ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"), | ||
306 | ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"), | ||
307 | ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"), | ||
308 | ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"), | ||
309 | ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"), | ||
310 | ALIAS(HCLK_CAM, NULL, "camif"), | ||
311 | ALIAS(CAMIF, NULL, "camif-upll"), | ||
312 | }; | ||
313 | |||
314 | /* S3C2440 specific clocks */ | ||
315 | |||
316 | PNAME(s3c2440_camif_p) = { "upll", "ff_cam" }; | ||
317 | |||
318 | struct samsung_mux_clock s3c2440_muxes[] __initdata = { | ||
319 | MUX(CAMIF, "camif", s3c2440_camif_p, CAMDIVN, 4, 1), | ||
320 | }; | ||
321 | |||
322 | struct samsung_gate_clock s3c2440_gates[] __initdata = { | ||
323 | GATE(PCLK_AC97, "ac97", "pclk", CLKCON, 20, 0, 0), | ||
324 | }; | ||
325 | |||
326 | /* S3C2442 specific clocks */ | ||
327 | |||
328 | struct samsung_fixed_factor_clock s3c2442_ffactor[] __initdata = { | ||
329 | FFACTOR(0, "upll_3", "upll", 1, 3, 0), | ||
330 | }; | ||
331 | |||
332 | PNAME(s3c2442_camif_p) = { "upll", "ff_cam", "upll", "upll_3" }; | ||
333 | |||
334 | struct samsung_mux_clock s3c2442_muxes[] __initdata = { | ||
335 | MUX(CAMIF, "camif", s3c2442_camif_p, CAMDIVN, 4, 2), | ||
336 | }; | ||
337 | |||
338 | /* | ||
339 | * fixed rate clocks generated outside the soc | ||
340 | * Only necessary until the devicetree-move is complete | ||
341 | */ | ||
342 | #define XTI 1 | ||
343 | struct samsung_fixed_rate_clock s3c2410_common_frate_clks[] __initdata = { | ||
344 | FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0), | ||
345 | }; | ||
346 | |||
347 | static void __init s3c2410_common_clk_register_fixed_ext(unsigned long xti_f) | ||
348 | { | ||
349 | struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal"); | ||
350 | |||
351 | s3c2410_common_frate_clks[0].fixed_rate = xti_f; | ||
352 | samsung_clk_register_fixed_rate(s3c2410_common_frate_clks, | ||
353 | ARRAY_SIZE(s3c2410_common_frate_clks)); | ||
354 | |||
355 | samsung_clk_register_alias(&xti_alias, 1); | ||
356 | } | ||
357 | |||
358 | void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f, | ||
359 | int current_soc, | ||
360 | void __iomem *base) | ||
361 | { | ||
362 | reg_base = base; | ||
363 | |||
364 | if (np) { | ||
365 | reg_base = of_iomap(np, 0); | ||
366 | if (!reg_base) | ||
367 | panic("%s: failed to map registers\n", __func__); | ||
368 | } | ||
369 | |||
370 | samsung_clk_init(np, reg_base, NR_CLKS); | ||
371 | |||
372 | /* Register external clocks only in non-dt cases */ | ||
373 | if (!np) | ||
374 | s3c2410_common_clk_register_fixed_ext(xti_f); | ||
375 | |||
376 | if (current_soc == 2410) { | ||
377 | if (_get_rate("xti") == 12 * MHZ) { | ||
378 | s3c2410_plls[mpll].rate_table = pll_s3c2410_12mhz_tbl; | ||
379 | s3c2410_plls[upll].rate_table = pll_s3c2410_12mhz_tbl; | ||
380 | } | ||
381 | |||
382 | /* Register PLLs. */ | ||
383 | samsung_clk_register_pll(s3c2410_plls, | ||
384 | ARRAY_SIZE(s3c2410_plls), reg_base); | ||
385 | |||
386 | } else { /* S3C2440, S3C2442 */ | ||
387 | if (_get_rate("xti") == 12 * MHZ) { | ||
388 | /* | ||
389 | * plls follow different calculation schemes, with the | ||
390 | * upll following the same scheme as the s3c2410 plls | ||
391 | */ | ||
392 | s3c244x_common_plls[mpll].rate_table = | ||
393 | pll_s3c244x_12mhz_tbl; | ||
394 | s3c244x_common_plls[upll].rate_table = | ||
395 | pll_s3c2410_12mhz_tbl; | ||
396 | } | ||
397 | |||
398 | /* Register PLLs. */ | ||
399 | samsung_clk_register_pll(s3c244x_common_plls, | ||
400 | ARRAY_SIZE(s3c244x_common_plls), reg_base); | ||
401 | } | ||
402 | |||
403 | /* Register common internal clocks. */ | ||
404 | samsung_clk_register_mux(s3c2410_common_muxes, | ||
405 | ARRAY_SIZE(s3c2410_common_muxes)); | ||
406 | samsung_clk_register_div(s3c2410_common_dividers, | ||
407 | ARRAY_SIZE(s3c2410_common_dividers)); | ||
408 | samsung_clk_register_gate(s3c2410_common_gates, | ||
409 | ARRAY_SIZE(s3c2410_common_gates)); | ||
410 | |||
411 | if (current_soc == S3C2440 || current_soc == S3C2442) { | ||
412 | samsung_clk_register_div(s3c244x_common_dividers, | ||
413 | ARRAY_SIZE(s3c244x_common_dividers)); | ||
414 | samsung_clk_register_gate(s3c244x_common_gates, | ||
415 | ARRAY_SIZE(s3c244x_common_gates)); | ||
416 | samsung_clk_register_mux(s3c244x_common_muxes, | ||
417 | ARRAY_SIZE(s3c244x_common_muxes)); | ||
418 | samsung_clk_register_fixed_factor(s3c244x_common_ffactor, | ||
419 | ARRAY_SIZE(s3c244x_common_ffactor)); | ||
420 | } | ||
421 | |||
422 | /* Register SoC-specific clocks. */ | ||
423 | switch (current_soc) { | ||
424 | case S3C2410: | ||
425 | samsung_clk_register_div(s3c2410_dividers, | ||
426 | ARRAY_SIZE(s3c2410_dividers)); | ||
427 | samsung_clk_register_fixed_factor(s3c2410_ffactor, | ||
428 | ARRAY_SIZE(s3c2410_ffactor)); | ||
429 | samsung_clk_register_alias(s3c2410_aliases, | ||
430 | ARRAY_SIZE(s3c2410_common_aliases)); | ||
431 | break; | ||
432 | case S3C2440: | ||
433 | samsung_clk_register_mux(s3c2440_muxes, | ||
434 | ARRAY_SIZE(s3c2440_muxes)); | ||
435 | samsung_clk_register_gate(s3c2440_gates, | ||
436 | ARRAY_SIZE(s3c2440_gates)); | ||
437 | break; | ||
438 | case S3C2442: | ||
439 | samsung_clk_register_mux(s3c2442_muxes, | ||
440 | ARRAY_SIZE(s3c2442_muxes)); | ||
441 | samsung_clk_register_fixed_factor(s3c2442_ffactor, | ||
442 | ARRAY_SIZE(s3c2442_ffactor)); | ||
443 | break; | ||
444 | } | ||
445 | |||
446 | /* | ||
447 | * Register common aliases at the end, as some of the aliased clocks | ||
448 | * are SoC specific. | ||
449 | */ | ||
450 | samsung_clk_register_alias(s3c2410_common_aliases, | ||
451 | ARRAY_SIZE(s3c2410_common_aliases)); | ||
452 | |||
453 | if (current_soc == S3C2440 || current_soc == S3C2442) { | ||
454 | samsung_clk_register_alias(s3c244x_common_aliases, | ||
455 | ARRAY_SIZE(s3c244x_common_aliases)); | ||
456 | } | ||
457 | |||
458 | s3c2410_clk_sleep_init(); | ||
459 | } | ||
460 | |||
461 | static void __init s3c2410_clk_init(struct device_node *np) | ||
462 | { | ||
463 | s3c2410_common_clk_init(np, 0, S3C2410, 0); | ||
464 | } | ||
465 | CLK_OF_DECLARE(s3c2410_clk, "samsung,s3c2410-clock", s3c2410_clk_init); | ||
466 | |||
467 | static void __init s3c2440_clk_init(struct device_node *np) | ||
468 | { | ||
469 | s3c2410_common_clk_init(np, 0, S3C2440, 0); | ||
470 | } | ||
471 | CLK_OF_DECLARE(s3c2440_clk, "samsung,s3c2440-clock", s3c2440_clk_init); | ||
472 | |||
473 | static void __init s3c2442_clk_init(struct device_node *np) | ||
474 | { | ||
475 | s3c2410_common_clk_init(np, 0, S3C2442, 0); | ||
476 | } | ||
477 | CLK_OF_DECLARE(s3c2442_clk, "samsung,s3c2442-clock", s3c2442_clk_init); | ||
diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c new file mode 100644 index 000000000000..0f11a07d5c01 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2412.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * Common Clock Framework support for S3C2412 and S3C2413. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk.h> | ||
12 | #include <linux/clkdev.h> | ||
13 | #include <linux/clk-provider.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/syscore_ops.h> | ||
17 | |||
18 | #include <dt-bindings/clock/s3c2412.h> | ||
19 | |||
20 | #include "clk.h" | ||
21 | #include "clk-pll.h" | ||
22 | |||
23 | #define LOCKTIME 0x00 | ||
24 | #define MPLLCON 0x04 | ||
25 | #define UPLLCON 0x08 | ||
26 | #define CLKCON 0x0c | ||
27 | #define CLKDIVN 0x14 | ||
28 | #define CLKSRC 0x1c | ||
29 | |||
30 | /* list of PLLs to be registered */ | ||
31 | enum s3c2412_plls { | ||
32 | mpll, upll, | ||
33 | }; | ||
34 | |||
35 | static void __iomem *reg_base; | ||
36 | |||
37 | #ifdef CONFIG_PM_SLEEP | ||
38 | static struct samsung_clk_reg_dump *s3c2412_save; | ||
39 | |||
40 | /* | ||
41 | * list of controller registers to be saved and restored during a | ||
42 | * suspend/resume cycle. | ||
43 | */ | ||
44 | static unsigned long s3c2412_clk_regs[] __initdata = { | ||
45 | LOCKTIME, | ||
46 | MPLLCON, | ||
47 | UPLLCON, | ||
48 | CLKCON, | ||
49 | CLKDIVN, | ||
50 | CLKSRC, | ||
51 | }; | ||
52 | |||
53 | static int s3c2412_clk_suspend(void) | ||
54 | { | ||
55 | samsung_clk_save(reg_base, s3c2412_save, | ||
56 | ARRAY_SIZE(s3c2412_clk_regs)); | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static void s3c2412_clk_resume(void) | ||
62 | { | ||
63 | samsung_clk_restore(reg_base, s3c2412_save, | ||
64 | ARRAY_SIZE(s3c2412_clk_regs)); | ||
65 | } | ||
66 | |||
67 | static struct syscore_ops s3c2412_clk_syscore_ops = { | ||
68 | .suspend = s3c2412_clk_suspend, | ||
69 | .resume = s3c2412_clk_resume, | ||
70 | }; | ||
71 | |||
72 | static void s3c2412_clk_sleep_init(void) | ||
73 | { | ||
74 | s3c2412_save = samsung_clk_alloc_reg_dump(s3c2412_clk_regs, | ||
75 | ARRAY_SIZE(s3c2412_clk_regs)); | ||
76 | if (!s3c2412_save) { | ||
77 | pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", | ||
78 | __func__); | ||
79 | return; | ||
80 | } | ||
81 | |||
82 | register_syscore_ops(&s3c2412_clk_syscore_ops); | ||
83 | return; | ||
84 | } | ||
85 | #else | ||
86 | static void s3c2412_clk_sleep_init(void) {} | ||
87 | #endif | ||
88 | |||
89 | static struct clk_div_table divxti_d[] = { | ||
90 | { .val = 0, .div = 1 }, | ||
91 | { .val = 1, .div = 2 }, | ||
92 | { .val = 2, .div = 4 }, | ||
93 | { .val = 3, .div = 6 }, | ||
94 | { .val = 4, .div = 8 }, | ||
95 | { .val = 5, .div = 10 }, | ||
96 | { .val = 6, .div = 12 }, | ||
97 | { .val = 7, .div = 14 }, | ||
98 | { /* sentinel */ }, | ||
99 | }; | ||
100 | |||
101 | struct samsung_div_clock s3c2412_dividers[] __initdata = { | ||
102 | DIV_T(0, "div_xti", "xti", CLKSRC, 0, 3, divxti_d), | ||
103 | DIV(0, "div_cam", "mux_cam", CLKDIVN, 16, 4), | ||
104 | DIV(0, "div_i2s", "mux_i2s", CLKDIVN, 12, 4), | ||
105 | DIV(0, "div_uart", "mux_uart", CLKDIVN, 8, 4), | ||
106 | DIV(0, "div_usb", "mux_usb", CLKDIVN, 6, 1), | ||
107 | DIV(0, "div_hclk_half", "hclk", CLKDIVN, 5, 1), | ||
108 | DIV(ARMDIV, "armdiv", "msysclk", CLKDIVN, 3, 1), | ||
109 | DIV(PCLK, "pclk", "hclk", CLKDIVN, 2, 1), | ||
110 | DIV(HCLK, "hclk", "armdiv", CLKDIVN, 0, 2), | ||
111 | }; | ||
112 | |||
113 | struct samsung_fixed_factor_clock s3c2412_ffactor[] __initdata = { | ||
114 | FFACTOR(0, "ff_hclk", "hclk", 2, 1, CLK_SET_RATE_PARENT), | ||
115 | }; | ||
116 | |||
117 | /* | ||
118 | * The first two use the OM[4] setting, which is not readable from | ||
119 | * software, so assume it is set to xti. | ||
120 | */ | ||
121 | PNAME(erefclk_p) = { "xti", "xti", "xti", "ext" }; | ||
122 | PNAME(urefclk_p) = { "xti", "xti", "xti", "ext" }; | ||
123 | |||
124 | PNAME(camclk_p) = { "usysclk", "hclk" }; | ||
125 | PNAME(usbclk_p) = { "usysclk", "hclk" }; | ||
126 | PNAME(i2sclk_p) = { "erefclk", "mpll" }; | ||
127 | PNAME(uartclk_p) = { "erefclk", "mpll" }; | ||
128 | PNAME(usysclk_p) = { "urefclk", "upll" }; | ||
129 | PNAME(msysclk_p) = { "mdivclk", "mpll" }; | ||
130 | PNAME(mdivclk_p) = { "xti", "div_xti" }; | ||
131 | PNAME(armclk_p) = { "armdiv", "hclk" }; | ||
132 | |||
133 | struct samsung_mux_clock s3c2412_muxes[] __initdata = { | ||
134 | MUX(0, "erefclk", erefclk_p, CLKSRC, 14, 2), | ||
135 | MUX(0, "urefclk", urefclk_p, CLKSRC, 12, 2), | ||
136 | MUX(0, "mux_cam", camclk_p, CLKSRC, 11, 1), | ||
137 | MUX(0, "mux_usb", usbclk_p, CLKSRC, 10, 1), | ||
138 | MUX(0, "mux_i2s", i2sclk_p, CLKSRC, 9, 1), | ||
139 | MUX(0, "mux_uart", uartclk_p, CLKSRC, 8, 1), | ||
140 | MUX(USYSCLK, "usysclk", usysclk_p, CLKSRC, 5, 1), | ||
141 | MUX(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1), | ||
142 | MUX(MDIVCLK, "mdivclk", mdivclk_p, CLKSRC, 3, 1), | ||
143 | MUX(ARMCLK, "armclk", armclk_p, CLKDIVN, 4, 1), | ||
144 | }; | ||
145 | |||
146 | static struct samsung_pll_clock s3c2412_plls[] __initdata = { | ||
147 | [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti", | ||
148 | LOCKTIME, MPLLCON, NULL), | ||
149 | [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "urefclk", | ||
150 | LOCKTIME, UPLLCON, NULL), | ||
151 | }; | ||
152 | |||
153 | struct samsung_gate_clock s3c2412_gates[] __initdata = { | ||
154 | GATE(PCLK_WDT, "wdt", "pclk", CLKCON, 28, 0, 0), | ||
155 | GATE(PCLK_SPI, "spi", "pclk", CLKCON, 27, 0, 0), | ||
156 | GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 26, 0, 0), | ||
157 | GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 25, 0, 0), | ||
158 | GATE(PCLK_ADC, "adc", "pclk", CLKCON, 24, 0, 0), | ||
159 | GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 23, 0, 0), | ||
160 | GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 22, CLK_IGNORE_UNUSED, 0), | ||
161 | GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 21, 0, 0), | ||
162 | GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 20, 0, 0), | ||
163 | GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 19, 0, 0), | ||
164 | GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 18, 0, 0), | ||
165 | GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 17, 0, 0), | ||
166 | GATE(PCLK_USBD, "usb-device", "pclk", CLKCON, 16, 0, 0), | ||
167 | GATE(SCLK_CAM, "sclk_cam", "div_cam", CLKCON, 15, 0, 0), | ||
168 | GATE(SCLK_UART, "sclk_uart", "div_uart", CLKCON, 14, 0, 0), | ||
169 | GATE(SCLK_I2S, "sclk_i2s", "div_i2s", CLKCON, 13, 0, 0), | ||
170 | GATE(SCLK_USBH, "sclk_usbh", "div_usb", CLKCON, 12, 0, 0), | ||
171 | GATE(SCLK_USBD, "sclk_usbd", "div_usb", CLKCON, 11, 0, 0), | ||
172 | GATE(HCLK_HALF, "hclk_half", "div_hclk_half", CLKCON, 10, CLK_IGNORE_UNUSED, 0), | ||
173 | GATE(HCLK_X2, "hclkx2", "ff_hclk", CLKCON, 9, CLK_IGNORE_UNUSED, 0), | ||
174 | GATE(HCLK_SDRAM, "sdram", "hclk", CLKCON, 8, CLK_IGNORE_UNUSED, 0), | ||
175 | GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0), | ||
176 | GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0), | ||
177 | GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0), | ||
178 | GATE(HCLK_DMA3, "dma3", "hclk", CLKCON, 3, CLK_IGNORE_UNUSED, 0), | ||
179 | GATE(HCLK_DMA2, "dma2", "hclk", CLKCON, 2, CLK_IGNORE_UNUSED, 0), | ||
180 | GATE(HCLK_DMA1, "dma1", "hclk", CLKCON, 1, CLK_IGNORE_UNUSED, 0), | ||
181 | GATE(HCLK_DMA0, "dma0", "hclk", CLKCON, 0, CLK_IGNORE_UNUSED, 0), | ||
182 | }; | ||
183 | |||
184 | struct samsung_clock_alias s3c2412_aliases[] __initdata = { | ||
185 | ALIAS(PCLK_UART0, "s3c2412-uart.0", "uart"), | ||
186 | ALIAS(PCLK_UART1, "s3c2412-uart.1", "uart"), | ||
187 | ALIAS(PCLK_UART2, "s3c2412-uart.2", "uart"), | ||
188 | ALIAS(PCLK_UART0, "s3c2412-uart.0", "clk_uart_baud2"), | ||
189 | ALIAS(PCLK_UART1, "s3c2412-uart.1", "clk_uart_baud2"), | ||
190 | ALIAS(PCLK_UART2, "s3c2412-uart.2", "clk_uart_baud2"), | ||
191 | ALIAS(SCLK_UART, NULL, "clk_uart_baud3"), | ||
192 | ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"), | ||
193 | ALIAS(PCLK_ADC, NULL, "adc"), | ||
194 | ALIAS(PCLK_RTC, NULL, "rtc"), | ||
195 | ALIAS(PCLK_PWM, NULL, "timers"), | ||
196 | ALIAS(HCLK_LCD, NULL, "lcd"), | ||
197 | ALIAS(PCLK_USBD, NULL, "usb-device"), | ||
198 | ALIAS(SCLK_USBD, NULL, "usb-bus-gadget"), | ||
199 | ALIAS(HCLK_USBH, NULL, "usb-host"), | ||
200 | ALIAS(SCLK_USBH, NULL, "usb-bus-host"), | ||
201 | ALIAS(ARMCLK, NULL, "armclk"), | ||
202 | ALIAS(HCLK, NULL, "hclk"), | ||
203 | ALIAS(MPLL, NULL, "mpll"), | ||
204 | ALIAS(MSYSCLK, NULL, "fclk"), | ||
205 | }; | ||
206 | |||
207 | /* | ||
208 | * fixed rate clocks generated outside the soc | ||
209 | * Only necessary until the devicetree-move is complete | ||
210 | */ | ||
211 | #define XTI 1 | ||
212 | struct samsung_fixed_rate_clock s3c2412_common_frate_clks[] __initdata = { | ||
213 | FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0), | ||
214 | FRATE(0, "ext", NULL, CLK_IS_ROOT, 0), | ||
215 | }; | ||
216 | |||
217 | static void __init s3c2412_common_clk_register_fixed_ext(unsigned long xti_f, | ||
218 | unsigned long ext_f) | ||
219 | { | ||
220 | /* xtal alias is necessary for the current cpufreq driver */ | ||
221 | struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal"); | ||
222 | |||
223 | s3c2412_common_frate_clks[0].fixed_rate = xti_f; | ||
224 | s3c2412_common_frate_clks[1].fixed_rate = ext_f; | ||
225 | samsung_clk_register_fixed_rate(s3c2412_common_frate_clks, | ||
226 | ARRAY_SIZE(s3c2412_common_frate_clks)); | ||
227 | |||
228 | samsung_clk_register_alias(&xti_alias, 1); | ||
229 | } | ||
230 | |||
231 | void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f, | ||
232 | unsigned long ext_f, void __iomem *base) | ||
233 | { | ||
234 | reg_base = base; | ||
235 | |||
236 | if (np) { | ||
237 | reg_base = of_iomap(np, 0); | ||
238 | if (!reg_base) | ||
239 | panic("%s: failed to map registers\n", __func__); | ||
240 | } | ||
241 | |||
242 | samsung_clk_init(np, reg_base, NR_CLKS); | ||
243 | |||
244 | /* Register external clocks only in non-dt cases */ | ||
245 | if (!np) | ||
246 | s3c2412_common_clk_register_fixed_ext(xti_f, ext_f); | ||
247 | |||
248 | /* Register PLLs. */ | ||
249 | samsung_clk_register_pll(s3c2412_plls, ARRAY_SIZE(s3c2412_plls), | ||
250 | reg_base); | ||
251 | |||
252 | /* Register common internal clocks. */ | ||
253 | samsung_clk_register_mux(s3c2412_muxes, ARRAY_SIZE(s3c2412_muxes)); | ||
254 | samsung_clk_register_div(s3c2412_dividers, | ||
255 | ARRAY_SIZE(s3c2412_dividers)); | ||
256 | samsung_clk_register_gate(s3c2412_gates, ARRAY_SIZE(s3c2412_gates)); | ||
257 | samsung_clk_register_fixed_factor(s3c2412_ffactor, | ||
258 | ARRAY_SIZE(s3c2412_ffactor)); | ||
259 | samsung_clk_register_alias(s3c2412_aliases, | ||
260 | ARRAY_SIZE(s3c2412_aliases)); | ||
261 | |||
262 | s3c2412_clk_sleep_init(); | ||
263 | } | ||
264 | |||
265 | static void __init s3c2412_clk_init(struct device_node *np) | ||
266 | { | ||
267 | s3c2412_common_clk_init(np, 0, 0, 0); | ||
268 | } | ||
269 | CLK_OF_DECLARE(s3c2412_clk, "samsung,s3c2412-clock", s3c2412_clk_init); | ||
diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c new file mode 100644 index 000000000000..8e4f4517e95c --- /dev/null +++ b/drivers/clk/samsung/clk-s3c2443.c | |||
@@ -0,0 +1,462 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * Common Clock Framework support for S3C2443 and following SoCs. | ||
9 | */ | ||
10 | |||
11 | #include <linux/clk.h> | ||
12 | #include <linux/clkdev.h> | ||
13 | #include <linux/clk-provider.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/syscore_ops.h> | ||
17 | |||
18 | #include <dt-bindings/clock/s3c2443.h> | ||
19 | |||
20 | #include "clk.h" | ||
21 | #include "clk-pll.h" | ||
22 | |||
23 | /* S3C2416 clock controller register offsets */ | ||
24 | #define LOCKCON0 0x00 | ||
25 | #define LOCKCON1 0x04 | ||
26 | #define MPLLCON 0x10 | ||
27 | #define EPLLCON 0x18 | ||
28 | #define EPLLCON_K 0x1C | ||
29 | #define CLKSRC 0x20 | ||
30 | #define CLKDIV0 0x24 | ||
31 | #define CLKDIV1 0x28 | ||
32 | #define CLKDIV2 0x2C | ||
33 | #define HCLKCON 0x30 | ||
34 | #define PCLKCON 0x34 | ||
35 | #define SCLKCON 0x38 | ||
36 | |||
37 | /* the soc types */ | ||
38 | enum supported_socs { | ||
39 | S3C2416, | ||
40 | S3C2443, | ||
41 | S3C2450, | ||
42 | }; | ||
43 | |||
44 | /* list of PLLs to be registered */ | ||
45 | enum s3c2443_plls { | ||
46 | mpll, epll, | ||
47 | }; | ||
48 | |||
49 | static void __iomem *reg_base; | ||
50 | |||
51 | #ifdef CONFIG_PM_SLEEP | ||
52 | static struct samsung_clk_reg_dump *s3c2443_save; | ||
53 | |||
54 | /* | ||
55 | * list of controller registers to be saved and restored during a | ||
56 | * suspend/resume cycle. | ||
57 | */ | ||
58 | static unsigned long s3c2443_clk_regs[] __initdata = { | ||
59 | LOCKCON0, | ||
60 | LOCKCON1, | ||
61 | MPLLCON, | ||
62 | EPLLCON, | ||
63 | EPLLCON_K, | ||
64 | CLKSRC, | ||
65 | CLKDIV0, | ||
66 | CLKDIV1, | ||
67 | CLKDIV2, | ||
68 | PCLKCON, | ||
69 | HCLKCON, | ||
70 | SCLKCON, | ||
71 | }; | ||
72 | |||
73 | static int s3c2443_clk_suspend(void) | ||
74 | { | ||
75 | samsung_clk_save(reg_base, s3c2443_save, | ||
76 | ARRAY_SIZE(s3c2443_clk_regs)); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static void s3c2443_clk_resume(void) | ||
82 | { | ||
83 | samsung_clk_restore(reg_base, s3c2443_save, | ||
84 | ARRAY_SIZE(s3c2443_clk_regs)); | ||
85 | } | ||
86 | |||
87 | static struct syscore_ops s3c2443_clk_syscore_ops = { | ||
88 | .suspend = s3c2443_clk_suspend, | ||
89 | .resume = s3c2443_clk_resume, | ||
90 | }; | ||
91 | |||
92 | static void s3c2443_clk_sleep_init(void) | ||
93 | { | ||
94 | s3c2443_save = samsung_clk_alloc_reg_dump(s3c2443_clk_regs, | ||
95 | ARRAY_SIZE(s3c2443_clk_regs)); | ||
96 | if (!s3c2443_save) { | ||
97 | pr_warn("%s: failed to allocate sleep save data, no sleep support!\n", | ||
98 | __func__); | ||
99 | return; | ||
100 | } | ||
101 | |||
102 | register_syscore_ops(&s3c2443_clk_syscore_ops); | ||
103 | return; | ||
104 | } | ||
105 | #else | ||
106 | static void s3c2443_clk_sleep_init(void) {} | ||
107 | #endif | ||
108 | |||
109 | PNAME(epllref_p) = { "mpllref", "mpllref", "xti", "ext" }; | ||
110 | PNAME(esysclk_p) = { "epllref", "epll" }; | ||
111 | PNAME(mpllref_p) = { "xti", "mdivclk" }; | ||
112 | PNAME(msysclk_p) = { "mpllref", "mpll" }; | ||
113 | PNAME(armclk_p) = { "armdiv" , "hclk" }; | ||
114 | PNAME(i2s0_p) = { "div_i2s0", "ext_i2s", "epllref", "epllref" }; | ||
115 | |||
116 | struct samsung_mux_clock s3c2443_common_muxes[] __initdata = { | ||
117 | MUX(0, "epllref", epllref_p, CLKSRC, 7, 2), | ||
118 | MUX(ESYSCLK, "esysclk", esysclk_p, CLKSRC, 6, 1), | ||
119 | MUX(0, "mpllref", mpllref_p, CLKSRC, 3, 1), | ||
120 | MUX_A(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1, "msysclk"), | ||
121 | MUX_A(ARMCLK, "armclk", armclk_p, CLKDIV0, 13, 1, "armclk"), | ||
122 | MUX(0, "mux_i2s0", i2s0_p, CLKSRC, 14, 2), | ||
123 | }; | ||
124 | |||
125 | static struct clk_div_table hclk_d[] = { | ||
126 | { .val = 0, .div = 1 }, | ||
127 | { .val = 1, .div = 2 }, | ||
128 | { .val = 3, .div = 4 }, | ||
129 | { /* sentinel */ }, | ||
130 | }; | ||
131 | |||
132 | static struct clk_div_table mdivclk_d[] = { | ||
133 | { .val = 0, .div = 1 }, | ||
134 | { .val = 1, .div = 3 }, | ||
135 | { .val = 2, .div = 5 }, | ||
136 | { .val = 3, .div = 7 }, | ||
137 | { .val = 4, .div = 9 }, | ||
138 | { .val = 5, .div = 11 }, | ||
139 | { .val = 6, .div = 13 }, | ||
140 | { .val = 7, .div = 15 }, | ||
141 | { /* sentinel */ }, | ||
142 | }; | ||
143 | |||
144 | struct samsung_div_clock s3c2443_common_dividers[] __initdata = { | ||
145 | DIV_T(0, "mdivclk", "xti", CLKDIV0, 6, 3, mdivclk_d), | ||
146 | DIV(0, "prediv", "msysclk", CLKDIV0, 4, 2), | ||
147 | DIV_T(HCLK, "hclk", "prediv", CLKDIV0, 0, 2, hclk_d), | ||
148 | DIV(PCLK, "pclk", "hclk", CLKDIV0, 2, 1), | ||
149 | DIV(0, "div_hsspi0_epll", "esysclk", CLKDIV1, 24, 2), | ||
150 | DIV(0, "div_fimd", "esysclk", CLKDIV1, 16, 8), | ||
151 | DIV(0, "div_i2s0", "esysclk", CLKDIV1, 12, 4), | ||
152 | DIV(0, "div_uart", "esysclk", CLKDIV1, 8, 4), | ||
153 | DIV(0, "div_hsmmc1", "esysclk", CLKDIV1, 6, 2), | ||
154 | DIV(0, "div_usbhost", "esysclk", CLKDIV1, 4, 2), | ||
155 | }; | ||
156 | |||
157 | struct samsung_gate_clock s3c2443_common_gates[] __initdata = { | ||
158 | GATE(SCLK_HSMMC_EXT, "sclk_hsmmcext", "ext", SCLKCON, 13, 0, 0), | ||
159 | GATE(SCLK_HSMMC1, "sclk_hsmmc1", "div_hsmmc1", SCLKCON, 12, 0, 0), | ||
160 | GATE(SCLK_FIMD, "sclk_fimd", "div_fimd", SCLKCON, 10, 0, 0), | ||
161 | GATE(SCLK_I2S0, "sclk_i2s0", "mux_i2s0", SCLKCON, 9, 0, 0), | ||
162 | GATE(SCLK_UART, "sclk_uart", "div_uart", SCLKCON, 8, 0, 0), | ||
163 | GATE(SCLK_USBH, "sclk_usbhost", "div_usbhost", SCLKCON, 1, 0, 0), | ||
164 | GATE(HCLK_DRAM, "dram", "hclk", HCLKCON, 19, CLK_IGNORE_UNUSED, 0), | ||
165 | GATE(HCLK_SSMC, "ssmc", "hclk", HCLKCON, 18, CLK_IGNORE_UNUSED, 0), | ||
166 | GATE(HCLK_HSMMC1, "hsmmc1", "hclk", HCLKCON, 16, 0, 0), | ||
167 | GATE(HCLK_USBD, "usb-device", "hclk", HCLKCON, 12, 0, 0), | ||
168 | GATE(HCLK_USBH, "usb-host", "hclk", HCLKCON, 11, 0, 0), | ||
169 | GATE(HCLK_LCD, "lcd", "hclk", HCLKCON, 9, 0, 0), | ||
170 | GATE(HCLK_DMA5, "dma5", "hclk", HCLKCON, 5, CLK_IGNORE_UNUSED, 0), | ||
171 | GATE(HCLK_DMA4, "dma4", "hclk", HCLKCON, 4, CLK_IGNORE_UNUSED, 0), | ||
172 | GATE(HCLK_DMA3, "dma3", "hclk", HCLKCON, 3, CLK_IGNORE_UNUSED, 0), | ||
173 | GATE(HCLK_DMA2, "dma2", "hclk", HCLKCON, 2, CLK_IGNORE_UNUSED, 0), | ||
174 | GATE(HCLK_DMA1, "dma1", "hclk", HCLKCON, 1, CLK_IGNORE_UNUSED, 0), | ||
175 | GATE(HCLK_DMA0, "dma0", "hclk", HCLKCON, 0, CLK_IGNORE_UNUSED, 0), | ||
176 | GATE(PCLK_GPIO, "gpio", "pclk", PCLKCON, 13, CLK_IGNORE_UNUSED, 0), | ||
177 | GATE(PCLK_RTC, "rtc", "pclk", PCLKCON, 12, 0, 0), | ||
178 | GATE(PCLK_WDT, "wdt", "pclk", PCLKCON, 11, 0, 0), | ||
179 | GATE(PCLK_PWM, "pwm", "pclk", PCLKCON, 10, 0, 0), | ||
180 | GATE(PCLK_I2S0, "i2s0", "pclk", PCLKCON, 9, 0, 0), | ||
181 | GATE(PCLK_AC97, "ac97", "pclk", PCLKCON, 8, 0, 0), | ||
182 | GATE(PCLK_ADC, "adc", "pclk", PCLKCON, 7, 0, 0), | ||
183 | GATE(PCLK_SPI0, "spi0", "pclk", PCLKCON, 6, 0, 0), | ||
184 | GATE(PCLK_I2C0, "i2c0", "pclk", PCLKCON, 4, 0, 0), | ||
185 | GATE(PCLK_UART3, "uart3", "pclk", PCLKCON, 3, 0, 0), | ||
186 | GATE(PCLK_UART2, "uart2", "pclk", PCLKCON, 2, 0, 0), | ||
187 | GATE(PCLK_UART1, "uart1", "pclk", PCLKCON, 1, 0, 0), | ||
188 | GATE(PCLK_UART0, "uart0", "pclk", PCLKCON, 0, 0, 0), | ||
189 | }; | ||
190 | |||
191 | struct samsung_clock_alias s3c2443_common_aliases[] __initdata = { | ||
192 | ALIAS(HCLK, NULL, "hclk"), | ||
193 | ALIAS(HCLK_SSMC, NULL, "nand"), | ||
194 | ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"), | ||
195 | ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"), | ||
196 | ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"), | ||
197 | ALIAS(PCLK_UART3, "s3c2440-uart.3", "uart"), | ||
198 | ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"), | ||
199 | ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"), | ||
200 | ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"), | ||
201 | ALIAS(PCLK_UART3, "s3c2440-uart.3", "clk_uart_baud2"), | ||
202 | ALIAS(SCLK_UART, NULL, "clk_uart_baud3"), | ||
203 | ALIAS(PCLK_PWM, NULL, "timers"), | ||
204 | ALIAS(PCLK_RTC, NULL, "rtc"), | ||
205 | ALIAS(PCLK_WDT, NULL, "watchdog"), | ||
206 | ALIAS(PCLK_ADC, NULL, "adc"), | ||
207 | ALIAS(PCLK_I2C0, "s3c2410-i2c.0", "i2c"), | ||
208 | ALIAS(HCLK_USBD, NULL, "usb-device"), | ||
209 | ALIAS(HCLK_USBH, NULL, "usb-host"), | ||
210 | ALIAS(SCLK_USBH, NULL, "usb-bus-host"), | ||
211 | ALIAS(PCLK_SPI0, "s3c2443-spi.0", "spi"), | ||
212 | ALIAS(PCLK_SPI0, "s3c2443-spi.0", "spi_busclk0"), | ||
213 | ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "hsmmc"), | ||
214 | ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"), | ||
215 | ALIAS(PCLK_I2S0, "samsung-i2s.0", "iis"), | ||
216 | ALIAS(SCLK_I2S0, NULL, "i2s-if"), | ||
217 | ALIAS(HCLK_LCD, NULL, "lcd"), | ||
218 | ALIAS(SCLK_FIMD, NULL, "sclk_fimd"), | ||
219 | }; | ||
220 | |||
221 | /* S3C2416 specific clocks */ | ||
222 | |||
223 | static struct samsung_pll_clock s3c2416_pll_clks[] __initdata = { | ||
224 | [mpll] = PLL(pll_6552_s3c2416, 0, "mpll", "mpllref", | ||
225 | LOCKCON0, MPLLCON, NULL), | ||
226 | [epll] = PLL(pll_6553, 0, "epll", "epllref", | ||
227 | LOCKCON1, EPLLCON, NULL), | ||
228 | }; | ||
229 | |||
230 | PNAME(s3c2416_hsmmc0_p) = { "sclk_hsmmc0", "sclk_hsmmcext" }; | ||
231 | PNAME(s3c2416_hsmmc1_p) = { "sclk_hsmmc1", "sclk_hsmmcext" }; | ||
232 | PNAME(s3c2416_hsspi0_p) = { "hsspi0_epll", "hsspi0_mpll" }; | ||
233 | |||
234 | static struct clk_div_table armdiv_s3c2416_d[] = { | ||
235 | { .val = 0, .div = 1 }, | ||
236 | { .val = 1, .div = 2 }, | ||
237 | { .val = 2, .div = 3 }, | ||
238 | { .val = 3, .div = 4 }, | ||
239 | { .val = 5, .div = 6 }, | ||
240 | { .val = 7, .div = 8 }, | ||
241 | { /* sentinel */ }, | ||
242 | }; | ||
243 | |||
244 | struct samsung_div_clock s3c2416_dividers[] __initdata = { | ||
245 | DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 3, armdiv_s3c2416_d), | ||
246 | DIV(0, "div_hsspi0_mpll", "msysclk", CLKDIV2, 0, 4), | ||
247 | DIV(0, "div_hsmmc0", "esysclk", CLKDIV2, 6, 2), | ||
248 | }; | ||
249 | |||
250 | struct samsung_mux_clock s3c2416_muxes[] __initdata = { | ||
251 | MUX(MUX_HSMMC0, "mux_hsmmc0", s3c2416_hsmmc0_p, CLKSRC, 16, 1), | ||
252 | MUX(MUX_HSMMC1, "mux_hsmmc1", s3c2416_hsmmc1_p, CLKSRC, 17, 1), | ||
253 | MUX(MUX_HSSPI0, "mux_hsspi0", s3c2416_hsspi0_p, CLKSRC, 18, 1), | ||
254 | }; | ||
255 | |||
256 | struct samsung_gate_clock s3c2416_gates[] __initdata = { | ||
257 | GATE(0, "hsspi0_mpll", "div_hsspi0_mpll", SCLKCON, 19, 0, 0), | ||
258 | GATE(0, "hsspi0_epll", "div_hsspi0_epll", SCLKCON, 14, 0, 0), | ||
259 | GATE(0, "sclk_hsmmc0", "div_hsmmc0", SCLKCON, 6, 0, 0), | ||
260 | GATE(HCLK_2D, "2d", "hclk", HCLKCON, 20, 0, 0), | ||
261 | GATE(HCLK_HSMMC0, "hsmmc0", "hclk", HCLKCON, 15, 0, 0), | ||
262 | GATE(HCLK_IROM, "irom", "hclk", HCLKCON, 13, CLK_IGNORE_UNUSED, 0), | ||
263 | GATE(PCLK_PCM, "pcm", "pclk", PCLKCON, 19, 0, 0), | ||
264 | }; | ||
265 | |||
266 | struct samsung_clock_alias s3c2416_aliases[] __initdata = { | ||
267 | ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"), | ||
268 | ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"), | ||
269 | ALIAS(MUX_HSMMC0, "s3c-sdhci.0", "mmc_busclk.2"), | ||
270 | ALIAS(MUX_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"), | ||
271 | ALIAS(MUX_HSSPI0, "s3c2443-spi.0", "spi_busclk2"), | ||
272 | ALIAS(ARMDIV, NULL, "armdiv"), | ||
273 | }; | ||
274 | |||
275 | /* S3C2443 specific clocks */ | ||
276 | |||
277 | static struct samsung_pll_clock s3c2443_pll_clks[] __initdata = { | ||
278 | [mpll] = PLL(pll_3000, 0, "mpll", "mpllref", | ||
279 | LOCKCON0, MPLLCON, NULL), | ||
280 | [epll] = PLL(pll_2126, 0, "epll", "epllref", | ||
281 | LOCKCON1, EPLLCON, NULL), | ||
282 | }; | ||
283 | |||
284 | static struct clk_div_table armdiv_s3c2443_d[] = { | ||
285 | { .val = 0, .div = 1 }, | ||
286 | { .val = 8, .div = 2 }, | ||
287 | { .val = 2, .div = 3 }, | ||
288 | { .val = 9, .div = 4 }, | ||
289 | { .val = 10, .div = 6 }, | ||
290 | { .val = 11, .div = 8 }, | ||
291 | { .val = 13, .div = 12 }, | ||
292 | { .val = 15, .div = 16 }, | ||
293 | { /* sentinel */ }, | ||
294 | }; | ||
295 | |||
296 | struct samsung_div_clock s3c2443_dividers[] __initdata = { | ||
297 | DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 4, armdiv_s3c2443_d), | ||
298 | DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4), | ||
299 | }; | ||
300 | |||
301 | struct samsung_gate_clock s3c2443_gates[] __initdata = { | ||
302 | GATE(SCLK_HSSPI0, "sclk_hsspi0", "div_hsspi0_epll", SCLKCON, 14, 0, 0), | ||
303 | GATE(SCLK_CAM, "sclk_cam", "div_cam", SCLKCON, 11, 0, 0), | ||
304 | GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, CLK_IGNORE_UNUSED, 0), | ||
305 | GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0), | ||
306 | GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 15, 0, 0), | ||
307 | GATE(PCLK_SDI, "sdi", "pclk", PCLKCON, 5, 0, 0), | ||
308 | }; | ||
309 | |||
310 | struct samsung_clock_alias s3c2443_aliases[] __initdata = { | ||
311 | ALIAS(SCLK_HSSPI0, "s3c2443-spi.0", "spi_busclk2"), | ||
312 | ALIAS(SCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"), | ||
313 | ALIAS(SCLK_CAM, NULL, "camif-upll"), | ||
314 | ALIAS(PCLK_SPI1, "s3c2410-spi.0", "spi"), | ||
315 | ALIAS(PCLK_SDI, NULL, "sdi"), | ||
316 | ALIAS(HCLK_CFC, NULL, "cfc"), | ||
317 | ALIAS(ARMDIV, NULL, "armdiv"), | ||
318 | }; | ||
319 | |||
320 | /* S3C2450 specific clocks */ | ||
321 | |||
322 | PNAME(s3c2450_cam_p) = { "div_cam", "hclk" }; | ||
323 | PNAME(s3c2450_hsspi1_p) = { "hsspi1_epll", "hsspi1_mpll" }; | ||
324 | PNAME(i2s1_p) = { "div_i2s1", "ext_i2s", "epllref", "epllref" }; | ||
325 | |||
326 | struct samsung_div_clock s3c2450_dividers[] __initdata = { | ||
327 | DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4), | ||
328 | DIV(0, "div_hsspi1_epll", "esysclk", CLKDIV2, 24, 2), | ||
329 | DIV(0, "div_hsspi1_mpll", "msysclk", CLKDIV2, 16, 4), | ||
330 | DIV(0, "div_i2s1", "esysclk", CLKDIV2, 12, 4), | ||
331 | }; | ||
332 | |||
333 | struct samsung_mux_clock s3c2450_muxes[] __initdata = { | ||
334 | MUX(0, "mux_cam", s3c2450_cam_p, CLKSRC, 20, 1), | ||
335 | MUX(MUX_HSSPI1, "mux_hsspi1", s3c2450_hsspi1_p, CLKSRC, 19, 1), | ||
336 | MUX(0, "mux_i2s1", i2s1_p, CLKSRC, 12, 2), | ||
337 | }; | ||
338 | |||
339 | struct samsung_gate_clock s3c2450_gates[] __initdata = { | ||
340 | GATE(SCLK_I2S1, "sclk_i2s1", "div_i2s1", SCLKCON, 5, 0, 0), | ||
341 | GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, 0, 0), | ||
342 | GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0), | ||
343 | GATE(HCLK_DMA7, "dma7", "hclk", HCLKCON, 7, CLK_IGNORE_UNUSED, 0), | ||
344 | GATE(HCLK_DMA6, "dma6", "hclk", HCLKCON, 6, CLK_IGNORE_UNUSED, 0), | ||
345 | GATE(PCLK_I2S1, "i2s1", "pclk", PCLKCON, 17, 0, 0), | ||
346 | GATE(PCLK_I2C1, "i2c1", "pclk", PCLKCON, 16, 0, 0), | ||
347 | GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 14, 0, 0), | ||
348 | }; | ||
349 | |||
350 | struct samsung_clock_alias s3c2450_aliases[] __initdata = { | ||
351 | ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi"), | ||
352 | ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi_busclk0"), | ||
353 | ALIAS(MUX_HSSPI1, "s3c2443-spi.1", "spi_busclk2"), | ||
354 | ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"), | ||
355 | }; | ||
356 | |||
357 | /* | ||
358 | * fixed rate clocks generated outside the soc | ||
359 | * Only necessary until the devicetree-move is complete | ||
360 | */ | ||
361 | struct samsung_fixed_rate_clock s3c2443_common_frate_clks[] __initdata = { | ||
362 | FRATE(0, "xti", NULL, CLK_IS_ROOT, 0), | ||
363 | FRATE(0, "ext", NULL, CLK_IS_ROOT, 0), | ||
364 | FRATE(0, "ext_i2s", NULL, CLK_IS_ROOT, 0), | ||
365 | FRATE(0, "ext_uart", NULL, CLK_IS_ROOT, 0), | ||
366 | }; | ||
367 | |||
368 | static void __init s3c2443_common_clk_register_fixed_ext(unsigned long xti_f) | ||
369 | { | ||
370 | s3c2443_common_frate_clks[0].fixed_rate = xti_f; | ||
371 | samsung_clk_register_fixed_rate(s3c2443_common_frate_clks, | ||
372 | ARRAY_SIZE(s3c2443_common_frate_clks)); | ||
373 | } | ||
374 | |||
375 | void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f, | ||
376 | int current_soc, | ||
377 | void __iomem *base) | ||
378 | { | ||
379 | reg_base = base; | ||
380 | |||
381 | if (np) { | ||
382 | reg_base = of_iomap(np, 0); | ||
383 | if (!reg_base) | ||
384 | panic("%s: failed to map registers\n", __func__); | ||
385 | } | ||
386 | |||
387 | samsung_clk_init(np, reg_base, NR_CLKS); | ||
388 | |||
389 | /* Register external clocks only in non-dt cases */ | ||
390 | if (!np) | ||
391 | s3c2443_common_clk_register_fixed_ext(xti_f); | ||
392 | |||
393 | /* Register PLLs. */ | ||
394 | if (current_soc == S3C2416 || current_soc == S3C2450) | ||
395 | samsung_clk_register_pll(s3c2416_pll_clks, | ||
396 | ARRAY_SIZE(s3c2416_pll_clks), reg_base); | ||
397 | else | ||
398 | samsung_clk_register_pll(s3c2443_pll_clks, | ||
399 | ARRAY_SIZE(s3c2443_pll_clks), reg_base); | ||
400 | |||
401 | /* Register common internal clocks. */ | ||
402 | samsung_clk_register_mux(s3c2443_common_muxes, | ||
403 | ARRAY_SIZE(s3c2443_common_muxes)); | ||
404 | samsung_clk_register_div(s3c2443_common_dividers, | ||
405 | ARRAY_SIZE(s3c2443_common_dividers)); | ||
406 | samsung_clk_register_gate(s3c2443_common_gates, | ||
407 | ARRAY_SIZE(s3c2443_common_gates)); | ||
408 | samsung_clk_register_alias(s3c2443_common_aliases, | ||
409 | ARRAY_SIZE(s3c2443_common_aliases)); | ||
410 | |||
411 | /* Register SoC-specific clocks. */ | ||
412 | switch (current_soc) { | ||
413 | case S3C2450: | ||
414 | samsung_clk_register_div(s3c2450_dividers, | ||
415 | ARRAY_SIZE(s3c2450_dividers)); | ||
416 | samsung_clk_register_mux(s3c2450_muxes, | ||
417 | ARRAY_SIZE(s3c2450_muxes)); | ||
418 | samsung_clk_register_gate(s3c2450_gates, | ||
419 | ARRAY_SIZE(s3c2450_gates)); | ||
420 | samsung_clk_register_alias(s3c2450_aliases, | ||
421 | ARRAY_SIZE(s3c2450_aliases)); | ||
422 | /* fall through, as s3c2450 extends the s3c2416 clocks */ | ||
423 | case S3C2416: | ||
424 | samsung_clk_register_div(s3c2416_dividers, | ||
425 | ARRAY_SIZE(s3c2416_dividers)); | ||
426 | samsung_clk_register_mux(s3c2416_muxes, | ||
427 | ARRAY_SIZE(s3c2416_muxes)); | ||
428 | samsung_clk_register_gate(s3c2416_gates, | ||
429 | ARRAY_SIZE(s3c2416_gates)); | ||
430 | samsung_clk_register_alias(s3c2416_aliases, | ||
431 | ARRAY_SIZE(s3c2416_aliases)); | ||
432 | break; | ||
433 | case S3C2443: | ||
434 | samsung_clk_register_div(s3c2443_dividers, | ||
435 | ARRAY_SIZE(s3c2443_dividers)); | ||
436 | samsung_clk_register_gate(s3c2443_gates, | ||
437 | ARRAY_SIZE(s3c2443_gates)); | ||
438 | samsung_clk_register_alias(s3c2443_aliases, | ||
439 | ARRAY_SIZE(s3c2443_aliases)); | ||
440 | break; | ||
441 | } | ||
442 | |||
443 | s3c2443_clk_sleep_init(); | ||
444 | } | ||
445 | |||
446 | static void __init s3c2416_clk_init(struct device_node *np) | ||
447 | { | ||
448 | s3c2443_common_clk_init(np, 0, S3C2416, 0); | ||
449 | } | ||
450 | CLK_OF_DECLARE(s3c2416_clk, "samsung,s3c2416-clock", s3c2416_clk_init); | ||
451 | |||
452 | static void __init s3c2443_clk_init(struct device_node *np) | ||
453 | { | ||
454 | s3c2443_common_clk_init(np, 0, S3C2443, 0); | ||
455 | } | ||
456 | CLK_OF_DECLARE(s3c2443_clk, "samsung,s3c2443-clock", s3c2443_clk_init); | ||
457 | |||
458 | static void __init s3c2450_clk_init(struct device_node *np) | ||
459 | { | ||
460 | s3c2443_common_clk_init(np, 0, S3C2450, 0); | ||
461 | } | ||
462 | CLK_OF_DECLARE(s3c2450_clk, "samsung,s3c2450-clock", s3c2450_clk_init); | ||