1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * JZ4760 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 <linux/clk.h> |
14 | |
15 | #include <dt-bindings/clock/ingenic,jz4760-cgu.h> |
16 | |
17 | #include "cgu.h" |
18 | #include "pm.h" |
19 | |
20 | #define MHZ (1000 * 1000) |
21 | |
22 | /* |
23 | * CPM registers offset address definition |
24 | */ |
25 | #define CGU_REG_CPCCR 0x00 |
26 | #define CGU_REG_LCR 0x04 |
27 | #define CGU_REG_CPPCR0 0x10 |
28 | #define CGU_REG_CLKGR0 0x20 |
29 | #define CGU_REG_OPCR 0x24 |
30 | #define CGU_REG_CLKGR1 0x28 |
31 | #define CGU_REG_CPPCR1 0x30 |
32 | #define CGU_REG_USBPCR 0x3c |
33 | #define CGU_REG_USBCDR 0x50 |
34 | #define CGU_REG_I2SCDR 0x60 |
35 | #define CGU_REG_LPCDR 0x64 |
36 | #define CGU_REG_MSCCDR 0x68 |
37 | #define CGU_REG_UHCCDR 0x6c |
38 | #define CGU_REG_SSICDR 0x74 |
39 | #define CGU_REG_CIMCDR 0x7c |
40 | #define CGU_REG_GPSCDR 0x80 |
41 | #define CGU_REG_PCMCDR 0x84 |
42 | #define CGU_REG_GPUCDR 0x88 |
43 | |
44 | static const s8 pll_od_encoding[8] = { |
45 | 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3, |
46 | }; |
47 | |
48 | static const u8 jz4760_cgu_cpccr_div_table[] = { |
49 | 1, 2, 3, 4, 6, 8, |
50 | }; |
51 | |
52 | static const u8 jz4760_cgu_pll_half_div_table[] = { |
53 | 2, 1, |
54 | }; |
55 | |
56 | static void |
57 | jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info, |
58 | unsigned long rate, unsigned long parent_rate, |
59 | unsigned int *pm, unsigned int *pn, unsigned int *pod) |
60 | { |
61 | unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 1; |
62 | |
63 | /* The frequency after the N divider must be between 1 and 50 MHz. */ |
64 | n = parent_rate / (1 * MHZ); |
65 | |
66 | /* The N divider must be >= 2. */ |
67 | n = clamp_val(n, 2, 1 << pll_info->n_bits); |
68 | |
69 | rate /= MHZ; |
70 | parent_rate /= MHZ; |
71 | |
72 | for (m = m_max; m >= m_max && n >= 2; n--) { |
73 | m = rate * n / parent_rate; |
74 | od = m & 1; |
75 | m <<= od; |
76 | } |
77 | |
78 | *pm = m; |
79 | *pn = n + 1; |
80 | *pod = 1 << od; |
81 | } |
82 | |
83 | static const struct ingenic_cgu_clk_info jz4760_cgu_clocks[] = { |
84 | |
85 | /* External clocks */ |
86 | |
87 | [JZ4760_CLK_EXT] = { "ext" , CGU_CLK_EXT }, |
88 | [JZ4760_CLK_OSC32K] = { "osc32k" , CGU_CLK_EXT }, |
89 | |
90 | /* PLLs */ |
91 | |
92 | [JZ4760_CLK_PLL0] = { |
93 | "pll0" , CGU_CLK_PLL, |
94 | .parents = { JZ4760_CLK_EXT }, |
95 | .pll = { |
96 | .reg = CGU_REG_CPPCR0, |
97 | .rate_multiplier = 1, |
98 | .m_shift = 23, |
99 | .m_bits = 8, |
100 | .m_offset = 0, |
101 | .n_shift = 18, |
102 | .n_bits = 4, |
103 | .n_offset = 0, |
104 | .od_shift = 16, |
105 | .od_bits = 2, |
106 | .od_max = 8, |
107 | .od_encoding = pll_od_encoding, |
108 | .bypass_reg = CGU_REG_CPPCR0, |
109 | .bypass_bit = 9, |
110 | .enable_bit = 8, |
111 | .stable_bit = 10, |
112 | .calc_m_n_od = jz4760_cgu_calc_m_n_od, |
113 | }, |
114 | }, |
115 | |
116 | [JZ4760_CLK_PLL1] = { |
117 | /* TODO: PLL1 can depend on PLL0 */ |
118 | "pll1" , CGU_CLK_PLL, |
119 | .parents = { JZ4760_CLK_EXT }, |
120 | .pll = { |
121 | .reg = CGU_REG_CPPCR1, |
122 | .rate_multiplier = 1, |
123 | .m_shift = 23, |
124 | .m_bits = 8, |
125 | .m_offset = 0, |
126 | .n_shift = 18, |
127 | .n_bits = 4, |
128 | .n_offset = 0, |
129 | .od_shift = 16, |
130 | .od_bits = 2, |
131 | .od_max = 8, |
132 | .od_encoding = pll_od_encoding, |
133 | .bypass_bit = -1, |
134 | .enable_bit = 7, |
135 | .stable_bit = 6, |
136 | .calc_m_n_od = jz4760_cgu_calc_m_n_od, |
137 | }, |
138 | }, |
139 | |
140 | /* Main clocks */ |
141 | |
142 | [JZ4760_CLK_CCLK] = { |
143 | "cclk" , CGU_CLK_DIV, |
144 | /* |
145 | * Disabling the CPU clock or any parent clocks will hang the |
146 | * system; mark it critical. |
147 | */ |
148 | .flags = CLK_IS_CRITICAL, |
149 | .parents = { JZ4760_CLK_PLL0, }, |
150 | .div = { |
151 | CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0, |
152 | jz4760_cgu_cpccr_div_table, |
153 | }, |
154 | }, |
155 | [JZ4760_CLK_HCLK] = { |
156 | "hclk" , CGU_CLK_DIV, |
157 | .parents = { JZ4760_CLK_PLL0, }, |
158 | .div = { |
159 | CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0, |
160 | jz4760_cgu_cpccr_div_table, |
161 | }, |
162 | }, |
163 | [JZ4760_CLK_SCLK] = { |
164 | "sclk" , CGU_CLK_DIV, |
165 | .parents = { JZ4760_CLK_PLL0, }, |
166 | .div = { |
167 | CGU_REG_CPCCR, 24, 1, 4, 22, -1, -1, 0, |
168 | jz4760_cgu_cpccr_div_table, |
169 | }, |
170 | }, |
171 | [JZ4760_CLK_H2CLK] = { |
172 | "h2clk" , CGU_CLK_DIV, |
173 | .parents = { JZ4760_CLK_PLL0, }, |
174 | .div = { |
175 | CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0, |
176 | jz4760_cgu_cpccr_div_table, |
177 | }, |
178 | }, |
179 | [JZ4760_CLK_MCLK] = { |
180 | "mclk" , CGU_CLK_DIV, |
181 | /* |
182 | * Disabling MCLK or its parents will render DRAM |
183 | * inaccessible; mark it critical. |
184 | */ |
185 | .flags = CLK_IS_CRITICAL, |
186 | .parents = { JZ4760_CLK_PLL0, }, |
187 | .div = { |
188 | CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0, |
189 | jz4760_cgu_cpccr_div_table, |
190 | }, |
191 | }, |
192 | [JZ4760_CLK_PCLK] = { |
193 | "pclk" , CGU_CLK_DIV, |
194 | .parents = { JZ4760_CLK_PLL0, }, |
195 | .div = { |
196 | CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0, |
197 | jz4760_cgu_cpccr_div_table, |
198 | }, |
199 | }, |
200 | |
201 | /* Divided clocks */ |
202 | |
203 | [JZ4760_CLK_PLL0_HALF] = { |
204 | "pll0_half" , CGU_CLK_DIV, |
205 | .parents = { JZ4760_CLK_PLL0 }, |
206 | .div = { |
207 | CGU_REG_CPCCR, 21, 1, 1, 22, -1, -1, 0, |
208 | jz4760_cgu_pll_half_div_table, |
209 | }, |
210 | }, |
211 | |
212 | /* Those divided clocks can connect to PLL0 or PLL1 */ |
213 | |
214 | [JZ4760_CLK_UHC] = { |
215 | "uhc" , CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, |
216 | .parents = { JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1, }, |
217 | .mux = { CGU_REG_UHCCDR, 31, 1 }, |
218 | .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 }, |
219 | .gate = { CGU_REG_CLKGR0, 24 }, |
220 | }, |
221 | [JZ4760_CLK_GPU] = { |
222 | "gpu" , CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, |
223 | .parents = { JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1, }, |
224 | .mux = { CGU_REG_GPUCDR, 31, 1 }, |
225 | .div = { CGU_REG_GPUCDR, 0, 1, 3, -1, -1, -1 }, |
226 | .gate = { CGU_REG_CLKGR1, 9 }, |
227 | }, |
228 | [JZ4760_CLK_LPCLK_DIV] = { |
229 | "lpclk_div" , CGU_CLK_DIV | CGU_CLK_MUX, |
230 | .parents = { JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1, }, |
231 | .mux = { CGU_REG_LPCDR, 29, 1 }, |
232 | .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 }, |
233 | }, |
234 | [JZ4760_CLK_TVE] = { |
235 | "tve" , CGU_CLK_GATE | CGU_CLK_MUX, |
236 | .parents = { JZ4760_CLK_LPCLK_DIV, JZ4760_CLK_EXT, }, |
237 | .mux = { CGU_REG_LPCDR, 31, 1 }, |
238 | .gate = { CGU_REG_CLKGR0, 27 }, |
239 | }, |
240 | [JZ4760_CLK_LPCLK] = { |
241 | "lpclk" , CGU_CLK_GATE | CGU_CLK_MUX, |
242 | .parents = { JZ4760_CLK_LPCLK_DIV, JZ4760_CLK_TVE, }, |
243 | .mux = { CGU_REG_LPCDR, 30, 1 }, |
244 | .gate = { CGU_REG_CLKGR0, 28 }, |
245 | }, |
246 | [JZ4760_CLK_GPS] = { |
247 | "gps" , CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, |
248 | .parents = { JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1, }, |
249 | .mux = { CGU_REG_GPSCDR, 31, 1 }, |
250 | .div = { CGU_REG_GPSCDR, 0, 1, 4, -1, -1, -1 }, |
251 | .gate = { CGU_REG_CLKGR0, 22 }, |
252 | }, |
253 | |
254 | /* Those divided clocks can connect to EXT, PLL0 or PLL1 */ |
255 | |
256 | [JZ4760_CLK_PCM] = { |
257 | "pcm" , CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, |
258 | .parents = { JZ4760_CLK_EXT, -1, |
259 | JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1 }, |
260 | .mux = { CGU_REG_PCMCDR, 30, 2 }, |
261 | .div = { CGU_REG_PCMCDR, 0, 1, 9, -1, -1, -1, BIT(0) }, |
262 | .gate = { CGU_REG_CLKGR1, 8 }, |
263 | }, |
264 | [JZ4760_CLK_I2S] = { |
265 | "i2s" , CGU_CLK_DIV | CGU_CLK_MUX, |
266 | .parents = { JZ4760_CLK_EXT, -1, |
267 | JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1 }, |
268 | .mux = { CGU_REG_I2SCDR, 30, 2 }, |
269 | .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1, BIT(0) }, |
270 | }, |
271 | [JZ4760_CLK_OTG] = { |
272 | "usb" , CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, |
273 | .parents = { JZ4760_CLK_EXT, -1, |
274 | JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1 }, |
275 | .mux = { CGU_REG_USBCDR, 30, 2 }, |
276 | .div = { CGU_REG_USBCDR, 0, 1, 8, -1, -1, -1 }, |
277 | .gate = { CGU_REG_CLKGR0, 2 }, |
278 | }, |
279 | |
280 | /* Those divided clocks can connect to EXT or PLL0 */ |
281 | [JZ4760_CLK_MMC_MUX] = { |
282 | "mmc_mux" , CGU_CLK_MUX | CGU_CLK_DIV, |
283 | .parents = { JZ4760_CLK_EXT, JZ4760_CLK_PLL0_HALF, }, |
284 | .mux = { CGU_REG_MSCCDR, 31, 1 }, |
285 | .div = { CGU_REG_MSCCDR, 0, 1, 6, -1, -1, -1, BIT(0) }, |
286 | }, |
287 | [JZ4760_CLK_SSI_MUX] = { |
288 | "ssi_mux" , CGU_CLK_DIV | CGU_CLK_MUX, |
289 | .parents = { JZ4760_CLK_EXT, JZ4760_CLK_PLL0_HALF, }, |
290 | .mux = { CGU_REG_SSICDR, 31, 1 }, |
291 | .div = { CGU_REG_SSICDR, 0, 1, 6, -1, -1, -1, BIT(0) }, |
292 | }, |
293 | |
294 | /* These divided clock can connect to PLL0 only */ |
295 | [JZ4760_CLK_CIM] = { |
296 | "cim" , CGU_CLK_DIV | CGU_CLK_GATE, |
297 | .parents = { JZ4760_CLK_PLL0_HALF }, |
298 | .div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 }, |
299 | .gate = { CGU_REG_CLKGR0, 26 }, |
300 | }, |
301 | |
302 | /* Gate-only clocks */ |
303 | |
304 | [JZ4760_CLK_SSI0] = { |
305 | "ssi0" , CGU_CLK_GATE, |
306 | .parents = { JZ4760_CLK_SSI_MUX, }, |
307 | .gate = { CGU_REG_CLKGR0, 4 }, |
308 | }, |
309 | [JZ4760_CLK_SSI1] = { |
310 | "ssi1" , CGU_CLK_GATE, |
311 | .parents = { JZ4760_CLK_SSI_MUX, }, |
312 | .gate = { CGU_REG_CLKGR0, 19 }, |
313 | }, |
314 | [JZ4760_CLK_SSI2] = { |
315 | "ssi2" , CGU_CLK_GATE, |
316 | .parents = { JZ4760_CLK_SSI_MUX, }, |
317 | .gate = { CGU_REG_CLKGR0, 20 }, |
318 | }, |
319 | [JZ4760_CLK_DMA] = { |
320 | "dma" , CGU_CLK_GATE, |
321 | .parents = { JZ4760_CLK_H2CLK, }, |
322 | .gate = { CGU_REG_CLKGR0, 21 }, |
323 | }, |
324 | [JZ4760_CLK_MDMA] = { |
325 | "mdma" , CGU_CLK_GATE, |
326 | .parents = { JZ4760_CLK_HCLK, }, |
327 | .gate = { CGU_REG_CLKGR0, 25 }, |
328 | }, |
329 | [JZ4760_CLK_BDMA] = { |
330 | "bdma" , CGU_CLK_GATE, |
331 | .parents = { JZ4760_CLK_HCLK, }, |
332 | .gate = { CGU_REG_CLKGR1, 0 }, |
333 | }, |
334 | [JZ4760_CLK_I2C0] = { |
335 | "i2c0" , CGU_CLK_GATE, |
336 | .parents = { JZ4760_CLK_EXT, }, |
337 | .gate = { CGU_REG_CLKGR0, 5 }, |
338 | }, |
339 | [JZ4760_CLK_I2C1] = { |
340 | "i2c1" , CGU_CLK_GATE, |
341 | .parents = { JZ4760_CLK_EXT, }, |
342 | .gate = { CGU_REG_CLKGR0, 6 }, |
343 | }, |
344 | [JZ4760_CLK_UART0] = { |
345 | "uart0" , CGU_CLK_GATE, |
346 | .parents = { JZ4760_CLK_EXT, }, |
347 | .gate = { CGU_REG_CLKGR0, 15 }, |
348 | }, |
349 | [JZ4760_CLK_UART1] = { |
350 | "uart1" , CGU_CLK_GATE, |
351 | .parents = { JZ4760_CLK_EXT, }, |
352 | .gate = { CGU_REG_CLKGR0, 16 }, |
353 | }, |
354 | [JZ4760_CLK_UART2] = { |
355 | "uart2" , CGU_CLK_GATE, |
356 | .parents = { JZ4760_CLK_EXT, }, |
357 | .gate = { CGU_REG_CLKGR0, 17 }, |
358 | }, |
359 | [JZ4760_CLK_UART3] = { |
360 | "uart3" , CGU_CLK_GATE, |
361 | .parents = { JZ4760_CLK_EXT, }, |
362 | .gate = { CGU_REG_CLKGR0, 18 }, |
363 | }, |
364 | [JZ4760_CLK_IPU] = { |
365 | "ipu" , CGU_CLK_GATE, |
366 | .parents = { JZ4760_CLK_HCLK, }, |
367 | .gate = { CGU_REG_CLKGR0, 29 }, |
368 | }, |
369 | [JZ4760_CLK_ADC] = { |
370 | "adc" , CGU_CLK_GATE, |
371 | .parents = { JZ4760_CLK_EXT, }, |
372 | .gate = { CGU_REG_CLKGR0, 14 }, |
373 | }, |
374 | [JZ4760_CLK_AIC] = { |
375 | "aic" , CGU_CLK_GATE, |
376 | .parents = { JZ4760_CLK_EXT, }, |
377 | .gate = { CGU_REG_CLKGR0, 8 }, |
378 | }, |
379 | [JZ4760_CLK_VPU] = { |
380 | "vpu" , CGU_CLK_GATE, |
381 | .parents = { JZ4760_CLK_HCLK, }, |
382 | .gate = { CGU_REG_LCR, 30, false, 150 }, |
383 | }, |
384 | [JZ4760_CLK_MMC0] = { |
385 | "mmc0" , CGU_CLK_GATE, |
386 | .parents = { JZ4760_CLK_MMC_MUX, }, |
387 | .gate = { CGU_REG_CLKGR0, 3 }, |
388 | }, |
389 | [JZ4760_CLK_MMC1] = { |
390 | "mmc1" , CGU_CLK_GATE, |
391 | .parents = { JZ4760_CLK_MMC_MUX, }, |
392 | .gate = { CGU_REG_CLKGR0, 11 }, |
393 | }, |
394 | [JZ4760_CLK_MMC2] = { |
395 | "mmc2" , CGU_CLK_GATE, |
396 | .parents = { JZ4760_CLK_MMC_MUX, }, |
397 | .gate = { CGU_REG_CLKGR0, 12 }, |
398 | }, |
399 | [JZ4760_CLK_UHC_PHY] = { |
400 | "uhc_phy" , CGU_CLK_GATE, |
401 | .parents = { JZ4760_CLK_UHC, }, |
402 | .gate = { CGU_REG_OPCR, 5 }, |
403 | }, |
404 | [JZ4760_CLK_OTG_PHY] = { |
405 | "usb_phy" , CGU_CLK_GATE, |
406 | .parents = { JZ4760_CLK_OTG }, |
407 | .gate = { CGU_REG_OPCR, 7, true, 50 }, |
408 | }, |
409 | |
410 | /* Custom clocks */ |
411 | [JZ4760_CLK_EXT512] = { |
412 | "ext/512" , CGU_CLK_FIXDIV, |
413 | .parents = { JZ4760_CLK_EXT }, |
414 | .fixdiv = { 512 }, |
415 | }, |
416 | [JZ4760_CLK_RTC] = { |
417 | "rtc" , CGU_CLK_MUX, |
418 | .parents = { JZ4760_CLK_EXT512, JZ4760_CLK_OSC32K, }, |
419 | .mux = { CGU_REG_OPCR, 2, 1}, |
420 | }, |
421 | }; |
422 | |
423 | static void __init jz4760_cgu_init(struct device_node *np) |
424 | { |
425 | struct ingenic_cgu *cgu; |
426 | int retval; |
427 | |
428 | cgu = ingenic_cgu_new(clock_info: jz4760_cgu_clocks, |
429 | ARRAY_SIZE(jz4760_cgu_clocks), np); |
430 | if (!cgu) { |
431 | pr_err("%s: failed to initialise CGU\n" , __func__); |
432 | return; |
433 | } |
434 | |
435 | retval = ingenic_cgu_register_clocks(cgu); |
436 | if (retval) |
437 | pr_err("%s: failed to register CGU Clocks\n" , __func__); |
438 | |
439 | ingenic_cgu_register_syscore_ops(cgu); |
440 | } |
441 | |
442 | /* We only probe via devicetree, no need for a platform driver */ |
443 | CLK_OF_DECLARE_DRIVER(jz4760_cgu, "ingenic,jz4760-cgu" , jz4760_cgu_init); |
444 | |
445 | /* JZ4760B has some small differences, but we don't implement them. */ |
446 | CLK_OF_DECLARE_DRIVER(jz4760b_cgu, "ingenic,jz4760b-cgu" , jz4760_cgu_init); |
447 | |