1// SPDX-License-Identifier: GPL-2.0
2/*
3 * X1830 SoC CGU driver
4 * Copyright (c) 2019 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
5 */
6
7#include <linux/clk-provider.h>
8#include <linux/delay.h>
9#include <linux/io.h>
10#include <linux/of.h>
11
12#include <dt-bindings/clock/ingenic,x1830-cgu.h>
13
14#include "cgu.h"
15#include "pm.h"
16
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
45
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)
50
51/* bits within the USBPCR register */
52#define USBPCR_SIDDQ BIT(21)
53#define USBPCR_OTG_DISABLE BIT(20)
54
55static struct ingenic_cgu *cgu;
56
57static int x1830_usb_phy_enable(struct clk_hw *hw)
58{
59 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
60 void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR;
61
62 writel(val: (readl(addr: reg_opcr) | OPCR_SPENDN0) & ~OPCR_GATE_USBPHYCLK, addr: reg_opcr);
63 writel(readl(addr: reg_usbpcr) & ~USBPCR_OTG_DISABLE & ~USBPCR_SIDDQ, addr: reg_usbpcr);
64 return 0;
65}
66
67static void x1830_usb_phy_disable(struct clk_hw *hw)
68{
69 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
70 void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR;
71
72 writel(val: (readl(addr: reg_opcr) & ~OPCR_SPENDN0) | OPCR_GATE_USBPHYCLK, addr: reg_opcr);
73 writel(readl(addr: reg_usbpcr) | USBPCR_OTG_DISABLE | USBPCR_SIDDQ, addr: reg_usbpcr);
74}
75
76static int x1830_usb_phy_is_enabled(struct clk_hw *hw)
77{
78 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR;
79 void __iomem *reg_usbpcr = cgu->base + CGU_REG_USBPCR;
80
81 return (readl(addr: reg_opcr) & OPCR_SPENDN0) &&
82 !(readl(addr: reg_usbpcr) & USBPCR_SIDDQ) &&
83 !(readl(addr: reg_usbpcr) & USBPCR_OTG_DISABLE);
84}
85
86static 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,
90};
91
92static 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,
101};
102
103static const struct ingenic_cgu_clk_info x1830_cgu_clocks[] = {
104
105 /* External clocks */
106
107 [X1830_CLK_EXCLK] = { "ext", CGU_CLK_EXT },
108 [X1830_CLK_RTCLK] = { "rtc", CGU_CLK_EXT },
109
110 /* PLLs */
111
112 [X1830_CLK_APLL] = {
113 "apll", CGU_CLK_PLL,
114 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
115 .pll = {
116 .reg = CGU_REG_APLL,
117 .rate_multiplier = 2,
118 .m_shift = 20,
119 .m_bits = 9,
120 .m_offset = 1,
121 .n_shift = 14,
122 .n_bits = 6,
123 .n_offset = 1,
124 .od_shift = 11,
125 .od_bits = 3,
126 .od_max = 64,
127 .od_encoding = pll_od_encoding,
128 .bypass_reg = CGU_REG_CPPCR,
129 .bypass_bit = 30,
130 .enable_bit = 0,
131 .stable_bit = 3,
132 },
133 },
134
135 [X1830_CLK_MPLL] = {
136 "mpll", CGU_CLK_PLL,
137 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
138 .pll = {
139 .reg = CGU_REG_MPLL,
140 .rate_multiplier = 2,
141 .m_shift = 20,
142 .m_bits = 9,
143 .m_offset = 1,
144 .n_shift = 14,
145 .n_bits = 6,
146 .n_offset = 1,
147 .od_shift = 11,
148 .od_bits = 3,
149 .od_max = 64,
150 .od_encoding = pll_od_encoding,
151 .bypass_reg = CGU_REG_CPPCR,
152 .bypass_bit = 28,
153 .enable_bit = 0,
154 .stable_bit = 3,
155 },
156 },
157
158 [X1830_CLK_EPLL] = {
159 "epll", CGU_CLK_PLL,
160 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
161 .pll = {
162 .reg = CGU_REG_EPLL,
163 .rate_multiplier = 2,
164 .m_shift = 20,
165 .m_bits = 9,
166 .m_offset = 1,
167 .n_shift = 14,
168 .n_bits = 6,
169 .n_offset = 1,
170 .od_shift = 11,
171 .od_bits = 3,
172 .od_max = 64,
173 .od_encoding = pll_od_encoding,
174 .bypass_reg = CGU_REG_CPPCR,
175 .bypass_bit = 24,
176 .enable_bit = 0,
177 .stable_bit = 3,
178 },
179 },
180
181 [X1830_CLK_VPLL] = {
182 "vpll", CGU_CLK_PLL,
183 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
184 .pll = {
185 .reg = CGU_REG_VPLL,
186 .rate_multiplier = 2,
187 .m_shift = 20,
188 .m_bits = 9,
189 .m_offset = 1,
190 .n_shift = 14,
191 .n_bits = 6,
192 .n_offset = 1,
193 .od_shift = 11,
194 .od_bits = 3,
195 .od_max = 64,
196 .od_encoding = pll_od_encoding,
197 .bypass_reg = CGU_REG_CPPCR,
198 .bypass_bit = 26,
199 .enable_bit = 0,
200 .stable_bit = 3,
201 },
202 },
203
204 /* Custom (SoC-specific) OTG PHY */
205
206 [X1830_CLK_OTGPHY] = {
207 "otg_phy", CGU_CLK_CUSTOM,
208 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
209 .custom = { &x1830_otg_phy_ops },
210 },
211
212 /* Muxes & dividers */
213
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 },
218 },
219
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 },
224 },
225
226 [X1830_CLK_CPU] = {
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 },
232 },
233
234 [X1830_CLK_L2CACHE] = {
235 "l2cache", CGU_CLK_DIV,
236 /*
237 * The L2 cache clock is critical if caches are enabled and
238 * disabling it or any parent clocks will hang the system.
239 */
240 .flags = CLK_IS_CRITICAL,
241 .parents = { X1830_CLK_CPUMUX, -1, -1, -1 },
242 .div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 },
243 },
244
245 [X1830_CLK_AHB0] = {
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 },
250 },
251
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 },
256 },
257
258 [X1830_CLK_AHB2] = {
259 "ahb2", CGU_CLK_DIV,
260 .parents = { X1830_CLK_AHB2PMUX, -1, -1, -1 },
261 .div = { CGU_REG_CPCCR, 12, 1, 4, 20, -1, -1 },
262 },
263
264 [X1830_CLK_PCLK] = {
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 },
269 },
270
271 [X1830_CLK_DDR] = {
272 "ddr", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
273 /*
274 * Disabling DDR clock or its parents will render DRAM
275 * inaccessible; mark it critical.
276 */
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 },
282 },
283
284 [X1830_CLK_MAC] = {
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 },
291 },
292
293 [X1830_CLK_LCD] = {
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 },
300 },
301
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 },
307 },
308
309 [X1830_CLK_MSC0] = {
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 },
314 },
315
316 [X1830_CLK_MSC1] = {
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 },
321 },
322
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 },
329 },
330
331 [X1830_CLK_SSIPLL_DIV2] = {
332 "ssi_pll_div2", CGU_CLK_FIXDIV,
333 .parents = { X1830_CLK_SSIPLL },
334 .fixdiv = { 2 },
335 },
336
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 },
341 },
342
343 [X1830_CLK_EXCLK_DIV512] = {
344 "exclk_div512", CGU_CLK_FIXDIV,
345 .parents = { X1830_CLK_EXCLK },
346 .fixdiv = { 512 },
347 },
348
349 [X1830_CLK_RTC] = {
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 },
354 },
355
356 /* Gate-only clocks */
357
358 [X1830_CLK_EMC] = {
359 "emc", CGU_CLK_GATE,
360 .parents = { X1830_CLK_AHB2, -1, -1, -1 },
361 .gate = { CGU_REG_CLKGR0, 0 },
362 },
363
364 [X1830_CLK_EFUSE] = {
365 "efuse", CGU_CLK_GATE,
366 .parents = { X1830_CLK_AHB2, -1, -1, -1 },
367 .gate = { CGU_REG_CLKGR0, 1 },
368 },
369
370 [X1830_CLK_OTG] = {
371 "otg", CGU_CLK_GATE,
372 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
373 .gate = { CGU_REG_CLKGR0, 3 },
374 },
375
376 [X1830_CLK_SSI0] = {
377 "ssi0", CGU_CLK_GATE,
378 .parents = { X1830_CLK_SSIMUX, -1, -1, -1 },
379 .gate = { CGU_REG_CLKGR0, 6 },
380 },
381
382 [X1830_CLK_SMB0] = {
383 "smb0", CGU_CLK_GATE,
384 .parents = { X1830_CLK_PCLK, -1, -1, -1 },
385 .gate = { CGU_REG_CLKGR0, 7 },
386 },
387
388 [X1830_CLK_SMB1] = {
389 "smb1", CGU_CLK_GATE,
390 .parents = { X1830_CLK_PCLK, -1, -1, -1 },
391 .gate = { CGU_REG_CLKGR0, 8 },
392 },
393
394 [X1830_CLK_SMB2] = {
395 "smb2", CGU_CLK_GATE,
396 .parents = { X1830_CLK_PCLK, -1, -1, -1 },
397 .gate = { CGU_REG_CLKGR0, 9 },
398 },
399
400 [X1830_CLK_UART0] = {
401 "uart0", CGU_CLK_GATE,
402 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
403 .gate = { CGU_REG_CLKGR0, 14 },
404 },
405
406 [X1830_CLK_UART1] = {
407 "uart1", CGU_CLK_GATE,
408 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
409 .gate = { CGU_REG_CLKGR0, 15 },
410 },
411
412 [X1830_CLK_SSI1] = {
413 "ssi1", CGU_CLK_GATE,
414 .parents = { X1830_CLK_SSIMUX, -1, -1, -1 },
415 .gate = { CGU_REG_CLKGR0, 19 },
416 },
417
418 [X1830_CLK_SFC] = {
419 "sfc", CGU_CLK_GATE,
420 .parents = { X1830_CLK_SSIPLL, -1, -1, -1 },
421 .gate = { CGU_REG_CLKGR0, 20 },
422 },
423
424 [X1830_CLK_PDMA] = {
425 "pdma", CGU_CLK_GATE,
426 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
427 .gate = { CGU_REG_CLKGR0, 21 },
428 },
429
430 [X1830_CLK_TCU] = {
431 "tcu", CGU_CLK_GATE,
432 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
433 .gate = { CGU_REG_CLKGR0, 30 },
434 },
435
436 [X1830_CLK_DTRNG] = {
437 "dtrng", CGU_CLK_GATE,
438 .parents = { X1830_CLK_PCLK, -1, -1, -1 },
439 .gate = { CGU_REG_CLKGR1, 1 },
440 },
441
442 [X1830_CLK_OST] = {
443 "ost", CGU_CLK_GATE,
444 .parents = { X1830_CLK_EXCLK, -1, -1, -1 },
445 .gate = { CGU_REG_CLKGR1, 11 },
446 },
447};
448
449static void __init x1830_cgu_init(struct device_node *np)
450{
451 int retval;
452
453 cgu = ingenic_cgu_new(clock_info: x1830_cgu_clocks,
454 ARRAY_SIZE(x1830_cgu_clocks), np);
455 if (!cgu) {
456 pr_err("%s: failed to initialise CGU\n", __func__);
457 return;
458 }
459
460 retval = ingenic_cgu_register_clocks(cgu);
461 if (retval) {
462 pr_err("%s: failed to register CGU Clocks\n", __func__);
463 return;
464 }
465
466 ingenic_cgu_register_syscore_ops(cgu);
467}
468/*
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".
471 */
472CLK_OF_DECLARE_DRIVER(x1830_cgu, "ingenic,x1830-cgu", x1830_cgu_init);
473

source code of linux/drivers/clk/ingenic/x1830-cgu.c