1 // SPDX-License-Identifier: GPL-2.0
3 * JZ4770 SoC CGU driver
4 * Copyright 2018, Paul Cercueil <paul@crapouillou.net>
7 #include <linux/bitops.h>
8 #include <linux/clk-provider.h>
9 #include <linux/delay.h>
11 #include <linux/syscore_ops.h>
12 #include <dt-bindings/clock/jz4770-cgu.h>
16 * CPM registers offset address definition
18 #define CGU_REG_CPCCR 0x00
19 #define CGU_REG_LCR 0x04
20 #define CGU_REG_CPPCR0 0x10
21 #define CGU_REG_CLKGR0 0x20
22 #define CGU_REG_OPCR 0x24
23 #define CGU_REG_CLKGR1 0x28
24 #define CGU_REG_CPPCR1 0x30
25 #define CGU_REG_USBPCR1 0x48
26 #define CGU_REG_USBCDR 0x50
27 #define CGU_REG_I2SCDR 0x60
28 #define CGU_REG_LPCDR 0x64
29 #define CGU_REG_MSC0CDR 0x68
30 #define CGU_REG_UHCCDR 0x6c
31 #define CGU_REG_SSICDR 0x74
32 #define CGU_REG_CIMCDR 0x7c
33 #define CGU_REG_GPSCDR 0x80
34 #define CGU_REG_PCMCDR 0x84
35 #define CGU_REG_GPUCDR 0x88
36 #define CGU_REG_MSC1CDR 0xA4
37 #define CGU_REG_MSC2CDR 0xA8
38 #define CGU_REG_BCHCDR 0xAC
40 /* bits within the LCR register */
41 #define LCR_LPM BIT(0) /* Low Power Mode */
43 /* bits within the OPCR register */
44 #define OPCR_SPENDH BIT(5) /* UHC PHY suspend */
45 #define OPCR_SPENDN BIT(7) /* OTG PHY suspend */
47 /* bits within the USBPCR1 register */
48 #define USBPCR1_UHC_POWER BIT(5) /* UHC PHY power down */
50 static struct ingenic_cgu
*cgu
;
52 static int jz4770_uhc_phy_enable(struct clk_hw
*hw
)
54 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
55 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
57 writel(readl(reg_opcr
) & ~OPCR_SPENDH
, reg_opcr
);
58 writel(readl(reg_usbpcr1
) | USBPCR1_UHC_POWER
, reg_usbpcr1
);
62 static void jz4770_uhc_phy_disable(struct clk_hw
*hw
)
64 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
65 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
67 writel(readl(reg_usbpcr1
) & ~USBPCR1_UHC_POWER
, reg_usbpcr1
);
68 writel(readl(reg_opcr
) | OPCR_SPENDH
, reg_opcr
);
71 static int jz4770_uhc_phy_is_enabled(struct clk_hw
*hw
)
73 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
74 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
76 return !(readl(reg_opcr
) & OPCR_SPENDH
) &&
77 (readl(reg_usbpcr1
) & USBPCR1_UHC_POWER
);
80 static const struct clk_ops jz4770_uhc_phy_ops
= {
81 .enable
= jz4770_uhc_phy_enable
,
82 .disable
= jz4770_uhc_phy_disable
,
83 .is_enabled
= jz4770_uhc_phy_is_enabled
,
86 static int jz4770_otg_phy_enable(struct clk_hw
*hw
)
88 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
90 writel(readl(reg_opcr
) | OPCR_SPENDN
, reg_opcr
);
92 /* Wait for the clock to be stable */
97 static void jz4770_otg_phy_disable(struct clk_hw
*hw
)
99 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
101 writel(readl(reg_opcr
) & ~OPCR_SPENDN
, reg_opcr
);
104 static int jz4770_otg_phy_is_enabled(struct clk_hw
*hw
)
106 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
108 return !!(readl(reg_opcr
) & OPCR_SPENDN
);
111 static const struct clk_ops jz4770_otg_phy_ops
= {
112 .enable
= jz4770_otg_phy_enable
,
113 .disable
= jz4770_otg_phy_disable
,
114 .is_enabled
= jz4770_otg_phy_is_enabled
,
117 static const s8 pll_od_encoding
[8] = {
118 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
121 static const struct ingenic_cgu_clk_info jz4770_cgu_clocks
[] = {
123 /* External clocks */
125 [JZ4770_CLK_EXT
] = { "ext", CGU_CLK_EXT
},
126 [JZ4770_CLK_OSC32K
] = { "osc32k", CGU_CLK_EXT
},
130 [JZ4770_CLK_PLL0
] = {
132 .parents
= { JZ4770_CLK_EXT
},
134 .reg
= CGU_REG_CPPCR0
,
144 .od_encoding
= pll_od_encoding
,
151 [JZ4770_CLK_PLL1
] = {
152 /* TODO: PLL1 can depend on PLL0 */
154 .parents
= { JZ4770_CLK_EXT
},
156 .reg
= CGU_REG_CPPCR1
,
166 .od_encoding
= pll_od_encoding
,
169 .no_bypass_bit
= true,
175 [JZ4770_CLK_CCLK
] = {
177 .parents
= { JZ4770_CLK_PLL0
, },
178 .div
= { CGU_REG_CPCCR
, 0, 1, 4, 22, -1, -1 },
180 [JZ4770_CLK_H0CLK
] = {
181 "h0clk", CGU_CLK_DIV
,
182 .parents
= { JZ4770_CLK_PLL0
, },
183 .div
= { CGU_REG_CPCCR
, 4, 1, 4, 22, -1, -1 },
185 [JZ4770_CLK_H1CLK
] = {
186 "h1clk", CGU_CLK_DIV
| CGU_CLK_GATE
,
187 .parents
= { JZ4770_CLK_PLL0
, },
188 .div
= { CGU_REG_CPCCR
, 24, 1, 4, 22, -1, -1 },
189 .gate
= { CGU_REG_LCR
, 30 },
191 [JZ4770_CLK_H2CLK
] = {
192 "h2clk", CGU_CLK_DIV
,
193 .parents
= { JZ4770_CLK_PLL0
, },
194 .div
= { CGU_REG_CPCCR
, 16, 1, 4, 22, -1, -1 },
196 [JZ4770_CLK_C1CLK
] = {
197 "c1clk", CGU_CLK_DIV
,
198 .parents
= { JZ4770_CLK_PLL0
, },
199 .div
= { CGU_REG_CPCCR
, 12, 1, 4, 22, -1, -1 },
201 [JZ4770_CLK_PCLK
] = {
203 .parents
= { JZ4770_CLK_PLL0
, },
204 .div
= { CGU_REG_CPCCR
, 8, 1, 4, 22, -1, -1 },
207 /* Those divided clocks can connect to PLL0 or PLL1 */
209 [JZ4770_CLK_MMC0_MUX
] = {
210 "mmc0_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
211 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
212 .mux
= { CGU_REG_MSC0CDR
, 30, 1 },
213 .div
= { CGU_REG_MSC0CDR
, 0, 1, 7, -1, -1, 31 },
214 .gate
= { CGU_REG_MSC0CDR
, 31 },
216 [JZ4770_CLK_MMC1_MUX
] = {
217 "mmc1_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
218 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
219 .mux
= { CGU_REG_MSC1CDR
, 30, 1 },
220 .div
= { CGU_REG_MSC1CDR
, 0, 1, 7, -1, -1, 31 },
221 .gate
= { CGU_REG_MSC1CDR
, 31 },
223 [JZ4770_CLK_MMC2_MUX
] = {
224 "mmc2_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
225 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
226 .mux
= { CGU_REG_MSC2CDR
, 30, 1 },
227 .div
= { CGU_REG_MSC2CDR
, 0, 1, 7, -1, -1, 31 },
228 .gate
= { CGU_REG_MSC2CDR
, 31 },
231 "cim", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
232 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
233 .mux
= { CGU_REG_CIMCDR
, 31, 1 },
234 .div
= { CGU_REG_CIMCDR
, 0, 1, 8, -1, -1, -1 },
235 .gate
= { CGU_REG_CLKGR0
, 26 },
238 "uhc", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
239 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
240 .mux
= { CGU_REG_UHCCDR
, 29, 1 },
241 .div
= { CGU_REG_UHCCDR
, 0, 1, 4, -1, -1, -1 },
242 .gate
= { CGU_REG_CLKGR0
, 24 },
245 "gpu", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
246 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, -1 },
247 .mux
= { CGU_REG_GPUCDR
, 31, 1 },
248 .div
= { CGU_REG_GPUCDR
, 0, 1, 3, -1, -1, -1 },
249 .gate
= { CGU_REG_CLKGR1
, 9 },
252 "bch", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
253 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
254 .mux
= { CGU_REG_BCHCDR
, 31, 1 },
255 .div
= { CGU_REG_BCHCDR
, 0, 1, 3, -1, -1, -1 },
256 .gate
= { CGU_REG_CLKGR0
, 1 },
258 [JZ4770_CLK_LPCLK_MUX
] = {
259 "lpclk", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
260 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
261 .mux
= { CGU_REG_LPCDR
, 29, 1 },
262 .div
= { CGU_REG_LPCDR
, 0, 1, 11, -1, -1, -1 },
263 .gate
= { CGU_REG_CLKGR0
, 28 },
266 "gps", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
267 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
268 .mux
= { CGU_REG_GPSCDR
, 31, 1 },
269 .div
= { CGU_REG_GPSCDR
, 0, 1, 4, -1, -1, -1 },
270 .gate
= { CGU_REG_CLKGR0
, 22 },
273 /* Those divided clocks can connect to EXT, PLL0 or PLL1 */
275 [JZ4770_CLK_SSI_MUX
] = {
276 "ssi_mux", CGU_CLK_DIV
| CGU_CLK_MUX
,
277 .parents
= { JZ4770_CLK_EXT
, -1,
278 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
279 .mux
= { CGU_REG_SSICDR
, 30, 2 },
280 .div
= { CGU_REG_SSICDR
, 0, 1, 6, -1, -1, -1 },
282 [JZ4770_CLK_PCM_MUX
] = {
283 "pcm_mux", CGU_CLK_DIV
| CGU_CLK_MUX
,
284 .parents
= { JZ4770_CLK_EXT
, -1,
285 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
286 .mux
= { CGU_REG_PCMCDR
, 30, 2 },
287 .div
= { CGU_REG_PCMCDR
, 0, 1, 9, -1, -1, -1 },
290 "i2s", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
291 .parents
= { JZ4770_CLK_EXT
, -1,
292 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
293 .mux
= { CGU_REG_I2SCDR
, 30, 2 },
294 .div
= { CGU_REG_I2SCDR
, 0, 1, 9, -1, -1, -1 },
295 .gate
= { CGU_REG_CLKGR1
, 13 },
298 "usb", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
299 .parents
= { JZ4770_CLK_EXT
, -1,
300 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
301 .mux
= { CGU_REG_USBCDR
, 30, 2 },
302 .div
= { CGU_REG_USBCDR
, 0, 1, 8, -1, -1, -1 },
303 .gate
= { CGU_REG_CLKGR0
, 2 },
306 /* Gate-only clocks */
308 [JZ4770_CLK_SSI0
] = {
309 "ssi0", CGU_CLK_GATE
,
310 .parents
= { JZ4770_CLK_SSI_MUX
, },
311 .gate
= { CGU_REG_CLKGR0
, 4 },
313 [JZ4770_CLK_SSI1
] = {
314 "ssi1", CGU_CLK_GATE
,
315 .parents
= { JZ4770_CLK_SSI_MUX
, },
316 .gate
= { CGU_REG_CLKGR0
, 19 },
318 [JZ4770_CLK_SSI2
] = {
319 "ssi2", CGU_CLK_GATE
,
320 .parents
= { JZ4770_CLK_SSI_MUX
, },
321 .gate
= { CGU_REG_CLKGR0
, 20 },
323 [JZ4770_CLK_PCM0
] = {
324 "pcm0", CGU_CLK_GATE
,
325 .parents
= { JZ4770_CLK_PCM_MUX
, },
326 .gate
= { CGU_REG_CLKGR1
, 8 },
328 [JZ4770_CLK_PCM1
] = {
329 "pcm1", CGU_CLK_GATE
,
330 .parents
= { JZ4770_CLK_PCM_MUX
, },
331 .gate
= { CGU_REG_CLKGR1
, 10 },
335 .parents
= { JZ4770_CLK_H2CLK
, },
336 .gate
= { CGU_REG_CLKGR0
, 21 },
338 [JZ4770_CLK_I2C0
] = {
339 "i2c0", CGU_CLK_GATE
,
340 .parents
= { JZ4770_CLK_EXT
, },
341 .gate
= { CGU_REG_CLKGR0
, 5 },
343 [JZ4770_CLK_I2C1
] = {
344 "i2c1", CGU_CLK_GATE
,
345 .parents
= { JZ4770_CLK_EXT
, },
346 .gate
= { CGU_REG_CLKGR0
, 6 },
348 [JZ4770_CLK_I2C2
] = {
349 "i2c2", CGU_CLK_GATE
,
350 .parents
= { JZ4770_CLK_EXT
, },
351 .gate
= { CGU_REG_CLKGR1
, 15 },
353 [JZ4770_CLK_UART0
] = {
354 "uart0", CGU_CLK_GATE
,
355 .parents
= { JZ4770_CLK_EXT
, },
356 .gate
= { CGU_REG_CLKGR0
, 15 },
358 [JZ4770_CLK_UART1
] = {
359 "uart1", CGU_CLK_GATE
,
360 .parents
= { JZ4770_CLK_EXT
, },
361 .gate
= { CGU_REG_CLKGR0
, 16 },
363 [JZ4770_CLK_UART2
] = {
364 "uart2", CGU_CLK_GATE
,
365 .parents
= { JZ4770_CLK_EXT
, },
366 .gate
= { CGU_REG_CLKGR0
, 17 },
368 [JZ4770_CLK_UART3
] = {
369 "uart3", CGU_CLK_GATE
,
370 .parents
= { JZ4770_CLK_EXT
, },
371 .gate
= { CGU_REG_CLKGR0
, 18 },
375 .parents
= { JZ4770_CLK_H0CLK
, },
376 .gate
= { CGU_REG_CLKGR0
, 29 },
380 .parents
= { JZ4770_CLK_EXT
, },
381 .gate
= { CGU_REG_CLKGR0
, 14 },
385 .parents
= { JZ4770_CLK_EXT
, },
386 .gate
= { CGU_REG_CLKGR0
, 8 },
390 .parents
= { JZ4770_CLK_C1CLK
, },
391 .gate
= { CGU_REG_CLKGR1
, 14 },
395 .parents
= { JZ4770_CLK_H1CLK
, },
396 .gate
= { CGU_REG_CLKGR1
, 7 },
398 [JZ4770_CLK_MMC0
] = {
399 "mmc0", CGU_CLK_GATE
,
400 .parents
= { JZ4770_CLK_MMC0_MUX
, },
401 .gate
= { CGU_REG_CLKGR0
, 3 },
403 [JZ4770_CLK_MMC1
] = {
404 "mmc1", CGU_CLK_GATE
,
405 .parents
= { JZ4770_CLK_MMC1_MUX
, },
406 .gate
= { CGU_REG_CLKGR0
, 11 },
408 [JZ4770_CLK_MMC2
] = {
409 "mmc2", CGU_CLK_GATE
,
410 .parents
= { JZ4770_CLK_MMC2_MUX
, },
411 .gate
= { CGU_REG_CLKGR0
, 12 },
416 [JZ4770_CLK_UHC_PHY
] = {
417 "uhc_phy", CGU_CLK_CUSTOM
,
418 .parents
= { JZ4770_CLK_UHC
, -1, -1, -1 },
419 .custom
= { &jz4770_uhc_phy_ops
},
421 [JZ4770_CLK_OTG_PHY
] = {
422 "usb_phy", CGU_CLK_CUSTOM
,
423 .parents
= { JZ4770_CLK_OTG
, -1, -1, -1 },
424 .custom
= { &jz4770_otg_phy_ops
},
427 [JZ4770_CLK_EXT512
] = {
428 "ext/512", CGU_CLK_FIXDIV
,
429 .parents
= { JZ4770_CLK_EXT
},
435 .parents
= { JZ4770_CLK_EXT512
, JZ4770_CLK_OSC32K
, },
436 .mux
= { CGU_REG_OPCR
, 2, 1},
440 #if IS_ENABLED(CONFIG_PM_SLEEP)
441 static int jz4770_cgu_pm_suspend(void)
445 val
= readl(cgu
->base
+ CGU_REG_LCR
);
446 writel(val
| LCR_LPM
, cgu
->base
+ CGU_REG_LCR
);
450 static void jz4770_cgu_pm_resume(void)
454 val
= readl(cgu
->base
+ CGU_REG_LCR
);
455 writel(val
& ~LCR_LPM
, cgu
->base
+ CGU_REG_LCR
);
458 static struct syscore_ops jz4770_cgu_pm_ops
= {
459 .suspend
= jz4770_cgu_pm_suspend
,
460 .resume
= jz4770_cgu_pm_resume
,
462 #endif /* CONFIG_PM_SLEEP */
464 static void __init
jz4770_cgu_init(struct device_node
*np
)
468 cgu
= ingenic_cgu_new(jz4770_cgu_clocks
,
469 ARRAY_SIZE(jz4770_cgu_clocks
), np
);
471 pr_err("%s: failed to initialise CGU\n", __func__
);
473 retval
= ingenic_cgu_register_clocks(cgu
);
475 pr_err("%s: failed to register CGU Clocks\n", __func__
);
477 #if IS_ENABLED(CONFIG_PM_SLEEP)
478 register_syscore_ops(&jz4770_cgu_pm_ops
);
482 /* We only probe via devicetree, no need for a platform driver */
483 CLK_OF_DECLARE(jz4770_cgu
, "ingenic,jz4770-cgu", jz4770_cgu_init
);