1// SPDX-License-Identifier: GPL-2.0
2/*
3 * (C) Copyright 2018-2019 Rockchip Electronics Co., Ltd
4 */
5 #include <common.h>
6#include <bitfield.h>
7#include <clk-uclass.h>
8#include <dm.h>
9#include <errno.h>
10#include <log.h>
11#include <asm/arch-rockchip/clock.h>
12#include <asm/arch-rockchip/hardware.h>
13#include <div64.h>
14#include <linux/delay.h>
15
16static struct rockchip_pll_rate_table rockchip_auto_table;
17
18#define PLL_MODE_MASK				0x3
19#define PLL_RK3328_MODE_MASK			0x1
20
21#define RK3036_PLLCON0_FBDIV_MASK		0xfff
22#define RK3036_PLLCON0_FBDIV_SHIFT		0
23#define RK3036_PLLCON0_POSTDIV1_MASK		0x7 << 12
24#define RK3036_PLLCON0_POSTDIV1_SHIFT		12
25#define RK3036_PLLCON1_REFDIV_MASK		0x3f
26#define RK3036_PLLCON1_REFDIV_SHIFT		0
27#define RK3036_PLLCON1_POSTDIV2_MASK		0x7 << 6
28#define RK3036_PLLCON1_POSTDIV2_SHIFT		6
29#define RK3036_PLLCON1_DSMPD_MASK		0x1 << 12
30#define RK3036_PLLCON1_DSMPD_SHIFT		12
31#define RK3036_PLLCON2_FRAC_MASK		0xffffff
32#define RK3036_PLLCON2_FRAC_SHIFT		0
33#define RK3036_PLLCON1_PWRDOWN_SHIFT		13
34
35#define MHZ		1000000
36#define KHZ		1000
37enum {
38	OSC_HZ			= 24 * 1000000,
39	VCO_MAX_HZ	= 3200U * 1000000,
40	VCO_MIN_HZ	= 800 * 1000000,
41	OUTPUT_MAX_HZ	= 3200U * 1000000,
42	OUTPUT_MIN_HZ	= 24 * 1000000,
43};
44
45#define MIN_FOUTVCO_FREQ	(800 * MHZ)
46#define MAX_FOUTVCO_FREQ	(2000 * MHZ)
47#define RK3588_VCO_MIN_HZ	(2250UL * MHZ)
48#define RK3588_VCO_MAX_HZ	(4500UL * MHZ)
49#define RK3588_FOUT_MIN_HZ	(37UL * MHZ)
50#define RK3588_FOUT_MAX_HZ	(4500UL * MHZ)
51
52int gcd(int m, int n)
53{
54	int t;
55
56	while (m > 0) {
57		if (n > m) {
58			t = m;
59			m = n;
60			n = t;
61		} /* swap */
62		m -= n;
63	}
64	return n;
65}
66
67/*
68 * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
69 * Formulas also embedded within the Fractional PLL Verilog model:
70 * If DSMPD = 1 (DSM is disabled, "integer mode")
71 * FOUTVCO = FREF / REFDIV * FBDIV
72 * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
73 * Where:
74 * FOUTVCO = Fractional PLL non-divided output frequency
75 * FOUTPOSTDIV = Fractional PLL divided output frequency
76 *               (output of second post divider)
77 * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
78 * REFDIV = Fractional PLL input reference clock divider
79 * FBDIV = Integer value programmed into feedback divide
80 *
81 */
82
83static int rockchip_pll_clk_set_postdiv(ulong fout_hz,
84					u32 *postdiv1,
85					u32 *postdiv2,
86					u32 *foutvco)
87{
88	ulong freq;
89
90	if (fout_hz < MIN_FOUTVCO_FREQ) {
91		for (*postdiv1 = 1; *postdiv1 <= 7; (*postdiv1)++) {
92			for (*postdiv2 = 1; *postdiv2 <= 7; (*postdiv2)++) {
93				freq = fout_hz * (*postdiv1) * (*postdiv2);
94				if (freq >= MIN_FOUTVCO_FREQ &&
95				    freq <= MAX_FOUTVCO_FREQ) {
96					*foutvco = freq;
97					return 0;
98				}
99			}
100		}
101		printf("Can't FIND postdiv1/2 to make fout=%lu in 800~2000M.\n",
102		       fout_hz);
103	} else {
104		*postdiv1 = 1;
105		*postdiv2 = 1;
106	}
107	return 0;
108}
109
110static struct rockchip_pll_rate_table *
111rockchip_pll_clk_set_by_auto(ulong fin_hz,
112			     ulong fout_hz)
113{
114	struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table;
115	/* FIXME set postdiv1/2 always 1*/
116	u32 foutvco = fout_hz;
117	ulong fin_64, frac_64;
118	u32 f_frac, postdiv1, postdiv2;
119	ulong clk_gcd = 0;
120
121	if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
122		return NULL;
123
124	rockchip_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, &foutvco);
125	rate_table->postdiv1 = postdiv1;
126	rate_table->postdiv2 = postdiv2;
127	rate_table->dsmpd = 1;
128
129	if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
130		fin_hz /= MHZ;
131		foutvco /= MHZ;
132		clk_gcd = gcd(fin_hz, foutvco);
133		rate_table->refdiv = fin_hz / clk_gcd;
134		rate_table->fbdiv = foutvco / clk_gcd;
135
136		rate_table->frac = 0;
137
138		debug("fin = %ld, fout = %ld, clk_gcd = %ld,\n",
139		      fin_hz, fout_hz, clk_gcd);
140		debug("refdiv= %d,fbdiv= %d,postdiv1= %d,postdiv2= %d\n",
141		      rate_table->refdiv,
142		      rate_table->fbdiv, rate_table->postdiv1,
143		      rate_table->postdiv2);
144	} else {
145		debug("frac div,fin_hz = %ld,fout_hz = %ld\n",
146		      fin_hz, fout_hz);
147		debug("frac get postdiv1 = %d,  postdiv2 = %d, foutvco = %d\n",
148		      rate_table->postdiv1, rate_table->postdiv2, foutvco);
149		clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ);
150		rate_table->refdiv = fin_hz / MHZ / clk_gcd;
151		rate_table->fbdiv = foutvco / MHZ / clk_gcd;
152		debug("frac get refdiv = %d,  fbdiv = %d\n",
153		      rate_table->refdiv, rate_table->fbdiv);
154
155		rate_table->frac = 0;
156
157		f_frac = (foutvco % MHZ);
158		fin_64 = fin_hz;
159		fin_64 = fin_64 / rate_table->refdiv;
160		frac_64 = f_frac << 24;
161		frac_64 = frac_64 / fin_64;
162		rate_table->frac = frac_64;
163		if (rate_table->frac > 0)
164			rate_table->dsmpd = 0;
165		debug("frac = %x\n", rate_table->frac);
166	}
167	return rate_table;
168}
169
170static u32
171rockchip_rk3588_pll_k_get(u32 m, u32 p, u32 s, u64 fin_hz, u64 fvco)
172{
173	u64 fref, fout, ffrac;
174	u32 k = 0;
175
176	fref = fin_hz / p;
177	ffrac = fvco - (m * fref);
178	fout = ffrac * 65536;
179	k = fout / fref;
180	if (k > 32767) {
181		fref = fin_hz / p;
182		ffrac = ((m + 1) * fref) - fvco;
183		fout = ffrac * 65536;
184		k = ((fout * 10 / fref) + 7) / 10;
185		if (k > 32767)
186			k = 0;
187		else
188			k = ~k + 1;
189	}
190	return k;
191}
192
193static struct rockchip_pll_rate_table *
194rockchip_rk3588_pll_frac_by_auto(unsigned long fin_hz, unsigned long fout_hz)
195{
196	struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table;
197	u32 p, m, s, k;
198	u64 fvco;
199
200	for (s = 0; s <= 6; s++) {
201		fvco = (u64)fout_hz << s;
202		if (fvco < RK3588_VCO_MIN_HZ || fvco > RK3588_VCO_MAX_HZ)
203			continue;
204		for (p = 1; p <= 4; p++) {
205			for (m = 64; m <= 1023; m++) {
206				if ((fvco >= m * fin_hz / p) &&
207				    (fvco < (m + 1) * fin_hz / p)) {
208					k = rockchip_rk3588_pll_k_get(m, p, s,
209								      fin_hz,
210								      fvco);
211					if (!k)
212						continue;
213					rate_table->p = p;
214					rate_table->s = s;
215					rate_table->k = k;
216					if (k > 32767)
217						rate_table->m = m + 1;
218					else
219						rate_table->m = m;
220					return rate_table;
221				}
222			}
223		}
224	}
225	return NULL;
226}
227
228static struct rockchip_pll_rate_table *
229rk3588_pll_clk_set_by_auto(unsigned long fin_hz,
230			   unsigned long fout_hz)
231{
232	struct rockchip_pll_rate_table *rate_table = &rockchip_auto_table;
233	u32 p, m, s;
234	ulong fvco;
235
236	if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
237		return NULL;
238
239	if (fout_hz > RK3588_FOUT_MAX_HZ || fout_hz < RK3588_FOUT_MIN_HZ)
240		return NULL;
241
242	if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
243		for (s = 0; s <= 6; s++) {
244			fvco = fout_hz << s;
245			if (fvco < RK3588_VCO_MIN_HZ ||
246			    fvco > RK3588_VCO_MAX_HZ)
247				continue;
248			for (p = 2; p <= 4; p++) {
249				for (m = 64; m <= 1023; m++) {
250					if (fvco == m * fin_hz / p) {
251						rate_table->p = p;
252						rate_table->m = m;
253						rate_table->s = s;
254						rate_table->k = 0;
255						return rate_table;
256					}
257				}
258			}
259		}
260		pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
261	} else {
262		rate_table = rockchip_rk3588_pll_frac_by_auto(fin_hz, fout_hz);
263		if (!rate_table)
264			pr_err("CANNOT FIND Fout by auto,fout = %lu\n",
265			       fout_hz);
266		else
267			return rate_table;
268	}
269	return NULL;
270}
271
272static const struct rockchip_pll_rate_table *
273rockchip_get_pll_settings(struct rockchip_pll_clock *pll, ulong rate)
274{
275	struct rockchip_pll_rate_table  *rate_table = pll->rate_table;
276
277	while (rate_table->rate) {
278		if (rate_table->rate == rate)
279			break;
280		rate_table++;
281	}
282	if (rate_table->rate != rate) {
283		if (pll->type == pll_rk3588)
284			return rk3588_pll_clk_set_by_auto(24 * MHZ, rate);
285		else
286			return rockchip_pll_clk_set_by_auto(24 * MHZ, rate);
287	} else {
288		return rate_table;
289	}
290}
291
292static int rk3036_pll_set_rate(struct rockchip_pll_clock *pll,
293			       void __iomem *base, ulong pll_id,
294			       ulong drate)
295{
296	const struct rockchip_pll_rate_table *rate;
297
298	rate = rockchip_get_pll_settings(pll, drate);
299	if (!rate) {
300		printf("%s unsupport rate\n", __func__);
301		return -EINVAL;
302	}
303
304	debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d\n",
305	      __func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv);
306	debug("%s: rate settings for %lu postdiv2: %d, dsmpd: %d, frac: %d\n",
307	      __func__, rate->rate, rate->postdiv2, rate->dsmpd, rate->frac);
308
309	/*
310	 * When power on or changing PLL setting,
311	 * we must force PLL into slow mode to ensure output stable clock.
312	 */
313	rk_clrsetreg(base + pll->mode_offset,
314		     pll->mode_mask << pll->mode_shift,
315		     RKCLK_PLL_MODE_SLOW << pll->mode_shift);
316
317	/* Power down */
318	rk_setreg(base + pll->con_offset + 0x4,
319		  1 << RK3036_PLLCON1_PWRDOWN_SHIFT);
320
321	rk_clrsetreg(base + pll->con_offset,
322		     (RK3036_PLLCON0_POSTDIV1_MASK |
323		     RK3036_PLLCON0_FBDIV_MASK),
324		     (rate->postdiv1 << RK3036_PLLCON0_POSTDIV1_SHIFT) |
325		     rate->fbdiv);
326	rk_clrsetreg(base + pll->con_offset + 0x4,
327		     (RK3036_PLLCON1_POSTDIV2_MASK |
328		     RK3036_PLLCON1_REFDIV_MASK),
329		     (rate->postdiv2 << RK3036_PLLCON1_POSTDIV2_SHIFT |
330		     rate->refdiv << RK3036_PLLCON1_REFDIV_SHIFT));
331	if (!rate->dsmpd) {
332		rk_clrsetreg(base + pll->con_offset + 0x4,
333			     RK3036_PLLCON1_DSMPD_MASK,
334			     rate->dsmpd << RK3036_PLLCON1_DSMPD_SHIFT);
335		writel((readl(base + pll->con_offset + 0x8) &
336			(~RK3036_PLLCON2_FRAC_MASK)) |
337			    (rate->frac << RK3036_PLLCON2_FRAC_SHIFT),
338			    base + pll->con_offset + 0x8);
339	}
340
341	/* Power Up */
342	rk_clrreg(base + pll->con_offset + 0x4,
343		  1 << RK3036_PLLCON1_PWRDOWN_SHIFT);
344
345	/* waiting for pll lock */
346	while (!(readl(base + pll->con_offset + 0x4) & (1 << pll->lock_shift)))
347		udelay(1);
348
349	rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift,
350		     RKCLK_PLL_MODE_NORMAL << pll->mode_shift);
351	debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n",
352	      pll, readl(base + pll->con_offset),
353	      readl(base + pll->con_offset + 0x4),
354	      readl(base + pll->con_offset + 0x8),
355	      readl(base + pll->mode_offset));
356
357	return 0;
358}
359
360static ulong rk3036_pll_get_rate(struct rockchip_pll_clock *pll,
361				 void __iomem *base, ulong pll_id)
362{
363	u32 refdiv, fbdiv, postdiv1, postdiv2, dsmpd, frac;
364	u32 con = 0, shift, mask;
365	ulong rate;
366
367	con = readl(base + pll->mode_offset);
368	shift = pll->mode_shift;
369	mask = pll->mode_mask << shift;
370
371	switch ((con & mask) >> shift) {
372	case RKCLK_PLL_MODE_SLOW:
373		return OSC_HZ;
374	case RKCLK_PLL_MODE_NORMAL:
375		/* normal mode */
376		con = readl(base + pll->con_offset);
377		postdiv1 = (con & RK3036_PLLCON0_POSTDIV1_MASK) >>
378			   RK3036_PLLCON0_POSTDIV1_SHIFT;
379		fbdiv = (con & RK3036_PLLCON0_FBDIV_MASK) >>
380			RK3036_PLLCON0_FBDIV_SHIFT;
381		con = readl(base + pll->con_offset + 0x4);
382		postdiv2 = (con & RK3036_PLLCON1_POSTDIV2_MASK) >>
383			   RK3036_PLLCON1_POSTDIV2_SHIFT;
384		refdiv = (con & RK3036_PLLCON1_REFDIV_MASK) >>
385			 RK3036_PLLCON1_REFDIV_SHIFT;
386		dsmpd = (con & RK3036_PLLCON1_DSMPD_MASK) >>
387			RK3036_PLLCON1_DSMPD_SHIFT;
388		con = readl(base + pll->con_offset + 0x8);
389		frac = (con & RK3036_PLLCON2_FRAC_MASK) >>
390			RK3036_PLLCON2_FRAC_SHIFT;
391		rate = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
392		if (dsmpd == 0) {
393			u64 frac_rate = OSC_HZ * (u64)frac;
394
395			do_div(frac_rate, refdiv);
396			frac_rate >>= 24;
397			do_div(frac_rate, postdiv1);
398			do_div(frac_rate, postdiv1);
399			rate += frac_rate;
400		}
401		return rate;
402	case RKCLK_PLL_MODE_DEEP:
403	default:
404		return 32768;
405	}
406}
407
408#define RK3588_PLLCON(i)		((i) * 0x4)
409#define RK3588_PLLCON0_M_MASK		0x3ff << 0
410#define RK3588_PLLCON0_M_SHIFT		0
411#define RK3588_PLLCON1_P_MASK		0x3f << 0
412#define RK3588_PLLCON1_P_SHIFT		0
413#define RK3588_PLLCON1_S_MASK		0x7 << 6
414#define RK3588_PLLCON1_S_SHIFT		6
415#define RK3588_PLLCON2_K_MASK		0xffff
416#define RK3588_PLLCON2_K_SHIFT		0
417#define RK3588_PLLCON1_PWRDOWN		BIT(13)
418#define RK3588_PLLCON6_LOCK_STATUS	BIT(15)
419#define RK3588_B0PLL_CLKSEL_CON(i)	((i) * 0x4 + 0x50000 + 0x300)
420#define RK3588_B1PLL_CLKSEL_CON(i)	((i) * 0x4 + 0x52000 + 0x300)
421#define RK3588_LPLL_CLKSEL_CON(i)	((i) * 0x4 + 0x58000 + 0x300)
422#define RK3588_CORE_DIV_MASK		0x1f
423#define RK3588_CORE_L02_DIV_SHIFT	0
424#define RK3588_CORE_L13_DIV_SHIFT	7
425#define RK3588_CORE_B02_DIV_SHIFT	8
426#define RK3588_CORE_B13_DIV_SHIFT	0
427
428static int rk3588_pll_set_rate(struct rockchip_pll_clock *pll,
429			       void __iomem *base, ulong pll_id,
430			       ulong drate)
431{
432	const struct rockchip_pll_rate_table *rate;
433
434	rate = rockchip_get_pll_settings(pll, drate);
435	if (!rate) {
436		printf("%s unsupported rate\n", __func__);
437		return -EINVAL;
438	}
439
440	debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n",
441	      __func__, rate->rate, rate->p, rate->m, rate->s, rate->k);
442
443	/*
444	 * When power on or changing PLL setting,
445	 * we must force PLL into slow mode to ensure output stable clock.
446	 */
447	if (pll_id == 3)
448		rk_clrsetreg(base + 0x84c, 0x1 << 1, 0x1 << 1);
449
450	rk_clrsetreg(base + pll->mode_offset,
451		     pll->mode_mask << pll->mode_shift,
452		     RKCLK_PLL_MODE_SLOW << pll->mode_shift);
453	if (pll_id == 0)
454		rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0),
455			     pll->mode_mask << 6,
456			     RKCLK_PLL_MODE_SLOW << 6);
457	else if (pll_id == 1)
458		rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0),
459			     pll->mode_mask << 6,
460			     RKCLK_PLL_MODE_SLOW << 6);
461	else if (pll_id == 2)
462		rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5),
463			     pll->mode_mask << 14,
464			     RKCLK_PLL_MODE_SLOW << 14);
465
466	/* Power down */
467	rk_setreg(base + pll->con_offset + RK3588_PLLCON(1),
468		  RK3588_PLLCON1_PWRDOWN);
469
470	rk_clrsetreg(base + pll->con_offset,
471		     RK3588_PLLCON0_M_MASK,
472		     (rate->m << RK3588_PLLCON0_M_SHIFT));
473	rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(1),
474		     (RK3588_PLLCON1_P_MASK |
475		     RK3588_PLLCON1_S_MASK),
476		     (rate->p << RK3588_PLLCON1_P_SHIFT |
477		     rate->s << RK3588_PLLCON1_S_SHIFT));
478	if (rate->k) {
479		rk_clrsetreg(base + pll->con_offset + RK3588_PLLCON(2),
480			     RK3588_PLLCON2_K_MASK,
481			     rate->k << RK3588_PLLCON2_K_SHIFT);
482	}
483	/* Power up */
484	rk_clrreg(base + pll->con_offset + RK3588_PLLCON(1),
485		  RK3588_PLLCON1_PWRDOWN);
486
487	/* waiting for pll lock */
488	while (!(readl(base + pll->con_offset + RK3588_PLLCON(6)) &
489		RK3588_PLLCON6_LOCK_STATUS)) {
490		udelay(1);
491		debug("%s: wait pll lock, pll_id=%ld\n", __func__, pll_id);
492	}
493
494	rk_clrsetreg(base + pll->mode_offset, pll->mode_mask << pll->mode_shift,
495		     RKCLK_PLL_MODE_NORMAL << pll->mode_shift);
496	if (pll_id == 0) {
497		rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0),
498			     pll->mode_mask << 6,
499			     2 << 6);
500		rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(0),
501			     RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT,
502			     0 << RK3588_CORE_B02_DIV_SHIFT);
503		rk_clrsetreg(base + RK3588_B0PLL_CLKSEL_CON(1),
504			     RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT,
505			     0 << RK3588_CORE_B13_DIV_SHIFT);
506	} else if (pll_id == 1) {
507		rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0),
508			     pll->mode_mask << 6,
509			     2 << 6);
510		rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(0),
511			     RK3588_CORE_DIV_MASK << RK3588_CORE_B02_DIV_SHIFT,
512			     0 << RK3588_CORE_B02_DIV_SHIFT);
513		rk_clrsetreg(base + RK3588_B1PLL_CLKSEL_CON(1),
514			     RK3588_CORE_DIV_MASK << RK3588_CORE_B13_DIV_SHIFT,
515			     0 << RK3588_CORE_B13_DIV_SHIFT);
516	} else if (pll_id == 2) {
517		rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(5),
518			     pll->mode_mask << 14,
519			     2 << 14);
520		rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6),
521			     RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT,
522			     0 << RK3588_CORE_L13_DIV_SHIFT);
523		rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(6),
524			     RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT,
525			     0 << RK3588_CORE_L02_DIV_SHIFT);
526		rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7),
527			     RK3588_CORE_DIV_MASK << RK3588_CORE_L13_DIV_SHIFT,
528			     0 << RK3588_CORE_L13_DIV_SHIFT);
529		rk_clrsetreg(base + RK3588_LPLL_CLKSEL_CON(7),
530			     RK3588_CORE_DIV_MASK << RK3588_CORE_L02_DIV_SHIFT,
531			     0 << RK3588_CORE_L02_DIV_SHIFT);
532	}
533
534	if (pll_id == 3)
535		rk_clrsetreg(base + 0x84c, 0x1 << 1, 0);
536
537	debug("PLL at %p: con0=%x con1= %x con2= %x mode= %x\n",
538	      pll, readl(base + pll->con_offset),
539	      readl(base + pll->con_offset + 0x4),
540	      readl(base + pll->con_offset + 0x8),
541	      readl(base + pll->mode_offset));
542
543	return 0;
544}
545
546static ulong rk3588_pll_get_rate(struct rockchip_pll_clock *pll,
547				 void __iomem *base, ulong pll_id)
548{
549	u32 m, p, s, k;
550	u32 con = 0, shift, mode;
551	u64 rate, postdiv;
552
553	con = readl(base + pll->mode_offset);
554	shift = pll->mode_shift;
555	if (pll_id == 8)
556		mode = RKCLK_PLL_MODE_NORMAL;
557	else
558		mode = (con & (pll->mode_mask << shift)) >> shift;
559	switch (mode) {
560	case RKCLK_PLL_MODE_SLOW:
561		return OSC_HZ;
562	case RKCLK_PLL_MODE_NORMAL:
563		/* normal mode */
564		con = readl(base + pll->con_offset);
565		m = (con & RK3588_PLLCON0_M_MASK) >>
566			   RK3588_PLLCON0_M_SHIFT;
567		con = readl(base + pll->con_offset + RK3588_PLLCON(1));
568		p = (con & RK3588_PLLCON1_P_MASK) >>
569			   RK3036_PLLCON0_FBDIV_SHIFT;
570		s = (con & RK3588_PLLCON1_S_MASK) >>
571			 RK3588_PLLCON1_S_SHIFT;
572		con = readl(base + pll->con_offset + RK3588_PLLCON(2));
573		k = (con & RK3588_PLLCON2_K_MASK) >>
574			RK3588_PLLCON2_K_SHIFT;
575
576		rate = OSC_HZ / p;
577		rate *= m;
578		if (k & BIT(15)) {
579			/* fractional mode */
580			u64 frac_rate64;
581
582			k = (~(k - 1)) & RK3588_PLLCON2_K_MASK;
583			frac_rate64 = OSC_HZ * k;
584			postdiv = p;
585			postdiv *= 65536;
586			do_div(frac_rate64, postdiv);
587			rate -= frac_rate64;
588		} else {
589			/* fractional mode */
590			u64 frac_rate64 = OSC_HZ * k;
591
592			postdiv = p;
593			postdiv *= 65536;
594			do_div(frac_rate64, postdiv);
595			rate += frac_rate64;
596		}
597		rate = rate >> s;
598		return rate;
599	case RKCLK_PLL_MODE_DEEP:
600	default:
601		return 32768;
602	}
603}
604
605ulong rockchip_pll_get_rate(struct rockchip_pll_clock *pll,
606			    void __iomem *base,
607			    ulong pll_id)
608{
609	ulong rate = 0;
610
611	switch (pll->type) {
612	case pll_rk3036:
613		pll->mode_mask = PLL_MODE_MASK;
614		rate = rk3036_pll_get_rate(pll, base, pll_id);
615		break;
616	case pll_rk3328:
617		pll->mode_mask = PLL_RK3328_MODE_MASK;
618		rate = rk3036_pll_get_rate(pll, base, pll_id);
619		break;
620	case pll_rk3588:
621		pll->mode_mask = PLL_MODE_MASK;
622		rate = rk3588_pll_get_rate(pll, base, pll_id);
623		break;
624	default:
625		printf("%s: Unknown pll type for pll clk %ld\n",
626		       __func__, pll_id);
627	}
628	return rate;
629}
630
631int rockchip_pll_set_rate(struct rockchip_pll_clock *pll,
632			  void __iomem *base, ulong pll_id,
633			  ulong drate)
634{
635	int ret = 0;
636
637	if (rockchip_pll_get_rate(pll, base, pll_id) == drate)
638		return 0;
639
640	switch (pll->type) {
641	case pll_rk3036:
642		pll->mode_mask = PLL_MODE_MASK;
643		ret = rk3036_pll_set_rate(pll, base, pll_id, drate);
644		break;
645	case pll_rk3328:
646		pll->mode_mask = PLL_RK3328_MODE_MASK;
647		ret = rk3036_pll_set_rate(pll, base, pll_id, drate);
648		break;
649	case pll_rk3588:
650		pll->mode_mask = PLL_MODE_MASK;
651		ret = rk3588_pll_set_rate(pll, base, pll_id, drate);
652		break;
653	default:
654		printf("%s: Unknown pll type for pll clk %ld\n",
655		       __func__, pll_id);
656	}
657	return ret;
658}
659
660const struct rockchip_cpu_rate_table *
661rockchip_get_cpu_settings(struct rockchip_cpu_rate_table *cpu_table,
662			  ulong rate)
663{
664	struct rockchip_cpu_rate_table *ps = cpu_table;
665
666	while (ps->rate) {
667		if (ps->rate == rate)
668			break;
669		ps++;
670	}
671	if (ps->rate != rate)
672		return NULL;
673	else
674		return ps;
675}
676