Linux Audio

Check our new training course

Linux debugging, profiling, tracing and performance analysis training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v3.5.6.
  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
 20static 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 */
 29static 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
 67static 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
 88static 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
 99static 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
114static 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
125static 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
140static 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
151static 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
166static 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
177static 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
192static 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
203static 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
218static 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
231static 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
246static 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 */
254static 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
263static 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
304static 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 */
326static 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
335static 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
377static 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
396static 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
435static 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
453static 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
471static 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
484static 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
501static 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
514static const struct reg_sequence s4_mpll0_init_regs[] = {
515	{ .reg = ANACTRL_MPLL_CTRL2, .def = 0x40000033 }
516};
517
518static 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
554static 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
568static const struct reg_sequence s4_mpll1_init_regs[] = {
569	{ .reg = ANACTRL_MPLL_CTRL4,	.def = 0x40000033 }
570};
571
572static 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
608static 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
622static const struct reg_sequence s4_mpll2_init_regs[] = {
623	{ .reg = ANACTRL_MPLL_CTRL6, .def = 0x40000033 }
624};
625
626static 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
662static 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
676static const struct reg_sequence s4_mpll3_init_regs[] = {
677	{ .reg = ANACTRL_MPLL_CTRL8, .def = 0x40000033 }
678};
679
680static 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
716static 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 */
731static 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
766static 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
793static const struct reg_sequence s4_init_regs[] = {
794	{ .reg = ANACTRL_MPLL_CTRL0,	.def = 0x00000543 },
795};
796
797static struct regmap_config clkc_regmap_config = {
798	.reg_bits       = 32,
799	.val_bits       = 32,
800	.reg_stride     = 4,
801};
802
803static struct meson_clk_hw_data s4_pll_clks = {
804	.hws = s4_pll_hw_clks,
805	.num = ARRAY_SIZE(s4_pll_hw_clks),
806};
807
808static 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
850static const struct of_device_id clkc_match_table[] = {
851	{
852		.compatible = "amlogic,s4-pll-clkc",
853	},
854	{}
855};
856
857static 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
865module_platform_driver(s4_driver);
866MODULE_AUTHOR("Yu Tu <yu.tu@amlogic.com>");
867MODULE_LICENSE("GPL");