1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2014 Marvell Technology Group Ltd. |
4 | * |
5 | * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> |
6 | * Alexandre Belloni <alexandre.belloni@free-electrons.com> |
7 | */ |
8 | |
9 | #include <linux/clk.h> |
10 | #include <linux/clk-provider.h> |
11 | #include <linux/io.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_address.h> |
15 | #include <linux/slab.h> |
16 | |
17 | #include <dt-bindings/clock/berlin2.h> |
18 | |
19 | #include "berlin2-avpll.h" |
20 | #include "berlin2-div.h" |
21 | #include "berlin2-pll.h" |
22 | #include "common.h" |
23 | |
24 | #define REG_PINMUX0 0x0000 |
25 | #define REG_PINMUX1 0x0004 |
26 | #define REG_SYSPLLCTL0 0x0014 |
27 | #define REG_SYSPLLCTL4 0x0024 |
28 | #define REG_MEMPLLCTL0 0x0028 |
29 | #define REG_MEMPLLCTL4 0x0038 |
30 | #define REG_CPUPLLCTL0 0x003c |
31 | #define REG_CPUPLLCTL4 0x004c |
32 | #define REG_AVPLLCTL0 0x0050 |
33 | #define REG_AVPLLCTL31 0x00cc |
34 | #define REG_AVPLLCTL62 0x0148 |
35 | #define REG_PLLSTATUS 0x014c |
36 | #define REG_CLKENABLE 0x0150 |
37 | #define REG_CLKSELECT0 0x0154 |
38 | #define REG_CLKSELECT1 0x0158 |
39 | #define REG_CLKSELECT2 0x015c |
40 | #define REG_CLKSELECT3 0x0160 |
41 | #define REG_CLKSWITCH0 0x0164 |
42 | #define REG_CLKSWITCH1 0x0168 |
43 | #define REG_RESET_TRIGGER 0x0178 |
44 | #define REG_RESET_STATUS0 0x017c |
45 | #define REG_RESET_STATUS1 0x0180 |
46 | #define REG_SW_GENERIC0 0x0184 |
47 | #define REG_SW_GENERIC3 0x0190 |
48 | #define REG_PRODUCTID 0x01cc |
49 | #define REG_PRODUCTID_EXT 0x01d0 |
50 | #define REG_GFX3DCORE_CLKCTL 0x022c |
51 | #define REG_GFX3DSYS_CLKCTL 0x0230 |
52 | #define REG_ARC_CLKCTL 0x0234 |
53 | #define REG_VIP_CLKCTL 0x0238 |
54 | #define REG_SDIO0XIN_CLKCTL 0x023c |
55 | #define REG_SDIO1XIN_CLKCTL 0x0240 |
56 | #define 0x0244 |
57 | #define REG_GFX3D_RESET 0x0248 |
58 | #define REG_GC360_CLKCTL 0x024c |
59 | #define REG_SDIO_DLLMST_CLKCTL 0x0250 |
60 | |
61 | /* |
62 | * BG2/BG2CD SoCs have the following audio/video I/O units: |
63 | * |
64 | * audiohd: HDMI TX audio |
65 | * audio0: 7.1ch TX |
66 | * audio1: 2ch TX |
67 | * audio2: 2ch RX |
68 | * audio3: SPDIF TX |
69 | * video0: HDMI video |
70 | * video1: Secondary video |
71 | * video2: SD auxiliary video |
72 | * |
73 | * There are no external audio clocks (ACLKI0, ACLKI1) and |
74 | * only one external video clock (VCLKI0). |
75 | * |
76 | * Currently missing bits and pieces: |
77 | * - audio_fast_pll is unknown |
78 | * - audiohd_pll is unknown |
79 | * - video0_pll is unknown |
80 | * - audio[023], audiohd parent pll is assumed to be audio_fast_pll |
81 | * |
82 | */ |
83 | |
84 | #define MAX_CLKS 41 |
85 | static struct clk_hw_onecell_data *clk_data; |
86 | static DEFINE_SPINLOCK(lock); |
87 | static void __iomem *gbase; |
88 | |
89 | enum { |
90 | REFCLK, VIDEO_EXT0, |
91 | SYSPLL, MEMPLL, CPUPLL, |
92 | AVPLL_A1, AVPLL_A2, AVPLL_A3, AVPLL_A4, |
93 | AVPLL_A5, AVPLL_A6, AVPLL_A7, AVPLL_A8, |
94 | AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4, |
95 | AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8, |
96 | AUDIO1_PLL, AUDIO_FAST_PLL, |
97 | VIDEO0_PLL, VIDEO0_IN, |
98 | VIDEO1_PLL, VIDEO1_IN, |
99 | VIDEO2_PLL, VIDEO2_IN, |
100 | }; |
101 | |
102 | static const char *clk_names[] = { |
103 | [REFCLK] = "refclk" , |
104 | [VIDEO_EXT0] = "video_ext0" , |
105 | [SYSPLL] = "syspll" , |
106 | [MEMPLL] = "mempll" , |
107 | [CPUPLL] = "cpupll" , |
108 | [AVPLL_A1] = "avpll_a1" , |
109 | [AVPLL_A2] = "avpll_a2" , |
110 | [AVPLL_A3] = "avpll_a3" , |
111 | [AVPLL_A4] = "avpll_a4" , |
112 | [AVPLL_A5] = "avpll_a5" , |
113 | [AVPLL_A6] = "avpll_a6" , |
114 | [AVPLL_A7] = "avpll_a7" , |
115 | [AVPLL_A8] = "avpll_a8" , |
116 | [AVPLL_B1] = "avpll_b1" , |
117 | [AVPLL_B2] = "avpll_b2" , |
118 | [AVPLL_B3] = "avpll_b3" , |
119 | [AVPLL_B4] = "avpll_b4" , |
120 | [AVPLL_B5] = "avpll_b5" , |
121 | [AVPLL_B6] = "avpll_b6" , |
122 | [AVPLL_B7] = "avpll_b7" , |
123 | [AVPLL_B8] = "avpll_b8" , |
124 | [AUDIO1_PLL] = "audio1_pll" , |
125 | [AUDIO_FAST_PLL] = "audio_fast_pll" , |
126 | [VIDEO0_PLL] = "video0_pll" , |
127 | [VIDEO0_IN] = "video0_in" , |
128 | [VIDEO1_PLL] = "video1_pll" , |
129 | [VIDEO1_IN] = "video1_in" , |
130 | [VIDEO2_PLL] = "video2_pll" , |
131 | [VIDEO2_IN] = "video2_in" , |
132 | }; |
133 | |
134 | static const struct berlin2_pll_map bg2_pll_map __initconst = { |
135 | .vcodiv = {10, 15, 20, 25, 30, 40, 50, 60, 80}, |
136 | .mult = 10, |
137 | .fbdiv_shift = 6, |
138 | .rfdiv_shift = 1, |
139 | .divsel_shift = 7, |
140 | }; |
141 | |
142 | static const u8 default_parent_ids[] = { |
143 | SYSPLL, AVPLL_B4, AVPLL_A5, AVPLL_B6, AVPLL_B7, SYSPLL |
144 | }; |
145 | |
146 | static const struct berlin2_div_data bg2_divs[] __initconst = { |
147 | { |
148 | .name = "sys" , |
149 | .parent_ids = (const u8 []){ |
150 | SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL |
151 | }, |
152 | .num_parents = 6, |
153 | .map = { |
154 | BERLIN2_DIV_GATE(REG_CLKENABLE, 0), |
155 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0), |
156 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3), |
157 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3), |
158 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4), |
159 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5), |
160 | }, |
161 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
162 | .flags = CLK_IGNORE_UNUSED, |
163 | }, |
164 | { |
165 | .name = "cpu" , |
166 | .parent_ids = (const u8 []){ |
167 | CPUPLL, MEMPLL, MEMPLL, MEMPLL, MEMPLL |
168 | }, |
169 | .num_parents = 5, |
170 | .map = { |
171 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6), |
172 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9), |
173 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6), |
174 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7), |
175 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8), |
176 | }, |
177 | .div_flags = BERLIN2_DIV_HAS_MUX, |
178 | .flags = 0, |
179 | }, |
180 | { |
181 | .name = "drmfigo" , |
182 | .parent_ids = default_parent_ids, |
183 | .num_parents = ARRAY_SIZE(default_parent_ids), |
184 | .map = { |
185 | BERLIN2_DIV_GATE(REG_CLKENABLE, 16), |
186 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 17), |
187 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 20), |
188 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12), |
189 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13), |
190 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14), |
191 | }, |
192 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
193 | .flags = 0, |
194 | }, |
195 | { |
196 | .name = "cfg" , |
197 | .parent_ids = default_parent_ids, |
198 | .num_parents = ARRAY_SIZE(default_parent_ids), |
199 | .map = { |
200 | BERLIN2_DIV_GATE(REG_CLKENABLE, 1), |
201 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 23), |
202 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 26), |
203 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15), |
204 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16), |
205 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17), |
206 | }, |
207 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
208 | .flags = 0, |
209 | }, |
210 | { |
211 | .name = "gfx" , |
212 | .parent_ids = default_parent_ids, |
213 | .num_parents = ARRAY_SIZE(default_parent_ids), |
214 | .map = { |
215 | BERLIN2_DIV_GATE(REG_CLKENABLE, 4), |
216 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 29), |
217 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 0), |
218 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18), |
219 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19), |
220 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20), |
221 | }, |
222 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
223 | .flags = 0, |
224 | }, |
225 | { |
226 | .name = "zsp" , |
227 | .parent_ids = default_parent_ids, |
228 | .num_parents = ARRAY_SIZE(default_parent_ids), |
229 | .map = { |
230 | BERLIN2_DIV_GATE(REG_CLKENABLE, 5), |
231 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 3), |
232 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 6), |
233 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21), |
234 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22), |
235 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23), |
236 | }, |
237 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
238 | .flags = 0, |
239 | }, |
240 | { |
241 | .name = "perif" , |
242 | .parent_ids = default_parent_ids, |
243 | .num_parents = ARRAY_SIZE(default_parent_ids), |
244 | .map = { |
245 | BERLIN2_DIV_GATE(REG_CLKENABLE, 6), |
246 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 9), |
247 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 12), |
248 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24), |
249 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25), |
250 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26), |
251 | }, |
252 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
253 | .flags = CLK_IGNORE_UNUSED, |
254 | }, |
255 | { |
256 | .name = "pcube" , |
257 | .parent_ids = default_parent_ids, |
258 | .num_parents = ARRAY_SIZE(default_parent_ids), |
259 | .map = { |
260 | BERLIN2_DIV_GATE(REG_CLKENABLE, 2), |
261 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 15), |
262 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 18), |
263 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27), |
264 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28), |
265 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29), |
266 | }, |
267 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
268 | .flags = 0, |
269 | }, |
270 | { |
271 | .name = "vscope" , |
272 | .parent_ids = default_parent_ids, |
273 | .num_parents = ARRAY_SIZE(default_parent_ids), |
274 | .map = { |
275 | BERLIN2_DIV_GATE(REG_CLKENABLE, 3), |
276 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 21), |
277 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 24), |
278 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30), |
279 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31), |
280 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0), |
281 | }, |
282 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
283 | .flags = 0, |
284 | }, |
285 | { |
286 | .name = "nfc_ecc" , |
287 | .parent_ids = default_parent_ids, |
288 | .num_parents = ARRAY_SIZE(default_parent_ids), |
289 | .map = { |
290 | BERLIN2_DIV_GATE(REG_CLKENABLE, 18), |
291 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 27), |
292 | BERLIN2_DIV_SELECT(REG_CLKSELECT2, 0), |
293 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1), |
294 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2), |
295 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3), |
296 | }, |
297 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
298 | .flags = 0, |
299 | }, |
300 | { |
301 | .name = "vpp" , |
302 | .parent_ids = default_parent_ids, |
303 | .num_parents = ARRAY_SIZE(default_parent_ids), |
304 | .map = { |
305 | BERLIN2_DIV_GATE(REG_CLKENABLE, 21), |
306 | BERLIN2_PLL_SELECT(REG_CLKSELECT2, 3), |
307 | BERLIN2_DIV_SELECT(REG_CLKSELECT2, 6), |
308 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 4), |
309 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 5), |
310 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 6), |
311 | }, |
312 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
313 | .flags = 0, |
314 | }, |
315 | { |
316 | .name = "app" , |
317 | .parent_ids = default_parent_ids, |
318 | .num_parents = ARRAY_SIZE(default_parent_ids), |
319 | .map = { |
320 | BERLIN2_DIV_GATE(REG_CLKENABLE, 20), |
321 | BERLIN2_PLL_SELECT(REG_CLKSELECT2, 9), |
322 | BERLIN2_DIV_SELECT(REG_CLKSELECT2, 12), |
323 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 7), |
324 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 8), |
325 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 9), |
326 | }, |
327 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
328 | .flags = 0, |
329 | }, |
330 | { |
331 | .name = "audio0" , |
332 | .parent_ids = (const u8 []){ AUDIO_FAST_PLL }, |
333 | .num_parents = 1, |
334 | .map = { |
335 | BERLIN2_DIV_GATE(REG_CLKENABLE, 22), |
336 | BERLIN2_DIV_SELECT(REG_CLKSELECT2, 17), |
337 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 10), |
338 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 11), |
339 | }, |
340 | .div_flags = BERLIN2_DIV_HAS_GATE, |
341 | .flags = 0, |
342 | }, |
343 | { |
344 | .name = "audio2" , |
345 | .parent_ids = (const u8 []){ AUDIO_FAST_PLL }, |
346 | .num_parents = 1, |
347 | .map = { |
348 | BERLIN2_DIV_GATE(REG_CLKENABLE, 24), |
349 | BERLIN2_DIV_SELECT(REG_CLKSELECT2, 20), |
350 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 14), |
351 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 15), |
352 | }, |
353 | .div_flags = BERLIN2_DIV_HAS_GATE, |
354 | .flags = 0, |
355 | }, |
356 | { |
357 | .name = "audio3" , |
358 | .parent_ids = (const u8 []){ AUDIO_FAST_PLL }, |
359 | .num_parents = 1, |
360 | .map = { |
361 | BERLIN2_DIV_GATE(REG_CLKENABLE, 25), |
362 | BERLIN2_DIV_SELECT(REG_CLKSELECT2, 23), |
363 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 16), |
364 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 17), |
365 | }, |
366 | .div_flags = BERLIN2_DIV_HAS_GATE, |
367 | .flags = 0, |
368 | }, |
369 | { |
370 | .name = "audio1" , |
371 | .parent_ids = (const u8 []){ AUDIO1_PLL }, |
372 | .num_parents = 1, |
373 | .map = { |
374 | BERLIN2_DIV_GATE(REG_CLKENABLE, 23), |
375 | BERLIN2_DIV_SELECT(REG_CLKSELECT3, 0), |
376 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 12), |
377 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 13), |
378 | }, |
379 | .div_flags = BERLIN2_DIV_HAS_GATE, |
380 | .flags = 0, |
381 | }, |
382 | { |
383 | .name = "gfx3d_core" , |
384 | .parent_ids = default_parent_ids, |
385 | .num_parents = ARRAY_SIZE(default_parent_ids), |
386 | .map = { |
387 | BERLIN2_SINGLE_DIV(REG_GFX3DCORE_CLKCTL), |
388 | }, |
389 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
390 | .flags = 0, |
391 | }, |
392 | { |
393 | .name = "gfx3d_sys" , |
394 | .parent_ids = default_parent_ids, |
395 | .num_parents = ARRAY_SIZE(default_parent_ids), |
396 | .map = { |
397 | BERLIN2_SINGLE_DIV(REG_GFX3DSYS_CLKCTL), |
398 | }, |
399 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
400 | .flags = 0, |
401 | }, |
402 | { |
403 | .name = "arc" , |
404 | .parent_ids = default_parent_ids, |
405 | .num_parents = ARRAY_SIZE(default_parent_ids), |
406 | .map = { |
407 | BERLIN2_SINGLE_DIV(REG_ARC_CLKCTL), |
408 | }, |
409 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
410 | .flags = 0, |
411 | }, |
412 | { |
413 | .name = "vip" , |
414 | .parent_ids = default_parent_ids, |
415 | .num_parents = ARRAY_SIZE(default_parent_ids), |
416 | .map = { |
417 | BERLIN2_SINGLE_DIV(REG_VIP_CLKCTL), |
418 | }, |
419 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
420 | .flags = 0, |
421 | }, |
422 | { |
423 | .name = "sdio0xin" , |
424 | .parent_ids = default_parent_ids, |
425 | .num_parents = ARRAY_SIZE(default_parent_ids), |
426 | .map = { |
427 | BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL), |
428 | }, |
429 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
430 | .flags = 0, |
431 | }, |
432 | { |
433 | .name = "sdio1xin" , |
434 | .parent_ids = default_parent_ids, |
435 | .num_parents = ARRAY_SIZE(default_parent_ids), |
436 | .map = { |
437 | BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL), |
438 | }, |
439 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
440 | .flags = 0, |
441 | }, |
442 | { |
443 | .name = "gfx3d_extra" , |
444 | .parent_ids = default_parent_ids, |
445 | .num_parents = ARRAY_SIZE(default_parent_ids), |
446 | .map = { |
447 | BERLIN2_SINGLE_DIV(REG_GFX3DEXTRA_CLKCTL), |
448 | }, |
449 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
450 | .flags = 0, |
451 | }, |
452 | { |
453 | .name = "gc360" , |
454 | .parent_ids = default_parent_ids, |
455 | .num_parents = ARRAY_SIZE(default_parent_ids), |
456 | .map = { |
457 | BERLIN2_SINGLE_DIV(REG_GC360_CLKCTL), |
458 | }, |
459 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
460 | .flags = 0, |
461 | }, |
462 | { |
463 | .name = "sdio_dllmst" , |
464 | .parent_ids = default_parent_ids, |
465 | .num_parents = ARRAY_SIZE(default_parent_ids), |
466 | .map = { |
467 | BERLIN2_SINGLE_DIV(REG_SDIO_DLLMST_CLKCTL), |
468 | }, |
469 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, |
470 | .flags = 0, |
471 | }, |
472 | }; |
473 | |
474 | static const struct berlin2_gate_data bg2_gates[] __initconst = { |
475 | { "geth0" , "perif" , 7 }, |
476 | { "geth1" , "perif" , 8 }, |
477 | { "sata" , "perif" , 9 }, |
478 | { "ahbapb" , "perif" , 10, CLK_IGNORE_UNUSED }, |
479 | { "usb0" , "perif" , 11 }, |
480 | { "usb1" , "perif" , 12 }, |
481 | { "pbridge" , "perif" , 13, CLK_IGNORE_UNUSED }, |
482 | { "sdio0" , "perif" , 14 }, |
483 | { "sdio1" , "perif" , 15 }, |
484 | { "nfc" , "perif" , 17 }, |
485 | { "smemc" , "perif" , 19 }, |
486 | { "audiohd" , "audiohd_pll" , 26 }, |
487 | { "video0" , "video0_in" , 27 }, |
488 | { "video1" , "video1_in" , 28 }, |
489 | { "video2" , "video2_in" , 29 }, |
490 | }; |
491 | |
492 | static void __init berlin2_clock_setup(struct device_node *np) |
493 | { |
494 | struct device_node *parent_np = of_get_parent(node: np); |
495 | const char *parent_names[9]; |
496 | struct clk *clk; |
497 | struct clk_hw *hw; |
498 | struct clk_hw **hws; |
499 | u8 avpll_flags = 0; |
500 | int n, ret; |
501 | |
502 | clk_data = kzalloc(struct_size(clk_data, hws, MAX_CLKS), GFP_KERNEL); |
503 | if (!clk_data) { |
504 | of_node_put(node: parent_np); |
505 | return; |
506 | } |
507 | clk_data->num = MAX_CLKS; |
508 | hws = clk_data->hws; |
509 | |
510 | gbase = of_iomap(node: parent_np, index: 0); |
511 | of_node_put(node: parent_np); |
512 | if (!gbase) |
513 | return; |
514 | |
515 | /* overwrite default clock names with DT provided ones */ |
516 | clk = of_clk_get_by_name(np, name: clk_names[REFCLK]); |
517 | if (!IS_ERR(ptr: clk)) { |
518 | clk_names[REFCLK] = __clk_get_name(clk); |
519 | clk_put(clk); |
520 | } |
521 | |
522 | clk = of_clk_get_by_name(np, name: clk_names[VIDEO_EXT0]); |
523 | if (!IS_ERR(ptr: clk)) { |
524 | clk_names[VIDEO_EXT0] = __clk_get_name(clk); |
525 | clk_put(clk); |
526 | } |
527 | |
528 | /* simple register PLLs */ |
529 | ret = berlin2_pll_register(map: &bg2_pll_map, base: gbase + REG_SYSPLLCTL0, |
530 | name: clk_names[SYSPLL], parent_name: clk_names[REFCLK], flags: 0); |
531 | if (ret) |
532 | goto bg2_fail; |
533 | |
534 | ret = berlin2_pll_register(map: &bg2_pll_map, base: gbase + REG_MEMPLLCTL0, |
535 | name: clk_names[MEMPLL], parent_name: clk_names[REFCLK], flags: 0); |
536 | if (ret) |
537 | goto bg2_fail; |
538 | |
539 | ret = berlin2_pll_register(map: &bg2_pll_map, base: gbase + REG_CPUPLLCTL0, |
540 | name: clk_names[CPUPLL], parent_name: clk_names[REFCLK], flags: 0); |
541 | if (ret) |
542 | goto bg2_fail; |
543 | |
544 | if (of_device_is_compatible(device: np, "marvell,berlin2-global-register" )) |
545 | avpll_flags |= BERLIN2_AVPLL_SCRAMBLE_QUIRK; |
546 | |
547 | /* audio/video VCOs */ |
548 | ret = berlin2_avpll_vco_register(base: gbase + REG_AVPLLCTL0, name: "avpll_vcoA" , |
549 | parent_name: clk_names[REFCLK], vco_flags: avpll_flags, flags: 0); |
550 | if (ret) |
551 | goto bg2_fail; |
552 | |
553 | for (n = 0; n < 8; n++) { |
554 | ret = berlin2_avpll_channel_register(base: gbase + REG_AVPLLCTL0, |
555 | name: clk_names[AVPLL_A1 + n], index: n, parent_name: "avpll_vcoA" , |
556 | ch_flags: avpll_flags, flags: 0); |
557 | if (ret) |
558 | goto bg2_fail; |
559 | } |
560 | |
561 | ret = berlin2_avpll_vco_register(base: gbase + REG_AVPLLCTL31, name: "avpll_vcoB" , |
562 | parent_name: clk_names[REFCLK], BERLIN2_AVPLL_BIT_QUIRK | |
563 | avpll_flags, flags: 0); |
564 | if (ret) |
565 | goto bg2_fail; |
566 | |
567 | for (n = 0; n < 8; n++) { |
568 | ret = berlin2_avpll_channel_register(base: gbase + REG_AVPLLCTL31, |
569 | name: clk_names[AVPLL_B1 + n], index: n, parent_name: "avpll_vcoB" , |
570 | BERLIN2_AVPLL_BIT_QUIRK | avpll_flags, flags: 0); |
571 | if (ret) |
572 | goto bg2_fail; |
573 | } |
574 | |
575 | /* reference clock bypass switches */ |
576 | parent_names[0] = clk_names[SYSPLL]; |
577 | parent_names[1] = clk_names[REFCLK]; |
578 | hw = clk_hw_register_mux(NULL, "syspll_byp" , parent_names, 2, |
579 | 0, gbase + REG_CLKSWITCH0, 0, 1, 0, &lock); |
580 | if (IS_ERR(ptr: hw)) |
581 | goto bg2_fail; |
582 | clk_names[SYSPLL] = clk_hw_get_name(hw); |
583 | |
584 | parent_names[0] = clk_names[MEMPLL]; |
585 | parent_names[1] = clk_names[REFCLK]; |
586 | hw = clk_hw_register_mux(NULL, "mempll_byp" , parent_names, 2, |
587 | 0, gbase + REG_CLKSWITCH0, 1, 1, 0, &lock); |
588 | if (IS_ERR(ptr: hw)) |
589 | goto bg2_fail; |
590 | clk_names[MEMPLL] = clk_hw_get_name(hw); |
591 | |
592 | parent_names[0] = clk_names[CPUPLL]; |
593 | parent_names[1] = clk_names[REFCLK]; |
594 | hw = clk_hw_register_mux(NULL, "cpupll_byp" , parent_names, 2, |
595 | 0, gbase + REG_CLKSWITCH0, 2, 1, 0, &lock); |
596 | if (IS_ERR(ptr: hw)) |
597 | goto bg2_fail; |
598 | clk_names[CPUPLL] = clk_hw_get_name(hw); |
599 | |
600 | /* clock muxes */ |
601 | parent_names[0] = clk_names[AVPLL_B3]; |
602 | parent_names[1] = clk_names[AVPLL_A3]; |
603 | hw = clk_hw_register_mux(NULL, clk_names[AUDIO1_PLL], parent_names, 2, |
604 | 0, gbase + REG_CLKSELECT2, 29, 1, 0, &lock); |
605 | if (IS_ERR(ptr: hw)) |
606 | goto bg2_fail; |
607 | |
608 | parent_names[0] = clk_names[VIDEO0_PLL]; |
609 | parent_names[1] = clk_names[VIDEO_EXT0]; |
610 | hw = clk_hw_register_mux(NULL, clk_names[VIDEO0_IN], parent_names, 2, |
611 | 0, gbase + REG_CLKSELECT3, 4, 1, 0, &lock); |
612 | if (IS_ERR(ptr: hw)) |
613 | goto bg2_fail; |
614 | |
615 | parent_names[0] = clk_names[VIDEO1_PLL]; |
616 | parent_names[1] = clk_names[VIDEO_EXT0]; |
617 | hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_IN], parent_names, 2, |
618 | 0, gbase + REG_CLKSELECT3, 6, 1, 0, &lock); |
619 | if (IS_ERR(ptr: hw)) |
620 | goto bg2_fail; |
621 | |
622 | parent_names[0] = clk_names[AVPLL_A2]; |
623 | parent_names[1] = clk_names[AVPLL_B2]; |
624 | hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_PLL], parent_names, 2, |
625 | 0, gbase + REG_CLKSELECT3, 7, 1, 0, &lock); |
626 | if (IS_ERR(ptr: hw)) |
627 | goto bg2_fail; |
628 | |
629 | parent_names[0] = clk_names[VIDEO2_PLL]; |
630 | parent_names[1] = clk_names[VIDEO_EXT0]; |
631 | hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_IN], parent_names, 2, |
632 | 0, gbase + REG_CLKSELECT3, 9, 1, 0, &lock); |
633 | if (IS_ERR(ptr: hw)) |
634 | goto bg2_fail; |
635 | |
636 | parent_names[0] = clk_names[AVPLL_B1]; |
637 | parent_names[1] = clk_names[AVPLL_A5]; |
638 | hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_PLL], parent_names, 2, |
639 | 0, gbase + REG_CLKSELECT3, 10, 1, 0, &lock); |
640 | if (IS_ERR(ptr: hw)) |
641 | goto bg2_fail; |
642 | |
643 | /* clock divider cells */ |
644 | for (n = 0; n < ARRAY_SIZE(bg2_divs); n++) { |
645 | const struct berlin2_div_data *dd = &bg2_divs[n]; |
646 | int k; |
647 | |
648 | for (k = 0; k < dd->num_parents; k++) |
649 | parent_names[k] = clk_names[dd->parent_ids[k]]; |
650 | |
651 | hws[CLKID_SYS + n] = berlin2_div_register(map: &dd->map, base: gbase, |
652 | name: dd->name, div_flags: dd->div_flags, parent_names, |
653 | num_parents: dd->num_parents, flags: dd->flags, lock: &lock); |
654 | } |
655 | |
656 | /* clock gate cells */ |
657 | for (n = 0; n < ARRAY_SIZE(bg2_gates); n++) { |
658 | const struct berlin2_gate_data *gd = &bg2_gates[n]; |
659 | |
660 | hws[CLKID_GETH0 + n] = clk_hw_register_gate(NULL, gd->name, |
661 | gd->parent_name, gd->flags, gbase + REG_CLKENABLE, |
662 | gd->bit_idx, 0, &lock); |
663 | } |
664 | |
665 | /* twdclk is derived from cpu/3 */ |
666 | hws[CLKID_TWD] = |
667 | clk_hw_register_fixed_factor(NULL, name: "twd" , parent_name: "cpu" , flags: 0, mult: 1, div: 3); |
668 | |
669 | /* check for errors on leaf clocks */ |
670 | for (n = 0; n < MAX_CLKS; n++) { |
671 | if (!IS_ERR(ptr: hws[n])) |
672 | continue; |
673 | |
674 | pr_err("%pOF: Unable to register leaf clock %d\n" , np, n); |
675 | goto bg2_fail; |
676 | } |
677 | |
678 | /* register clk-provider */ |
679 | of_clk_add_hw_provider(np, get: of_clk_hw_onecell_get, data: clk_data); |
680 | |
681 | return; |
682 | |
683 | bg2_fail: |
684 | iounmap(addr: gbase); |
685 | } |
686 | CLK_OF_DECLARE(berlin2_clk, "marvell,berlin2-clk" , |
687 | berlin2_clock_setup); |
688 | |