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);