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>
12 #include <dt-bindings/clock/jz4770-cgu.h>
17 * CPM registers offset address definition
19 #define CGU_REG_CPCCR 0x00
20 #define CGU_REG_LCR 0x04
21 #define CGU_REG_CPPCR0 0x10
22 #define CGU_REG_CLKGR0 0x20
23 #define CGU_REG_OPCR 0x24
24 #define CGU_REG_CLKGR1 0x28
25 #define CGU_REG_CPPCR1 0x30
26 #define CGU_REG_USBPCR1 0x48
27 #define CGU_REG_USBCDR 0x50
28 #define CGU_REG_I2SCDR 0x60
29 #define CGU_REG_LPCDR 0x64
30 #define CGU_REG_MSC0CDR 0x68
31 #define CGU_REG_UHCCDR 0x6c
32 #define CGU_REG_SSICDR 0x74
33 #define CGU_REG_CIMCDR 0x7c
34 #define CGU_REG_GPSCDR 0x80
35 #define CGU_REG_PCMCDR 0x84
36 #define CGU_REG_GPUCDR 0x88
37 #define CGU_REG_MSC1CDR 0xA4
38 #define CGU_REG_MSC2CDR 0xA8
39 #define CGU_REG_BCHCDR 0xAC
41 /* bits within the OPCR register */
42 #define OPCR_SPENDH BIT(5) /* UHC PHY suspend */
44 /* bits within the USBPCR1 register */
45 #define USBPCR1_UHC_POWER BIT(5) /* UHC PHY power down */
47 static struct ingenic_cgu
*cgu
;
49 static int jz4770_uhc_phy_enable(struct clk_hw
*hw
)
51 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
52 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
54 writel(readl(reg_opcr
) & ~OPCR_SPENDH
, reg_opcr
);
55 writel(readl(reg_usbpcr1
) | USBPCR1_UHC_POWER
, reg_usbpcr1
);
59 static void jz4770_uhc_phy_disable(struct clk_hw
*hw
)
61 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
62 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
64 writel(readl(reg_usbpcr1
) & ~USBPCR1_UHC_POWER
, reg_usbpcr1
);
65 writel(readl(reg_opcr
) | OPCR_SPENDH
, reg_opcr
);
68 static int jz4770_uhc_phy_is_enabled(struct clk_hw
*hw
)
70 void __iomem
*reg_opcr
= cgu
->base
+ CGU_REG_OPCR
;
71 void __iomem
*reg_usbpcr1
= cgu
->base
+ CGU_REG_USBPCR1
;
73 return !(readl(reg_opcr
) & OPCR_SPENDH
) &&
74 (readl(reg_usbpcr1
) & USBPCR1_UHC_POWER
);
77 static const struct clk_ops jz4770_uhc_phy_ops
= {
78 .enable
= jz4770_uhc_phy_enable
,
79 .disable
= jz4770_uhc_phy_disable
,
80 .is_enabled
= jz4770_uhc_phy_is_enabled
,
83 static const s8 pll_od_encoding
[8] = {
84 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
87 static const u8 jz4770_cgu_cpccr_div_table
[] = {
91 static const struct ingenic_cgu_clk_info jz4770_cgu_clocks
[] = {
95 [JZ4770_CLK_EXT
] = { "ext", CGU_CLK_EXT
},
96 [JZ4770_CLK_OSC32K
] = { "osc32k", CGU_CLK_EXT
},
100 [JZ4770_CLK_PLL0
] = {
102 .parents
= { JZ4770_CLK_EXT
},
104 .reg
= CGU_REG_CPPCR0
,
114 .od_encoding
= pll_od_encoding
,
121 [JZ4770_CLK_PLL1
] = {
122 /* TODO: PLL1 can depend on PLL0 */
124 .parents
= { JZ4770_CLK_EXT
},
126 .reg
= CGU_REG_CPPCR1
,
136 .od_encoding
= pll_od_encoding
,
139 .no_bypass_bit
= true,
145 [JZ4770_CLK_CCLK
] = {
147 .parents
= { JZ4770_CLK_PLL0
, },
149 CGU_REG_CPCCR
, 0, 1, 4, 22, -1, -1,
150 jz4770_cgu_cpccr_div_table
,
153 [JZ4770_CLK_H0CLK
] = {
154 "h0clk", CGU_CLK_DIV
,
155 .parents
= { JZ4770_CLK_PLL0
, },
157 CGU_REG_CPCCR
, 4, 1, 4, 22, -1, -1,
158 jz4770_cgu_cpccr_div_table
,
161 [JZ4770_CLK_H1CLK
] = {
162 "h1clk", CGU_CLK_DIV
| CGU_CLK_GATE
,
163 .parents
= { JZ4770_CLK_PLL0
, },
165 CGU_REG_CPCCR
, 24, 1, 4, 22, -1, -1,
166 jz4770_cgu_cpccr_div_table
,
168 .gate
= { CGU_REG_CLKGR1
, 7 },
170 [JZ4770_CLK_H2CLK
] = {
171 "h2clk", CGU_CLK_DIV
,
172 .parents
= { JZ4770_CLK_PLL0
, },
174 CGU_REG_CPCCR
, 16, 1, 4, 22, -1, -1,
175 jz4770_cgu_cpccr_div_table
,
178 [JZ4770_CLK_C1CLK
] = {
179 "c1clk", CGU_CLK_DIV
| CGU_CLK_GATE
,
180 .parents
= { JZ4770_CLK_PLL0
, },
182 CGU_REG_CPCCR
, 12, 1, 4, 22, -1, -1,
183 jz4770_cgu_cpccr_div_table
,
185 .gate
= { CGU_REG_OPCR
, 31, true }, // disable CCLK stop on idle
187 [JZ4770_CLK_PCLK
] = {
189 .parents
= { JZ4770_CLK_PLL0
, },
191 CGU_REG_CPCCR
, 8, 1, 4, 22, -1, -1,
192 jz4770_cgu_cpccr_div_table
,
196 /* Those divided clocks can connect to PLL0 or PLL1 */
198 [JZ4770_CLK_MMC0_MUX
] = {
199 "mmc0_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
200 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
201 .mux
= { CGU_REG_MSC0CDR
, 30, 1 },
202 .div
= { CGU_REG_MSC0CDR
, 0, 1, 7, -1, -1, 31 },
203 .gate
= { CGU_REG_MSC0CDR
, 31 },
205 [JZ4770_CLK_MMC1_MUX
] = {
206 "mmc1_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
207 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
208 .mux
= { CGU_REG_MSC1CDR
, 30, 1 },
209 .div
= { CGU_REG_MSC1CDR
, 0, 1, 7, -1, -1, 31 },
210 .gate
= { CGU_REG_MSC1CDR
, 31 },
212 [JZ4770_CLK_MMC2_MUX
] = {
213 "mmc2_mux", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
214 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
215 .mux
= { CGU_REG_MSC2CDR
, 30, 1 },
216 .div
= { CGU_REG_MSC2CDR
, 0, 1, 7, -1, -1, 31 },
217 .gate
= { CGU_REG_MSC2CDR
, 31 },
220 "cim", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
221 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
222 .mux
= { CGU_REG_CIMCDR
, 31, 1 },
223 .div
= { CGU_REG_CIMCDR
, 0, 1, 8, -1, -1, -1 },
224 .gate
= { CGU_REG_CLKGR0
, 26 },
227 "uhc", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
228 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
229 .mux
= { CGU_REG_UHCCDR
, 29, 1 },
230 .div
= { CGU_REG_UHCCDR
, 0, 1, 4, -1, -1, -1 },
231 .gate
= { CGU_REG_CLKGR0
, 24 },
234 "gpu", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
235 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, -1 },
236 .mux
= { CGU_REG_GPUCDR
, 31, 1 },
237 .div
= { CGU_REG_GPUCDR
, 0, 1, 3, -1, -1, -1 },
238 .gate
= { CGU_REG_CLKGR1
, 9 },
241 "bch", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
242 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
243 .mux
= { CGU_REG_BCHCDR
, 31, 1 },
244 .div
= { CGU_REG_BCHCDR
, 0, 1, 3, -1, -1, -1 },
245 .gate
= { CGU_REG_CLKGR0
, 1 },
247 [JZ4770_CLK_LPCLK_MUX
] = {
248 "lpclk", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
249 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
250 .mux
= { CGU_REG_LPCDR
, 29, 1 },
251 .div
= { CGU_REG_LPCDR
, 0, 1, 11, -1, -1, -1 },
252 .gate
= { CGU_REG_CLKGR0
, 28 },
255 "gps", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
256 .parents
= { JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
, },
257 .mux
= { CGU_REG_GPSCDR
, 31, 1 },
258 .div
= { CGU_REG_GPSCDR
, 0, 1, 4, -1, -1, -1 },
259 .gate
= { CGU_REG_CLKGR0
, 22 },
262 /* Those divided clocks can connect to EXT, PLL0 or PLL1 */
264 [JZ4770_CLK_SSI_MUX
] = {
265 "ssi_mux", CGU_CLK_DIV
| CGU_CLK_MUX
,
266 .parents
= { JZ4770_CLK_EXT
, -1,
267 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
268 .mux
= { CGU_REG_SSICDR
, 30, 2 },
269 .div
= { CGU_REG_SSICDR
, 0, 1, 6, -1, -1, -1 },
271 [JZ4770_CLK_PCM_MUX
] = {
272 "pcm_mux", CGU_CLK_DIV
| CGU_CLK_MUX
,
273 .parents
= { JZ4770_CLK_EXT
, -1,
274 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
275 .mux
= { CGU_REG_PCMCDR
, 30, 2 },
276 .div
= { CGU_REG_PCMCDR
, 0, 1, 9, -1, -1, -1 },
279 "i2s", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
280 .parents
= { JZ4770_CLK_EXT
, -1,
281 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
282 .mux
= { CGU_REG_I2SCDR
, 30, 2 },
283 .div
= { CGU_REG_I2SCDR
, 0, 1, 9, -1, -1, -1 },
284 .gate
= { CGU_REG_CLKGR1
, 13 },
287 "usb", CGU_CLK_DIV
| CGU_CLK_GATE
| CGU_CLK_MUX
,
288 .parents
= { JZ4770_CLK_EXT
, -1,
289 JZ4770_CLK_PLL0
, JZ4770_CLK_PLL1
},
290 .mux
= { CGU_REG_USBCDR
, 30, 2 },
291 .div
= { CGU_REG_USBCDR
, 0, 1, 8, -1, -1, -1 },
292 .gate
= { CGU_REG_CLKGR0
, 2 },
295 /* Gate-only clocks */
297 [JZ4770_CLK_SSI0
] = {
298 "ssi0", CGU_CLK_GATE
,
299 .parents
= { JZ4770_CLK_SSI_MUX
, },
300 .gate
= { CGU_REG_CLKGR0
, 4 },
302 [JZ4770_CLK_SSI1
] = {
303 "ssi1", CGU_CLK_GATE
,
304 .parents
= { JZ4770_CLK_SSI_MUX
, },
305 .gate
= { CGU_REG_CLKGR0
, 19 },
307 [JZ4770_CLK_SSI2
] = {
308 "ssi2", CGU_CLK_GATE
,
309 .parents
= { JZ4770_CLK_SSI_MUX
, },
310 .gate
= { CGU_REG_CLKGR0
, 20 },
312 [JZ4770_CLK_PCM0
] = {
313 "pcm0", CGU_CLK_GATE
,
314 .parents
= { JZ4770_CLK_PCM_MUX
, },
315 .gate
= { CGU_REG_CLKGR1
, 8 },
317 [JZ4770_CLK_PCM1
] = {
318 "pcm1", CGU_CLK_GATE
,
319 .parents
= { JZ4770_CLK_PCM_MUX
, },
320 .gate
= { CGU_REG_CLKGR1
, 10 },
324 .parents
= { JZ4770_CLK_H2CLK
, },
325 .gate
= { CGU_REG_CLKGR0
, 21 },
327 [JZ4770_CLK_I2C0
] = {
328 "i2c0", CGU_CLK_GATE
,
329 .parents
= { JZ4770_CLK_EXT
, },
330 .gate
= { CGU_REG_CLKGR0
, 5 },
332 [JZ4770_CLK_I2C1
] = {
333 "i2c1", CGU_CLK_GATE
,
334 .parents
= { JZ4770_CLK_EXT
, },
335 .gate
= { CGU_REG_CLKGR0
, 6 },
337 [JZ4770_CLK_I2C2
] = {
338 "i2c2", CGU_CLK_GATE
,
339 .parents
= { JZ4770_CLK_EXT
, },
340 .gate
= { CGU_REG_CLKGR1
, 15 },
342 [JZ4770_CLK_UART0
] = {
343 "uart0", CGU_CLK_GATE
,
344 .parents
= { JZ4770_CLK_EXT
, },
345 .gate
= { CGU_REG_CLKGR0
, 15 },
347 [JZ4770_CLK_UART1
] = {
348 "uart1", CGU_CLK_GATE
,
349 .parents
= { JZ4770_CLK_EXT
, },
350 .gate
= { CGU_REG_CLKGR0
, 16 },
352 [JZ4770_CLK_UART2
] = {
353 "uart2", CGU_CLK_GATE
,
354 .parents
= { JZ4770_CLK_EXT
, },
355 .gate
= { CGU_REG_CLKGR0
, 17 },
357 [JZ4770_CLK_UART3
] = {
358 "uart3", CGU_CLK_GATE
,
359 .parents
= { JZ4770_CLK_EXT
, },
360 .gate
= { CGU_REG_CLKGR0
, 18 },
364 .parents
= { JZ4770_CLK_H0CLK
, },
365 .gate
= { CGU_REG_CLKGR0
, 29 },
369 .parents
= { JZ4770_CLK_EXT
, },
370 .gate
= { CGU_REG_CLKGR0
, 14 },
374 .parents
= { JZ4770_CLK_EXT
, },
375 .gate
= { CGU_REG_CLKGR0
, 8 },
379 .parents
= { JZ4770_CLK_C1CLK
, },
380 .gate
= { CGU_REG_CLKGR1
, 14 },
384 .parents
= { JZ4770_CLK_H1CLK
, },
385 .gate
= { CGU_REG_LCR
, 30, false, 150 },
387 [JZ4770_CLK_MMC0
] = {
388 "mmc0", CGU_CLK_GATE
,
389 .parents
= { JZ4770_CLK_MMC0_MUX
, },
390 .gate
= { CGU_REG_CLKGR0
, 3 },
392 [JZ4770_CLK_MMC1
] = {
393 "mmc1", CGU_CLK_GATE
,
394 .parents
= { JZ4770_CLK_MMC1_MUX
, },
395 .gate
= { CGU_REG_CLKGR0
, 11 },
397 [JZ4770_CLK_MMC2
] = {
398 "mmc2", CGU_CLK_GATE
,
399 .parents
= { JZ4770_CLK_MMC2_MUX
, },
400 .gate
= { CGU_REG_CLKGR0
, 12 },
402 [JZ4770_CLK_OTG_PHY
] = {
403 "usb_phy", CGU_CLK_GATE
,
404 .parents
= { JZ4770_CLK_OTG
},
405 .gate
= { CGU_REG_OPCR
, 7, true, 50 },
410 [JZ4770_CLK_UHC_PHY
] = {
411 "uhc_phy", CGU_CLK_CUSTOM
,
412 .parents
= { JZ4770_CLK_UHC
, -1, -1, -1 },
413 .custom
= { &jz4770_uhc_phy_ops
},
416 [JZ4770_CLK_EXT512
] = {
417 "ext/512", CGU_CLK_FIXDIV
,
418 .parents
= { JZ4770_CLK_EXT
},
424 .parents
= { JZ4770_CLK_EXT512
, JZ4770_CLK_OSC32K
, },
425 .mux
= { CGU_REG_OPCR
, 2, 1},
429 static void __init
jz4770_cgu_init(struct device_node
*np
)
433 cgu
= ingenic_cgu_new(jz4770_cgu_clocks
,
434 ARRAY_SIZE(jz4770_cgu_clocks
), np
);
436 pr_err("%s: failed to initialise CGU\n", __func__
);
440 retval
= ingenic_cgu_register_clocks(cgu
);
442 pr_err("%s: failed to register CGU Clocks\n", __func__
);
444 ingenic_cgu_register_syscore_ops(cgu
);
447 /* We only probe via devicetree, no need for a platform driver */
448 CLK_OF_DECLARE_DRIVER(jz4770_cgu
, "ingenic,jz4770-cgu", jz4770_cgu_init
);