Linux Audio

Check our new training course

Real-Time Linux with PREEMPT_RT training

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