1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Ingenic JZ4725B SoC CGU driver
4  *
5  * Copyright (C) 2018 Paul Cercueil
6  * Author: Paul Cercueil <paul@crapouillou.net>
7  */
8 
9 #include <linux/clk-provider.h>
10 #include <linux/delay.h>
11 #include <linux/of.h>
12 #include <dt-bindings/clock/jz4725b-cgu.h>
13 #include "cgu.h"
14 
15 /* CGU register offsets */
16 #define CGU_REG_CPCCR		0x00
17 #define CGU_REG_LCR		0x04
18 #define CGU_REG_CPPCR		0x10
19 #define CGU_REG_CLKGR		0x20
20 #define CGU_REG_OPCR		0x24
21 #define CGU_REG_I2SCDR		0x60
22 #define CGU_REG_LPCDR		0x64
23 #define CGU_REG_MSCCDR		0x68
24 #define CGU_REG_SSICDR		0x74
25 #define CGU_REG_CIMCDR		0x78
26 
27 /* bits within the LCR register */
28 #define LCR_SLEEP		BIT(0)
29 
30 static struct ingenic_cgu *cgu;
31 
32 static const s8 pll_od_encoding[4] = {
33 	0x0, 0x1, -1, 0x3,
34 };
35 
36 static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
37 
38 	/* External clocks */
39 
40 	[JZ4725B_CLK_EXT] = { "ext", CGU_CLK_EXT },
41 	[JZ4725B_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT },
42 
43 	[JZ4725B_CLK_PLL] = {
44 		"pll", CGU_CLK_PLL,
45 		.parents = { JZ4725B_CLK_EXT, -1, -1, -1 },
46 		.pll = {
47 			.reg = CGU_REG_CPPCR,
48 			.m_shift = 23,
49 			.m_bits = 9,
50 			.m_offset = 2,
51 			.n_shift = 18,
52 			.n_bits = 5,
53 			.n_offset = 2,
54 			.od_shift = 16,
55 			.od_bits = 2,
56 			.od_max = 4,
57 			.od_encoding = pll_od_encoding,
58 			.stable_bit = 10,
59 			.bypass_bit = 9,
60 			.enable_bit = 8,
61 		},
62 	},
63 
64 	/* Muxes & dividers */
65 
66 	[JZ4725B_CLK_PLL_HALF] = {
67 		"pll half", CGU_CLK_DIV,
68 		.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
69 		.div = { CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1 },
70 	},
71 
72 	[JZ4725B_CLK_CCLK] = {
73 		"cclk", CGU_CLK_DIV,
74 		.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
75 		.div = { CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1 },
76 	},
77 
78 	[JZ4725B_CLK_HCLK] = {
79 		"hclk", CGU_CLK_DIV,
80 		.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
81 		.div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 },
82 	},
83 
84 	[JZ4725B_CLK_PCLK] = {
85 		"pclk", CGU_CLK_DIV,
86 		.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
87 		.div = { CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1 },
88 	},
89 
90 	[JZ4725B_CLK_MCLK] = {
91 		"mclk", CGU_CLK_DIV,
92 		.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
93 		.div = { CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1 },
94 	},
95 
96 	[JZ4725B_CLK_IPU] = {
97 		"ipu", CGU_CLK_DIV | CGU_CLK_GATE,
98 		.parents = { JZ4725B_CLK_PLL, -1, -1, -1 },
99 		.div = { CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1 },
100 		.gate = { CGU_REG_CLKGR, 13 },
101 	},
102 
103 	[JZ4725B_CLK_LCD] = {
104 		"lcd", CGU_CLK_DIV | CGU_CLK_GATE,
105 		.parents = { JZ4725B_CLK_PLL_HALF, -1, -1, -1 },
106 		.div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },
107 		.gate = { CGU_REG_CLKGR, 9 },
108 	},
109 
110 	[JZ4725B_CLK_I2S] = {
111 		"i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
112 		.parents = { JZ4725B_CLK_EXT, JZ4725B_CLK_PLL_HALF, -1, -1 },
113 		.mux = { CGU_REG_CPCCR, 31, 1 },
114 		.div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },
115 		.gate = { CGU_REG_CLKGR, 6 },
116 	},
117 
118 	[JZ4725B_CLK_SPI] = {
119 		"spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
120 		.parents = { JZ4725B_CLK_EXT, JZ4725B_CLK_PLL, -1, -1 },
121 		.mux = { CGU_REG_SSICDR, 31, 1 },
122 		.div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 },
123 		.gate = { CGU_REG_CLKGR, 4 },
124 	},
125 
126 	[JZ4725B_CLK_MMC_MUX] = {
127 		"mmc_mux", CGU_CLK_DIV,
128 		.parents = { JZ4725B_CLK_PLL_HALF, -1, -1, -1 },
129 		.div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 },
130 	},
131 
132 	[JZ4725B_CLK_UDC] = {
133 		"udc", CGU_CLK_MUX | CGU_CLK_DIV,
134 		.parents = { JZ4725B_CLK_EXT, JZ4725B_CLK_PLL_HALF, -1, -1 },
135 		.mux = { CGU_REG_CPCCR, 29, 1 },
136 		.div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },
137 	},
138 
139 	/* Gate-only clocks */
140 
141 	[JZ4725B_CLK_UART] = {
142 		"uart", CGU_CLK_GATE,
143 		.parents = { JZ4725B_CLK_EXT, -1, -1, -1 },
144 		.gate = { CGU_REG_CLKGR, 0 },
145 	},
146 
147 	[JZ4725B_CLK_DMA] = {
148 		"dma", CGU_CLK_GATE,
149 		.parents = { JZ4725B_CLK_PCLK, -1, -1, -1 },
150 		.gate = { CGU_REG_CLKGR, 12 },
151 	},
152 
153 	[JZ4725B_CLK_ADC] = {
154 		"adc", CGU_CLK_GATE,
155 		.parents = { JZ4725B_CLK_EXT, -1, -1, -1 },
156 		.gate = { CGU_REG_CLKGR, 7 },
157 	},
158 
159 	[JZ4725B_CLK_I2C] = {
160 		"i2c", CGU_CLK_GATE,
161 		.parents = { JZ4725B_CLK_EXT, -1, -1, -1 },
162 		.gate = { CGU_REG_CLKGR, 3 },
163 	},
164 
165 	[JZ4725B_CLK_AIC] = {
166 		"aic", CGU_CLK_GATE,
167 		.parents = { JZ4725B_CLK_EXT, -1, -1, -1 },
168 		.gate = { CGU_REG_CLKGR, 5 },
169 	},
170 
171 	[JZ4725B_CLK_MMC0] = {
172 		"mmc0", CGU_CLK_GATE,
173 		.parents = { JZ4725B_CLK_MMC_MUX, -1, -1, -1 },
174 		.gate = { CGU_REG_CLKGR, 6 },
175 	},
176 
177 	[JZ4725B_CLK_MMC1] = {
178 		"mmc1", CGU_CLK_GATE,
179 		.parents = { JZ4725B_CLK_MMC_MUX, -1, -1, -1 },
180 		.gate = { CGU_REG_CLKGR, 16 },
181 	},
182 
183 	[JZ4725B_CLK_BCH] = {
184 		"bch", CGU_CLK_GATE,
185 		.parents = { JZ4725B_CLK_MCLK/* not sure */, -1, -1, -1 },
186 		.gate = { CGU_REG_CLKGR, 11 },
187 	},
188 
189 	[JZ4725B_CLK_TCU] = {
190 		"tcu", CGU_CLK_GATE,
191 		.parents = { JZ4725B_CLK_EXT/* not sure */, -1, -1, -1 },
192 		.gate = { CGU_REG_CLKGR, 1 },
193 	},
194 
195 	[JZ4725B_CLK_EXT512] = {
196 		"ext/512", CGU_CLK_FIXDIV,
197 		.parents = { JZ4725B_CLK_EXT },
198 
199 		/* Doc calls it EXT512, but it seems to be /256... */
200 		.fixdiv = { 256 },
201 	},
202 
203 	[JZ4725B_CLK_RTC] = {
204 		"rtc", CGU_CLK_MUX,
205 		.parents = { JZ4725B_CLK_EXT512, JZ4725B_CLK_OSC32K, -1, -1 },
206 		.mux = { CGU_REG_OPCR, 2, 1},
207 	},
208 
209 	[JZ4725B_CLK_UDC_PHY] = {
210 		"udc_phy", CGU_CLK_GATE,
211 		.parents = { JZ4725B_CLK_EXT, -1, -1, -1 },
212 		.gate = { CGU_REG_OPCR, 6, true },
213 	},
214 };
215 
216 static void __init jz4725b_cgu_init(struct device_node *np)
217 {
218 	int retval;
219 
220 	cgu = ingenic_cgu_new(jz4725b_cgu_clocks,
221 			      ARRAY_SIZE(jz4725b_cgu_clocks), np);
222 	if (!cgu) {
223 		pr_err("%s: failed to initialise CGU\n", __func__);
224 		return;
225 	}
226 
227 	retval = ingenic_cgu_register_clocks(cgu);
228 	if (retval)
229 		pr_err("%s: failed to register CGU Clocks\n", __func__);
230 }
231 CLK_OF_DECLARE(jz4725b_cgu, "ingenic,jz4725b-cgu", jz4725b_cgu_init);
232