1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * X1830 SoC CGU driver |
4 | * Copyright (c) 2019 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> |
5 | */ |
6 | |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/io.h> |
10 | #include <linux/of.h> |
11 | |
12 | #include <dt-bindings/clock/ingenic,x1830-cgu.h> |
13 | |
14 | #include "cgu.h" |
15 | #include "pm.h" |
16 | |
17 | /* CGU register offsets */ |
18 | #define CGU_REG_CPCCR 0x00 |
19 | #define CGU_REG_CPPCR 0x0c |
20 | #define CGU_REG_APLL 0x10 |
21 | #define CGU_REG_MPLL 0x14 |
22 | #define CGU_REG_CLKGR0 0x20 |
23 | #define CGU_REG_OPCR 0x24 |
24 | #define CGU_REG_CLKGR1 0x28 |
25 | #define CGU_REG_DDRCDR 0x2c |
26 | #define CGU_REG_USBPCR 0x3c |
27 | #define CGU_REG_USBRDT 0x40 |
28 | #define CGU_REG_USBVBFIL 0x44 |
29 | #define CGU_REG_USBPCR1 0x48 |
30 | #define CGU_REG_MACCDR 0x54 |
31 | #define CGU_REG_EPLL 0x58 |
32 | #define CGU_REG_I2SCDR 0x60 |
33 | #define CGU_REG_LPCDR 0x64 |
34 | #define CGU_REG_MSC0CDR 0x68 |
35 | #define CGU_REG_I2SCDR1 0x70 |
36 | #define CGU_REG_SSICDR 0x74 |
37 | #define CGU_REG_CIMCDR 0x7c |
38 | #define CGU_REG_MSC1CDR 0xa4 |
39 | #define CGU_REG_CMP_INTR 0xb0 |
40 | #define CGU_REG_CMP_INTRE 0xb4 |
41 | #define CGU_REG_DRCG 0xd0 |
42 | #define CGU_REG_CPCSR 0xd4 |
43 | #define CGU_REG_VPLL 0xe0 |
44 | #define CGU_REG_MACPHYC 0xe8 |
45 | |
46 | /* bits within the OPCR register */ |
47 | #define OPCR_GATE_USBPHYCLK BIT(23) |
48 | #define OPCR_SPENDN0 BIT(7) |
49 | #define OPCR_SPENDN1 BIT(6) |
50 | |
51 | /* bits within the USBPCR register */ |
52 | #define USBPCR_SIDDQ BIT(21) |
53 | #define USBPCR_OTG_DISABLE BIT(20) |
54 | |
55 | static struct ingenic_cgu *cgu; |
56 | |
57 | static int x1830_usb_phy_enable(struct clk_hw *hw) |
58 | { |
59 | void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR; |
60 | void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR; |
61 | |
62 | writel(val: (readl(addr: reg_opcr) | OPCR_SPENDN0) & ~OPCR_GATE_USBPHYCLK, addr: reg_opcr); |
63 | writel(readl(addr: reg_usbpcr) & ~USBPCR_OTG_DISABLE & ~USBPCR_SIDDQ, addr: reg_usbpcr); |
64 | return 0; |
65 | } |
66 | |
67 | static void x1830_usb_phy_disable(struct clk_hw *hw) |
68 | { |
69 | void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR; |
70 | void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR; |
71 | |
72 | writel(val: (readl(addr: reg_opcr) & ~OPCR_SPENDN0) | OPCR_GATE_USBPHYCLK, addr: reg_opcr); |
73 | writel(readl(addr: reg_usbpcr) | USBPCR_OTG_DISABLE | USBPCR_SIDDQ, addr: reg_usbpcr); |
74 | } |
75 | |
76 | static int x1830_usb_phy_is_enabled(struct clk_hw *hw) |
77 | { |
78 | void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR; |
79 | void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR; |
80 | |
81 | return (readl(addr: reg_opcr) & OPCR_SPENDN0) && |
82 | !(readl(addr: reg_usbpcr) & USBPCR_SIDDQ) && |
83 | !(readl(addr: reg_usbpcr) & USBPCR_OTG_DISABLE); |
84 | } |
85 | |
86 | static const struct clk_ops x1830_otg_phy_ops = { |
87 | .enable = x1830_usb_phy_enable, |
88 | .disable = x1830_usb_phy_disable, |
89 | .is_enabled = x1830_usb_phy_is_enabled, |
90 | }; |
91 | |
92 | static const s8 pll_od_encoding[64] = { |
93 | 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3, |
94 | -1, -1, -1, -1, -1, -1, -1, 0x4, |
95 | -1, -1, -1, -1, -1, -1, -1, -1, |
96 | -1, -1, -1, -1, -1, -1, -1, 0x5, |
97 | -1, -1, -1, -1, -1, -1, -1, -1, |
98 | -1, -1, -1, -1, -1, -1, -1, -1, |
99 | -1, -1, -1, -1, -1, -1, -1, -1, |
100 | -1, -1, -1, -1, -1, -1, -1, 0x6, |
101 | }; |
102 | |
103 | static const struct ingenic_cgu_clk_info x1830_cgu_clocks[] = { |
104 | |
105 | /* External clocks */ |
106 | |
107 | [X1830_CLK_EXCLK] = { "ext" , CGU_CLK_EXT }, |
108 | [X1830_CLK_RTCLK] = { "rtc" , CGU_CLK_EXT }, |
109 | |
110 | /* PLLs */ |
111 | |
112 | [X1830_CLK_APLL] = { |
113 | "apll" , CGU_CLK_PLL, |
114 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
115 | .pll = { |
116 | .reg = CGU_REG_APLL, |
117 | .rate_multiplier = 2, |
118 | .m_shift = 20, |
119 | .m_bits = 9, |
120 | .m_offset = 1, |
121 | .n_shift = 14, |
122 | .n_bits = 6, |
123 | .n_offset = 1, |
124 | .od_shift = 11, |
125 | .od_bits = 3, |
126 | .od_max = 64, |
127 | .od_encoding = pll_od_encoding, |
128 | .bypass_reg = CGU_REG_CPPCR, |
129 | .bypass_bit = 30, |
130 | .enable_bit = 0, |
131 | .stable_bit = 3, |
132 | }, |
133 | }, |
134 | |
135 | [X1830_CLK_MPLL] = { |
136 | "mpll" , CGU_CLK_PLL, |
137 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
138 | .pll = { |
139 | .reg = CGU_REG_MPLL, |
140 | .rate_multiplier = 2, |
141 | .m_shift = 20, |
142 | .m_bits = 9, |
143 | .m_offset = 1, |
144 | .n_shift = 14, |
145 | .n_bits = 6, |
146 | .n_offset = 1, |
147 | .od_shift = 11, |
148 | .od_bits = 3, |
149 | .od_max = 64, |
150 | .od_encoding = pll_od_encoding, |
151 | .bypass_reg = CGU_REG_CPPCR, |
152 | .bypass_bit = 28, |
153 | .enable_bit = 0, |
154 | .stable_bit = 3, |
155 | }, |
156 | }, |
157 | |
158 | [X1830_CLK_EPLL] = { |
159 | "epll" , CGU_CLK_PLL, |
160 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
161 | .pll = { |
162 | .reg = CGU_REG_EPLL, |
163 | .rate_multiplier = 2, |
164 | .m_shift = 20, |
165 | .m_bits = 9, |
166 | .m_offset = 1, |
167 | .n_shift = 14, |
168 | .n_bits = 6, |
169 | .n_offset = 1, |
170 | .od_shift = 11, |
171 | .od_bits = 3, |
172 | .od_max = 64, |
173 | .od_encoding = pll_od_encoding, |
174 | .bypass_reg = CGU_REG_CPPCR, |
175 | .bypass_bit = 24, |
176 | .enable_bit = 0, |
177 | .stable_bit = 3, |
178 | }, |
179 | }, |
180 | |
181 | [X1830_CLK_VPLL] = { |
182 | "vpll" , CGU_CLK_PLL, |
183 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
184 | .pll = { |
185 | .reg = CGU_REG_VPLL, |
186 | .rate_multiplier = 2, |
187 | .m_shift = 20, |
188 | .m_bits = 9, |
189 | .m_offset = 1, |
190 | .n_shift = 14, |
191 | .n_bits = 6, |
192 | .n_offset = 1, |
193 | .od_shift = 11, |
194 | .od_bits = 3, |
195 | .od_max = 64, |
196 | .od_encoding = pll_od_encoding, |
197 | .bypass_reg = CGU_REG_CPPCR, |
198 | .bypass_bit = 26, |
199 | .enable_bit = 0, |
200 | .stable_bit = 3, |
201 | }, |
202 | }, |
203 | |
204 | /* Custom (SoC-specific) OTG PHY */ |
205 | |
206 | [X1830_CLK_OTGPHY] = { |
207 | "otg_phy" , CGU_CLK_CUSTOM, |
208 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
209 | .custom = { &x1830_otg_phy_ops }, |
210 | }, |
211 | |
212 | /* Muxes & dividers */ |
213 | |
214 | [X1830_CLK_SCLKA] = { |
215 | "sclk_a" , CGU_CLK_MUX, |
216 | .parents = { -1, X1830_CLK_EXCLK, X1830_CLK_APLL, -1 }, |
217 | .mux = { CGU_REG_CPCCR, 30, 2 }, |
218 | }, |
219 | |
220 | [X1830_CLK_CPUMUX] = { |
221 | "cpu_mux" , CGU_CLK_MUX, |
222 | .parents = { -1, X1830_CLK_SCLKA, X1830_CLK_MPLL, -1 }, |
223 | .mux = { CGU_REG_CPCCR, 28, 2 }, |
224 | }, |
225 | |
226 | [X1830_CLK_CPU] = { |
227 | "cpu" , CGU_CLK_DIV | CGU_CLK_GATE, |
228 | .flags = CLK_IS_CRITICAL, |
229 | .parents = { X1830_CLK_CPUMUX, -1, -1, -1 }, |
230 | .div = { CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1 }, |
231 | .gate = { CGU_REG_CLKGR1, 15 }, |
232 | }, |
233 | |
234 | [X1830_CLK_L2CACHE] = { |
235 | "l2cache" , CGU_CLK_DIV, |
236 | /* |
237 | * The L2 cache clock is critical if caches are enabled and |
238 | * disabling it or any parent clocks will hang the system. |
239 | */ |
240 | .flags = CLK_IS_CRITICAL, |
241 | .parents = { X1830_CLK_CPUMUX, -1, -1, -1 }, |
242 | .div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 }, |
243 | }, |
244 | |
245 | [X1830_CLK_AHB0] = { |
246 | "ahb0" , CGU_CLK_MUX | CGU_CLK_DIV, |
247 | .parents = { -1, X1830_CLK_SCLKA, X1830_CLK_MPLL, -1 }, |
248 | .mux = { CGU_REG_CPCCR, 26, 2 }, |
249 | .div = { CGU_REG_CPCCR, 8, 1, 4, 21, -1, -1 }, |
250 | }, |
251 | |
252 | [X1830_CLK_AHB2PMUX] = { |
253 | "ahb2_apb_mux" , CGU_CLK_MUX, |
254 | .parents = { -1, X1830_CLK_SCLKA, X1830_CLK_MPLL, -1 }, |
255 | .mux = { CGU_REG_CPCCR, 24, 2 }, |
256 | }, |
257 | |
258 | [X1830_CLK_AHB2] = { |
259 | "ahb2" , CGU_CLK_DIV, |
260 | .parents = { X1830_CLK_AHB2PMUX, -1, -1, -1 }, |
261 | .div = { CGU_REG_CPCCR, 12, 1, 4, 20, -1, -1 }, |
262 | }, |
263 | |
264 | [X1830_CLK_PCLK] = { |
265 | "pclk" , CGU_CLK_DIV | CGU_CLK_GATE, |
266 | .parents = { X1830_CLK_AHB2PMUX, -1, -1, -1 }, |
267 | .div = { CGU_REG_CPCCR, 16, 1, 4, 20, -1, -1 }, |
268 | .gate = { CGU_REG_CLKGR1, 14 }, |
269 | }, |
270 | |
271 | [X1830_CLK_DDR] = { |
272 | "ddr" , CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, |
273 | /* |
274 | * Disabling DDR clock or its parents will render DRAM |
275 | * inaccessible; mark it critical. |
276 | */ |
277 | .flags = CLK_IS_CRITICAL, |
278 | .parents = { -1, X1830_CLK_SCLKA, X1830_CLK_MPLL, -1 }, |
279 | .mux = { CGU_REG_DDRCDR, 30, 2 }, |
280 | .div = { CGU_REG_DDRCDR, 0, 1, 4, 29, 28, 27 }, |
281 | .gate = { CGU_REG_CLKGR0, 31 }, |
282 | }, |
283 | |
284 | [X1830_CLK_MAC] = { |
285 | "mac" , CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, |
286 | .parents = { X1830_CLK_SCLKA, X1830_CLK_MPLL, |
287 | X1830_CLK_VPLL, X1830_CLK_EPLL }, |
288 | .mux = { CGU_REG_MACCDR, 30, 2 }, |
289 | .div = { CGU_REG_MACCDR, 0, 1, 8, 29, 28, 27 }, |
290 | .gate = { CGU_REG_CLKGR1, 4 }, |
291 | }, |
292 | |
293 | [X1830_CLK_LCD] = { |
294 | "lcd" , CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, |
295 | .parents = { X1830_CLK_SCLKA, X1830_CLK_MPLL, |
296 | X1830_CLK_VPLL, X1830_CLK_EPLL }, |
297 | .mux = { CGU_REG_LPCDR, 30, 2 }, |
298 | .div = { CGU_REG_LPCDR, 0, 1, 8, 28, 27, 26 }, |
299 | .gate = { CGU_REG_CLKGR1, 9 }, |
300 | }, |
301 | |
302 | [X1830_CLK_MSCMUX] = { |
303 | "msc_mux" , CGU_CLK_MUX, |
304 | .parents = { X1830_CLK_SCLKA, X1830_CLK_MPLL, |
305 | X1830_CLK_VPLL, X1830_CLK_EPLL }, |
306 | .mux = { CGU_REG_MSC0CDR, 30, 2 }, |
307 | }, |
308 | |
309 | [X1830_CLK_MSC0] = { |
310 | "msc0" , CGU_CLK_DIV | CGU_CLK_GATE, |
311 | .parents = { X1830_CLK_MSCMUX, -1, -1, -1 }, |
312 | .div = { CGU_REG_MSC0CDR, 0, 2, 8, 29, 28, 27 }, |
313 | .gate = { CGU_REG_CLKGR0, 4 }, |
314 | }, |
315 | |
316 | [X1830_CLK_MSC1] = { |
317 | "msc1" , CGU_CLK_DIV | CGU_CLK_GATE, |
318 | .parents = { X1830_CLK_MSCMUX, -1, -1, -1 }, |
319 | .div = { CGU_REG_MSC1CDR, 0, 2, 8, 29, 28, 27 }, |
320 | .gate = { CGU_REG_CLKGR0, 5 }, |
321 | }, |
322 | |
323 | [X1830_CLK_SSIPLL] = { |
324 | "ssi_pll" , CGU_CLK_MUX | CGU_CLK_DIV, |
325 | .parents = { X1830_CLK_SCLKA, X1830_CLK_MPLL, |
326 | X1830_CLK_VPLL, X1830_CLK_EPLL }, |
327 | .mux = { CGU_REG_SSICDR, 30, 2 }, |
328 | .div = { CGU_REG_SSICDR, 0, 1, 8, 28, 27, 26 }, |
329 | }, |
330 | |
331 | [X1830_CLK_SSIPLL_DIV2] = { |
332 | "ssi_pll_div2" , CGU_CLK_FIXDIV, |
333 | .parents = { X1830_CLK_SSIPLL }, |
334 | .fixdiv = { 2 }, |
335 | }, |
336 | |
337 | [X1830_CLK_SSIMUX] = { |
338 | "ssi_mux" , CGU_CLK_MUX, |
339 | .parents = { X1830_CLK_EXCLK, X1830_CLK_SSIPLL_DIV2, -1, -1 }, |
340 | .mux = { CGU_REG_SSICDR, 29, 1 }, |
341 | }, |
342 | |
343 | [X1830_CLK_EXCLK_DIV512] = { |
344 | "exclk_div512" , CGU_CLK_FIXDIV, |
345 | .parents = { X1830_CLK_EXCLK }, |
346 | .fixdiv = { 512 }, |
347 | }, |
348 | |
349 | [X1830_CLK_RTC] = { |
350 | "rtc_ercs" , CGU_CLK_MUX | CGU_CLK_GATE, |
351 | .parents = { X1830_CLK_EXCLK_DIV512, X1830_CLK_RTCLK }, |
352 | .mux = { CGU_REG_OPCR, 2, 1}, |
353 | .gate = { CGU_REG_CLKGR0, 29 }, |
354 | }, |
355 | |
356 | /* Gate-only clocks */ |
357 | |
358 | [X1830_CLK_EMC] = { |
359 | "emc" , CGU_CLK_GATE, |
360 | .parents = { X1830_CLK_AHB2, -1, -1, -1 }, |
361 | .gate = { CGU_REG_CLKGR0, 0 }, |
362 | }, |
363 | |
364 | [X1830_CLK_EFUSE] = { |
365 | "efuse" , CGU_CLK_GATE, |
366 | .parents = { X1830_CLK_AHB2, -1, -1, -1 }, |
367 | .gate = { CGU_REG_CLKGR0, 1 }, |
368 | }, |
369 | |
370 | [X1830_CLK_OTG] = { |
371 | "otg" , CGU_CLK_GATE, |
372 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
373 | .gate = { CGU_REG_CLKGR0, 3 }, |
374 | }, |
375 | |
376 | [X1830_CLK_SSI0] = { |
377 | "ssi0" , CGU_CLK_GATE, |
378 | .parents = { X1830_CLK_SSIMUX, -1, -1, -1 }, |
379 | .gate = { CGU_REG_CLKGR0, 6 }, |
380 | }, |
381 | |
382 | [X1830_CLK_SMB0] = { |
383 | "smb0" , CGU_CLK_GATE, |
384 | .parents = { X1830_CLK_PCLK, -1, -1, -1 }, |
385 | .gate = { CGU_REG_CLKGR0, 7 }, |
386 | }, |
387 | |
388 | [X1830_CLK_SMB1] = { |
389 | "smb1" , CGU_CLK_GATE, |
390 | .parents = { X1830_CLK_PCLK, -1, -1, -1 }, |
391 | .gate = { CGU_REG_CLKGR0, 8 }, |
392 | }, |
393 | |
394 | [X1830_CLK_SMB2] = { |
395 | "smb2" , CGU_CLK_GATE, |
396 | .parents = { X1830_CLK_PCLK, -1, -1, -1 }, |
397 | .gate = { CGU_REG_CLKGR0, 9 }, |
398 | }, |
399 | |
400 | [X1830_CLK_UART0] = { |
401 | "uart0" , CGU_CLK_GATE, |
402 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
403 | .gate = { CGU_REG_CLKGR0, 14 }, |
404 | }, |
405 | |
406 | [X1830_CLK_UART1] = { |
407 | "uart1" , CGU_CLK_GATE, |
408 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
409 | .gate = { CGU_REG_CLKGR0, 15 }, |
410 | }, |
411 | |
412 | [X1830_CLK_SSI1] = { |
413 | "ssi1" , CGU_CLK_GATE, |
414 | .parents = { X1830_CLK_SSIMUX, -1, -1, -1 }, |
415 | .gate = { CGU_REG_CLKGR0, 19 }, |
416 | }, |
417 | |
418 | [X1830_CLK_SFC] = { |
419 | "sfc" , CGU_CLK_GATE, |
420 | .parents = { X1830_CLK_SSIPLL, -1, -1, -1 }, |
421 | .gate = { CGU_REG_CLKGR0, 20 }, |
422 | }, |
423 | |
424 | [X1830_CLK_PDMA] = { |
425 | "pdma" , CGU_CLK_GATE, |
426 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
427 | .gate = { CGU_REG_CLKGR0, 21 }, |
428 | }, |
429 | |
430 | [X1830_CLK_TCU] = { |
431 | "tcu" , CGU_CLK_GATE, |
432 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
433 | .gate = { CGU_REG_CLKGR0, 30 }, |
434 | }, |
435 | |
436 | [X1830_CLK_DTRNG] = { |
437 | "dtrng" , CGU_CLK_GATE, |
438 | .parents = { X1830_CLK_PCLK, -1, -1, -1 }, |
439 | .gate = { CGU_REG_CLKGR1, 1 }, |
440 | }, |
441 | |
442 | [X1830_CLK_OST] = { |
443 | "ost" , CGU_CLK_GATE, |
444 | .parents = { X1830_CLK_EXCLK, -1, -1, -1 }, |
445 | .gate = { CGU_REG_CLKGR1, 11 }, |
446 | }, |
447 | }; |
448 | |
449 | static void __init x1830_cgu_init(struct device_node *np) |
450 | { |
451 | int retval; |
452 | |
453 | cgu = ingenic_cgu_new(clock_info: x1830_cgu_clocks, |
454 | ARRAY_SIZE(x1830_cgu_clocks), np); |
455 | if (!cgu) { |
456 | pr_err("%s: failed to initialise CGU\n" , __func__); |
457 | return; |
458 | } |
459 | |
460 | retval = ingenic_cgu_register_clocks(cgu); |
461 | if (retval) { |
462 | pr_err("%s: failed to register CGU Clocks\n" , __func__); |
463 | return; |
464 | } |
465 | |
466 | ingenic_cgu_register_syscore_ops(cgu); |
467 | } |
468 | /* |
469 | * CGU has some children devices, this is useful for probing children devices |
470 | * in the case where the device node is compatible with "simple-mfd". |
471 | */ |
472 | CLK_OF_DECLARE_DRIVER(x1830_cgu, "ingenic,x1830-cgu" , x1830_cgu_init); |
473 | |