1 // SPDX-License-Identifier: GPL-2.0
4 * Copyright (c) 2019 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
7 #include <linux/clk-provider.h>
8 #include <linux/delay.h>
12 #include <dt-bindings/clock/ingenic,x1830-cgu.h>
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
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)
51 /* bits within the USBPCR register */
52 #define USBPCR_SIDDQ BIT(21)
53 #define USBPCR_OTG_DISABLE BIT(20)
55 static struct ingenic_cgu
*cgu
;
57 static int x1830_usb_phy_enable(struct clk_hw
*hw
)
59 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
60 void __iomem
*reg_usbpcr
= cgu
->base
+ CGU_REG_USBPCR
;
62 writel((readl(reg_opcr
) | OPCR_SPENDN0
) & ~OPCR_GATE_USBPHYCLK
, reg_opcr
);
63 writel(readl(reg_usbpcr
) & ~USBPCR_OTG_DISABLE
& ~USBPCR_SIDDQ
, reg_usbpcr
);
67 static void x1830_usb_phy_disable(struct clk_hw
*hw
)
69 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
70 void __iomem
*reg_usbpcr
= cgu
->base
+ CGU_REG_USBPCR
;
72 writel((readl(reg_opcr
) & ~OPCR_SPENDN0
) | OPCR_GATE_USBPHYCLK
, reg_opcr
);
73 writel(readl(reg_usbpcr
) | USBPCR_OTG_DISABLE
| USBPCR_SIDDQ
, reg_usbpcr
);
76 static int x1830_usb_phy_is_enabled(struct clk_hw
*hw
)
78 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
79 void __iomem
*reg_usbpcr
= cgu
->base
+ CGU_REG_USBPCR
;
81 return (readl(reg_opcr
) & OPCR_SPENDN0
) &&
82 !(readl(reg_usbpcr
) & USBPCR_SIDDQ
) &&
83 !(readl(reg_usbpcr
) & USBPCR_OTG_DISABLE
);
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
,
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,
103 static const struct ingenic_cgu_clk_info x1830_cgu_clocks
[] = {
105 /* External clocks */
107 [X1830_CLK_EXCLK
] = { "ext", CGU_CLK_EXT
},
108 [X1830_CLK_RTCLK
] = { "rtc", CGU_CLK_EXT
},
114 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
117 .rate_multiplier
= 2,
127 .od_encoding
= pll_od_encoding
,
128 .bypass_reg
= CGU_REG_CPPCR
,
137 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
140 .rate_multiplier
= 2,
150 .od_encoding
= pll_od_encoding
,
151 .bypass_reg
= CGU_REG_CPPCR
,
160 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
163 .rate_multiplier
= 2,
173 .od_encoding
= pll_od_encoding
,
174 .bypass_reg
= CGU_REG_CPPCR
,
183 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
186 .rate_multiplier
= 2,
196 .od_encoding
= pll_od_encoding
,
197 .bypass_reg
= CGU_REG_CPPCR
,
204 /* Custom (SoC-specific) OTG PHY */
206 [X1830_CLK_OTGPHY
] = {
207 "otg_phy", CGU_CLK_CUSTOM
,
208 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
209 .custom
= { &x1830_otg_phy_ops
},
212 /* Muxes & dividers */
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 },
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 },
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 },
234 [X1830_CLK_L2CACHE
] = {
235 "l2cache", CGU_CLK_DIV
,
237 * The L2 cache clock is critical if caches are enabled and
238 * disabling it or any parent clocks will hang the system.
240 .flags
= CLK_IS_CRITICAL
,
241 .parents
= { X1830_CLK_CPUMUX
, -1, -1, -1 },
242 .div
= { CGU_REG_CPCCR
, 4, 1, 4, 22, -1, -1 },
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 },
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 },
260 .parents
= { X1830_CLK_AHB2PMUX
, -1, -1, -1 },
261 .div
= { CGU_REG_CPCCR
, 12, 1, 4, 20, -1, -1 },
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 },
272 "ddr", CGU_CLK_MUX
| CGU_CLK_DIV
| CGU_CLK_GATE
,
274 * Disabling DDR clock or its parents will render DRAM
275 * inaccessible; mark it critical.
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 },
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 },
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 },
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 },
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 },
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 },
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 },
331 [X1830_CLK_SSIPLL_DIV2
] = {
332 "ssi_pll_div2", CGU_CLK_FIXDIV
,
333 .parents
= { X1830_CLK_SSIPLL
},
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 },
343 [X1830_CLK_EXCLK_DIV512
] = {
344 "exclk_div512", CGU_CLK_FIXDIV
,
345 .parents
= { X1830_CLK_EXCLK
},
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 },
356 /* Gate-only clocks */
360 .parents
= { X1830_CLK_AHB2
, -1, -1, -1 },
361 .gate
= { CGU_REG_CLKGR0
, 0 },
364 [X1830_CLK_EFUSE
] = {
365 "efuse", CGU_CLK_GATE
,
366 .parents
= { X1830_CLK_AHB2
, -1, -1, -1 },
367 .gate
= { CGU_REG_CLKGR0
, 1 },
372 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
373 .gate
= { CGU_REG_CLKGR0
, 3 },
377 "ssi0", CGU_CLK_GATE
,
378 .parents
= { X1830_CLK_SSIMUX
, -1, -1, -1 },
379 .gate
= { CGU_REG_CLKGR0
, 6 },
383 "smb0", CGU_CLK_GATE
,
384 .parents
= { X1830_CLK_PCLK
, -1, -1, -1 },
385 .gate
= { CGU_REG_CLKGR0
, 7 },
389 "smb1", CGU_CLK_GATE
,
390 .parents
= { X1830_CLK_PCLK
, -1, -1, -1 },
391 .gate
= { CGU_REG_CLKGR0
, 8 },
395 "smb2", CGU_CLK_GATE
,
396 .parents
= { X1830_CLK_PCLK
, -1, -1, -1 },
397 .gate
= { CGU_REG_CLKGR0
, 9 },
400 [X1830_CLK_UART0
] = {
401 "uart0", CGU_CLK_GATE
,
402 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
403 .gate
= { CGU_REG_CLKGR0
, 14 },
406 [X1830_CLK_UART1
] = {
407 "uart1", CGU_CLK_GATE
,
408 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
409 .gate
= { CGU_REG_CLKGR0
, 15 },
413 "ssi1", CGU_CLK_GATE
,
414 .parents
= { X1830_CLK_SSIMUX
, -1, -1, -1 },
415 .gate
= { CGU_REG_CLKGR0
, 19 },
420 .parents
= { X1830_CLK_SSIPLL
, -1, -1, -1 },
421 .gate
= { CGU_REG_CLKGR0
, 20 },
425 "pdma", CGU_CLK_GATE
,
426 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
427 .gate
= { CGU_REG_CLKGR0
, 21 },
432 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
433 .gate
= { CGU_REG_CLKGR0
, 30 },
436 [X1830_CLK_DTRNG
] = {
437 "dtrng", CGU_CLK_GATE
,
438 .parents
= { X1830_CLK_PCLK
, -1, -1, -1 },
439 .gate
= { CGU_REG_CLKGR1
, 1 },
444 .parents
= { X1830_CLK_EXCLK
, -1, -1, -1 },
445 .gate
= { CGU_REG_CLKGR1
, 11 },
449 static void __init
x1830_cgu_init(struct device_node
*np
)
453 cgu
= ingenic_cgu_new(x1830_cgu_clocks
,
454 ARRAY_SIZE(x1830_cgu_clocks
), np
);
456 pr_err("%s: failed to initialise CGU\n", __func__
);
460 retval
= ingenic_cgu_register_clocks(cgu
);
462 pr_err("%s: failed to register CGU Clocks\n", __func__
);
466 ingenic_cgu_register_syscore_ops(cgu
);
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".
472 CLK_OF_DECLARE_DRIVER(x1830_cgu
, "ingenic,x1830-cgu", x1830_cgu_init
);