xref: /linux/drivers/clk/meson/s4-pll.c (revision 1553a1c48281243359a9529a10ddb551f3b967ab)
1 // SPDX-License-Identifier: (GPL-2.0-only OR MIT)
2 /*
3  * Amlogic S4 PLL Clock Controller Driver
4  *
5  * Copyright (c) 2022-2023 Amlogic, inc. All rights reserved
6  * Author: Yu Tu <yu.tu@amlogic.com>
7  */
8 
9 #include <linux/clk-provider.h>
10 #include <linux/of_device.h>
11 #include <linux/platform_device.h>
12 
13 #include "clk-mpll.h"
14 #include "clk-pll.h"
15 #include "clk-regmap.h"
16 #include "s4-pll.h"
17 #include "meson-clkc-utils.h"
18 #include <dt-bindings/clock/amlogic,s4-pll-clkc.h>
19 
20 static DEFINE_SPINLOCK(meson_clk_lock);
21 
22 /*
23  * These clock are a fixed value (fixed_pll is 2GHz) that is initialized by ROMcode.
24  * The chip was changed fixed pll for security reasons. Fixed PLL registers are not writable
25  * in the kernel phase. Write of fixed PLL-related register will cause the system to crash.
26  * Meanwhile, these clock won't ever change at runtime.
27  * For the above reasons, we can only use ro_ops for fixed PLL related clocks.
28  */
29 static struct clk_regmap s4_fixed_pll_dco = {
30 	.data = &(struct meson_clk_pll_data){
31 		.en = {
32 			.reg_off = ANACTRL_FIXPLL_CTRL0,
33 			.shift   = 28,
34 			.width   = 1,
35 		},
36 		.m = {
37 			.reg_off = ANACTRL_FIXPLL_CTRL0,
38 			.shift   = 0,
39 			.width   = 8,
40 		},
41 		.n = {
42 			.reg_off = ANACTRL_FIXPLL_CTRL0,
43 			.shift   = 10,
44 			.width   = 5,
45 		},
46 		.l = {
47 			.reg_off = ANACTRL_FIXPLL_CTRL0,
48 			.shift   = 31,
49 			.width   = 1,
50 		},
51 		.rst = {
52 			.reg_off = ANACTRL_FIXPLL_CTRL0,
53 			.shift   = 29,
54 			.width   = 1,
55 		},
56 	},
57 	.hw.init = &(struct clk_init_data){
58 		.name = "fixed_pll_dco",
59 		.ops = &meson_clk_pll_ro_ops,
60 		.parent_data = (const struct clk_parent_data []) {
61 			{ .fw_name = "xtal", }
62 		},
63 		.num_parents = 1,
64 	},
65 };
66 
67 static struct clk_regmap s4_fixed_pll = {
68 	.data = &(struct clk_regmap_div_data){
69 		.offset = ANACTRL_FIXPLL_CTRL0,
70 		.shift = 16,
71 		.width = 2,
72 		.flags = CLK_DIVIDER_POWER_OF_TWO,
73 	},
74 	.hw.init = &(struct clk_init_data){
75 		.name = "fixed_pll",
76 		.ops = &clk_regmap_divider_ro_ops,
77 		.parent_hws = (const struct clk_hw *[]) {
78 			&s4_fixed_pll_dco.hw
79 		},
80 		.num_parents = 1,
81 		/*
82 		 * This clock won't ever change at runtime so
83 		 * CLK_SET_RATE_PARENT is not required
84 		 */
85 	},
86 };
87 
88 static struct clk_fixed_factor s4_fclk_div2_div = {
89 	.mult = 1,
90 	.div = 2,
91 	.hw.init = &(struct clk_init_data){
92 		.name = "fclk_div2_div",
93 		.ops = &clk_fixed_factor_ops,
94 		.parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
95 		.num_parents = 1,
96 	},
97 };
98 
99 static struct clk_regmap s4_fclk_div2 = {
100 	.data = &(struct clk_regmap_gate_data){
101 		.offset = ANACTRL_FIXPLL_CTRL1,
102 		.bit_idx = 24,
103 	},
104 	.hw.init = &(struct clk_init_data){
105 		.name = "fclk_div2",
106 		.ops = &clk_regmap_gate_ro_ops,
107 		.parent_hws = (const struct clk_hw *[]) {
108 			&s4_fclk_div2_div.hw
109 		},
110 		.num_parents = 1,
111 	},
112 };
113 
114 static struct clk_fixed_factor s4_fclk_div3_div = {
115 	.mult = 1,
116 	.div = 3,
117 	.hw.init = &(struct clk_init_data){
118 		.name = "fclk_div3_div",
119 		.ops = &clk_fixed_factor_ops,
120 		.parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
121 		.num_parents = 1,
122 	},
123 };
124 
125 static struct clk_regmap s4_fclk_div3 = {
126 	.data = &(struct clk_regmap_gate_data){
127 		.offset = ANACTRL_FIXPLL_CTRL1,
128 		.bit_idx = 20,
129 	},
130 	.hw.init = &(struct clk_init_data){
131 		.name = "fclk_div3",
132 		.ops = &clk_regmap_gate_ro_ops,
133 		.parent_hws = (const struct clk_hw *[]) {
134 			&s4_fclk_div3_div.hw
135 		},
136 		.num_parents = 1,
137 	},
138 };
139 
140 static struct clk_fixed_factor s4_fclk_div4_div = {
141 	.mult = 1,
142 	.div = 4,
143 	.hw.init = &(struct clk_init_data){
144 		.name = "fclk_div4_div",
145 		.ops = &clk_fixed_factor_ops,
146 		.parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
147 		.num_parents = 1,
148 	},
149 };
150 
151 static struct clk_regmap s4_fclk_div4 = {
152 	.data = &(struct clk_regmap_gate_data){
153 		.offset = ANACTRL_FIXPLL_CTRL1,
154 		.bit_idx = 21,
155 	},
156 	.hw.init = &(struct clk_init_data){
157 		.name = "fclk_div4",
158 		.ops = &clk_regmap_gate_ro_ops,
159 		.parent_hws = (const struct clk_hw *[]) {
160 			&s4_fclk_div4_div.hw
161 		},
162 		.num_parents = 1,
163 	},
164 };
165 
166 static struct clk_fixed_factor s4_fclk_div5_div = {
167 	.mult = 1,
168 	.div = 5,
169 	.hw.init = &(struct clk_init_data){
170 		.name = "fclk_div5_div",
171 		.ops = &clk_fixed_factor_ops,
172 		.parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
173 		.num_parents = 1,
174 	},
175 };
176 
177 static struct clk_regmap s4_fclk_div5 = {
178 	.data = &(struct clk_regmap_gate_data){
179 		.offset = ANACTRL_FIXPLL_CTRL1,
180 		.bit_idx = 22,
181 	},
182 	.hw.init = &(struct clk_init_data){
183 		.name = "fclk_div5",
184 		.ops = &clk_regmap_gate_ro_ops,
185 		.parent_hws = (const struct clk_hw *[]) {
186 			&s4_fclk_div5_div.hw
187 		},
188 		.num_parents = 1,
189 	},
190 };
191 
192 static struct clk_fixed_factor s4_fclk_div7_div = {
193 	.mult = 1,
194 	.div = 7,
195 	.hw.init = &(struct clk_init_data){
196 		.name = "fclk_div7_div",
197 		.ops = &clk_fixed_factor_ops,
198 		.parent_hws = (const struct clk_hw *[]) { &s4_fixed_pll.hw },
199 		.num_parents = 1,
200 	},
201 };
202 
203 static struct clk_regmap s4_fclk_div7 = {
204 	.data = &(struct clk_regmap_gate_data){
205 		.offset = ANACTRL_FIXPLL_CTRL1,
206 		.bit_idx = 23,
207 	},
208 	.hw.init = &(struct clk_init_data){
209 		.name = "fclk_div7",
210 		.ops = &clk_regmap_gate_ro_ops,
211 		.parent_hws = (const struct clk_hw *[]) {
212 			&s4_fclk_div7_div.hw
213 		},
214 		.num_parents = 1,
215 	},
216 };
217 
218 static struct clk_fixed_factor s4_fclk_div2p5_div = {
219 	.mult = 2,
220 	.div = 5,
221 	.hw.init = &(struct clk_init_data){
222 		.name = "fclk_div2p5_div",
223 		.ops = &clk_fixed_factor_ops,
224 		.parent_hws = (const struct clk_hw *[]) {
225 			&s4_fixed_pll.hw
226 		},
227 		.num_parents = 1,
228 	},
229 };
230 
231 static struct clk_regmap s4_fclk_div2p5 = {
232 	.data = &(struct clk_regmap_gate_data){
233 		.offset = ANACTRL_FIXPLL_CTRL1,
234 		.bit_idx = 25,
235 	},
236 	.hw.init = &(struct clk_init_data){
237 		.name = "fclk_div2p5",
238 		.ops = &clk_regmap_gate_ro_ops,
239 		.parent_hws = (const struct clk_hw *[]) {
240 			&s4_fclk_div2p5_div.hw
241 		},
242 		.num_parents = 1,
243 	},
244 };
245 
246 static const struct pll_mult_range s4_gp0_pll_mult_range = {
247 	.min = 125,
248 	.max = 250,
249 };
250 
251 /*
252  * Internal gp0 pll emulation configuration parameters
253  */
254 static const struct reg_sequence s4_gp0_init_regs[] = {
255 	{ .reg = ANACTRL_GP0PLL_CTRL1,	.def = 0x00000000 },
256 	{ .reg = ANACTRL_GP0PLL_CTRL2,	.def = 0x00000000 },
257 	{ .reg = ANACTRL_GP0PLL_CTRL3,	.def = 0x48681c00 },
258 	{ .reg = ANACTRL_GP0PLL_CTRL4,	.def = 0x88770290 },
259 	{ .reg = ANACTRL_GP0PLL_CTRL5,	.def = 0x39272000 },
260 	{ .reg = ANACTRL_GP0PLL_CTRL6,	.def = 0x56540000 }
261 };
262 
263 static struct clk_regmap s4_gp0_pll_dco = {
264 	.data = &(struct meson_clk_pll_data){
265 		.en = {
266 			.reg_off = ANACTRL_GP0PLL_CTRL0,
267 			.shift   = 28,
268 			.width   = 1,
269 		},
270 		.m = {
271 			.reg_off = ANACTRL_GP0PLL_CTRL0,
272 			.shift   = 0,
273 			.width   = 8,
274 		},
275 		.n = {
276 			.reg_off = ANACTRL_GP0PLL_CTRL0,
277 			.shift   = 10,
278 			.width   = 5,
279 		},
280 		.l = {
281 			.reg_off = ANACTRL_GP0PLL_CTRL0,
282 			.shift   = 31,
283 			.width   = 1,
284 		},
285 		.rst = {
286 			.reg_off = ANACTRL_GP0PLL_CTRL0,
287 			.shift   = 29,
288 			.width   = 1,
289 		},
290 		.range = &s4_gp0_pll_mult_range,
291 		.init_regs = s4_gp0_init_regs,
292 		.init_count = ARRAY_SIZE(s4_gp0_init_regs),
293 	},
294 	.hw.init = &(struct clk_init_data){
295 		.name = "gp0_pll_dco",
296 		.ops = &meson_clk_pll_ops,
297 		.parent_data = (const struct clk_parent_data []) {
298 			{ .fw_name = "xtal", }
299 		},
300 		.num_parents = 1,
301 	},
302 };
303 
304 static struct clk_regmap s4_gp0_pll = {
305 	.data = &(struct clk_regmap_div_data){
306 		.offset = ANACTRL_GP0PLL_CTRL0,
307 		.shift = 16,
308 		.width = 3,
309 		.flags = (CLK_DIVIDER_POWER_OF_TWO |
310 			  CLK_DIVIDER_ROUND_CLOSEST),
311 	},
312 	.hw.init = &(struct clk_init_data){
313 		.name = "gp0_pll",
314 		.ops = &clk_regmap_divider_ops,
315 		.parent_hws = (const struct clk_hw *[]) {
316 			&s4_gp0_pll_dco.hw
317 		},
318 		.num_parents = 1,
319 		.flags = CLK_SET_RATE_PARENT,
320 	},
321 };
322 
323 /*
324  * Internal hifi pll emulation configuration parameters
325  */
326 static const struct reg_sequence s4_hifi_init_regs[] = {
327 	{ .reg = ANACTRL_HIFIPLL_CTRL1,	.def = 0x00010e56 },
328 	{ .reg = ANACTRL_HIFIPLL_CTRL2,	.def = 0x00000000 },
329 	{ .reg = ANACTRL_HIFIPLL_CTRL3,	.def = 0x6a285c00 },
330 	{ .reg = ANACTRL_HIFIPLL_CTRL4,	.def = 0x65771290 },
331 	{ .reg = ANACTRL_HIFIPLL_CTRL5,	.def = 0x39272000 },
332 	{ .reg = ANACTRL_HIFIPLL_CTRL6,	.def = 0x56540000 }
333 };
334 
335 static struct clk_regmap s4_hifi_pll_dco = {
336 	.data = &(struct meson_clk_pll_data){
337 		.en = {
338 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
339 			.shift   = 28,
340 			.width   = 1,
341 		},
342 		.m = {
343 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
344 			.shift   = 0,
345 			.width   = 8,
346 		},
347 		.n = {
348 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
349 			.shift   = 10,
350 			.width   = 5,
351 		},
352 		.l = {
353 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
354 			.shift   = 31,
355 			.width   = 1,
356 		},
357 		.rst = {
358 			.reg_off = ANACTRL_HIFIPLL_CTRL0,
359 			.shift   = 29,
360 			.width   = 1,
361 		},
362 		.range = &s4_gp0_pll_mult_range,
363 		.init_regs = s4_hifi_init_regs,
364 		.init_count = ARRAY_SIZE(s4_hifi_init_regs),
365 		.flags = CLK_MESON_PLL_ROUND_CLOSEST,
366 	},
367 	.hw.init = &(struct clk_init_data){
368 		.name = "hifi_pll_dco",
369 		.ops = &meson_clk_pll_ops,
370 		.parent_data = (const struct clk_parent_data []) {
371 			{ .fw_name = "xtal", }
372 		},
373 		.num_parents = 1,
374 	},
375 };
376 
377 static struct clk_regmap s4_hifi_pll = {
378 	.data = &(struct clk_regmap_div_data){
379 		.offset = ANACTRL_HIFIPLL_CTRL0,
380 		.shift = 16,
381 		.width = 2,
382 		.flags = (CLK_DIVIDER_POWER_OF_TWO |
383 			  CLK_DIVIDER_ROUND_CLOSEST),
384 	},
385 	.hw.init = &(struct clk_init_data){
386 		.name = "hifi_pll",
387 		.ops = &clk_regmap_divider_ops,
388 		.parent_hws = (const struct clk_hw *[]) {
389 			&s4_hifi_pll_dco.hw
390 		},
391 		.num_parents = 1,
392 		.flags = CLK_SET_RATE_PARENT,
393 	},
394 };
395 
396 static struct clk_regmap s4_hdmi_pll_dco = {
397 	.data = &(struct meson_clk_pll_data){
398 		.en = {
399 			.reg_off = ANACTRL_HDMIPLL_CTRL0,
400 			.shift   = 28,
401 			.width   = 1,
402 		},
403 		.m = {
404 			.reg_off = ANACTRL_HDMIPLL_CTRL0,
405 			.shift   = 0,
406 			.width   = 8,
407 		},
408 		.n = {
409 			.reg_off = ANACTRL_HDMIPLL_CTRL0,
410 			.shift   = 10,
411 			.width   = 5,
412 		},
413 		.l = {
414 			.reg_off = ANACTRL_HDMIPLL_CTRL0,
415 			.shift   = 31,
416 			.width   = 1,
417 		},
418 		.rst = {
419 			.reg_off = ANACTRL_HDMIPLL_CTRL0,
420 			.shift   = 29,
421 			.width   = 1,
422 		},
423 		.range = &s4_gp0_pll_mult_range,
424 	},
425 	.hw.init = &(struct clk_init_data){
426 		.name = "hdmi_pll_dco",
427 		.ops = &meson_clk_pll_ops,
428 		.parent_data = (const struct clk_parent_data []) {
429 			{ .fw_name = "xtal", }
430 		},
431 		.num_parents = 1,
432 	},
433 };
434 
435 static struct clk_regmap s4_hdmi_pll_od = {
436 	.data = &(struct clk_regmap_div_data){
437 		.offset = ANACTRL_HDMIPLL_CTRL0,
438 		.shift = 16,
439 		.width = 4,
440 		.flags = CLK_DIVIDER_POWER_OF_TWO,
441 	},
442 	.hw.init = &(struct clk_init_data){
443 		.name = "hdmi_pll_od",
444 		.ops = &clk_regmap_divider_ops,
445 		.parent_hws = (const struct clk_hw *[]) {
446 			&s4_hdmi_pll_dco.hw
447 		},
448 		.num_parents = 1,
449 		.flags = CLK_SET_RATE_PARENT,
450 	},
451 };
452 
453 static struct clk_regmap s4_hdmi_pll = {
454 	.data = &(struct clk_regmap_div_data){
455 		.offset = ANACTRL_HDMIPLL_CTRL0,
456 		.shift = 20,
457 		.width = 2,
458 		.flags = CLK_DIVIDER_POWER_OF_TWO,
459 	},
460 	.hw.init = &(struct clk_init_data){
461 		.name = "hdmi_pll",
462 		.ops = &clk_regmap_divider_ops,
463 		.parent_hws = (const struct clk_hw *[]) {
464 			&s4_hdmi_pll_od.hw
465 		},
466 		.num_parents = 1,
467 		.flags = CLK_SET_RATE_PARENT,
468 	},
469 };
470 
471 static struct clk_fixed_factor s4_mpll_50m_div = {
472 	.mult = 1,
473 	.div = 80,
474 	.hw.init = &(struct clk_init_data){
475 		.name = "mpll_50m_div",
476 		.ops = &clk_fixed_factor_ops,
477 		.parent_hws = (const struct clk_hw *[]) {
478 			&s4_fixed_pll_dco.hw
479 		},
480 		.num_parents = 1,
481 	},
482 };
483 
484 static struct clk_regmap s4_mpll_50m = {
485 	.data = &(struct clk_regmap_mux_data){
486 		.offset = ANACTRL_FIXPLL_CTRL3,
487 		.mask = 0x1,
488 		.shift = 5,
489 	},
490 	.hw.init = &(struct clk_init_data){
491 		.name = "mpll_50m",
492 		.ops = &clk_regmap_mux_ro_ops,
493 		.parent_data = (const struct clk_parent_data []) {
494 			{ .fw_name = "xtal", },
495 			{ .hw = &s4_mpll_50m_div.hw },
496 		},
497 		.num_parents = 2,
498 	},
499 };
500 
501 static struct clk_fixed_factor s4_mpll_prediv = {
502 	.mult = 1,
503 	.div = 2,
504 	.hw.init = &(struct clk_init_data){
505 		.name = "mpll_prediv",
506 		.ops = &clk_fixed_factor_ops,
507 		.parent_hws = (const struct clk_hw *[]) {
508 			&s4_fixed_pll_dco.hw
509 		},
510 		.num_parents = 1,
511 	},
512 };
513 
514 static const struct reg_sequence s4_mpll0_init_regs[] = {
515 	{ .reg = ANACTRL_MPLL_CTRL2, .def = 0x40000033 }
516 };
517 
518 static struct clk_regmap s4_mpll0_div = {
519 	.data = &(struct meson_clk_mpll_data){
520 		.sdm = {
521 			.reg_off = ANACTRL_MPLL_CTRL1,
522 			.shift   = 0,
523 			.width   = 14,
524 		},
525 		.sdm_en = {
526 			.reg_off = ANACTRL_MPLL_CTRL1,
527 			.shift   = 30,
528 			.width	 = 1,
529 		},
530 		.n2 = {
531 			.reg_off = ANACTRL_MPLL_CTRL1,
532 			.shift   = 20,
533 			.width   = 9,
534 		},
535 		.ssen = {
536 			.reg_off = ANACTRL_MPLL_CTRL1,
537 			.shift   = 29,
538 			.width	 = 1,
539 		},
540 		.lock = &meson_clk_lock,
541 		.init_regs = s4_mpll0_init_regs,
542 		.init_count = ARRAY_SIZE(s4_mpll0_init_regs),
543 	},
544 	.hw.init = &(struct clk_init_data){
545 		.name = "mpll0_div",
546 		.ops = &meson_clk_mpll_ops,
547 		.parent_hws = (const struct clk_hw *[]) {
548 			&s4_mpll_prediv.hw
549 		},
550 		.num_parents = 1,
551 	},
552 };
553 
554 static struct clk_regmap s4_mpll0 = {
555 	.data = &(struct clk_regmap_gate_data){
556 		.offset = ANACTRL_MPLL_CTRL1,
557 		.bit_idx = 31,
558 	},
559 	.hw.init = &(struct clk_init_data){
560 		.name = "mpll0",
561 		.ops = &clk_regmap_gate_ops,
562 		.parent_hws = (const struct clk_hw *[]) { &s4_mpll0_div.hw },
563 		.num_parents = 1,
564 		.flags = CLK_SET_RATE_PARENT,
565 	},
566 };
567 
568 static const struct reg_sequence s4_mpll1_init_regs[] = {
569 	{ .reg = ANACTRL_MPLL_CTRL4,	.def = 0x40000033 }
570 };
571 
572 static struct clk_regmap s4_mpll1_div = {
573 	.data = &(struct meson_clk_mpll_data){
574 		.sdm = {
575 			.reg_off = ANACTRL_MPLL_CTRL3,
576 			.shift   = 0,
577 			.width   = 14,
578 		},
579 		.sdm_en = {
580 			.reg_off = ANACTRL_MPLL_CTRL3,
581 			.shift   = 30,
582 			.width	 = 1,
583 		},
584 		.n2 = {
585 			.reg_off = ANACTRL_MPLL_CTRL3,
586 			.shift   = 20,
587 			.width   = 9,
588 		},
589 		.ssen = {
590 			.reg_off = ANACTRL_MPLL_CTRL3,
591 			.shift   = 29,
592 			.width	 = 1,
593 		},
594 		.lock = &meson_clk_lock,
595 		.init_regs = s4_mpll1_init_regs,
596 		.init_count = ARRAY_SIZE(s4_mpll1_init_regs),
597 	},
598 	.hw.init = &(struct clk_init_data){
599 		.name = "mpll1_div",
600 		.ops = &meson_clk_mpll_ops,
601 		.parent_hws = (const struct clk_hw *[]) {
602 			&s4_mpll_prediv.hw
603 		},
604 		.num_parents = 1,
605 	},
606 };
607 
608 static struct clk_regmap s4_mpll1 = {
609 	.data = &(struct clk_regmap_gate_data){
610 		.offset = ANACTRL_MPLL_CTRL3,
611 		.bit_idx = 31,
612 	},
613 	.hw.init = &(struct clk_init_data){
614 		.name = "mpll1",
615 		.ops = &clk_regmap_gate_ops,
616 		.parent_hws = (const struct clk_hw *[]) { &s4_mpll1_div.hw },
617 		.num_parents = 1,
618 		.flags = CLK_SET_RATE_PARENT,
619 	},
620 };
621 
622 static const struct reg_sequence s4_mpll2_init_regs[] = {
623 	{ .reg = ANACTRL_MPLL_CTRL6, .def = 0x40000033 }
624 };
625 
626 static struct clk_regmap s4_mpll2_div = {
627 	.data = &(struct meson_clk_mpll_data){
628 		.sdm = {
629 			.reg_off = ANACTRL_MPLL_CTRL5,
630 			.shift   = 0,
631 			.width   = 14,
632 		},
633 		.sdm_en = {
634 			.reg_off = ANACTRL_MPLL_CTRL5,
635 			.shift   = 30,
636 			.width	 = 1,
637 		},
638 		.n2 = {
639 			.reg_off = ANACTRL_MPLL_CTRL5,
640 			.shift   = 20,
641 			.width   = 9,
642 		},
643 		.ssen = {
644 			.reg_off = ANACTRL_MPLL_CTRL5,
645 			.shift   = 29,
646 			.width	 = 1,
647 		},
648 		.lock = &meson_clk_lock,
649 		.init_regs = s4_mpll2_init_regs,
650 		.init_count = ARRAY_SIZE(s4_mpll2_init_regs),
651 	},
652 	.hw.init = &(struct clk_init_data){
653 		.name = "mpll2_div",
654 		.ops = &meson_clk_mpll_ops,
655 		.parent_hws = (const struct clk_hw *[]) {
656 			&s4_mpll_prediv.hw
657 		},
658 		.num_parents = 1,
659 	},
660 };
661 
662 static struct clk_regmap s4_mpll2 = {
663 	.data = &(struct clk_regmap_gate_data){
664 		.offset = ANACTRL_MPLL_CTRL5,
665 		.bit_idx = 31,
666 	},
667 	.hw.init = &(struct clk_init_data){
668 		.name = "mpll2",
669 		.ops = &clk_regmap_gate_ops,
670 		.parent_hws = (const struct clk_hw *[]) { &s4_mpll2_div.hw },
671 		.num_parents = 1,
672 		.flags = CLK_SET_RATE_PARENT,
673 	},
674 };
675 
676 static const struct reg_sequence s4_mpll3_init_regs[] = {
677 	{ .reg = ANACTRL_MPLL_CTRL8, .def = 0x40000033 }
678 };
679 
680 static struct clk_regmap s4_mpll3_div = {
681 	.data = &(struct meson_clk_mpll_data){
682 		.sdm = {
683 			.reg_off = ANACTRL_MPLL_CTRL7,
684 			.shift   = 0,
685 			.width   = 14,
686 		},
687 		.sdm_en = {
688 			.reg_off = ANACTRL_MPLL_CTRL7,
689 			.shift   = 30,
690 			.width	 = 1,
691 		},
692 		.n2 = {
693 			.reg_off = ANACTRL_MPLL_CTRL7,
694 			.shift   = 20,
695 			.width   = 9,
696 		},
697 		.ssen = {
698 			.reg_off = ANACTRL_MPLL_CTRL7,
699 			.shift   = 29,
700 			.width	 = 1,
701 		},
702 		.lock = &meson_clk_lock,
703 		.init_regs = s4_mpll3_init_regs,
704 		.init_count = ARRAY_SIZE(s4_mpll3_init_regs),
705 	},
706 	.hw.init = &(struct clk_init_data){
707 		.name = "mpll3_div",
708 		.ops = &meson_clk_mpll_ops,
709 		.parent_hws = (const struct clk_hw *[]) {
710 			&s4_mpll_prediv.hw
711 		},
712 		.num_parents = 1,
713 	},
714 };
715 
716 static struct clk_regmap s4_mpll3 = {
717 	.data = &(struct clk_regmap_gate_data){
718 		.offset = ANACTRL_MPLL_CTRL7,
719 		.bit_idx = 31,
720 	},
721 	.hw.init = &(struct clk_init_data){
722 		.name = "mpll3",
723 		.ops = &clk_regmap_gate_ops,
724 		.parent_hws = (const struct clk_hw *[]) { &s4_mpll3_div.hw },
725 		.num_parents = 1,
726 		.flags = CLK_SET_RATE_PARENT,
727 	},
728 };
729 
730 /* Array of all clocks provided by this provider */
731 static struct clk_hw *s4_pll_hw_clks[] = {
732 	[CLKID_FIXED_PLL_DCO]		= &s4_fixed_pll_dco.hw,
733 	[CLKID_FIXED_PLL]		= &s4_fixed_pll.hw,
734 	[CLKID_FCLK_DIV2_DIV]		= &s4_fclk_div2_div.hw,
735 	[CLKID_FCLK_DIV2]		= &s4_fclk_div2.hw,
736 	[CLKID_FCLK_DIV3_DIV]		= &s4_fclk_div3_div.hw,
737 	[CLKID_FCLK_DIV3]		= &s4_fclk_div3.hw,
738 	[CLKID_FCLK_DIV4_DIV]		= &s4_fclk_div4_div.hw,
739 	[CLKID_FCLK_DIV4]		= &s4_fclk_div4.hw,
740 	[CLKID_FCLK_DIV5_DIV]		= &s4_fclk_div5_div.hw,
741 	[CLKID_FCLK_DIV5]		= &s4_fclk_div5.hw,
742 	[CLKID_FCLK_DIV7_DIV]		= &s4_fclk_div7_div.hw,
743 	[CLKID_FCLK_DIV7]		= &s4_fclk_div7.hw,
744 	[CLKID_FCLK_DIV2P5_DIV]		= &s4_fclk_div2p5_div.hw,
745 	[CLKID_FCLK_DIV2P5]		= &s4_fclk_div2p5.hw,
746 	[CLKID_GP0_PLL_DCO]		= &s4_gp0_pll_dco.hw,
747 	[CLKID_GP0_PLL]			= &s4_gp0_pll.hw,
748 	[CLKID_HIFI_PLL_DCO]		= &s4_hifi_pll_dco.hw,
749 	[CLKID_HIFI_PLL]		= &s4_hifi_pll.hw,
750 	[CLKID_HDMI_PLL_DCO]		= &s4_hdmi_pll_dco.hw,
751 	[CLKID_HDMI_PLL_OD]		= &s4_hdmi_pll_od.hw,
752 	[CLKID_HDMI_PLL]		= &s4_hdmi_pll.hw,
753 	[CLKID_MPLL_50M_DIV]		= &s4_mpll_50m_div.hw,
754 	[CLKID_MPLL_50M]		= &s4_mpll_50m.hw,
755 	[CLKID_MPLL_PREDIV]		= &s4_mpll_prediv.hw,
756 	[CLKID_MPLL0_DIV]		= &s4_mpll0_div.hw,
757 	[CLKID_MPLL0]			= &s4_mpll0.hw,
758 	[CLKID_MPLL1_DIV]		= &s4_mpll1_div.hw,
759 	[CLKID_MPLL1]			= &s4_mpll1.hw,
760 	[CLKID_MPLL2_DIV]		= &s4_mpll2_div.hw,
761 	[CLKID_MPLL2]			= &s4_mpll2.hw,
762 	[CLKID_MPLL3_DIV]		= &s4_mpll3_div.hw,
763 	[CLKID_MPLL3]			= &s4_mpll3.hw,
764 };
765 
766 static struct clk_regmap *const s4_pll_clk_regmaps[] = {
767 	&s4_fixed_pll_dco,
768 	&s4_fixed_pll,
769 	&s4_fclk_div2,
770 	&s4_fclk_div3,
771 	&s4_fclk_div4,
772 	&s4_fclk_div5,
773 	&s4_fclk_div7,
774 	&s4_fclk_div2p5,
775 	&s4_gp0_pll_dco,
776 	&s4_gp0_pll,
777 	&s4_hifi_pll_dco,
778 	&s4_hifi_pll,
779 	&s4_hdmi_pll_dco,
780 	&s4_hdmi_pll_od,
781 	&s4_hdmi_pll,
782 	&s4_mpll_50m,
783 	&s4_mpll0_div,
784 	&s4_mpll0,
785 	&s4_mpll1_div,
786 	&s4_mpll1,
787 	&s4_mpll2_div,
788 	&s4_mpll2,
789 	&s4_mpll3_div,
790 	&s4_mpll3,
791 };
792 
793 static const struct reg_sequence s4_init_regs[] = {
794 	{ .reg = ANACTRL_MPLL_CTRL0,	.def = 0x00000543 },
795 };
796 
797 static struct regmap_config clkc_regmap_config = {
798 	.reg_bits       = 32,
799 	.val_bits       = 32,
800 	.reg_stride     = 4,
801 };
802 
803 static struct meson_clk_hw_data s4_pll_clks = {
804 	.hws = s4_pll_hw_clks,
805 	.num = ARRAY_SIZE(s4_pll_hw_clks),
806 };
807 
808 static int meson_s4_pll_probe(struct platform_device *pdev)
809 {
810 	struct device *dev = &pdev->dev;
811 	struct regmap *regmap;
812 	void __iomem *base;
813 	int ret, i;
814 
815 	base = devm_platform_ioremap_resource(pdev, 0);
816 	if (IS_ERR(base))
817 		return dev_err_probe(dev, PTR_ERR(base),
818 				     "can't ioremap resource\n");
819 
820 	regmap = devm_regmap_init_mmio(dev, base, &clkc_regmap_config);
821 	if (IS_ERR(regmap))
822 		return dev_err_probe(dev, PTR_ERR(regmap),
823 				     "can't init regmap mmio region\n");
824 
825 	ret = regmap_multi_reg_write(regmap, s4_init_regs, ARRAY_SIZE(s4_init_regs));
826 	if (ret)
827 		return dev_err_probe(dev, ret,
828 				     "Failed to init registers\n");
829 
830 	/* Populate regmap for the regmap backed clocks */
831 	for (i = 0; i < ARRAY_SIZE(s4_pll_clk_regmaps); i++)
832 		s4_pll_clk_regmaps[i]->map = regmap;
833 
834 	/* Register clocks */
835 	for (i = 0; i < s4_pll_clks.num; i++) {
836 		/* array might be sparse */
837 		if (!s4_pll_clks.hws[i])
838 			continue;
839 
840 		ret = devm_clk_hw_register(dev, s4_pll_clks.hws[i]);
841 		if (ret)
842 			return dev_err_probe(dev, ret,
843 					     "clock[%d] registration failed\n", i);
844 	}
845 
846 	return devm_of_clk_add_hw_provider(dev, meson_clk_hw_get,
847 					   &s4_pll_clks);
848 }
849 
850 static const struct of_device_id clkc_match_table[] = {
851 	{
852 		.compatible = "amlogic,s4-pll-clkc",
853 	},
854 	{}
855 };
856 
857 static struct platform_driver s4_driver = {
858 	.probe		= meson_s4_pll_probe,
859 	.driver		= {
860 		.name	= "s4-pll-clkc",
861 		.of_match_table = clkc_match_table,
862 	},
863 };
864 
865 module_platform_driver(s4_driver);
866 MODULE_AUTHOR("Yu Tu <yu.tu@amlogic.com>");
867 MODULE_LICENSE("GPL");
868