1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2013 Samsung Electronics Co., Ltd. |
4 | * Author: Tarek Dakhran <t.dakhran@samsung.com> |
5 | * |
6 | * Common Clock Framework support for Exynos5410 SoC. |
7 | */ |
8 | |
9 | #include <dt-bindings/clock/exynos5410.h> |
10 | |
11 | #include <linux/clk-provider.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/clk.h> |
15 | |
16 | #include "clk.h" |
17 | |
18 | #define APLL_LOCK 0x0 |
19 | #define APLL_CON0 0x100 |
20 | #define CPLL_LOCK 0x10020 |
21 | #define CPLL_CON0 0x10120 |
22 | #define EPLL_LOCK 0x10040 |
23 | #define EPLL_CON0 0x10130 |
24 | #define MPLL_LOCK 0x4000 |
25 | #define MPLL_CON0 0x4100 |
26 | #define BPLL_LOCK 0x20010 |
27 | #define BPLL_CON0 0x20110 |
28 | #define KPLL_LOCK 0x28000 |
29 | #define KPLL_CON0 0x28100 |
30 | |
31 | #define SRC_CPU 0x200 |
32 | #define DIV_CPU0 0x500 |
33 | #define SRC_CPERI1 0x4204 |
34 | #define GATE_IP_G2D 0x8800 |
35 | #define DIV_TOP0 0x10510 |
36 | #define DIV_TOP1 0x10514 |
37 | #define DIV_FSYS0 0x10548 |
38 | #define DIV_FSYS1 0x1054c |
39 | #define DIV_FSYS2 0x10550 |
40 | #define DIV_PERIC0 0x10558 |
41 | #define DIV_PERIC3 0x10564 |
42 | #define SRC_TOP0 0x10210 |
43 | #define SRC_TOP1 0x10214 |
44 | #define SRC_TOP2 0x10218 |
45 | #define SRC_FSYS 0x10244 |
46 | #define SRC_PERIC0 0x10250 |
47 | #define SRC_MASK_FSYS 0x10340 |
48 | #define SRC_MASK_PERIC0 0x10350 |
49 | #define GATE_BUS_FSYS0 0x10740 |
50 | #define GATE_TOP_SCLK_FSYS 0x10840 |
51 | #define GATE_TOP_SCLK_PERIC 0x10850 |
52 | #define GATE_IP_FSYS 0x10944 |
53 | #define GATE_IP_PERIC 0x10950 |
54 | #define GATE_IP_PERIS 0x10960 |
55 | #define SRC_CDREX 0x20200 |
56 | #define SRC_KFC 0x28200 |
57 | #define DIV_KFC0 0x28500 |
58 | |
59 | /* NOTE: Must be equal to the last clock ID increased by one */ |
60 | #define CLKS_NR 512 |
61 | |
62 | /* list of PLLs */ |
63 | enum exynos5410_plls { |
64 | apll, cpll, epll, mpll, |
65 | bpll, kpll, |
66 | nr_plls /* number of PLLs */ |
67 | }; |
68 | |
69 | /* list of all parent clocks */ |
70 | PNAME(apll_p) = { "fin_pll" , "fout_apll" , }; |
71 | PNAME(bpll_p) = { "fin_pll" , "fout_bpll" , }; |
72 | PNAME(cpll_p) = { "fin_pll" , "fout_cpll" }; |
73 | PNAME(epll_p) = { "fin_pll" , "fout_epll" }; |
74 | PNAME(mpll_p) = { "fin_pll" , "fout_mpll" , }; |
75 | PNAME(kpll_p) = { "fin_pll" , "fout_kpll" , }; |
76 | |
77 | PNAME(mout_cpu_p) = { "mout_apll" , "sclk_mpll" , }; |
78 | PNAME(mout_kfc_p) = { "mout_kpll" , "sclk_mpll" , }; |
79 | |
80 | PNAME(mpll_user_p) = { "fin_pll" , "sclk_mpll" , }; |
81 | PNAME(bpll_user_p) = { "fin_pll" , "sclk_bpll" , }; |
82 | PNAME(mpll_bpll_p) = { "sclk_mpll_muxed" , "sclk_bpll_muxed" , }; |
83 | PNAME(sclk_mpll_bpll_p) = { "sclk_mpll_bpll" , "fin_pll" , }; |
84 | |
85 | PNAME(group2_p) = { "fin_pll" , "fin_pll" , "none" , "none" , |
86 | "none" , "none" , "sclk_mpll_bpll" , |
87 | "none" , "none" , "sclk_cpll" }; |
88 | |
89 | static const struct samsung_mux_clock exynos5410_mux_clks[] __initconst = { |
90 | MUX(0, "mout_apll" , apll_p, SRC_CPU, 0, 1), |
91 | MUX(0, "mout_cpu" , mout_cpu_p, SRC_CPU, 16, 1), |
92 | |
93 | MUX(0, "mout_kpll" , kpll_p, SRC_KFC, 0, 1), |
94 | MUX(0, "mout_kfc" , mout_kfc_p, SRC_KFC, 16, 1), |
95 | |
96 | MUX(0, "sclk_mpll" , mpll_p, SRC_CPERI1, 8, 1), |
97 | MUX(0, "sclk_mpll_muxed" , mpll_user_p, SRC_TOP2, 20, 1), |
98 | |
99 | MUX(0, "sclk_bpll" , bpll_p, SRC_CDREX, 0, 1), |
100 | MUX(0, "sclk_bpll_muxed" , bpll_user_p, SRC_TOP2, 24, 1), |
101 | |
102 | MUX(0, "sclk_epll" , epll_p, SRC_TOP2, 12, 1), |
103 | |
104 | MUX(0, "sclk_cpll" , cpll_p, SRC_TOP2, 8, 1), |
105 | |
106 | MUX(0, "sclk_mpll_bpll" , mpll_bpll_p, SRC_TOP1, 20, 1), |
107 | |
108 | MUX(0, "mout_mmc0" , group2_p, SRC_FSYS, 0, 4), |
109 | MUX(0, "mout_mmc1" , group2_p, SRC_FSYS, 4, 4), |
110 | MUX(0, "mout_mmc2" , group2_p, SRC_FSYS, 8, 4), |
111 | MUX(0, "mout_usbd300" , sclk_mpll_bpll_p, SRC_FSYS, 28, 1), |
112 | MUX(0, "mout_usbd301" , sclk_mpll_bpll_p, SRC_FSYS, 29, 1), |
113 | |
114 | MUX(0, "mout_uart0" , group2_p, SRC_PERIC0, 0, 4), |
115 | MUX(0, "mout_uart1" , group2_p, SRC_PERIC0, 4, 4), |
116 | MUX(0, "mout_uart2" , group2_p, SRC_PERIC0, 8, 4), |
117 | MUX(0, "mout_uart3" , group2_p, SRC_PERIC0, 12, 4), |
118 | MUX(0, "mout_pwm" , group2_p, SRC_PERIC0, 24, 4), |
119 | |
120 | MUX(0, "mout_aclk200" , mpll_bpll_p, SRC_TOP0, 12, 1), |
121 | MUX(0, "mout_aclk400" , mpll_bpll_p, SRC_TOP0, 20, 1), |
122 | }; |
123 | |
124 | static const struct samsung_div_clock exynos5410_div_clks[] __initconst = { |
125 | DIV(0, "div_arm" , "mout_cpu" , DIV_CPU0, 0, 3), |
126 | DIV(0, "div_arm2" , "div_arm" , DIV_CPU0, 28, 3), |
127 | |
128 | DIV(0, "div_acp" , "div_arm2" , DIV_CPU0, 8, 3), |
129 | DIV(0, "div_cpud" , "div_arm2" , DIV_CPU0, 4, 3), |
130 | DIV(0, "div_atb" , "div_arm2" , DIV_CPU0, 16, 3), |
131 | DIV(0, "pclk_dbg" , "div_arm2" , DIV_CPU0, 20, 3), |
132 | |
133 | DIV(0, "div_kfc" , "mout_kfc" , DIV_KFC0, 0, 3), |
134 | DIV(0, "div_aclk" , "div_kfc" , DIV_KFC0, 4, 3), |
135 | DIV(0, "div_pclk" , "div_kfc" , DIV_KFC0, 20, 3), |
136 | |
137 | DIV(0, "aclk66_pre" , "sclk_mpll_muxed" , DIV_TOP1, 24, 3), |
138 | DIV(0, "aclk66" , "aclk66_pre" , DIV_TOP0, 0, 3), |
139 | |
140 | DIV(0, "dout_usbphy300" , "mout_usbd300" , DIV_FSYS0, 16, 4), |
141 | DIV(0, "dout_usbphy301" , "mout_usbd301" , DIV_FSYS0, 20, 4), |
142 | DIV(0, "dout_usbd300" , "mout_usbd300" , DIV_FSYS0, 24, 4), |
143 | DIV(0, "dout_usbd301" , "mout_usbd301" , DIV_FSYS0, 28, 4), |
144 | |
145 | DIV(0, "div_mmc0" , "mout_mmc0" , DIV_FSYS1, 0, 4), |
146 | DIV(0, "div_mmc1" , "mout_mmc1" , DIV_FSYS1, 16, 4), |
147 | DIV(0, "div_mmc2" , "mout_mmc2" , DIV_FSYS2, 0, 4), |
148 | |
149 | DIV_F(0, "div_mmc_pre0" , "div_mmc0" , |
150 | DIV_FSYS1, 8, 8, CLK_SET_RATE_PARENT, 0), |
151 | DIV_F(0, "div_mmc_pre1" , "div_mmc1" , |
152 | DIV_FSYS1, 24, 8, CLK_SET_RATE_PARENT, 0), |
153 | DIV_F(0, "div_mmc_pre2" , "div_mmc2" , |
154 | DIV_FSYS2, 8, 8, CLK_SET_RATE_PARENT, 0), |
155 | |
156 | DIV(0, "div_uart0" , "mout_uart0" , DIV_PERIC0, 0, 4), |
157 | DIV(0, "div_uart1" , "mout_uart1" , DIV_PERIC0, 4, 4), |
158 | DIV(0, "div_uart2" , "mout_uart2" , DIV_PERIC0, 8, 4), |
159 | DIV(0, "div_uart3" , "mout_uart3" , DIV_PERIC0, 12, 4), |
160 | |
161 | DIV(0, "dout_pwm" , "mout_pwm" , DIV_PERIC3, 0, 4), |
162 | |
163 | DIV(0, "aclk200" , "mout_aclk200" , DIV_TOP0, 12, 3), |
164 | DIV(0, "aclk266" , "mpll_user_p" , DIV_TOP0, 16, 3), |
165 | DIV(0, "aclk400" , "mout_aclk400" , DIV_TOP0, 24, 3), |
166 | }; |
167 | |
168 | static const struct samsung_gate_clock exynos5410_gate_clks[] __initconst = { |
169 | GATE(CLK_SSS, "sss" , "aclk266" , GATE_IP_G2D, 2, 0, 0), |
170 | GATE(CLK_MCT, "mct" , "aclk66" , GATE_IP_PERIS, 18, 0, 0), |
171 | GATE(CLK_WDT, "wdt" , "aclk66" , GATE_IP_PERIS, 19, 0, 0), |
172 | GATE(CLK_RTC, "rtc" , "aclk66" , GATE_IP_PERIS, 20, 0, 0), |
173 | GATE(CLK_TMU, "tmu" , "aclk66" , GATE_IP_PERIS, 21, 0, 0), |
174 | |
175 | GATE(CLK_SCLK_MMC0, "sclk_mmc0" , "div_mmc_pre0" , |
176 | SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0), |
177 | GATE(CLK_SCLK_MMC1, "sclk_mmc1" , "div_mmc_pre1" , |
178 | SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0), |
179 | GATE(CLK_SCLK_MMC2, "sclk_mmc2" , "div_mmc_pre2" , |
180 | SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0), |
181 | |
182 | GATE(CLK_MMC0, "sdmmc0" , "aclk200" , GATE_BUS_FSYS0, 12, 0, 0), |
183 | GATE(CLK_MMC1, "sdmmc1" , "aclk200" , GATE_BUS_FSYS0, 13, 0, 0), |
184 | GATE(CLK_MMC2, "sdmmc2" , "aclk200" , GATE_BUS_FSYS0, 14, 0, 0), |
185 | GATE(CLK_PDMA1, "pdma1" , "aclk200" , GATE_BUS_FSYS0, 2, 0, 0), |
186 | GATE(CLK_PDMA0, "pdma0" , "aclk200" , GATE_BUS_FSYS0, 1, 0, 0), |
187 | |
188 | GATE(CLK_SCLK_USBPHY301, "sclk_usbphy301" , "dout_usbphy301" , |
189 | GATE_TOP_SCLK_FSYS, 7, CLK_SET_RATE_PARENT, 0), |
190 | GATE(CLK_SCLK_USBPHY300, "sclk_usbphy300" , "dout_usbphy300" , |
191 | GATE_TOP_SCLK_FSYS, 8, CLK_SET_RATE_PARENT, 0), |
192 | GATE(CLK_SCLK_USBD300, "sclk_usbd300" , "dout_usbd300" , |
193 | GATE_TOP_SCLK_FSYS, 9, CLK_SET_RATE_PARENT, 0), |
194 | GATE(CLK_SCLK_USBD301, "sclk_usbd301" , "dout_usbd301" , |
195 | GATE_TOP_SCLK_FSYS, 10, CLK_SET_RATE_PARENT, 0), |
196 | |
197 | GATE(CLK_SCLK_PWM, "sclk_pwm" , "dout_pwm" , |
198 | GATE_TOP_SCLK_PERIC, 11, CLK_SET_RATE_PARENT, 0), |
199 | |
200 | GATE(CLK_UART0, "uart0" , "aclk66" , GATE_IP_PERIC, 0, 0, 0), |
201 | GATE(CLK_UART1, "uart1" , "aclk66" , GATE_IP_PERIC, 1, 0, 0), |
202 | GATE(CLK_UART2, "uart2" , "aclk66" , GATE_IP_PERIC, 2, 0, 0), |
203 | GATE(CLK_UART3, "uart3" , "aclk66" , GATE_IP_PERIC, 3, 0, 0), |
204 | GATE(CLK_I2C0, "i2c0" , "aclk66" , GATE_IP_PERIC, 6, 0, 0), |
205 | GATE(CLK_I2C1, "i2c1" , "aclk66" , GATE_IP_PERIC, 7, 0, 0), |
206 | GATE(CLK_I2C2, "i2c2" , "aclk66" , GATE_IP_PERIC, 8, 0, 0), |
207 | GATE(CLK_I2C3, "i2c3" , "aclk66" , GATE_IP_PERIC, 9, 0, 0), |
208 | GATE(CLK_USI0, "usi0" , "aclk66" , GATE_IP_PERIC, 10, 0, 0), |
209 | GATE(CLK_USI1, "usi1" , "aclk66" , GATE_IP_PERIC, 11, 0, 0), |
210 | GATE(CLK_USI2, "usi2" , "aclk66" , GATE_IP_PERIC, 12, 0, 0), |
211 | GATE(CLK_USI3, "usi3" , "aclk66" , GATE_IP_PERIC, 13, 0, 0), |
212 | GATE(CLK_TSADC, "tsadc" , "aclk66" , GATE_IP_PERIC, 15, 0, 0), |
213 | GATE(CLK_PWM, "pwm" , "aclk66" , GATE_IP_PERIC, 24, 0, 0), |
214 | |
215 | GATE(CLK_SCLK_UART0, "sclk_uart0" , "div_uart0" , |
216 | SRC_MASK_PERIC0, 0, CLK_SET_RATE_PARENT, 0), |
217 | GATE(CLK_SCLK_UART1, "sclk_uart1" , "div_uart1" , |
218 | SRC_MASK_PERIC0, 4, CLK_SET_RATE_PARENT, 0), |
219 | GATE(CLK_SCLK_UART2, "sclk_uart2" , "div_uart2" , |
220 | SRC_MASK_PERIC0, 8, CLK_SET_RATE_PARENT, 0), |
221 | GATE(CLK_SCLK_UART3, "sclk_uart3" , "div_uart3" , |
222 | SRC_MASK_PERIC0, 12, CLK_SET_RATE_PARENT, 0), |
223 | |
224 | GATE(CLK_USBH20, "usbh20" , "aclk200_fsys" , GATE_IP_FSYS, 18, 0, 0), |
225 | GATE(CLK_USBD300, "usbd300" , "aclk200_fsys" , GATE_IP_FSYS, 19, 0, 0), |
226 | GATE(CLK_USBD301, "usbd301" , "aclk200_fsys" , GATE_IP_FSYS, 20, 0, 0), |
227 | }; |
228 | |
229 | static const struct samsung_pll_rate_table exynos5410_pll2550x_24mhz_tbl[] __initconst = { |
230 | PLL_36XX_RATE(24 * MHZ, 400000000U, 200, 3, 2, 0), |
231 | PLL_36XX_RATE(24 * MHZ, 333000000U, 111, 2, 2, 0), |
232 | PLL_36XX_RATE(24 * MHZ, 300000000U, 100, 2, 2, 0), |
233 | PLL_36XX_RATE(24 * MHZ, 266000000U, 266, 3, 3, 0), |
234 | PLL_36XX_RATE(24 * MHZ, 200000000U, 200, 3, 3, 0), |
235 | PLL_36XX_RATE(24 * MHZ, 192000000U, 192, 3, 3, 0), |
236 | PLL_36XX_RATE(24 * MHZ, 166000000U, 166, 3, 3, 0), |
237 | PLL_36XX_RATE(24 * MHZ, 133000000U, 266, 3, 4, 0), |
238 | PLL_36XX_RATE(24 * MHZ, 100000000U, 200, 3, 4, 0), |
239 | PLL_36XX_RATE(24 * MHZ, 66000000U, 176, 2, 5, 0), |
240 | }; |
241 | |
242 | static struct samsung_pll_clock exynos5410_plls[nr_plls] __initdata = { |
243 | [apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll" , "fin_pll" , APLL_LOCK, |
244 | APLL_CON0, NULL), |
245 | [cpll] = PLL(pll_35xx, CLK_FOUT_CPLL, "fout_cpll" , "fin_pll" , CPLL_LOCK, |
246 | CPLL_CON0, NULL), |
247 | [epll] = PLL(pll_2650x, CLK_FOUT_EPLL, "fout_epll" , "fin_pll" , EPLL_LOCK, |
248 | EPLL_CON0, NULL), |
249 | [mpll] = PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll" , "fin_pll" , MPLL_LOCK, |
250 | MPLL_CON0, NULL), |
251 | [bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll" , "fin_pll" , BPLL_LOCK, |
252 | BPLL_CON0, NULL), |
253 | [kpll] = PLL(pll_35xx, CLK_FOUT_KPLL, "fout_kpll" , "fin_pll" , KPLL_LOCK, |
254 | KPLL_CON0, NULL), |
255 | }; |
256 | |
257 | static const struct samsung_cmu_info cmu __initconst = { |
258 | .pll_clks = exynos5410_plls, |
259 | .nr_pll_clks = ARRAY_SIZE(exynos5410_plls), |
260 | .mux_clks = exynos5410_mux_clks, |
261 | .nr_mux_clks = ARRAY_SIZE(exynos5410_mux_clks), |
262 | .div_clks = exynos5410_div_clks, |
263 | .nr_div_clks = ARRAY_SIZE(exynos5410_div_clks), |
264 | .gate_clks = exynos5410_gate_clks, |
265 | .nr_gate_clks = ARRAY_SIZE(exynos5410_gate_clks), |
266 | .nr_clk_ids = CLKS_NR, |
267 | }; |
268 | |
269 | /* register exynos5410 clocks */ |
270 | static void __init exynos5410_clk_init(struct device_node *np) |
271 | { |
272 | struct clk *xxti = of_clk_get(np, index: 0); |
273 | |
274 | if (!IS_ERR(ptr: xxti) && clk_get_rate(clk: xxti) == 24 * MHZ) |
275 | exynos5410_plls[epll].rate_table = exynos5410_pll2550x_24mhz_tbl; |
276 | |
277 | samsung_cmu_register_one(np, &cmu); |
278 | |
279 | pr_debug("Exynos5410: clock setup completed.\n" ); |
280 | } |
281 | CLK_OF_DECLARE(exynos5410_clk, "samsung,exynos5410-clock" , exynos5410_clk_init); |
282 | |