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 */
46 /* bits within the USBPCR1 register */
47 #define USBPCR1_UHC_POWER BIT(5) /* UHC PHY power down */
49 static struct ingenic_cgu
*cgu
;
51 static int jz4770_uhc_phy_enable(struct clk_hw
*hw
)
53 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
54 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
56 writel(readl(reg_opcr
) & ~OPCR_SPENDH
, reg_opcr
);
57 writel(readl(reg_usbpcr1
) | USBPCR1_UHC_POWER
, reg_usbpcr1
);
61 static void jz4770_uhc_phy_disable(struct clk_hw
*hw
)
63 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
64 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
66 writel(readl(reg_usbpcr1
) & ~USBPCR1_UHC_POWER
, reg_usbpcr1
);
67 writel(readl(reg_opcr
) | OPCR_SPENDH
, reg_opcr
);
70 static int jz4770_uhc_phy_is_enabled(struct clk_hw
*hw
)
72 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
73 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
75 return !(readl(reg_opcr
) & OPCR_SPENDH
) &&
76 (readl(reg_usbpcr1
) & USBPCR1_UHC_POWER
);
79 static const struct clk_ops jz4770_uhc_phy_ops
= {
80 .enable
= jz4770_uhc_phy_enable
,
81 .disable
= jz4770_uhc_phy_disable
,
82 .is_enabled
= jz4770_uhc_phy_is_enabled
,
85 static const s8 pll_od_encoding
[8] = {
86 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
89 static const struct ingenic_cgu_clk_info jz4770_cgu_clocks
[] = {
93 [JZ4770_CLK_EXT
] = { "ext", CGU_CLK_EXT
},
94 [JZ4770_CLK_OSC32K
] = { "osc32k", CGU_CLK_EXT
},
100 .parents
= { JZ4770_CLK_EXT
},
102 .reg
= CGU_REG_CPPCR0
,
112 .od_encoding
= pll_od_encoding
,
119 [JZ4770_CLK_PLL1
] = {
120 /* TODO: PLL1 can depend on PLL0 */
122 .parents
= { JZ4770_CLK_EXT
},
124 .reg
= CGU_REG_CPPCR1
,
134 .od_encoding
= pll_od_encoding
,
137 .no_bypass_bit
= true,
143 [JZ4770_CLK_CCLK
] = {
145 .parents
= { JZ4770_CLK_PLL0
, },
146 .div
= { CGU_REG_CPCCR
, 0, 1, 4, 22, -1, -1 },
148 [JZ4770_CLK_H0CLK
] = {
149 "h0clk", CGU_CLK_DIV
,
150 .parents
= { JZ4770_CLK_PLL0
, },
151 .div
= { CGU_REG_CPCCR
, 4, 1, 4, 22, -1, -1 },
153 [JZ4770_CLK_H1CLK
] = {
154 "h1clk", CGU_CLK_DIV
| CGU_CLK_GATE
,
155 .parents
= { JZ4770_CLK_PLL0
, },
156 .div
= { CGU_REG_CPCCR
, 24, 1, 4, 22, -1, -1 },
157 .gate
= { CGU_REG_CLKGR1
, 7 },
159 [JZ4770_CLK_H2CLK
] = {
160 "h2clk", CGU_CLK_DIV
,
161 .parents
= { JZ4770_CLK_PLL0
, },
162 .div
= { CGU_REG_CPCCR
, 16, 1, 4, 22, -1, -1 },
164 [JZ4770_CLK_C1CLK
] = {
165 "c1clk", CGU_CLK_DIV
| CGU_CLK_GATE
,
166 .parents
= { JZ4770_CLK_PLL0
, },
167 .div
= { CGU_REG_CPCCR
, 12, 1, 4, 22, -1, -1 },
168 .gate
= { CGU_REG_OPCR
, 31, true }, // disable CCLK stop on idle
170 [JZ4770_CLK_PCLK
] = {
172 .parents
= { JZ4770_CLK_PLL0
, },
173 .div
= { CGU_REG_CPCCR
, 8, 1, 4, 22, -1, -1 },
176 /* Those divided clocks can connect to PLL0 or PLL1 */
178 [JZ4770_CLK_MMC0_MUX
] = {
179 "mmc0_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
180 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
181 .mux
= { CGU_REG_MSC0CDR
, 30, 1 },
182 .div
= { CGU_REG_MSC0CDR
, 0, 1, 7, -1, -1, 31 },
183 .gate
= { CGU_REG_MSC0CDR
, 31 },
185 [JZ4770_CLK_MMC1_MUX
] = {
186 "mmc1_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
187 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
188 .mux
= { CGU_REG_MSC1CDR
, 30, 1 },
189 .div
= { CGU_REG_MSC1CDR
, 0, 1, 7, -1, -1, 31 },
190 .gate
= { CGU_REG_MSC1CDR
, 31 },
192 [JZ4770_CLK_MMC2_MUX
] = {
193 "mmc2_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
194 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
195 .mux
= { CGU_REG_MSC2CDR
, 30, 1 },
196 .div
= { CGU_REG_MSC2CDR
, 0, 1, 7, -1, -1, 31 },
197 .gate
= { CGU_REG_MSC2CDR
, 31 },
200 "cim", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
201 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
202 .mux
= { CGU_REG_CIMCDR
, 31, 1 },
203 .div
= { CGU_REG_CIMCDR
, 0, 1, 8, -1, -1, -1 },
204 .gate
= { CGU_REG_CLKGR0
, 26 },
207 "uhc", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
208 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
209 .mux
= { CGU_REG_UHCCDR
, 29, 1 },
210 .div
= { CGU_REG_UHCCDR
, 0, 1, 4, -1, -1, -1 },
211 .gate
= { CGU_REG_CLKGR0
, 24 },
214 "gpu", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
215 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, -1 },
216 .mux
= { CGU_REG_GPUCDR
, 31, 1 },
217 .div
= { CGU_REG_GPUCDR
, 0, 1, 3, -1, -1, -1 },
218 .gate
= { CGU_REG_CLKGR1
, 9 },
221 "bch", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
222 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
223 .mux
= { CGU_REG_BCHCDR
, 31, 1 },
224 .div
= { CGU_REG_BCHCDR
, 0, 1, 3, -1, -1, -1 },
225 .gate
= { CGU_REG_CLKGR0
, 1 },
227 [JZ4770_CLK_LPCLK_MUX
] = {
228 "lpclk", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
229 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
230 .mux
= { CGU_REG_LPCDR
, 29, 1 },
231 .div
= { CGU_REG_LPCDR
, 0, 1, 11, -1, -1, -1 },
232 .gate
= { CGU_REG_CLKGR0
, 28 },
235 "gps", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
236 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
237 .mux
= { CGU_REG_GPSCDR
, 31, 1 },
238 .div
= { CGU_REG_GPSCDR
, 0, 1, 4, -1, -1, -1 },
239 .gate
= { CGU_REG_CLKGR0
, 22 },
242 /* Those divided clocks can connect to EXT, PLL0 or PLL1 */
244 [JZ4770_CLK_SSI_MUX
] = {
245 "ssi_mux", CGU_CLK_DIV
| CGU_CLK_MUX
,
246 .parents
= { JZ4770_CLK_EXT
, -1,
247 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
248 .mux
= { CGU_REG_SSICDR
, 30, 2 },
249 .div
= { CGU_REG_SSICDR
, 0, 1, 6, -1, -1, -1 },
251 [JZ4770_CLK_PCM_MUX
] = {
252 "pcm_mux", CGU_CLK_DIV
| CGU_CLK_MUX
,
253 .parents
= { JZ4770_CLK_EXT
, -1,
254 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
255 .mux
= { CGU_REG_PCMCDR
, 30, 2 },
256 .div
= { CGU_REG_PCMCDR
, 0, 1, 9, -1, -1, -1 },
259 "i2s", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
260 .parents
= { JZ4770_CLK_EXT
, -1,
261 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
262 .mux
= { CGU_REG_I2SCDR
, 30, 2 },
263 .div
= { CGU_REG_I2SCDR
, 0, 1, 9, -1, -1, -1 },
264 .gate
= { CGU_REG_CLKGR1
, 13 },
267 "usb", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
268 .parents
= { JZ4770_CLK_EXT
, -1,
269 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
270 .mux
= { CGU_REG_USBCDR
, 30, 2 },
271 .div
= { CGU_REG_USBCDR
, 0, 1, 8, -1, -1, -1 },
272 .gate
= { CGU_REG_CLKGR0
, 2 },
275 /* Gate-only clocks */
277 [JZ4770_CLK_SSI0
] = {
278 "ssi0", CGU_CLK_GATE
,
279 .parents
= { JZ4770_CLK_SSI_MUX
, },
280 .gate
= { CGU_REG_CLKGR0
, 4 },
282 [JZ4770_CLK_SSI1
] = {
283 "ssi1", CGU_CLK_GATE
,
284 .parents
= { JZ4770_CLK_SSI_MUX
, },
285 .gate
= { CGU_REG_CLKGR0
, 19 },
287 [JZ4770_CLK_SSI2
] = {
288 "ssi2", CGU_CLK_GATE
,
289 .parents
= { JZ4770_CLK_SSI_MUX
, },
290 .gate
= { CGU_REG_CLKGR0
, 20 },
292 [JZ4770_CLK_PCM0
] = {
293 "pcm0", CGU_CLK_GATE
,
294 .parents
= { JZ4770_CLK_PCM_MUX
, },
295 .gate
= { CGU_REG_CLKGR1
, 8 },
297 [JZ4770_CLK_PCM1
] = {
298 "pcm1", CGU_CLK_GATE
,
299 .parents
= { JZ4770_CLK_PCM_MUX
, },
300 .gate
= { CGU_REG_CLKGR1
, 10 },
304 .parents
= { JZ4770_CLK_H2CLK
, },
305 .gate
= { CGU_REG_CLKGR0
, 21 },
307 [JZ4770_CLK_I2C0
] = {
308 "i2c0", CGU_CLK_GATE
,
309 .parents
= { JZ4770_CLK_EXT
, },
310 .gate
= { CGU_REG_CLKGR0
, 5 },
312 [JZ4770_CLK_I2C1
] = {
313 "i2c1", CGU_CLK_GATE
,
314 .parents
= { JZ4770_CLK_EXT
, },
315 .gate
= { CGU_REG_CLKGR0
, 6 },
317 [JZ4770_CLK_I2C2
] = {
318 "i2c2", CGU_CLK_GATE
,
319 .parents
= { JZ4770_CLK_EXT
, },
320 .gate
= { CGU_REG_CLKGR1
, 15 },
322 [JZ4770_CLK_UART0
] = {
323 "uart0", CGU_CLK_GATE
,
324 .parents
= { JZ4770_CLK_EXT
, },
325 .gate
= { CGU_REG_CLKGR0
, 15 },
327 [JZ4770_CLK_UART1
] = {
328 "uart1", CGU_CLK_GATE
,
329 .parents
= { JZ4770_CLK_EXT
, },
330 .gate
= { CGU_REG_CLKGR0
, 16 },
332 [JZ4770_CLK_UART2
] = {
333 "uart2", CGU_CLK_GATE
,
334 .parents
= { JZ4770_CLK_EXT
, },
335 .gate
= { CGU_REG_CLKGR0
, 17 },
337 [JZ4770_CLK_UART3
] = {
338 "uart3", CGU_CLK_GATE
,
339 .parents
= { JZ4770_CLK_EXT
, },
340 .gate
= { CGU_REG_CLKGR0
, 18 },
344 .parents
= { JZ4770_CLK_H0CLK
, },
345 .gate
= { CGU_REG_CLKGR0
, 29 },
349 .parents
= { JZ4770_CLK_EXT
, },
350 .gate
= { CGU_REG_CLKGR0
, 14 },
354 .parents
= { JZ4770_CLK_EXT
, },
355 .gate
= { CGU_REG_CLKGR0
, 8 },
359 .parents
= { JZ4770_CLK_C1CLK
, },
360 .gate
= { CGU_REG_CLKGR1
, 14 },
364 .parents
= { JZ4770_CLK_H1CLK
, },
365 .gate
= { CGU_REG_LCR
, 30, false, 150 },
367 [JZ4770_CLK_MMC0
] = {
368 "mmc0", CGU_CLK_GATE
,
369 .parents
= { JZ4770_CLK_MMC0_MUX
, },
370 .gate
= { CGU_REG_CLKGR0
, 3 },
372 [JZ4770_CLK_MMC1
] = {
373 "mmc1", CGU_CLK_GATE
,
374 .parents
= { JZ4770_CLK_MMC1_MUX
, },
375 .gate
= { CGU_REG_CLKGR0
, 11 },
377 [JZ4770_CLK_MMC2
] = {
378 "mmc2", CGU_CLK_GATE
,
379 .parents
= { JZ4770_CLK_MMC2_MUX
, },
380 .gate
= { CGU_REG_CLKGR0
, 12 },
382 [JZ4770_CLK_OTG_PHY
] = {
383 "usb_phy", CGU_CLK_GATE
,
384 .parents
= { JZ4770_CLK_OTG
},
385 .gate
= { CGU_REG_OPCR
, 7, true, 50 },
390 [JZ4770_CLK_UHC_PHY
] = {
391 "uhc_phy", CGU_CLK_CUSTOM
,
392 .parents
= { JZ4770_CLK_UHC
, -1, -1, -1 },
393 .custom
= { &jz4770_uhc_phy_ops
},
396 [JZ4770_CLK_EXT512
] = {
397 "ext/512", CGU_CLK_FIXDIV
,
398 .parents
= { JZ4770_CLK_EXT
},
404 .parents
= { JZ4770_CLK_EXT512
, JZ4770_CLK_OSC32K
, },
405 .mux
= { CGU_REG_OPCR
, 2, 1},
409 #if IS_ENABLED(CONFIG_PM_SLEEP)
410 static int jz4770_cgu_pm_suspend(void)
414 val
= readl(cgu
->base
+ CGU_REG_LCR
);
415 writel(val
| LCR_LPM
, cgu
->base
+ CGU_REG_LCR
);
419 static void jz4770_cgu_pm_resume(void)
423 val
= readl(cgu
->base
+ CGU_REG_LCR
);
424 writel(val
& ~LCR_LPM
, cgu
->base
+ CGU_REG_LCR
);
427 static struct syscore_ops jz4770_cgu_pm_ops
= {
428 .suspend
= jz4770_cgu_pm_suspend
,
429 .resume
= jz4770_cgu_pm_resume
,
431 #endif /* CONFIG_PM_SLEEP */
433 static void __init
jz4770_cgu_init(struct device_node
*np
)
437 cgu
= ingenic_cgu_new(jz4770_cgu_clocks
,
438 ARRAY_SIZE(jz4770_cgu_clocks
), np
);
440 pr_err("%s: failed to initialise CGU\n", __func__
);
442 retval
= ingenic_cgu_register_clocks(cgu
);
444 pr_err("%s: failed to register CGU Clocks\n", __func__
);
446 #if IS_ENABLED(CONFIG_PM_SLEEP)
447 register_syscore_ops(&jz4770_cgu_pm_ops
);
451 /* We only probe via devicetree, no need for a platform driver */
452 CLK_OF_DECLARE(jz4770_cgu
, "ingenic,jz4770-cgu", jz4770_cgu_init
);