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>
13 #include <dt-bindings/clock/jz4770-cgu.h>
19 * CPM registers offset address definition
21 #define CGU_REG_CPCCR 0x00
22 #define CGU_REG_LCR 0x04
23 #define CGU_REG_CPPCR0 0x10
24 #define CGU_REG_CLKGR0 0x20
25 #define CGU_REG_OPCR 0x24
26 #define CGU_REG_CLKGR1 0x28
27 #define CGU_REG_CPPCR1 0x30
28 #define CGU_REG_USBPCR1 0x48
29 #define CGU_REG_USBCDR 0x50
30 #define CGU_REG_I2SCDR 0x60
31 #define CGU_REG_LPCDR 0x64
32 #define CGU_REG_MSC0CDR 0x68
33 #define CGU_REG_UHCCDR 0x6c
34 #define CGU_REG_SSICDR 0x74
35 #define CGU_REG_CIMCDR 0x7c
36 #define CGU_REG_GPSCDR 0x80
37 #define CGU_REG_PCMCDR 0x84
38 #define CGU_REG_GPUCDR 0x88
39 #define CGU_REG_MSC1CDR 0xA4
40 #define CGU_REG_MSC2CDR 0xA8
41 #define CGU_REG_BCHCDR 0xAC
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 u8 jz4770_cgu_cpccr_div_table
[] = {
93 static const struct ingenic_cgu_clk_info jz4770_cgu_clocks
[] = {
97 [JZ4770_CLK_EXT
] = { "ext", CGU_CLK_EXT
},
98 [JZ4770_CLK_OSC32K
] = { "osc32k", CGU_CLK_EXT
},
102 [JZ4770_CLK_PLL0
] = {
104 .parents
= { JZ4770_CLK_EXT
},
106 .reg
= CGU_REG_CPPCR0
,
107 .rate_multiplier
= 1,
117 .od_encoding
= pll_od_encoding
,
118 .bypass_reg
= CGU_REG_CPPCR0
,
125 [JZ4770_CLK_PLL1
] = {
126 /* TODO: PLL1 can depend on PLL0 */
128 .parents
= { JZ4770_CLK_EXT
},
130 .reg
= CGU_REG_CPPCR1
,
131 .rate_multiplier
= 1,
141 .od_encoding
= pll_od_encoding
,
142 .bypass_reg
= CGU_REG_CPPCR1
,
143 .no_bypass_bit
= true,
151 [JZ4770_CLK_CCLK
] = {
153 .parents
= { JZ4770_CLK_PLL0
, },
155 CGU_REG_CPCCR
, 0, 1, 4, 22, -1, -1,
156 jz4770_cgu_cpccr_div_table
,
159 [JZ4770_CLK_H0CLK
] = {
160 "h0clk", CGU_CLK_DIV
,
161 .parents
= { JZ4770_CLK_PLL0
, },
163 CGU_REG_CPCCR
, 4, 1, 4, 22, -1, -1,
164 jz4770_cgu_cpccr_div_table
,
167 [JZ4770_CLK_H1CLK
] = {
168 "h1clk", CGU_CLK_DIV
| CGU_CLK_GATE
,
169 .parents
= { JZ4770_CLK_PLL0
, },
171 CGU_REG_CPCCR
, 24, 1, 4, 22, -1, -1,
172 jz4770_cgu_cpccr_div_table
,
174 .gate
= { CGU_REG_CLKGR1
, 7 },
176 [JZ4770_CLK_H2CLK
] = {
177 "h2clk", CGU_CLK_DIV
,
178 .parents
= { JZ4770_CLK_PLL0
, },
180 CGU_REG_CPCCR
, 16, 1, 4, 22, -1, -1,
181 jz4770_cgu_cpccr_div_table
,
184 [JZ4770_CLK_C1CLK
] = {
185 "c1clk", CGU_CLK_DIV
| CGU_CLK_GATE
,
186 .parents
= { JZ4770_CLK_PLL0
, },
188 CGU_REG_CPCCR
, 12, 1, 4, 22, -1, -1,
189 jz4770_cgu_cpccr_div_table
,
191 .gate
= { CGU_REG_OPCR
, 31, true }, // disable CCLK stop on idle
193 [JZ4770_CLK_PCLK
] = {
195 .parents
= { JZ4770_CLK_PLL0
, },
197 CGU_REG_CPCCR
, 8, 1, 4, 22, -1, -1,
198 jz4770_cgu_cpccr_div_table
,
202 /* Those divided clocks can connect to PLL0 or PLL1 */
204 [JZ4770_CLK_MMC0_MUX
] = {
205 "mmc0_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
206 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
207 .mux
= { CGU_REG_MSC0CDR
, 30, 1 },
208 .div
= { CGU_REG_MSC0CDR
, 0, 1, 7, -1, -1, 31 },
209 .gate
= { CGU_REG_MSC0CDR
, 31 },
211 [JZ4770_CLK_MMC1_MUX
] = {
212 "mmc1_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
213 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
214 .mux
= { CGU_REG_MSC1CDR
, 30, 1 },
215 .div
= { CGU_REG_MSC1CDR
, 0, 1, 7, -1, -1, 31 },
216 .gate
= { CGU_REG_MSC1CDR
, 31 },
218 [JZ4770_CLK_MMC2_MUX
] = {
219 "mmc2_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
220 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
221 .mux
= { CGU_REG_MSC2CDR
, 30, 1 },
222 .div
= { CGU_REG_MSC2CDR
, 0, 1, 7, -1, -1, 31 },
223 .gate
= { CGU_REG_MSC2CDR
, 31 },
226 "cim", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
227 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
228 .mux
= { CGU_REG_CIMCDR
, 31, 1 },
229 .div
= { CGU_REG_CIMCDR
, 0, 1, 8, -1, -1, -1 },
230 .gate
= { CGU_REG_CLKGR0
, 26 },
233 "uhc", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
234 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
235 .mux
= { CGU_REG_UHCCDR
, 29, 1 },
236 .div
= { CGU_REG_UHCCDR
, 0, 1, 4, -1, -1, -1 },
237 .gate
= { CGU_REG_CLKGR0
, 24 },
240 "gpu", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
241 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, -1 },
242 .mux
= { CGU_REG_GPUCDR
, 31, 1 },
243 .div
= { CGU_REG_GPUCDR
, 0, 1, 3, -1, -1, -1 },
244 .gate
= { CGU_REG_CLKGR1
, 9 },
247 "bch", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
248 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
249 .mux
= { CGU_REG_BCHCDR
, 31, 1 },
250 .div
= { CGU_REG_BCHCDR
, 0, 1, 3, -1, -1, -1 },
251 .gate
= { CGU_REG_CLKGR0
, 1 },
253 [JZ4770_CLK_LPCLK_MUX
] = {
254 "lpclk", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
255 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
256 .mux
= { CGU_REG_LPCDR
, 29, 1 },
257 .div
= { CGU_REG_LPCDR
, 0, 1, 11, -1, -1, -1 },
258 .gate
= { CGU_REG_CLKGR0
, 28 },
261 "gps", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
262 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
263 .mux
= { CGU_REG_GPSCDR
, 31, 1 },
264 .div
= { CGU_REG_GPSCDR
, 0, 1, 4, -1, -1, -1 },
265 .gate
= { CGU_REG_CLKGR0
, 22 },
268 /* Those divided clocks can connect to EXT, PLL0 or PLL1 */
270 [JZ4770_CLK_SSI_MUX
] = {
271 "ssi_mux", CGU_CLK_DIV
| CGU_CLK_MUX
,
272 .parents
= { JZ4770_CLK_EXT
, -1,
273 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
274 .mux
= { CGU_REG_SSICDR
, 30, 2 },
275 .div
= { CGU_REG_SSICDR
, 0, 1, 6, -1, -1, -1 },
277 [JZ4770_CLK_PCM_MUX
] = {
278 "pcm_mux", CGU_CLK_DIV
| CGU_CLK_MUX
,
279 .parents
= { JZ4770_CLK_EXT
, -1,
280 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
281 .mux
= { CGU_REG_PCMCDR
, 30, 2 },
282 .div
= { CGU_REG_PCMCDR
, 0, 1, 9, -1, -1, -1 },
285 "i2s", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
286 .parents
= { JZ4770_CLK_EXT
, -1,
287 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
288 .mux
= { CGU_REG_I2SCDR
, 30, 2 },
289 .div
= { CGU_REG_I2SCDR
, 0, 1, 9, -1, -1, -1 },
290 .gate
= { CGU_REG_CLKGR1
, 13 },
293 "usb", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
294 .parents
= { JZ4770_CLK_EXT
, -1,
295 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
296 .mux
= { CGU_REG_USBCDR
, 30, 2 },
297 .div
= { CGU_REG_USBCDR
, 0, 1, 8, -1, -1, -1 },
298 .gate
= { CGU_REG_CLKGR0
, 2 },
301 /* Gate-only clocks */
303 [JZ4770_CLK_SSI0
] = {
304 "ssi0", CGU_CLK_GATE
,
305 .parents
= { JZ4770_CLK_SSI_MUX
, },
306 .gate
= { CGU_REG_CLKGR0
, 4 },
308 [JZ4770_CLK_SSI1
] = {
309 "ssi1", CGU_CLK_GATE
,
310 .parents
= { JZ4770_CLK_SSI_MUX
, },
311 .gate
= { CGU_REG_CLKGR0
, 19 },
313 [JZ4770_CLK_SSI2
] = {
314 "ssi2", CGU_CLK_GATE
,
315 .parents
= { JZ4770_CLK_SSI_MUX
, },
316 .gate
= { CGU_REG_CLKGR0
, 20 },
318 [JZ4770_CLK_PCM0
] = {
319 "pcm0", CGU_CLK_GATE
,
320 .parents
= { JZ4770_CLK_PCM_MUX
, },
321 .gate
= { CGU_REG_CLKGR1
, 8 },
323 [JZ4770_CLK_PCM1
] = {
324 "pcm1", CGU_CLK_GATE
,
325 .parents
= { JZ4770_CLK_PCM_MUX
, },
326 .gate
= { CGU_REG_CLKGR1
, 10 },
330 .parents
= { JZ4770_CLK_H2CLK
, },
331 .gate
= { CGU_REG_CLKGR0
, 21 },
333 [JZ4770_CLK_I2C0
] = {
334 "i2c0", CGU_CLK_GATE
,
335 .parents
= { JZ4770_CLK_EXT
, },
336 .gate
= { CGU_REG_CLKGR0
, 5 },
338 [JZ4770_CLK_I2C1
] = {
339 "i2c1", CGU_CLK_GATE
,
340 .parents
= { JZ4770_CLK_EXT
, },
341 .gate
= { CGU_REG_CLKGR0
, 6 },
343 [JZ4770_CLK_I2C2
] = {
344 "i2c2", CGU_CLK_GATE
,
345 .parents
= { JZ4770_CLK_EXT
, },
346 .gate
= { CGU_REG_CLKGR1
, 15 },
348 [JZ4770_CLK_UART0
] = {
349 "uart0", CGU_CLK_GATE
,
350 .parents
= { JZ4770_CLK_EXT
, },
351 .gate
= { CGU_REG_CLKGR0
, 15 },
353 [JZ4770_CLK_UART1
] = {
354 "uart1", CGU_CLK_GATE
,
355 .parents
= { JZ4770_CLK_EXT
, },
356 .gate
= { CGU_REG_CLKGR0
, 16 },
358 [JZ4770_CLK_UART2
] = {
359 "uart2", CGU_CLK_GATE
,
360 .parents
= { JZ4770_CLK_EXT
, },
361 .gate
= { CGU_REG_CLKGR0
, 17 },
363 [JZ4770_CLK_UART3
] = {
364 "uart3", CGU_CLK_GATE
,
365 .parents
= { JZ4770_CLK_EXT
, },
366 .gate
= { CGU_REG_CLKGR0
, 18 },
370 .parents
= { JZ4770_CLK_H0CLK
, },
371 .gate
= { CGU_REG_CLKGR0
, 29 },
375 .parents
= { JZ4770_CLK_EXT
, },
376 .gate
= { CGU_REG_CLKGR0
, 14 },
380 .parents
= { JZ4770_CLK_EXT
, },
381 .gate
= { CGU_REG_CLKGR0
, 8 },
385 .parents
= { JZ4770_CLK_C1CLK
, },
386 .gate
= { CGU_REG_CLKGR1
, 14 },
390 .parents
= { JZ4770_CLK_H1CLK
, },
391 .gate
= { CGU_REG_LCR
, 30, false, 150 },
393 [JZ4770_CLK_MMC0
] = {
394 "mmc0", CGU_CLK_GATE
,
395 .parents
= { JZ4770_CLK_MMC0_MUX
, },
396 .gate
= { CGU_REG_CLKGR0
, 3 },
398 [JZ4770_CLK_MMC1
] = {
399 "mmc1", CGU_CLK_GATE
,
400 .parents
= { JZ4770_CLK_MMC1_MUX
, },
401 .gate
= { CGU_REG_CLKGR0
, 11 },
403 [JZ4770_CLK_MMC2
] = {
404 "mmc2", CGU_CLK_GATE
,
405 .parents
= { JZ4770_CLK_MMC2_MUX
, },
406 .gate
= { CGU_REG_CLKGR0
, 12 },
408 [JZ4770_CLK_OTG_PHY
] = {
409 "usb_phy", CGU_CLK_GATE
,
410 .parents
= { JZ4770_CLK_OTG
},
411 .gate
= { CGU_REG_OPCR
, 7, true, 50 },
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
},
422 [JZ4770_CLK_EXT512
] = {
423 "ext/512", CGU_CLK_FIXDIV
,
424 .parents
= { JZ4770_CLK_EXT
},
430 .parents
= { JZ4770_CLK_EXT512
, JZ4770_CLK_OSC32K
, },
431 .mux
= { CGU_REG_OPCR
, 2, 1},
435 static void __init
jz4770_cgu_init(struct device_node
*np
)
439 cgu
= ingenic_cgu_new(jz4770_cgu_clocks
,
440 ARRAY_SIZE(jz4770_cgu_clocks
), np
);
442 pr_err("%s: failed to initialise CGU\n", __func__
);
446 retval
= ingenic_cgu_register_clocks(cgu
);
448 pr_err("%s: failed to register CGU Clocks\n", __func__
);
450 ingenic_cgu_register_syscore_ops(cgu
);
453 /* We only probe via devicetree, no need for a platform driver */
454 CLK_OF_DECLARE_DRIVER(jz4770_cgu
, "ingenic,jz4770-cgu", jz4770_cgu_init
);