1// SPDX-License-Identifier: GPL-2.0
2/*
3 * JZ4770 SoC CGU driver
4 * Copyright 2018, Paul Cercueil <paul@crapouillou.net>
5 */
6
7#include <linux/bitops.h>
8#include <linux/clk-provider.h>
9#include <linux/delay.h>
10#include <linux/io.h>
11#include <linux/of.h>
12
13#include <dt-bindings/clock/ingenic,jz4770-cgu.h>
14
15#include "cgu.h"
16#include "pm.h"
17
18/*
19 * CPM registers offset address definition
20 */
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
42
43/* bits within the OPCR register */
44#define OPCR_SPENDH		BIT(5)		/* UHC PHY suspend */
45
46/* bits within the USBPCR1 register */
47#define USBPCR1_UHC_POWER	BIT(5)		/* UHC PHY power down */
48
49static struct ingenic_cgu *cgu;
50
51static int jz4770_uhc_phy_enable(struct clk_hw *hw)
52{
53	void __iomem *reg_opcr		= cgu->base + CGU_REG_OPCR;
54	void __iomem *reg_usbpcr1	= cgu->base + CGU_REG_USBPCR1;
55
56	writel(readl(reg_opcr) & ~OPCR_SPENDH, reg_opcr);
57	writel(readl(reg_usbpcr1) | USBPCR1_UHC_POWER, reg_usbpcr1);
58	return 0;
59}
60
61static void jz4770_uhc_phy_disable(struct clk_hw *hw)
62{
63	void __iomem *reg_opcr		= cgu->base + CGU_REG_OPCR;
64	void __iomem *reg_usbpcr1	= cgu->base + CGU_REG_USBPCR1;
65
66	writel(readl(reg_usbpcr1) & ~USBPCR1_UHC_POWER, reg_usbpcr1);
67	writel(readl(reg_opcr) | OPCR_SPENDH, reg_opcr);
68}
69
70static int jz4770_uhc_phy_is_enabled(struct clk_hw *hw)
71{
72	void __iomem *reg_opcr		= cgu->base + CGU_REG_OPCR;
73	void __iomem *reg_usbpcr1	= cgu->base + CGU_REG_USBPCR1;
74
75	return !(readl(reg_opcr) & OPCR_SPENDH) &&
76		(readl(reg_usbpcr1) & USBPCR1_UHC_POWER);
77}
78
79static 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,
83};
84
85static const s8 pll_od_encoding[8] = {
86	0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
87};
88
89static const u8 jz4770_cgu_cpccr_div_table[] = {
90	1, 2, 3, 4, 6, 8, 12,
91};
92
93static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
94
95	/* External clocks */
96
97	[JZ4770_CLK_EXT] = { "ext", CGU_CLK_EXT },
98	[JZ4770_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT },
99
100	/* PLLs */
101
102	[JZ4770_CLK_PLL0] = {
103		"pll0", CGU_CLK_PLL,
104		.parents = { JZ4770_CLK_EXT },
105		.pll = {
106			.reg = CGU_REG_CPPCR0,
107			.rate_multiplier = 1,
108			.m_shift = 24,
109			.m_bits = 7,
110			.m_offset = 1,
111			.n_shift = 18,
112			.n_bits = 5,
113			.n_offset = 1,
114			.od_shift = 16,
115			.od_bits = 2,
116			.od_max = 8,
117			.od_encoding = pll_od_encoding,
118			.bypass_reg = CGU_REG_CPPCR0,
119			.bypass_bit = 9,
120			.enable_bit = 8,
121			.stable_bit = 10,
122		},
123	},
124
125	[JZ4770_CLK_PLL1] = {
126		/* TODO: PLL1 can depend on PLL0 */
127		"pll1", CGU_CLK_PLL,
128		.parents = { JZ4770_CLK_EXT },
129		.pll = {
130			.reg = CGU_REG_CPPCR1,
131			.rate_multiplier = 1,
132			.m_shift = 24,
133			.m_bits = 7,
134			.m_offset = 1,
135			.n_shift = 18,
136			.n_bits = 5,
137			.n_offset = 1,
138			.od_shift = 16,
139			.od_bits = 2,
140			.od_max = 8,
141			.od_encoding = pll_od_encoding,
142			.bypass_bit = -1,
143			.enable_bit = 7,
144			.stable_bit = 6,
145		},
146	},
147
148	/* Main clocks */
149
150	[JZ4770_CLK_CCLK] = {
151		"cclk", CGU_CLK_DIV,
152		/*
153		 * Disabling the CPU clock or any parent clocks will hang the
154		 * system; mark it critical.
155		 */
156		.flags = CLK_IS_CRITICAL,
157		.parents = { JZ4770_CLK_PLL0, },
158		.div = {
159			CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,
160			jz4770_cgu_cpccr_div_table,
161		},
162	},
163	[JZ4770_CLK_H0CLK] = {
164		"h0clk", CGU_CLK_DIV,
165		.parents = { JZ4770_CLK_PLL0, },
166		.div = {
167			CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,
168			jz4770_cgu_cpccr_div_table,
169		},
170	},
171	[JZ4770_CLK_H1CLK] = {
172		"h1clk", CGU_CLK_DIV | CGU_CLK_GATE,
173		.parents = { JZ4770_CLK_PLL0, },
174		.div = {
175			CGU_REG_CPCCR, 24, 1, 4, 22, -1, -1, 0,
176			jz4770_cgu_cpccr_div_table,
177		},
178		.gate = { CGU_REG_CLKGR1, 7 },
179	},
180	[JZ4770_CLK_H2CLK] = {
181		"h2clk", CGU_CLK_DIV,
182		.parents = { JZ4770_CLK_PLL0, },
183		.div = {
184			CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,
185			jz4770_cgu_cpccr_div_table,
186		},
187	},
188	[JZ4770_CLK_C1CLK] = {
189		"c1clk", CGU_CLK_DIV | CGU_CLK_GATE,
190		.parents = { JZ4770_CLK_PLL0, },
191		.div = {
192			CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,
193			jz4770_cgu_cpccr_div_table,
194		},
195		.gate = { CGU_REG_OPCR, 31, true }, // disable CCLK stop on idle
196	},
197	[JZ4770_CLK_PCLK] = {
198		"pclk", CGU_CLK_DIV,
199		.parents = { JZ4770_CLK_PLL0, },
200		.div = {
201			CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,
202			jz4770_cgu_cpccr_div_table,
203		},
204	},
205
206	/* Those divided clocks can connect to PLL0 or PLL1 */
207
208	[JZ4770_CLK_MMC0_MUX] = {
209		"mmc0_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
210		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },
211		.mux = { CGU_REG_MSC0CDR, 30, 1 },
212		.div = { CGU_REG_MSC0CDR, 0, 1, 7, -1, -1, 31 },
213		.gate = { CGU_REG_MSC0CDR, 31 },
214	},
215	[JZ4770_CLK_MMC1_MUX] = {
216		"mmc1_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
217		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },
218		.mux = { CGU_REG_MSC1CDR, 30, 1 },
219		.div = { CGU_REG_MSC1CDR, 0, 1, 7, -1, -1, 31 },
220		.gate = { CGU_REG_MSC1CDR, 31 },
221	},
222	[JZ4770_CLK_MMC2_MUX] = {
223		"mmc2_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
224		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },
225		.mux = { CGU_REG_MSC2CDR, 30, 1 },
226		.div = { CGU_REG_MSC2CDR, 0, 1, 7, -1, -1, 31 },
227		.gate = { CGU_REG_MSC2CDR, 31 },
228	},
229	[JZ4770_CLK_CIM] = {
230		"cim", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
231		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },
232		.mux = { CGU_REG_CIMCDR, 31, 1 },
233		.div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 },
234		.gate = { CGU_REG_CLKGR0, 26 },
235	},
236	[JZ4770_CLK_UHC] = {
237		"uhc", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
238		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },
239		.mux = { CGU_REG_UHCCDR, 29, 1 },
240		.div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 },
241		.gate = { CGU_REG_CLKGR0, 24 },
242	},
243	[JZ4770_CLK_GPU] = {
244		"gpu", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
245		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, -1 },
246		.mux = { CGU_REG_GPUCDR, 31, 1 },
247		.div = { CGU_REG_GPUCDR, 0, 1, 3, -1, -1, -1 },
248		.gate = { CGU_REG_CLKGR1, 9 },
249	},
250	[JZ4770_CLK_BCH] = {
251		"bch", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
252		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },
253		.mux = { CGU_REG_BCHCDR, 31, 1 },
254		.div = { CGU_REG_BCHCDR, 0, 1, 3, -1, -1, -1 },
255		.gate = { CGU_REG_CLKGR0, 1 },
256	},
257	[JZ4770_CLK_LPCLK_MUX] = {
258		"lpclk", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
259		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },
260		.mux = { CGU_REG_LPCDR, 29, 1 },
261		.div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },
262		.gate = { CGU_REG_CLKGR0, 28 },
263	},
264	[JZ4770_CLK_GPS] = {
265		"gps", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
266		.parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },
267		.mux = { CGU_REG_GPSCDR, 31, 1 },
268		.div = { CGU_REG_GPSCDR, 0, 1, 4, -1, -1, -1 },
269		.gate = { CGU_REG_CLKGR0, 22 },
270	},
271
272	/* Those divided clocks can connect to EXT, PLL0 or PLL1 */
273
274	[JZ4770_CLK_SSI_MUX] = {
275		"ssi_mux", CGU_CLK_DIV | CGU_CLK_MUX,
276		.parents = { JZ4770_CLK_EXT, -1,
277			JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 },
278		.mux = { CGU_REG_SSICDR, 30, 2 },
279		.div = { CGU_REG_SSICDR, 0, 1, 6, -1, -1, -1 },
280	},
281	[JZ4770_CLK_PCM_MUX] = {
282		"pcm_mux", CGU_CLK_DIV | CGU_CLK_MUX,
283		.parents = { JZ4770_CLK_EXT, -1,
284			JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 },
285		.mux = { CGU_REG_PCMCDR, 30, 2 },
286		.div = { CGU_REG_PCMCDR, 0, 1, 9, -1, -1, -1 },
287	},
288	[JZ4770_CLK_I2S] = {
289		"i2s", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
290		.parents = { JZ4770_CLK_EXT, -1,
291			JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 },
292		.mux = { CGU_REG_I2SCDR, 30, 2 },
293		.div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },
294		.gate = { CGU_REG_CLKGR1, 13 },
295	},
296	[JZ4770_CLK_OTG] = {
297		"usb", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
298		.parents = { JZ4770_CLK_EXT, -1,
299			JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 },
300		.mux = { CGU_REG_USBCDR, 30, 2 },
301		.div = { CGU_REG_USBCDR, 0, 1, 8, -1, -1, -1 },
302		.gate = { CGU_REG_CLKGR0, 2 },
303	},
304
305	/* Gate-only clocks */
306
307	[JZ4770_CLK_SSI0] = {
308		"ssi0", CGU_CLK_GATE,
309		.parents = { JZ4770_CLK_SSI_MUX, },
310		.gate = { CGU_REG_CLKGR0, 4 },
311	},
312	[JZ4770_CLK_SSI1] = {
313		"ssi1", CGU_CLK_GATE,
314		.parents = { JZ4770_CLK_SSI_MUX, },
315		.gate = { CGU_REG_CLKGR0, 19 },
316	},
317	[JZ4770_CLK_SSI2] = {
318		"ssi2", CGU_CLK_GATE,
319		.parents = { JZ4770_CLK_SSI_MUX, },
320		.gate = { CGU_REG_CLKGR0, 20 },
321	},
322	[JZ4770_CLK_PCM0] = {
323		"pcm0", CGU_CLK_GATE,
324		.parents = { JZ4770_CLK_PCM_MUX, },
325		.gate = { CGU_REG_CLKGR1, 8 },
326	},
327	[JZ4770_CLK_PCM1] = {
328		"pcm1", CGU_CLK_GATE,
329		.parents = { JZ4770_CLK_PCM_MUX, },
330		.gate = { CGU_REG_CLKGR1, 10 },
331	},
332	[JZ4770_CLK_DMA] = {
333		"dma", CGU_CLK_GATE,
334		.parents = { JZ4770_CLK_H2CLK, },
335		.gate = { CGU_REG_CLKGR0, 21 },
336	},
337	[JZ4770_CLK_BDMA] = {
338		"bdma", CGU_CLK_GATE,
339		.parents = { JZ4770_CLK_H2CLK, },
340		.gate = { CGU_REG_CLKGR1, 0 },
341	},
342	[JZ4770_CLK_I2C0] = {
343		"i2c0", CGU_CLK_GATE,
344		.parents = { JZ4770_CLK_EXT, },
345		.gate = { CGU_REG_CLKGR0, 5 },
346	},
347	[JZ4770_CLK_I2C1] = {
348		"i2c1", CGU_CLK_GATE,
349		.parents = { JZ4770_CLK_EXT, },
350		.gate = { CGU_REG_CLKGR0, 6 },
351	},
352	[JZ4770_CLK_I2C2] = {
353		"i2c2", CGU_CLK_GATE,
354		.parents = { JZ4770_CLK_EXT, },
355		.gate = { CGU_REG_CLKGR1, 15 },
356	},
357	[JZ4770_CLK_UART0] = {
358		"uart0", CGU_CLK_GATE,
359		.parents = { JZ4770_CLK_EXT, },
360		.gate = { CGU_REG_CLKGR0, 15 },
361	},
362	[JZ4770_CLK_UART1] = {
363		"uart1", CGU_CLK_GATE,
364		.parents = { JZ4770_CLK_EXT, },
365		.gate = { CGU_REG_CLKGR0, 16 },
366	},
367	[JZ4770_CLK_UART2] = {
368		"uart2", CGU_CLK_GATE,
369		.parents = { JZ4770_CLK_EXT, },
370		.gate = { CGU_REG_CLKGR0, 17 },
371	},
372	[JZ4770_CLK_UART3] = {
373		"uart3", CGU_CLK_GATE,
374		.parents = { JZ4770_CLK_EXT, },
375		.gate = { CGU_REG_CLKGR0, 18 },
376	},
377	[JZ4770_CLK_IPU] = {
378		"ipu", CGU_CLK_GATE,
379		.parents = { JZ4770_CLK_H0CLK, },
380		.gate = { CGU_REG_CLKGR0, 29 },
381	},
382	[JZ4770_CLK_ADC] = {
383		"adc", CGU_CLK_GATE,
384		.parents = { JZ4770_CLK_EXT, },
385		.gate = { CGU_REG_CLKGR0, 14 },
386	},
387	[JZ4770_CLK_AIC] = {
388		"aic", CGU_CLK_GATE,
389		.parents = { JZ4770_CLK_EXT, },
390		.gate = { CGU_REG_CLKGR0, 8 },
391	},
392	[JZ4770_CLK_AUX] = {
393		"aux", CGU_CLK_GATE,
394		.parents = { JZ4770_CLK_C1CLK, },
395		.gate = { CGU_REG_CLKGR1, 14 },
396	},
397	[JZ4770_CLK_VPU] = {
398		"vpu", CGU_CLK_GATE,
399		.parents = { JZ4770_CLK_H1CLK, },
400		.gate = { CGU_REG_LCR, 30, false, 150 },
401	},
402	[JZ4770_CLK_MMC0] = {
403		"mmc0", CGU_CLK_GATE,
404		.parents = { JZ4770_CLK_MMC0_MUX, },
405		.gate = { CGU_REG_CLKGR0, 3 },
406	},
407	[JZ4770_CLK_MMC1] = {
408		"mmc1", CGU_CLK_GATE,
409		.parents = { JZ4770_CLK_MMC1_MUX, },
410		.gate = { CGU_REG_CLKGR0, 11 },
411	},
412	[JZ4770_CLK_MMC2] = {
413		"mmc2", CGU_CLK_GATE,
414		.parents = { JZ4770_CLK_MMC2_MUX, },
415		.gate = { CGU_REG_CLKGR0, 12 },
416	},
417	[JZ4770_CLK_OTG_PHY] = {
418		"usb_phy", CGU_CLK_GATE,
419		.parents = { JZ4770_CLK_OTG },
420		.gate = { CGU_REG_OPCR, 7, true, 50 },
421	},
422
423	/* Custom clocks */
424
425	[JZ4770_CLK_UHC_PHY] = {
426		"uhc_phy", CGU_CLK_CUSTOM,
427		.parents = { JZ4770_CLK_UHC, -1, -1, -1 },
428		.custom = { &jz4770_uhc_phy_ops },
429	},
430
431	[JZ4770_CLK_EXT512] = {
432		"ext/512", CGU_CLK_FIXDIV,
433		.parents = { JZ4770_CLK_EXT },
434		.fixdiv = { 512 },
435	},
436
437	[JZ4770_CLK_RTC] = {
438		"rtc", CGU_CLK_MUX,
439		.parents = { JZ4770_CLK_EXT512, JZ4770_CLK_OSC32K, },
440		.mux = { CGU_REG_OPCR, 2, 1},
441	},
442};
443
444static void __init jz4770_cgu_init(struct device_node *np)
445{
446	int retval;
447
448	cgu = ingenic_cgu_new(jz4770_cgu_clocks,
449			      ARRAY_SIZE(jz4770_cgu_clocks), np);
450	if (!cgu) {
451		pr_err("%s: failed to initialise CGU\n", __func__);
452		return;
453	}
454
455	retval = ingenic_cgu_register_clocks(cgu);
456	if (retval)
457		pr_err("%s: failed to register CGU Clocks\n", __func__);
458
459	ingenic_cgu_register_syscore_ops(cgu);
460}
461
462/* We only probe via devicetree, no need for a platform driver */
463CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-cgu", jz4770_cgu_init);
464