xref: /rk3399_rockchip-uboot/drivers/clk/rockchip/clk_px30.c (revision f909d4a8c942dccbf2cb7d34d2b1d8e4bca0bfa6)
1a60961a3SKever Yang /*
2a60961a3SKever Yang  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3a60961a3SKever Yang  *
4a60961a3SKever Yang  * SPDX-License-Identifier:	GPL-2.0
5a60961a3SKever Yang  */
6a60961a3SKever Yang 
7a60961a3SKever Yang #include <common.h>
8a60961a3SKever Yang #include <bitfield.h>
9a60961a3SKever Yang #include <clk-uclass.h>
10a60961a3SKever Yang #include <dm.h>
11a60961a3SKever Yang #include <errno.h>
12a60961a3SKever Yang #include <syscon.h>
13a60961a3SKever Yang #include <asm/arch/clock.h>
14a60961a3SKever Yang #include <asm/arch/cru_px30.h>
15a60961a3SKever Yang #include <asm/arch/hardware.h>
16a60961a3SKever Yang #include <asm/io.h>
17a60961a3SKever Yang #include <dm/lists.h>
18a60961a3SKever Yang #include <dt-bindings/clock/px30-cru.h>
19a60961a3SKever Yang 
20a60961a3SKever Yang DECLARE_GLOBAL_DATA_PTR;
21a60961a3SKever Yang 
22a60961a3SKever Yang enum {
23a60961a3SKever Yang 	VCO_MAX_HZ	= 3200U * 1000000,
24a60961a3SKever Yang 	VCO_MIN_HZ	= 800 * 1000000,
25a60961a3SKever Yang 	OUTPUT_MAX_HZ	= 3200U * 1000000,
26a60961a3SKever Yang 	OUTPUT_MIN_HZ	= 24 * 1000000,
27a60961a3SKever Yang };
28a60961a3SKever Yang 
29*f909d4a8SFinley Xiao #define PX30_VOP_PLL_LIMIT			600000000
30*f909d4a8SFinley Xiao 
31cefa5186SFinley Xiao #define PX30_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1,	\
32cefa5186SFinley Xiao 			_postdiv2, _dsmpd, _frac)		\
33cefa5186SFinley Xiao {								\
34cefa5186SFinley Xiao 	.rate	= _rate##U,					\
35cefa5186SFinley Xiao 	.fbdiv = _fbdiv,					\
36cefa5186SFinley Xiao 	.postdiv1 = _postdiv1,					\
37cefa5186SFinley Xiao 	.refdiv = _refdiv,					\
38cefa5186SFinley Xiao 	.postdiv2 = _postdiv2,					\
39cefa5186SFinley Xiao 	.dsmpd = _dsmpd,					\
40cefa5186SFinley Xiao 	.frac = _frac,						\
41cefa5186SFinley Xiao }
42cefa5186SFinley Xiao 
4337428b92SFinley Xiao #define PX30_CPUCLK_RATE(_rate, _aclk_div, _pclk_div)		\
4437428b92SFinley Xiao {								\
4537428b92SFinley Xiao 	.rate	= _rate##U,					\
4637428b92SFinley Xiao 	.aclk_div = _aclk_div,					\
4737428b92SFinley Xiao 	.pclk_div = _pclk_div,					\
4837428b92SFinley Xiao }
4937428b92SFinley Xiao 
50a60961a3SKever Yang #define DIV_TO_RATE(input_rate, div)    ((input_rate) / ((div) + 1))
51a60961a3SKever Yang 
527a1915c0SFinley Xiao #define PX30_CLK_DUMP(_id, _name, _iscru)	\
537a1915c0SFinley Xiao {						\
547a1915c0SFinley Xiao 	.id = _id,				\
557a1915c0SFinley Xiao 	.name = _name,				\
567a1915c0SFinley Xiao 	.is_cru = _iscru,			\
577a1915c0SFinley Xiao }
587a1915c0SFinley Xiao 
59cefa5186SFinley Xiao static struct pll_rate_table px30_pll_rates[] = {
60cefa5186SFinley Xiao 	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
61cefa5186SFinley Xiao 	PX30_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
62cefa5186SFinley Xiao 	PX30_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
63cefa5186SFinley Xiao 	PX30_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0),
6437428b92SFinley Xiao 	PX30_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0),
65cefa5186SFinley Xiao 	PX30_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0),
66cefa5186SFinley Xiao 	PX30_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0),
67dd472d4fSFinley Xiao 	PX30_PLL_RATE(600000000, 1, 75, 3, 1, 1, 0),
68a60961a3SKever Yang };
69a60961a3SKever Yang 
707a1915c0SFinley Xiao static const struct px30_clk_info clks_dump[] = {
717a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_APLL, "apll", true),
727a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_DPLL, "dpll", true),
737a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_CPLL, "cpll", true),
747a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_NPLL, "npll", true),
757a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_GPLL, "gpll", false),
767a1915c0SFinley Xiao 	PX30_CLK_DUMP(ACLK_BUS_PRE, "aclk_bus", true),
777a1915c0SFinley Xiao 	PX30_CLK_DUMP(HCLK_BUS_PRE, "hclk_bus", true),
787a1915c0SFinley Xiao 	PX30_CLK_DUMP(PCLK_BUS_PRE, "pclk_bus", true),
797a1915c0SFinley Xiao 	PX30_CLK_DUMP(ACLK_PERI_PRE, "aclk_peri", true),
807a1915c0SFinley Xiao 	PX30_CLK_DUMP(HCLK_PERI_PRE, "hclk_peri", true),
817a1915c0SFinley Xiao 	PX30_CLK_DUMP(PCLK_PMU_PRE, "pclk_pmu", false),
827a1915c0SFinley Xiao };
837a1915c0SFinley Xiao 
8437428b92SFinley Xiao static struct cpu_rate_table px30_cpu_rates[] = {
8537428b92SFinley Xiao 	PX30_CPUCLK_RATE(1200000000, 1, 5),
8637428b92SFinley Xiao 	PX30_CPUCLK_RATE(1008000000, 1, 5),
8737428b92SFinley Xiao 	PX30_CPUCLK_RATE(816000000, 1, 3),
8837428b92SFinley Xiao 	PX30_CPUCLK_RATE(600000000, 1, 3),
8937428b92SFinley Xiao };
9037428b92SFinley Xiao 
91bcefd077SFinley Xiao static u8 pll_mode_shift[PLL_COUNT] = {
92bcefd077SFinley Xiao 	APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
93bcefd077SFinley Xiao 	NPLL_MODE_SHIFT, GPLL_MODE_SHIFT
94bcefd077SFinley Xiao };
95bcefd077SFinley Xiao static u32 pll_mode_mask[PLL_COUNT] = {
96bcefd077SFinley Xiao 	APLL_MODE_MASK, DPLL_MODE_MASK, CPLL_MODE_MASK,
97bcefd077SFinley Xiao 	NPLL_MODE_MASK, GPLL_MODE_MASK
98bcefd077SFinley Xiao };
99bcefd077SFinley Xiao 
100cefa5186SFinley Xiao static struct pll_rate_table auto_table;
101cefa5186SFinley Xiao 
102cefa5186SFinley Xiao static struct pll_rate_table *pll_clk_set_by_auto(u32 drate)
103cefa5186SFinley Xiao {
104cefa5186SFinley Xiao 	struct pll_rate_table *rate = &auto_table;
105cefa5186SFinley Xiao 	u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0;
106cefa5186SFinley Xiao 	u32 postdiv1, postdiv2 = 1;
107cefa5186SFinley Xiao 	u32 fref_khz;
108cefa5186SFinley Xiao 	u32 diff_khz, best_diff_khz;
109cefa5186SFinley Xiao 	const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16;
110cefa5186SFinley Xiao 	const u32 max_postdiv1 = 7, max_postdiv2 = 7;
111cefa5186SFinley Xiao 	u32 vco_khz;
112cefa5186SFinley Xiao 	u32 rate_khz = drate / KHz;
113cefa5186SFinley Xiao 
114cefa5186SFinley Xiao 	if (!drate) {
115cefa5186SFinley Xiao 		printf("%s: the frequency can't be 0 Hz\n", __func__);
116cefa5186SFinley Xiao 		return NULL;
117cefa5186SFinley Xiao 	}
118cefa5186SFinley Xiao 
119cefa5186SFinley Xiao 	postdiv1 = DIV_ROUND_UP(VCO_MIN_HZ / 1000, rate_khz);
120cefa5186SFinley Xiao 	if (postdiv1 > max_postdiv1) {
121cefa5186SFinley Xiao 		postdiv2 = DIV_ROUND_UP(postdiv1, max_postdiv1);
122cefa5186SFinley Xiao 		postdiv1 = DIV_ROUND_UP(postdiv1, postdiv2);
123cefa5186SFinley Xiao 	}
124cefa5186SFinley Xiao 
125cefa5186SFinley Xiao 	vco_khz = rate_khz * postdiv1 * postdiv2;
126cefa5186SFinley Xiao 
127cefa5186SFinley Xiao 	if (vco_khz < (VCO_MIN_HZ / KHz) || vco_khz > (VCO_MAX_HZ / KHz) ||
128cefa5186SFinley Xiao 	    postdiv2 > max_postdiv2) {
129cefa5186SFinley Xiao 		printf("%s: Cannot find out a supported VCO for Freq (%uHz)\n",
130cefa5186SFinley Xiao 		       __func__, rate_khz);
131cefa5186SFinley Xiao 		return NULL;
132cefa5186SFinley Xiao 	}
133cefa5186SFinley Xiao 
134cefa5186SFinley Xiao 	rate->postdiv1 = postdiv1;
135cefa5186SFinley Xiao 	rate->postdiv2 = postdiv2;
136cefa5186SFinley Xiao 
137cefa5186SFinley Xiao 	best_diff_khz = vco_khz;
138cefa5186SFinley Xiao 	for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) {
139cefa5186SFinley Xiao 		fref_khz = ref_khz / refdiv;
140cefa5186SFinley Xiao 
141cefa5186SFinley Xiao 		fbdiv = vco_khz / fref_khz;
142cefa5186SFinley Xiao 		if ((fbdiv >= max_fbdiv) || (fbdiv <= min_fbdiv))
143cefa5186SFinley Xiao 			continue;
144cefa5186SFinley Xiao 		diff_khz = vco_khz - fbdiv * fref_khz;
145cefa5186SFinley Xiao 		if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) {
146cefa5186SFinley Xiao 			fbdiv++;
147cefa5186SFinley Xiao 			diff_khz = fref_khz - diff_khz;
148cefa5186SFinley Xiao 		}
149cefa5186SFinley Xiao 
150cefa5186SFinley Xiao 		if (diff_khz >= best_diff_khz)
151cefa5186SFinley Xiao 			continue;
152cefa5186SFinley Xiao 
153cefa5186SFinley Xiao 		best_diff_khz = diff_khz;
154cefa5186SFinley Xiao 		rate->refdiv = refdiv;
155cefa5186SFinley Xiao 		rate->fbdiv = fbdiv;
156cefa5186SFinley Xiao 	}
157cefa5186SFinley Xiao 
158cefa5186SFinley Xiao 	if (best_diff_khz > 4 * (MHz / KHz)) {
159cefa5186SFinley Xiao 		printf("%s: Failed to match output frequency %u bestis %u Hz\n",
160cefa5186SFinley Xiao 		       __func__, rate_khz,
161cefa5186SFinley Xiao 		       best_diff_khz * KHz);
162cefa5186SFinley Xiao 		return NULL;
163cefa5186SFinley Xiao 	}
164cefa5186SFinley Xiao 
165cefa5186SFinley Xiao 	return rate;
166cefa5186SFinley Xiao }
167cefa5186SFinley Xiao 
168cefa5186SFinley Xiao static const struct pll_rate_table *get_pll_settings(unsigned long rate)
169cefa5186SFinley Xiao {
170cefa5186SFinley Xiao 	unsigned int rate_count = ARRAY_SIZE(px30_pll_rates);
171cefa5186SFinley Xiao 	int i;
172cefa5186SFinley Xiao 
173cefa5186SFinley Xiao 	for (i = 0; i < rate_count; i++) {
174cefa5186SFinley Xiao 		if (rate == px30_pll_rates[i].rate)
175cefa5186SFinley Xiao 			return &px30_pll_rates[i];
176cefa5186SFinley Xiao 	}
177cefa5186SFinley Xiao 
178cefa5186SFinley Xiao 	return pll_clk_set_by_auto(rate);
179cefa5186SFinley Xiao }
180a60961a3SKever Yang 
18137428b92SFinley Xiao static const struct cpu_rate_table *get_cpu_settings(unsigned long rate)
18237428b92SFinley Xiao {
18337428b92SFinley Xiao 	unsigned int rate_count = ARRAY_SIZE(px30_cpu_rates);
18437428b92SFinley Xiao 	int i;
18537428b92SFinley Xiao 
18637428b92SFinley Xiao 	for (i = 0; i < rate_count; i++) {
18737428b92SFinley Xiao 		if (rate == px30_cpu_rates[i].rate)
18837428b92SFinley Xiao 			return &px30_cpu_rates[i];
18937428b92SFinley Xiao 	}
19037428b92SFinley Xiao 
19137428b92SFinley Xiao 	return NULL;
19237428b92SFinley Xiao }
19337428b92SFinley Xiao 
194a60961a3SKever Yang /*
195a60961a3SKever Yang  * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
196a60961a3SKever Yang  * Formulas also embedded within the Fractional PLL Verilog model:
197a60961a3SKever Yang  * If DSMPD = 1 (DSM is disabled, "integer mode")
198a60961a3SKever Yang  * FOUTVCO = FREF / REFDIV * FBDIV
199a60961a3SKever Yang  * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
200a60961a3SKever Yang  * Where:
201a60961a3SKever Yang  * FOUTVCO = Fractional PLL non-divided output frequency
202a60961a3SKever Yang  * FOUTPOSTDIV = Fractional PLL divided output frequency
203a60961a3SKever Yang  *               (output of second post divider)
204a60961a3SKever Yang  * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
205a60961a3SKever Yang  * REFDIV = Fractional PLL input reference clock divider
206a60961a3SKever Yang  * FBDIV = Integer value programmed into feedback divide
207a60961a3SKever Yang  *
208a60961a3SKever Yang  */
209cefa5186SFinley Xiao static int rkclk_set_pll(struct px30_pll *pll, unsigned int *mode,
210cefa5186SFinley Xiao 			 enum px30_pll_id pll_id,
211cefa5186SFinley Xiao 			 unsigned long drate)
212a60961a3SKever Yang {
213cefa5186SFinley Xiao 	const struct pll_rate_table *rate;
214cefa5186SFinley Xiao 	uint vco_hz, output_hz;
215db235eb5SFinley Xiao 
216cefa5186SFinley Xiao 	rate = get_pll_settings(drate);
217cefa5186SFinley Xiao 	if (!rate) {
218cefa5186SFinley Xiao 		printf("%s unsupport rate\n", __func__);
219cefa5186SFinley Xiao 		return -EINVAL;
220cefa5186SFinley Xiao 	}
221cefa5186SFinley Xiao 
222cefa5186SFinley Xiao 	/* All PLLs have same VCO and output frequency range restrictions. */
223cefa5186SFinley Xiao 	vco_hz = OSC_HZ / 1000 * rate->fbdiv / rate->refdiv * 1000;
224cefa5186SFinley Xiao 	output_hz = vco_hz / rate->postdiv1 / rate->postdiv2;
225a60961a3SKever Yang 
226a60961a3SKever Yang 	debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n",
227cefa5186SFinley Xiao 	      pll, rate->fbdiv, rate->refdiv, rate->postdiv1,
228cefa5186SFinley Xiao 	      rate->postdiv2, vco_hz, output_hz);
229a60961a3SKever Yang 	assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
230a60961a3SKever Yang 	       output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ);
231a60961a3SKever Yang 
232db235eb5SFinley Xiao 	/*
233db235eb5SFinley Xiao 	 * When power on or changing PLL setting,
234db235eb5SFinley Xiao 	 * we must force PLL into slow mode to ensure output stable clock.
235db235eb5SFinley Xiao 	 */
236bcefd077SFinley Xiao 	rk_clrsetreg(mode, pll_mode_mask[pll_id],
237bcefd077SFinley Xiao 		     PLLMUX_FROM_XIN24M << pll_mode_shift[pll_id]);
238db235eb5SFinley Xiao 
239a60961a3SKever Yang 	/* use integer mode */
240a60961a3SKever Yang 	rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
241a60961a3SKever Yang 	/* Power down */
242a60961a3SKever Yang 	rk_setreg(&pll->con1, 1 << PLL_PD_SHIFT);
243a60961a3SKever Yang 
244a60961a3SKever Yang 	rk_clrsetreg(&pll->con0,
245a60961a3SKever Yang 		     PLL_POSTDIV1_MASK | PLL_FBDIV_MASK,
246cefa5186SFinley Xiao 		     (rate->postdiv1 << PLL_POSTDIV1_SHIFT) | rate->fbdiv);
247a60961a3SKever Yang 	rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK,
248cefa5186SFinley Xiao 		     (rate->postdiv2 << PLL_POSTDIV2_SHIFT |
249cefa5186SFinley Xiao 		     rate->refdiv << PLL_REFDIV_SHIFT));
250a60961a3SKever Yang 
251a60961a3SKever Yang 	/* Power Up */
252a60961a3SKever Yang 	rk_clrreg(&pll->con1, 1 << PLL_PD_SHIFT);
253a60961a3SKever Yang 
254a60961a3SKever Yang 	/* waiting for pll lock */
2556fb52eadSFinley Xiao 	while (!(readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)))
256a60961a3SKever Yang 		udelay(1);
257a60961a3SKever Yang 
258bcefd077SFinley Xiao 	rk_clrsetreg(mode, pll_mode_mask[pll_id],
259bcefd077SFinley Xiao 		     PLLMUX_FROM_PLL << pll_mode_shift[pll_id]);
260db235eb5SFinley Xiao 
261cefa5186SFinley Xiao 	return 0;
262a60961a3SKever Yang }
263a60961a3SKever Yang 
264cefa5186SFinley Xiao static uint32_t rkclk_pll_get_rate(struct px30_pll *pll, unsigned int *mode,
26530f1f38dSFinley Xiao 				   enum px30_pll_id pll_id)
26630f1f38dSFinley Xiao {
26730f1f38dSFinley Xiao 	u32 refdiv, fbdiv, postdiv1, postdiv2;
268cefa5186SFinley Xiao 	u32 con, shift, mask;
26930f1f38dSFinley Xiao 
270cefa5186SFinley Xiao 	con = readl(mode);
271bcefd077SFinley Xiao 	shift = pll_mode_shift[pll_id];
272bcefd077SFinley Xiao 	mask = pll_mode_mask[pll_id];
27330f1f38dSFinley Xiao 
27430f1f38dSFinley Xiao 	switch ((con & mask) >> shift) {
27530f1f38dSFinley Xiao 	case PLLMUX_FROM_XIN24M:
27630f1f38dSFinley Xiao 		return OSC_HZ;
27730f1f38dSFinley Xiao 	case PLLMUX_FROM_PLL:
27830f1f38dSFinley Xiao 		/* normal mode */
27930f1f38dSFinley Xiao 		con = readl(&pll->con0);
28030f1f38dSFinley Xiao 		postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
28130f1f38dSFinley Xiao 		fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
28230f1f38dSFinley Xiao 		con = readl(&pll->con1);
28330f1f38dSFinley Xiao 		postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
28430f1f38dSFinley Xiao 		refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
28530f1f38dSFinley Xiao 		return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
28630f1f38dSFinley Xiao 	case PLLMUX_FROM_RTC32K:
28730f1f38dSFinley Xiao 	default:
28830f1f38dSFinley Xiao 		return 32768;
28930f1f38dSFinley Xiao 	}
29030f1f38dSFinley Xiao }
29130f1f38dSFinley Xiao 
292cefa5186SFinley Xiao static ulong px30_i2c_get_clk(struct px30_clk_priv *priv, ulong clk_id)
29330f1f38dSFinley Xiao {
294cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
295a60961a3SKever Yang 	u32 div, con;
296a60961a3SKever Yang 
297a60961a3SKever Yang 	switch (clk_id) {
298a60961a3SKever Yang 	case SCLK_I2C0:
299f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[49]);
300f67f522bSFinley Xiao 		div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
301a60961a3SKever Yang 		break;
302a60961a3SKever Yang 	case SCLK_I2C1:
303f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[49]);
304f67f522bSFinley Xiao 		div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
305a60961a3SKever Yang 		break;
306a60961a3SKever Yang 	case SCLK_I2C2:
307f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[50]);
308f67f522bSFinley Xiao 		div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
309a60961a3SKever Yang 		break;
310a60961a3SKever Yang 	case SCLK_I2C3:
311f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[50]);
312f67f522bSFinley Xiao 		div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
313a60961a3SKever Yang 		break;
314a60961a3SKever Yang 	default:
315a60961a3SKever Yang 		printf("do not support this i2c bus\n");
316a60961a3SKever Yang 		return -EINVAL;
317a60961a3SKever Yang 	}
318a60961a3SKever Yang 
319cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
320a60961a3SKever Yang }
321a60961a3SKever Yang 
322cefa5186SFinley Xiao static ulong px30_i2c_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
323a60961a3SKever Yang {
324cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
325a60961a3SKever Yang 	int src_clk_div;
326a60961a3SKever Yang 
327cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
328cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 127);
329a60961a3SKever Yang 
330a60961a3SKever Yang 	switch (clk_id) {
331a60961a3SKever Yang 	case SCLK_I2C0:
332f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[49],
333f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT |
334f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT,
335f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT |
336f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT);
337a60961a3SKever Yang 		break;
338a60961a3SKever Yang 	case SCLK_I2C1:
339f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[49],
340f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT |
341f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT,
342f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT |
343f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT);
344a60961a3SKever Yang 		break;
345a60961a3SKever Yang 	case SCLK_I2C2:
346f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[50],
347f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT |
348f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT,
349f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT |
350f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT);
351a60961a3SKever Yang 		break;
352a60961a3SKever Yang 	case SCLK_I2C3:
353f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[50],
354f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT |
355f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT,
356f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT |
357f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT);
358a60961a3SKever Yang 		break;
359a60961a3SKever Yang 	default:
360a60961a3SKever Yang 		printf("do not support this i2c bus\n");
361a60961a3SKever Yang 		return -EINVAL;
362a60961a3SKever Yang 	}
363a60961a3SKever Yang 
364cefa5186SFinley Xiao 	return px30_i2c_get_clk(priv, clk_id);
365a60961a3SKever Yang }
366a60961a3SKever Yang 
367cefa5186SFinley Xiao static ulong px30_nandc_get_clk(struct px30_clk_priv *priv)
368a60961a3SKever Yang {
369cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
370cefa5186SFinley Xiao 	u32 div, con;
371cefa5186SFinley Xiao 
372cefa5186SFinley Xiao 	con = readl(&cru->clksel_con[15]);
373cefa5186SFinley Xiao 	div = (con & NANDC_DIV_MASK) >> NANDC_DIV_SHIFT;
374cefa5186SFinley Xiao 
37560a1199eSYifeng Zhao 	return DIV_TO_RATE(priv->gpll_hz, div);
376cefa5186SFinley Xiao }
377cefa5186SFinley Xiao 
378cefa5186SFinley Xiao static ulong px30_nandc_set_clk(struct px30_clk_priv *priv,
379cefa5186SFinley Xiao 				ulong set_rate)
380cefa5186SFinley Xiao {
381cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
382cefa5186SFinley Xiao 	int src_clk_div;
383cefa5186SFinley Xiao 
384cefa5186SFinley Xiao 	/* Select nandc source from GPLL by default */
385cefa5186SFinley Xiao 	/* nandc clock defaulg div 2 internal, need provide double in cru */
38660a1199eSYifeng Zhao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, set_rate);
387cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 31);
388cefa5186SFinley Xiao 
389cefa5186SFinley Xiao 	rk_clrsetreg(&cru->clksel_con[15],
390cefa5186SFinley Xiao 		     NANDC_CLK_SEL_MASK | NANDC_PLL_MASK |
391cefa5186SFinley Xiao 		     NANDC_DIV_MASK,
392cefa5186SFinley Xiao 		     NANDC_CLK_SEL_NANDC << NANDC_CLK_SEL_SHIFT |
393cefa5186SFinley Xiao 		     NANDC_SEL_GPLL << NANDC_PLL_SHIFT |
394cefa5186SFinley Xiao 		     (src_clk_div - 1) << NANDC_DIV_SHIFT);
395cefa5186SFinley Xiao 
396cefa5186SFinley Xiao 	return px30_nandc_get_clk(priv);
397cefa5186SFinley Xiao }
398cefa5186SFinley Xiao 
399cefa5186SFinley Xiao static ulong px30_mmc_get_clk(struct px30_clk_priv *priv, uint clk_id)
400cefa5186SFinley Xiao {
401cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
402a60961a3SKever Yang 	u32 div, con, con_id;
403a60961a3SKever Yang 
404a60961a3SKever Yang 	switch (clk_id) {
405a60961a3SKever Yang 	case HCLK_SDMMC:
406a60961a3SKever Yang 	case SCLK_SDMMC:
407a60961a3SKever Yang 		con_id = 16;
408a60961a3SKever Yang 		break;
409a60961a3SKever Yang 	case HCLK_EMMC:
410a60961a3SKever Yang 	case SCLK_EMMC:
41196f1b3d9SKever Yang 	case SCLK_EMMC_SAMPLE:
412a60961a3SKever Yang 		con_id = 20;
413a60961a3SKever Yang 		break;
414a60961a3SKever Yang 	default:
415a60961a3SKever Yang 		return -EINVAL;
416a60961a3SKever Yang 	}
417a60961a3SKever Yang 
418a60961a3SKever Yang 	con = readl(&cru->clksel_con[con_id]);
419a60961a3SKever Yang 	div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT;
420a60961a3SKever Yang 
421a60961a3SKever Yang 	if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT
422a60961a3SKever Yang 	    == EMMC_SEL_24M)
423a60961a3SKever Yang 		return DIV_TO_RATE(OSC_HZ, div) / 2;
424a60961a3SKever Yang 	else
425cefa5186SFinley Xiao 		return DIV_TO_RATE(priv->gpll_hz, div) / 2;
426a60961a3SKever Yang 
427a60961a3SKever Yang }
428a60961a3SKever Yang 
429cefa5186SFinley Xiao static ulong px30_mmc_set_clk(struct px30_clk_priv *priv,
430a60961a3SKever Yang 			      ulong clk_id, ulong set_rate)
431a60961a3SKever Yang {
432cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
433a60961a3SKever Yang 	int src_clk_div;
434a60961a3SKever Yang 	u32 con_id;
435a60961a3SKever Yang 
436a60961a3SKever Yang 	switch (clk_id) {
437a60961a3SKever Yang 	case HCLK_SDMMC:
438a60961a3SKever Yang 	case SCLK_SDMMC:
439a60961a3SKever Yang 		con_id = 16;
440a60961a3SKever Yang 		break;
441a60961a3SKever Yang 	case HCLK_EMMC:
442a60961a3SKever Yang 	case SCLK_EMMC:
443a60961a3SKever Yang 		con_id = 20;
444a60961a3SKever Yang 		break;
445a60961a3SKever Yang 	default:
446a60961a3SKever Yang 		return -EINVAL;
447a60961a3SKever Yang 	}
448cefa5186SFinley Xiao 
449a60961a3SKever Yang 	/* Select clk_sdmmc/emmc source from GPLL by default */
450a60961a3SKever Yang 	/* mmc clock defaulg div 2 internal, need provide double in cru */
451cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz / 2, set_rate);
452a60961a3SKever Yang 
453a60961a3SKever Yang 	if (src_clk_div > 127) {
454a60961a3SKever Yang 		/* use 24MHz source for 400KHz clock */
455a60961a3SKever Yang 		src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate);
456a60961a3SKever Yang 		rk_clrsetreg(&cru->clksel_con[con_id],
457a60961a3SKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
458a60961a3SKever Yang 			     EMMC_SEL_24M << EMMC_PLL_SHIFT |
459a60961a3SKever Yang 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
460a60961a3SKever Yang 	} else {
461a60961a3SKever Yang 		rk_clrsetreg(&cru->clksel_con[con_id],
462a60961a3SKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
463a60961a3SKever Yang 			     EMMC_SEL_GPLL << EMMC_PLL_SHIFT |
464a60961a3SKever Yang 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
465a60961a3SKever Yang 	}
466a60961a3SKever Yang 	rk_clrsetreg(&cru->clksel_con[con_id +1], EMMC_CLK_SEL_MASK,
467a60961a3SKever Yang 		     EMMC_CLK_SEL_EMMC);
468a60961a3SKever Yang 
469cefa5186SFinley Xiao 	return px30_mmc_get_clk(priv, clk_id);
470a60961a3SKever Yang }
471a60961a3SKever Yang 
472cefa5186SFinley Xiao static ulong px30_pwm_get_clk(struct px30_clk_priv *priv, ulong clk_id)
473a60961a3SKever Yang {
474cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
475a60961a3SKever Yang 	u32 div, con;
476a60961a3SKever Yang 
477f67f522bSFinley Xiao 	switch (clk_id) {
478f67f522bSFinley Xiao 	case SCLK_PWM0:
479f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[52]);
480f67f522bSFinley Xiao 		div = con >> CLK_PWM0_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK;
481f67f522bSFinley Xiao 		break;
482f67f522bSFinley Xiao 	case SCLK_PWM1:
483f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[52]);
484f67f522bSFinley Xiao 		div = con >> CLK_PWM1_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK;
485f67f522bSFinley Xiao 		break;
486f67f522bSFinley Xiao 	default:
487f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
488f67f522bSFinley Xiao 		return -EINVAL;
489f67f522bSFinley Xiao 	}
490f67f522bSFinley Xiao 
491cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
492a60961a3SKever Yang }
493a60961a3SKever Yang 
494cefa5186SFinley Xiao static ulong px30_pwm_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
495a60961a3SKever Yang {
496cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
497f67f522bSFinley Xiao 	int src_clk_div;
498a60961a3SKever Yang 
499cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
500cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 127);
501f67f522bSFinley Xiao 
502f67f522bSFinley Xiao 	switch (clk_id) {
503f67f522bSFinley Xiao 	case SCLK_PWM0:
504f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[52],
505f67f522bSFinley Xiao 			     CLK_PWM_DIV_CON_MASK << CLK_PWM0_DIV_CON_SHIFT |
506f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_MASK << CLK_PWM0_PLL_SEL_SHIFT,
507f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_PWM0_DIV_CON_SHIFT |
508f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_GPLL << CLK_PWM0_PLL_SEL_SHIFT);
509f67f522bSFinley Xiao 		break;
510f67f522bSFinley Xiao 	case SCLK_PWM1:
511f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[52],
512f67f522bSFinley Xiao 			     CLK_PWM_DIV_CON_MASK << CLK_PWM1_DIV_CON_SHIFT |
513f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_MASK << CLK_PWM1_PLL_SEL_SHIFT,
514f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_PWM1_DIV_CON_SHIFT |
515f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_GPLL << CLK_PWM1_PLL_SEL_SHIFT);
516f67f522bSFinley Xiao 		break;
517f67f522bSFinley Xiao 	default:
518f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
519f67f522bSFinley Xiao 		return -EINVAL;
520f67f522bSFinley Xiao 	}
521f67f522bSFinley Xiao 
522cefa5186SFinley Xiao 	return px30_pwm_get_clk(priv, clk_id);
523a60961a3SKever Yang }
524a60961a3SKever Yang 
525cefa5186SFinley Xiao static ulong px30_saradc_get_clk(struct px30_clk_priv *priv)
526a60961a3SKever Yang {
527cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
528f67f522bSFinley Xiao 	u32 div, con;
529f67f522bSFinley Xiao 
530f67f522bSFinley Xiao 	con = readl(&cru->clksel_con[55]);
531f9157291SFinley Xiao 	div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK;
532a60961a3SKever Yang 
533a60961a3SKever Yang 	return DIV_TO_RATE(OSC_HZ, div);
534a60961a3SKever Yang }
535a60961a3SKever Yang 
536cefa5186SFinley Xiao static ulong px30_saradc_set_clk(struct px30_clk_priv *priv, uint hz)
537a60961a3SKever Yang {
538cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
539a60961a3SKever Yang 	int src_clk_div;
540a60961a3SKever Yang 
541cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz);
542cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 2047);
543a60961a3SKever Yang 
544f67f522bSFinley Xiao 	rk_clrsetreg(&cru->clksel_con[55],
545f67f522bSFinley Xiao 		     CLK_SARADC_DIV_CON_MASK,
546fce7cb7bSFinley Xiao 		     (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT);
547f67f522bSFinley Xiao 
548cefa5186SFinley Xiao 	return px30_saradc_get_clk(priv);
549f67f522bSFinley Xiao }
550f67f522bSFinley Xiao 
551cefa5186SFinley Xiao static ulong px30_spi_get_clk(struct px30_clk_priv *priv, ulong clk_id)
552f67f522bSFinley Xiao {
553cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
554f67f522bSFinley Xiao 	u32 div, con;
555f67f522bSFinley Xiao 
556f67f522bSFinley Xiao 	switch (clk_id) {
557fce7cb7bSFinley Xiao 	case SCLK_SPI0:
558f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[53]);
559f67f522bSFinley Xiao 		div = con >> CLK_SPI0_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK;
560f67f522bSFinley Xiao 		break;
561fce7cb7bSFinley Xiao 	case SCLK_SPI1:
562f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[53]);
563f67f522bSFinley Xiao 		div = con >> CLK_SPI1_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK;
564f67f522bSFinley Xiao 		break;
565f67f522bSFinley Xiao 	default:
566f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
567f67f522bSFinley Xiao 		return -EINVAL;
568f67f522bSFinley Xiao 	}
569f67f522bSFinley Xiao 
570cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
571f67f522bSFinley Xiao }
572f67f522bSFinley Xiao 
573cefa5186SFinley Xiao static ulong px30_spi_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
574f67f522bSFinley Xiao {
575cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
576f67f522bSFinley Xiao 	int src_clk_div;
577f67f522bSFinley Xiao 
578cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
579cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 127);
580f67f522bSFinley Xiao 
581f67f522bSFinley Xiao 	switch (clk_id) {
582f67f522bSFinley Xiao 	case SCLK_SPI0:
583f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[53],
584f67f522bSFinley Xiao 			     CLK_SPI_DIV_CON_MASK << CLK_SPI0_DIV_CON_SHIFT |
585f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_MASK << CLK_SPI0_PLL_SEL_SHIFT,
586f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_SPI0_DIV_CON_SHIFT |
587f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_GPLL << CLK_SPI0_PLL_SEL_SHIFT);
588f67f522bSFinley Xiao 		break;
589f67f522bSFinley Xiao 	case SCLK_SPI1:
590f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[53],
591f67f522bSFinley Xiao 			     CLK_SPI_DIV_CON_MASK << CLK_SPI1_DIV_CON_SHIFT |
592f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_MASK << CLK_SPI1_PLL_SEL_SHIFT,
593f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_SPI1_DIV_CON_SHIFT |
594f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_GPLL << CLK_SPI1_PLL_SEL_SHIFT);
595f67f522bSFinley Xiao 		break;
596f67f522bSFinley Xiao 	default:
597f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
598f67f522bSFinley Xiao 		return -EINVAL;
599f67f522bSFinley Xiao 	}
600f67f522bSFinley Xiao 
601cefa5186SFinley Xiao 	return px30_spi_get_clk(priv, clk_id);
602a60961a3SKever Yang }
603a60961a3SKever Yang 
604cefa5186SFinley Xiao static ulong px30_vop_get_clk(struct px30_clk_priv *priv, ulong clk_id)
60530f1f38dSFinley Xiao {
606cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
60730f1f38dSFinley Xiao 	u32 div, con, parent;
60830f1f38dSFinley Xiao 
60930f1f38dSFinley Xiao 	switch (clk_id) {
61030f1f38dSFinley Xiao 	case ACLK_VOPB:
61130f1f38dSFinley Xiao 		con = readl(&cru->clksel_con[3]);
61230f1f38dSFinley Xiao 		div = con & ACLK_VO_DIV_MASK;
613cefa5186SFinley Xiao 		parent = priv->gpll_hz;
61430f1f38dSFinley Xiao 		break;
61530f1f38dSFinley Xiao 	case DCLK_VOPB:
61630f1f38dSFinley Xiao 		con = readl(&cru->clksel_con[5]);
61730f1f38dSFinley Xiao 		div = con & DCLK_VOPB_DIV_MASK;
618cefa5186SFinley Xiao 		parent = rkclk_pll_get_rate(&cru->pll[CPLL], &cru->mode, CPLL);
61930f1f38dSFinley Xiao 		break;
62030f1f38dSFinley Xiao 	default:
62130f1f38dSFinley Xiao 		return -ENOENT;
62230f1f38dSFinley Xiao 	}
62330f1f38dSFinley Xiao 
62430f1f38dSFinley Xiao 	return DIV_TO_RATE(parent, div);
62530f1f38dSFinley Xiao }
62630f1f38dSFinley Xiao 
627cefa5186SFinley Xiao static ulong px30_vop_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
62830f1f38dSFinley Xiao {
629cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
63030f1f38dSFinley Xiao 	int src_clk_div;
63130f1f38dSFinley Xiao 
63230f1f38dSFinley Xiao 	switch (clk_id) {
63330f1f38dSFinley Xiao 	case ACLK_VOPB:
634*f909d4a8SFinley Xiao 		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
635*f909d4a8SFinley Xiao 		assert(src_clk_div - 1 <= 31);
63630f1f38dSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[3],
63730f1f38dSFinley Xiao 			     ACLK_VO_PLL_MASK | ACLK_VO_DIV_MASK,
63830f1f38dSFinley Xiao 			     ACLK_VO_SEL_GPLL << ACLK_VO_PLL_SHIFT |
63930f1f38dSFinley Xiao 			     (src_clk_div - 1) << ACLK_VO_DIV_SHIFT);
64030f1f38dSFinley Xiao 		break;
64130f1f38dSFinley Xiao 	case DCLK_VOPB:
642*f909d4a8SFinley Xiao 		if (hz < PX30_VOP_PLL_LIMIT)
643*f909d4a8SFinley Xiao 			src_clk_div = DIV_ROUND_UP(PX30_VOP_PLL_LIMIT, hz);
644*f909d4a8SFinley Xiao 		else
645*f909d4a8SFinley Xiao 			src_clk_div = 1;
646*f909d4a8SFinley Xiao 		assert(src_clk_div - 1 <= 255);
647*f909d4a8SFinley Xiao 		rkclk_set_pll(&cru->pll[CPLL], &cru->mode, CPLL, hz * src_clk_div);
64830f1f38dSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[5],
64930f1f38dSFinley Xiao 			     DCLK_VOPB_SEL_MASK | DCLK_VOPB_PLL_SEL_MASK |
65030f1f38dSFinley Xiao 			     DCLK_VOPB_DIV_MASK,
65130f1f38dSFinley Xiao 			     DCLK_VOPB_SEL_DIVOUT << DCLK_VOPB_SEL_SHIFT |
65230f1f38dSFinley Xiao 			     DCLK_VOPB_PLL_SEL_CPLL << DCLK_VOPB_PLL_SEL_SHIFT |
653*f909d4a8SFinley Xiao 			     (src_clk_div - 1) << DCLK_VOPB_DIV_SHIFT);
65430f1f38dSFinley Xiao 		break;
65530f1f38dSFinley Xiao 	default:
65630f1f38dSFinley Xiao 		printf("do not support this vop freq\n");
65730f1f38dSFinley Xiao 		return -EINVAL;
65830f1f38dSFinley Xiao 	}
65930f1f38dSFinley Xiao 
660cefa5186SFinley Xiao 	return px30_vop_get_clk(priv, clk_id);
66130f1f38dSFinley Xiao }
66230f1f38dSFinley Xiao 
663cefa5186SFinley Xiao static ulong px30_bus_get_clk(struct px30_clk_priv *priv, ulong clk_id)
664df8f8a42SFinley Xiao {
665cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
666df8f8a42SFinley Xiao 	u32 div, con, parent;
667df8f8a42SFinley Xiao 
668df8f8a42SFinley Xiao 	switch (clk_id) {
669df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
670df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[23]);
671df8f8a42SFinley Xiao 		div = (con & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT;
672cefa5186SFinley Xiao 		parent = priv->gpll_hz;
673df8f8a42SFinley Xiao 		break;
674df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
675df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[24]);
676df8f8a42SFinley Xiao 		div = (con & BUS_HCLK_DIV_MASK) >> BUS_HCLK_DIV_SHIFT;
677cefa5186SFinley Xiao 		parent = priv->gpll_hz;
678df8f8a42SFinley Xiao 		break;
679df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
680cefa5186SFinley Xiao 		parent = px30_bus_get_clk(priv, ACLK_BUS_PRE);
681df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[24]);
682df8f8a42SFinley Xiao 		div = (con & BUS_PCLK_DIV_MASK) >> BUS_PCLK_DIV_SHIFT;
683df8f8a42SFinley Xiao 		break;
684df8f8a42SFinley Xiao 	default:
685df8f8a42SFinley Xiao 		return -ENOENT;
686df8f8a42SFinley Xiao 	}
687df8f8a42SFinley Xiao 
688df8f8a42SFinley Xiao 	return DIV_TO_RATE(parent, div);
689df8f8a42SFinley Xiao }
690df8f8a42SFinley Xiao 
691cefa5186SFinley Xiao static ulong px30_bus_set_clk(struct px30_clk_priv *priv, ulong clk_id,
692cefa5186SFinley Xiao 			      ulong hz)
693df8f8a42SFinley Xiao {
694cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
695df8f8a42SFinley Xiao 	int src_clk_div;
696df8f8a42SFinley Xiao 
697df8f8a42SFinley Xiao 	/*
698df8f8a42SFinley Xiao 	 * select gpll as pd_bus bus clock source and
699df8f8a42SFinley Xiao 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
700df8f8a42SFinley Xiao 	 */
701df8f8a42SFinley Xiao 	switch (clk_id) {
702df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
703cefa5186SFinley Xiao 		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
704cb981eeaSFinley Xiao 		assert(src_clk_div - 1 <= 31);
705df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[23],
706df8f8a42SFinley Xiao 			     BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK,
707df8f8a42SFinley Xiao 			     BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT |
708df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_ACLK_DIV_SHIFT);
709df8f8a42SFinley Xiao 		break;
710df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
711cefa5186SFinley Xiao 		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
712cb981eeaSFinley Xiao 		assert(src_clk_div - 1 <= 31);
713df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[24],
714df8f8a42SFinley Xiao 			     BUS_PLL_SEL_MASK | BUS_HCLK_DIV_MASK,
715df8f8a42SFinley Xiao 			     BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT |
716df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_HCLK_DIV_SHIFT);
717df8f8a42SFinley Xiao 		break;
718df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
719cefa5186SFinley Xiao 		src_clk_div =
720cefa5186SFinley Xiao 			DIV_ROUND_UP(px30_bus_get_clk(priv, ACLK_BUS_PRE), hz);
721cb981eeaSFinley Xiao 		assert(src_clk_div - 1 <= 3);
722df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[24],
723df8f8a42SFinley Xiao 			     BUS_PCLK_DIV_MASK,
724df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_PCLK_DIV_SHIFT);
725df8f8a42SFinley Xiao 		break;
726df8f8a42SFinley Xiao 	default:
727df8f8a42SFinley Xiao 		printf("do not support this bus freq\n");
728df8f8a42SFinley Xiao 		return -EINVAL;
729df8f8a42SFinley Xiao 	}
730df8f8a42SFinley Xiao 
731cefa5186SFinley Xiao 	return px30_bus_get_clk(priv, clk_id);
732df8f8a42SFinley Xiao }
733df8f8a42SFinley Xiao 
734cefa5186SFinley Xiao static ulong px30_peri_get_clk(struct px30_clk_priv *priv, ulong clk_id)
735df8f8a42SFinley Xiao {
736cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
737df8f8a42SFinley Xiao 	u32 div, con, parent;
738df8f8a42SFinley Xiao 
739df8f8a42SFinley Xiao 	switch (clk_id) {
740df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
741df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[14]);
742df8f8a42SFinley Xiao 		div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT;
743cefa5186SFinley Xiao 		parent = priv->gpll_hz;
744df8f8a42SFinley Xiao 		break;
745df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
746df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[14]);
747df8f8a42SFinley Xiao 		div = (con & PERI_HCLK_DIV_MASK) >> PERI_HCLK_DIV_SHIFT;
748cefa5186SFinley Xiao 		parent = priv->gpll_hz;
749df8f8a42SFinley Xiao 		break;
750df8f8a42SFinley Xiao 	default:
751df8f8a42SFinley Xiao 		return -ENOENT;
752df8f8a42SFinley Xiao 	}
753df8f8a42SFinley Xiao 
754df8f8a42SFinley Xiao 	return DIV_TO_RATE(parent, div);
755df8f8a42SFinley Xiao }
756df8f8a42SFinley Xiao 
757cefa5186SFinley Xiao static ulong px30_peri_set_clk(struct px30_clk_priv *priv, ulong clk_id,
758cefa5186SFinley Xiao 			       ulong hz)
759df8f8a42SFinley Xiao {
760cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
761df8f8a42SFinley Xiao 	int src_clk_div;
762df8f8a42SFinley Xiao 
763cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
764cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 31);
765df8f8a42SFinley Xiao 
766df8f8a42SFinley Xiao 	/*
767df8f8a42SFinley Xiao 	 * select gpll as pd_peri bus clock source and
768df8f8a42SFinley Xiao 	 * set up dependent divisors for HCLK and ACLK clocks.
769df8f8a42SFinley Xiao 	 */
770df8f8a42SFinley Xiao 	switch (clk_id) {
771df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
772df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[14],
773df8f8a42SFinley Xiao 			     PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK,
774df8f8a42SFinley Xiao 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
775df8f8a42SFinley Xiao 			     (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT);
776df8f8a42SFinley Xiao 		break;
777df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
778df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[14],
779df8f8a42SFinley Xiao 			     PERI_PLL_SEL_MASK | PERI_HCLK_DIV_MASK,
780df8f8a42SFinley Xiao 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
781df8f8a42SFinley Xiao 			     (src_clk_div - 1) << PERI_HCLK_DIV_SHIFT);
782df8f8a42SFinley Xiao 		break;
783df8f8a42SFinley Xiao 	default:
784df8f8a42SFinley Xiao 		printf("do not support this peri freq\n");
785df8f8a42SFinley Xiao 		return -EINVAL;
786df8f8a42SFinley Xiao 	}
787df8f8a42SFinley Xiao 
788cefa5186SFinley Xiao 	return px30_peri_get_clk(priv, clk_id);
789cefa5186SFinley Xiao }
790cefa5186SFinley Xiao 
791cefa5186SFinley Xiao static int px30_clk_get_gpll_rate(ulong *rate)
792cefa5186SFinley Xiao {
793cefa5186SFinley Xiao 	struct udevice *pmucru_dev;
794cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv;
795cefa5186SFinley Xiao 	int ret;
796cefa5186SFinley Xiao 
797cefa5186SFinley Xiao 	ret = uclass_get_device_by_driver(UCLASS_CLK,
798cefa5186SFinley Xiao 					  DM_GET_DRIVER(rockchip_px30_pmucru),
799cefa5186SFinley Xiao 					  &pmucru_dev);
800cefa5186SFinley Xiao 	if (ret) {
801cefa5186SFinley Xiao 		printf("%s: could not find pmucru device\n", __func__);
802cefa5186SFinley Xiao 		return ret;
803cefa5186SFinley Xiao 	}
804cefa5186SFinley Xiao 	priv = dev_get_priv(pmucru_dev);
805c4d4e4dcSFinley Xiao 	*rate =  priv->gpll_hz;
806cefa5186SFinley Xiao 
807cefa5186SFinley Xiao 	return 0;
808df8f8a42SFinley Xiao }
809df8f8a42SFinley Xiao 
8108b1aed51SFinley Xiao static ulong px30_clk_get_pll_rate(struct px30_clk_priv *priv,
8118b1aed51SFinley Xiao 				   enum px30_pll_id pll_id)
8128b1aed51SFinley Xiao {
8138b1aed51SFinley Xiao 	struct px30_cru *cru = priv->cru;
8148b1aed51SFinley Xiao 
8158b1aed51SFinley Xiao 	return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id);
8168b1aed51SFinley Xiao }
8178b1aed51SFinley Xiao 
818a221d6e6SFinley Xiao static ulong px30_clk_set_pll_rate(struct px30_clk_priv *priv,
819a221d6e6SFinley Xiao 				   enum px30_pll_id pll_id, ulong hz)
820a221d6e6SFinley Xiao {
821a221d6e6SFinley Xiao 	struct px30_cru *cru = priv->cru;
822a221d6e6SFinley Xiao 
823a221d6e6SFinley Xiao 	if (rkclk_set_pll(&cru->pll[pll_id], &cru->mode, pll_id, hz))
824a221d6e6SFinley Xiao 		return -EINVAL;
825a221d6e6SFinley Xiao 	return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id);
826a221d6e6SFinley Xiao }
827a221d6e6SFinley Xiao 
82837428b92SFinley Xiao static ulong px30_armclk_set_clk(struct px30_clk_priv *priv, ulong hz)
82937428b92SFinley Xiao {
83037428b92SFinley Xiao 	struct px30_cru *cru = priv->cru;
83137428b92SFinley Xiao 	const struct cpu_rate_table *rate;
83237428b92SFinley Xiao 	ulong old_rate;
83337428b92SFinley Xiao 
83437428b92SFinley Xiao 	rate = get_cpu_settings(hz);
83537428b92SFinley Xiao 	if (!rate) {
83637428b92SFinley Xiao 		printf("%s unsupport rate\n", __func__);
83737428b92SFinley Xiao 		return -EINVAL;
83837428b92SFinley Xiao 	}
83937428b92SFinley Xiao 
84037428b92SFinley Xiao 	/*
84137428b92SFinley Xiao 	 * select apll as cpu/core clock pll source and
84237428b92SFinley Xiao 	 * set up dependent divisors for PERI and ACLK clocks.
84337428b92SFinley Xiao 	 * core hz : apll = 1:1
84437428b92SFinley Xiao 	 */
84537428b92SFinley Xiao 	old_rate = px30_clk_get_pll_rate(priv, APLL);
84637428b92SFinley Xiao 	if (old_rate > hz) {
84737428b92SFinley Xiao 		if (rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, hz))
84837428b92SFinley Xiao 			return -EINVAL;
84937428b92SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[0],
85037428b92SFinley Xiao 			     CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK |
85137428b92SFinley Xiao 			     CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK,
85237428b92SFinley Xiao 			     rate->aclk_div << CORE_ACLK_DIV_SHIFT |
85337428b92SFinley Xiao 			     rate->pclk_div << CORE_DBG_DIV_SHIFT |
85437428b92SFinley Xiao 			     CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
85537428b92SFinley Xiao 			     0 << CORE_DIV_CON_SHIFT);
85637428b92SFinley Xiao 	} else if (old_rate < hz) {
85737428b92SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[0],
85837428b92SFinley Xiao 			     CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK |
85937428b92SFinley Xiao 			     CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK,
86037428b92SFinley Xiao 			     rate->aclk_div << CORE_ACLK_DIV_SHIFT |
86137428b92SFinley Xiao 			     rate->pclk_div << CORE_DBG_DIV_SHIFT |
86237428b92SFinley Xiao 			     CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
86337428b92SFinley Xiao 			     0 << CORE_DIV_CON_SHIFT);
86437428b92SFinley Xiao 		if (rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, hz))
86537428b92SFinley Xiao 			return -EINVAL;
86637428b92SFinley Xiao 	}
86737428b92SFinley Xiao 
86837428b92SFinley Xiao 	return px30_clk_get_pll_rate(priv, APLL);
86937428b92SFinley Xiao }
87037428b92SFinley Xiao 
871a60961a3SKever Yang static ulong px30_clk_get_rate(struct clk *clk)
872a60961a3SKever Yang {
873a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
874a60961a3SKever Yang 	ulong rate = 0;
875a60961a3SKever Yang 
876c4d4e4dcSFinley Xiao 	if (!priv->gpll_hz && clk->id > ARMCLK) {
877c4d4e4dcSFinley Xiao 		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
878c4d4e4dcSFinley Xiao 		return -ENOENT;
879cefa5186SFinley Xiao 	}
880cefa5186SFinley Xiao 
881cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
882a60961a3SKever Yang 	switch (clk->id) {
8838b1aed51SFinley Xiao 	case PLL_APLL:
8848b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, APLL);
8858b1aed51SFinley Xiao 		break;
8868b1aed51SFinley Xiao 	case PLL_DPLL:
8878b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, DPLL);
8888b1aed51SFinley Xiao 		break;
8898b1aed51SFinley Xiao 	case PLL_CPLL:
8908b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, CPLL);
8918b1aed51SFinley Xiao 		break;
8928b1aed51SFinley Xiao 	case PLL_NPLL:
8938b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, NPLL);
8948b1aed51SFinley Xiao 		break;
89537428b92SFinley Xiao 	case ARMCLK:
89637428b92SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, APLL);
89737428b92SFinley Xiao 		break;
898a60961a3SKever Yang 	case HCLK_SDMMC:
899a60961a3SKever Yang 	case HCLK_EMMC:
900a60961a3SKever Yang 	case SCLK_SDMMC:
901a60961a3SKever Yang 	case SCLK_EMMC:
90296f1b3d9SKever Yang 	case SCLK_EMMC_SAMPLE:
903cefa5186SFinley Xiao 		rate = px30_mmc_get_clk(priv, clk->id);
904a60961a3SKever Yang 		break;
905a60961a3SKever Yang 	case SCLK_I2C0:
906a60961a3SKever Yang 	case SCLK_I2C1:
907a60961a3SKever Yang 	case SCLK_I2C2:
908a60961a3SKever Yang 	case SCLK_I2C3:
909cefa5186SFinley Xiao 		rate = px30_i2c_get_clk(priv, clk->id);
910a60961a3SKever Yang 		break;
911a60961a3SKever Yang 	case SCLK_PWM0:
912fce7cb7bSFinley Xiao 	case SCLK_PWM1:
913cefa5186SFinley Xiao 		rate = px30_pwm_get_clk(priv, clk->id);
914a60961a3SKever Yang 		break;
915a60961a3SKever Yang 	case SCLK_SARADC:
916cefa5186SFinley Xiao 		rate = px30_saradc_get_clk(priv);
917a60961a3SKever Yang 		break;
918f67f522bSFinley Xiao 	case SCLK_SPI0:
919f67f522bSFinley Xiao 	case SCLK_SPI1:
920cefa5186SFinley Xiao 		rate = px30_spi_get_clk(priv, clk->id);
921f67f522bSFinley Xiao 		break;
92230f1f38dSFinley Xiao 	case ACLK_VOPB:
92330f1f38dSFinley Xiao 	case DCLK_VOPB:
924cefa5186SFinley Xiao 		rate = px30_vop_get_clk(priv, clk->id);
92530f1f38dSFinley Xiao 		break;
926df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
927df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
928df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
929cefa5186SFinley Xiao 		rate = px30_bus_get_clk(priv, clk->id);
930df8f8a42SFinley Xiao 		break;
931df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
932df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
933cefa5186SFinley Xiao 		rate = px30_peri_get_clk(priv, clk->id);
934df8f8a42SFinley Xiao 		break;
935a60961a3SKever Yang 	default:
936a60961a3SKever Yang 		return -ENOENT;
937a60961a3SKever Yang 	}
938a60961a3SKever Yang 
939a60961a3SKever Yang 	return rate;
940a60961a3SKever Yang }
941a60961a3SKever Yang 
942a60961a3SKever Yang static ulong px30_clk_set_rate(struct clk *clk, ulong rate)
943a60961a3SKever Yang {
944a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
945a60961a3SKever Yang 	ulong ret = 0;
946a60961a3SKever Yang 
947c4d4e4dcSFinley Xiao 	if (!priv->gpll_hz && clk->id > ARMCLK) {
948c4d4e4dcSFinley Xiao 		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
949c4d4e4dcSFinley Xiao 		return -ENOENT;
950cefa5186SFinley Xiao 	}
951cefa5186SFinley Xiao 
952c4867301SKever Yang 	debug("%s %ld %ld\n", __func__, clk->id, rate);
953a60961a3SKever Yang 	switch (clk->id) {
954a221d6e6SFinley Xiao 	case PLL_NPLL:
955a221d6e6SFinley Xiao 		ret = px30_clk_set_pll_rate(priv, NPLL, rate);
956a221d6e6SFinley Xiao 		break;
95737428b92SFinley Xiao 	case ARMCLK:
95837428b92SFinley Xiao 		if (priv->armclk_hz)
95937428b92SFinley Xiao 			px30_armclk_set_clk(priv, rate);
96037428b92SFinley Xiao 		priv->armclk_hz = rate;
96137428b92SFinley Xiao 		break;
962a60961a3SKever Yang 	case HCLK_SDMMC:
963a60961a3SKever Yang 	case HCLK_EMMC:
964a60961a3SKever Yang 	case SCLK_SDMMC:
965a60961a3SKever Yang 	case SCLK_EMMC:
966cefa5186SFinley Xiao 		ret = px30_mmc_set_clk(priv, clk->id, rate);
967a60961a3SKever Yang 		break;
968a60961a3SKever Yang 	case SCLK_I2C0:
969a60961a3SKever Yang 	case SCLK_I2C1:
970a60961a3SKever Yang 	case SCLK_I2C2:
971a60961a3SKever Yang 	case SCLK_I2C3:
972cefa5186SFinley Xiao 		ret = px30_i2c_set_clk(priv, clk->id, rate);
973a60961a3SKever Yang 		break;
974a60961a3SKever Yang 	case SCLK_PWM0:
975f67f522bSFinley Xiao 	case SCLK_PWM1:
976cefa5186SFinley Xiao 		ret = px30_pwm_set_clk(priv, clk->id, rate);
977a60961a3SKever Yang 		break;
978a60961a3SKever Yang 	case SCLK_SARADC:
979cefa5186SFinley Xiao 		ret = px30_saradc_set_clk(priv, rate);
980a60961a3SKever Yang 		break;
981f67f522bSFinley Xiao 	case SCLK_SPI0:
982f67f522bSFinley Xiao 	case SCLK_SPI1:
983cefa5186SFinley Xiao 		ret = px30_spi_set_clk(priv, clk->id, rate);
984f67f522bSFinley Xiao 		break;
98530f1f38dSFinley Xiao 	case ACLK_VOPB:
98630f1f38dSFinley Xiao 	case DCLK_VOPB:
987cefa5186SFinley Xiao 		ret = px30_vop_set_clk(priv, clk->id, rate);
98830f1f38dSFinley Xiao 		break;
989df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
990df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
991df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
992cefa5186SFinley Xiao 		ret = px30_bus_set_clk(priv, clk->id, rate);
993df8f8a42SFinley Xiao 		break;
994df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
995df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
996cefa5186SFinley Xiao 		ret = px30_peri_set_clk(priv, clk->id, rate);
997df8f8a42SFinley Xiao 		break;
998a60961a3SKever Yang 	default:
999a60961a3SKever Yang 		return -ENOENT;
1000a60961a3SKever Yang 	}
1001a60961a3SKever Yang 
1002a60961a3SKever Yang 	return ret;
1003a60961a3SKever Yang }
1004a60961a3SKever Yang 
1005a60961a3SKever Yang #define ROCKCHIP_MMC_DELAY_SEL		BIT(10)
1006a60961a3SKever Yang #define ROCKCHIP_MMC_DEGREE_MASK	0x3
1007a60961a3SKever Yang #define ROCKCHIP_MMC_DELAYNUM_OFFSET	2
1008a60961a3SKever Yang #define ROCKCHIP_MMC_DELAYNUM_MASK	(0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
1009a60961a3SKever Yang 
1010a60961a3SKever Yang #define PSECS_PER_SEC 1000000000000LL
1011a60961a3SKever Yang /*
1012a60961a3SKever Yang  * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
1013a60961a3SKever Yang  * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
1014a60961a3SKever Yang  */
1015a60961a3SKever Yang #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
1016a60961a3SKever Yang 
1017a60961a3SKever Yang int rockchip_mmc_get_phase(struct clk *clk)
1018a60961a3SKever Yang {
1019a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
1020a60961a3SKever Yang 	struct px30_cru *cru = priv->cru;
1021a60961a3SKever Yang 	u32 raw_value, delay_num;
1022a60961a3SKever Yang 	u16 degrees = 0;
1023a60961a3SKever Yang 	ulong rate;
1024a60961a3SKever Yang 
1025a60961a3SKever Yang 	rate = px30_clk_get_rate(clk);
1026a60961a3SKever Yang 
1027a60961a3SKever Yang 	if (rate < 0)
1028a60961a3SKever Yang 		return rate;
1029a60961a3SKever Yang 
1030a60961a3SKever Yang 	if (clk->id == SCLK_EMMC_SAMPLE)
1031a60961a3SKever Yang 		raw_value = readl(&cru->emmc_con[1]);
1032a60961a3SKever Yang 	else
1033a60961a3SKever Yang 		raw_value = readl(&cru->sdmmc_con[1]);
1034a60961a3SKever Yang 
103596f1b3d9SKever Yang 	raw_value >>= 1;
1036a60961a3SKever Yang 	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
1037a60961a3SKever Yang 
1038a60961a3SKever Yang 	if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
1039a60961a3SKever Yang 		/* degrees/delaynum * 10000 */
1040a60961a3SKever Yang 		unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
1041a60961a3SKever Yang 					36 * (rate / 1000000);
1042a60961a3SKever Yang 
1043a60961a3SKever Yang 		delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
1044a60961a3SKever Yang 		delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
1045a60961a3SKever Yang 		degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
1046a60961a3SKever Yang 	}
1047a60961a3SKever Yang 
1048a60961a3SKever Yang 	return degrees % 360;
1049a60961a3SKever Yang }
1050a60961a3SKever Yang 
1051a60961a3SKever Yang int rockchip_mmc_set_phase(struct clk *clk, u32 degrees)
1052a60961a3SKever Yang {
1053a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
1054a60961a3SKever Yang 	struct px30_cru *cru = priv->cru;
1055a60961a3SKever Yang 	u8 nineties, remainder, delay_num;
1056a60961a3SKever Yang 	u32 raw_value, delay;
1057a60961a3SKever Yang 	ulong rate;
1058a60961a3SKever Yang 
1059a60961a3SKever Yang 	rate = px30_clk_get_rate(clk);
1060a60961a3SKever Yang 
1061a60961a3SKever Yang 	if (rate < 0)
1062a60961a3SKever Yang 		return rate;
1063a60961a3SKever Yang 
1064a60961a3SKever Yang 	nineties = degrees / 90;
1065a60961a3SKever Yang 	remainder = (degrees % 90);
1066a60961a3SKever Yang 
1067a60961a3SKever Yang 	/*
1068a60961a3SKever Yang 	 * Convert to delay; do a little extra work to make sure we
1069a60961a3SKever Yang 	 * don't overflow 32-bit / 64-bit numbers.
1070a60961a3SKever Yang 	 */
1071a60961a3SKever Yang 	delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
1072a60961a3SKever Yang 	delay *= remainder;
1073a60961a3SKever Yang 	delay = DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 *
1074a60961a3SKever Yang 				(ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
1075a60961a3SKever Yang 
1076a60961a3SKever Yang 	delay_num = (u8)min_t(u32, delay, 255);
1077a60961a3SKever Yang 
1078a60961a3SKever Yang 	raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
1079a60961a3SKever Yang 	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
1080a60961a3SKever Yang 	raw_value |= nineties;
1081a60961a3SKever Yang 
108296f1b3d9SKever Yang 	raw_value <<= 1;
1083a60961a3SKever Yang 	if (clk->id == SCLK_EMMC_SAMPLE)
1084a60961a3SKever Yang 		writel(raw_value | 0xffff0000, &cru->emmc_con[1]);
1085a60961a3SKever Yang 	else
1086a60961a3SKever Yang 		writel(raw_value | 0xffff0000, &cru->sdmmc_con[1]);
1087a60961a3SKever Yang 
1088a60961a3SKever Yang 	debug("mmc set_phase(%d) delay_nums=%u reg=%#x actual_degrees=%d\n",
1089a60961a3SKever Yang 	      degrees, delay_num, raw_value, rockchip_mmc_get_phase(clk));
1090a60961a3SKever Yang 
1091a60961a3SKever Yang 	return 0;
1092a60961a3SKever Yang }
1093a60961a3SKever Yang 
1094a60961a3SKever Yang static int px30_clk_get_phase(struct clk *clk)
1095a60961a3SKever Yang {
1096a60961a3SKever Yang 	int ret;
1097eb46e717SFinley Xiao 
1098cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1099a60961a3SKever Yang 	switch (clk->id) {
1100a60961a3SKever Yang 	case SCLK_EMMC_SAMPLE:
1101a60961a3SKever Yang 	case SCLK_SDMMC_SAMPLE:
1102a60961a3SKever Yang 		ret = rockchip_mmc_get_phase(clk);
1103a60961a3SKever Yang 		break;
1104a60961a3SKever Yang 	default:
1105a60961a3SKever Yang 		return -ENOENT;
1106a60961a3SKever Yang 	}
1107a60961a3SKever Yang 
1108a60961a3SKever Yang 	return ret;
1109a60961a3SKever Yang }
1110a60961a3SKever Yang 
1111a60961a3SKever Yang static int px30_clk_set_phase(struct clk *clk, int degrees)
1112a60961a3SKever Yang {
1113a60961a3SKever Yang 	int ret;
1114a60961a3SKever Yang 
1115cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1116a60961a3SKever Yang 	switch (clk->id) {
1117a60961a3SKever Yang 	case SCLK_EMMC_SAMPLE:
1118a60961a3SKever Yang 	case SCLK_SDMMC_SAMPLE:
1119a60961a3SKever Yang 		ret = rockchip_mmc_set_phase(clk, degrees);
1120a60961a3SKever Yang 		break;
1121a60961a3SKever Yang 	default:
1122a60961a3SKever Yang 		return -ENOENT;
1123a60961a3SKever Yang 	}
1124a60961a3SKever Yang 
1125a60961a3SKever Yang 	return ret;
1126a60961a3SKever Yang }
1127a60961a3SKever Yang 
1128a60961a3SKever Yang static struct clk_ops px30_clk_ops = {
1129a60961a3SKever Yang 	.get_rate = px30_clk_get_rate,
1130a60961a3SKever Yang 	.set_rate = px30_clk_set_rate,
1131a60961a3SKever Yang 	.get_phase	= px30_clk_get_phase,
1132a60961a3SKever Yang 	.set_phase	= px30_clk_set_phase,
1133a60961a3SKever Yang };
1134a60961a3SKever Yang 
1135a60961a3SKever Yang static int px30_clk_probe(struct udevice *dev)
1136a60961a3SKever Yang {
1137a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(dev);
1138c4d4e4dcSFinley Xiao 	int ret;
1139a60961a3SKever Yang 
1140c4d4e4dcSFinley Xiao 	if (px30_clk_get_pll_rate(priv, APLL) != APLL_HZ) {
1141c4d4e4dcSFinley Xiao 		ret = px30_armclk_set_clk(priv, APLL_HZ);
1142c4d4e4dcSFinley Xiao 		if (ret < 0)
1143c4d4e4dcSFinley Xiao 			printf("%s failed to set armclk rate\n", __func__);
1144c4d4e4dcSFinley Xiao 	}
1145dd472d4fSFinley Xiao 
1146c4d4e4dcSFinley Xiao 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
1147c4d4e4dcSFinley Xiao 	ret = clk_set_defaults(dev);
1148c4d4e4dcSFinley Xiao 	if (ret)
1149c4d4e4dcSFinley Xiao 		debug("%s clk_set_defaults failed %d\n", __func__, ret);
1150c4d4e4dcSFinley Xiao 
1151c4d4e4dcSFinley Xiao 	if (!priv->gpll_hz) {
1152c4d4e4dcSFinley Xiao 		ret = px30_clk_get_gpll_rate(&priv->gpll_hz);
1153c4d4e4dcSFinley Xiao 		if (ret) {
1154c4d4e4dcSFinley Xiao 			printf("%s failed to get gpll rate\n", __func__);
1155c4d4e4dcSFinley Xiao 			return ret;
1156c4d4e4dcSFinley Xiao 		}
1157c4d4e4dcSFinley Xiao 	}
1158a60961a3SKever Yang 
1159a60961a3SKever Yang 	return 0;
1160a60961a3SKever Yang }
1161a60961a3SKever Yang 
1162a60961a3SKever Yang static int px30_clk_ofdata_to_platdata(struct udevice *dev)
1163a60961a3SKever Yang {
1164a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(dev);
1165a60961a3SKever Yang 
11664203970bSKever Yang 	priv->cru = dev_read_addr_ptr(dev);
1167a60961a3SKever Yang 
1168a60961a3SKever Yang 	return 0;
1169a60961a3SKever Yang }
1170a60961a3SKever Yang 
1171a60961a3SKever Yang static int px30_clk_bind(struct udevice *dev)
1172a60961a3SKever Yang {
1173a60961a3SKever Yang 	int ret;
1174a60961a3SKever Yang 	struct udevice *sys_child, *sf_child;
1175a60961a3SKever Yang 	struct sysreset_reg *priv;
1176a60961a3SKever Yang 	struct softreset_reg *sf_priv;
1177a60961a3SKever Yang 
1178a60961a3SKever Yang 	/* The reset driver does not have a device node, so bind it here */
1179a60961a3SKever Yang 	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
1180a60961a3SKever Yang 				 &sys_child);
1181a60961a3SKever Yang 	if (ret) {
1182a60961a3SKever Yang 		debug("Warning: No sysreset driver: ret=%d\n", ret);
1183a60961a3SKever Yang 	} else {
1184a60961a3SKever Yang 		priv = malloc(sizeof(struct sysreset_reg));
1185a60961a3SKever Yang 		priv->glb_srst_fst_value = offsetof(struct px30_cru,
1186a60961a3SKever Yang 						    glb_srst_fst);
1187a60961a3SKever Yang 		priv->glb_srst_snd_value = offsetof(struct px30_cru,
1188a60961a3SKever Yang 						    glb_srst_snd);
1189a60961a3SKever Yang 		sys_child->priv = priv;
1190a60961a3SKever Yang 	}
1191a60961a3SKever Yang 
1192a60961a3SKever Yang 	ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset",
1193a60961a3SKever Yang 					 dev_ofnode(dev), &sf_child);
1194a60961a3SKever Yang 	if (ret) {
1195a60961a3SKever Yang 		debug("Warning: No rockchip reset driver: ret=%d\n", ret);
1196a60961a3SKever Yang 	} else {
1197a60961a3SKever Yang 		sf_priv = malloc(sizeof(struct softreset_reg));
1198a60961a3SKever Yang 		sf_priv->sf_reset_offset = offsetof(struct px30_cru,
1199a60961a3SKever Yang 						    softrst_con[0]);
1200a60961a3SKever Yang 		sf_priv->sf_reset_num = 12;
1201a60961a3SKever Yang 		sf_child->priv = sf_priv;
1202a60961a3SKever Yang 	}
1203a60961a3SKever Yang 
1204a60961a3SKever Yang 	return 0;
1205a60961a3SKever Yang }
1206a60961a3SKever Yang 
1207a60961a3SKever Yang static const struct udevice_id px30_clk_ids[] = {
1208a60961a3SKever Yang 	{ .compatible = "rockchip,px30-cru" },
1209a60961a3SKever Yang 	{ }
1210a60961a3SKever Yang };
1211a60961a3SKever Yang 
1212a60961a3SKever Yang U_BOOT_DRIVER(rockchip_px30_cru) = {
1213a60961a3SKever Yang 	.name		= "rockchip_px30_cru",
1214a60961a3SKever Yang 	.id		= UCLASS_CLK,
1215a60961a3SKever Yang 	.of_match	= px30_clk_ids,
1216a60961a3SKever Yang 	.priv_auto_alloc_size = sizeof(struct px30_clk_priv),
1217a60961a3SKever Yang 	.ofdata_to_platdata = px30_clk_ofdata_to_platdata,
1218a60961a3SKever Yang 	.ops		= &px30_clk_ops,
1219a60961a3SKever Yang 	.bind		= px30_clk_bind,
1220a60961a3SKever Yang 	.probe		= px30_clk_probe,
1221a60961a3SKever Yang };
1222cefa5186SFinley Xiao 
1223cefa5186SFinley Xiao static ulong px30_pclk_pmu_get_pmuclk(struct px30_pmuclk_priv *priv)
1224cefa5186SFinley Xiao {
1225cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1226cefa5186SFinley Xiao 	u32 div, con;
1227cefa5186SFinley Xiao 
1228cefa5186SFinley Xiao 	con = readl(&pmucru->pmu_clksel_con[0]);
1229cefa5186SFinley Xiao 	div = (con & CLK_PMU_PCLK_DIV_MASK) >> CLK_PMU_PCLK_DIV_SHIFT;
1230cefa5186SFinley Xiao 
1231cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
1232cefa5186SFinley Xiao }
1233cefa5186SFinley Xiao 
1234cefa5186SFinley Xiao static ulong px30_pclk_pmu_set_pmuclk(struct px30_pmuclk_priv *priv, ulong hz)
1235cefa5186SFinley Xiao {
1236cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1237cefa5186SFinley Xiao 	int src_clk_div;
1238cefa5186SFinley Xiao 
1239cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
1240cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 31);
1241cefa5186SFinley Xiao 
1242cefa5186SFinley Xiao 	rk_clrsetreg(&pmucru->pmu_clksel_con[0],
1243cefa5186SFinley Xiao 		     CLK_PMU_PCLK_DIV_MASK,
1244cefa5186SFinley Xiao 		     (src_clk_div - 1) << CLK_PMU_PCLK_DIV_SHIFT);
1245cefa5186SFinley Xiao 
1246cefa5186SFinley Xiao 	return px30_pclk_pmu_get_pmuclk(priv);
1247cefa5186SFinley Xiao }
1248cefa5186SFinley Xiao 
1249cefa5186SFinley Xiao static ulong px30_gpll_get_pmuclk(struct px30_pmuclk_priv *priv)
1250cefa5186SFinley Xiao {
1251cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1252cefa5186SFinley Xiao 
1253cefa5186SFinley Xiao 	return rkclk_pll_get_rate(&pmucru->pll, &pmucru->pmu_mode, GPLL);
1254cefa5186SFinley Xiao }
1255cefa5186SFinley Xiao 
1256cefa5186SFinley Xiao static ulong px30_gpll_set_pmuclk(struct px30_pmuclk_priv *priv, ulong hz)
1257cefa5186SFinley Xiao {
1258cefa5186SFinley Xiao 	struct udevice *cru_dev;
1259cefa5186SFinley Xiao 	struct px30_clk_priv *cru_priv;
1260cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1261cefa5186SFinley Xiao 	u32 div;
1262cefa5186SFinley Xiao 	ulong emmc_rate, sdmmc_rate, nandc_rate;
1263eb46e717SFinley Xiao 	ulong aclk_bus_rate, hclk_bus_rate, pclk_bus_rate;
1264eb46e717SFinley Xiao 	ulong aclk_peri_rate, hclk_peri_rate, pclk_pmu_rate;
1265cefa5186SFinley Xiao 	int ret;
1266cefa5186SFinley Xiao 
1267cefa5186SFinley Xiao 	ret = uclass_get_device_by_name(UCLASS_CLK,
1268cefa5186SFinley Xiao 					"clock-controller@ff2b0000",
1269cefa5186SFinley Xiao 					 &cru_dev);
1270cefa5186SFinley Xiao 	if (ret) {
1271cefa5186SFinley Xiao 		printf("%s failed to get cru device\n", __func__);
1272cefa5186SFinley Xiao 		return ret;
1273cefa5186SFinley Xiao 	}
1274cefa5186SFinley Xiao 	cru_priv = dev_get_priv(cru_dev);
1275bf97d0d6SFinley Xiao 
1276bf97d0d6SFinley Xiao 	if (priv->gpll_hz == hz)
1277bf97d0d6SFinley Xiao 		return priv->gpll_hz;
1278bf97d0d6SFinley Xiao 
1279cefa5186SFinley Xiao 	cru_priv->gpll_hz = priv->gpll_hz;
1280cefa5186SFinley Xiao 	div = DIV_ROUND_UP(hz, priv->gpll_hz);
1281cefa5186SFinley Xiao 
1282eb46e717SFinley Xiao 	/* save clock rate */
1283eb46e717SFinley Xiao 	aclk_bus_rate = px30_bus_get_clk(cru_priv, ACLK_BUS_PRE);
1284eb46e717SFinley Xiao 	hclk_bus_rate = px30_bus_get_clk(cru_priv, HCLK_BUS_PRE);
1285eb46e717SFinley Xiao 	pclk_bus_rate = px30_bus_get_clk(cru_priv, PCLK_BUS_PRE);
1286eb46e717SFinley Xiao 	aclk_peri_rate = px30_peri_get_clk(cru_priv, ACLK_PERI_PRE);
1287eb46e717SFinley Xiao 	hclk_peri_rate = px30_peri_get_clk(cru_priv, HCLK_PERI_PRE);
1288eb46e717SFinley Xiao 	pclk_pmu_rate = px30_pclk_pmu_get_pmuclk(priv);
1289eb46e717SFinley Xiao 	debug("%s aclk_bus=%lu, hclk_bus=%lu, pclk_bus=%lu\n", __func__,
1290eb46e717SFinley Xiao 	      aclk_bus_rate, hclk_bus_rate, pclk_bus_rate);
1291eb46e717SFinley Xiao 	debug("%s aclk_peri=%lu, hclk_peri=%lu, pclk_pmu=%lu\n", __func__,
1292eb46e717SFinley Xiao 	      aclk_peri_rate, hclk_peri_rate, pclk_pmu_rate);
1293cefa5186SFinley Xiao 	emmc_rate = px30_mmc_get_clk(cru_priv, SCLK_EMMC);
1294cefa5186SFinley Xiao 	sdmmc_rate = px30_mmc_get_clk(cru_priv, SCLK_SDMMC);
1295cefa5186SFinley Xiao 	nandc_rate = px30_nandc_get_clk(cru_priv);
1296eb46e717SFinley Xiao 	debug("%s emmc=%lu, sdmmc=%lu, nandc=%lu\n", __func__,
1297eb46e717SFinley Xiao 	      emmc_rate, sdmmc_rate, nandc_rate);
1298eb46e717SFinley Xiao 
12994a9de4c9SFinley Xiao 	/* avoid rate too large, reduce rate first */
1300eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, ACLK_BUS_PRE, aclk_bus_rate / div);
1301eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, HCLK_BUS_PRE, hclk_bus_rate / div);
1302eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, PCLK_BUS_PRE, pclk_bus_rate / div);
1303eb46e717SFinley Xiao 	px30_peri_set_clk(cru_priv, ACLK_PERI_PRE, aclk_peri_rate / div);
1304eb46e717SFinley Xiao 	px30_peri_set_clk(cru_priv, HCLK_PERI_PRE, hclk_peri_rate / div);
1305eb46e717SFinley Xiao 	px30_pclk_pmu_set_pmuclk(priv, pclk_pmu_rate / div);
1306eb46e717SFinley Xiao 
13074a9de4c9SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_EMMC, emmc_rate / div);
13084a9de4c9SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_SDMMC, sdmmc_rate / div);
13094a9de4c9SFinley Xiao 	px30_nandc_set_clk(cru_priv, nandc_rate / div);
1310cefa5186SFinley Xiao 
1311eb46e717SFinley Xiao 	/* change gpll rate */
1312cefa5186SFinley Xiao 	rkclk_set_pll(&pmucru->pll, &pmucru->pmu_mode, GPLL, hz);
1313cefa5186SFinley Xiao 	priv->gpll_hz = px30_gpll_get_pmuclk(priv);
1314cefa5186SFinley Xiao 	cru_priv->gpll_hz = priv->gpll_hz;
1315cefa5186SFinley Xiao 
1316eb46e717SFinley Xiao 	/* restore clock rate */
1317eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, ACLK_BUS_PRE, aclk_bus_rate);
1318eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, HCLK_BUS_PRE, hclk_bus_rate);
1319eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, PCLK_BUS_PRE, pclk_bus_rate);
1320eb46e717SFinley Xiao 	px30_peri_set_clk(cru_priv, ACLK_PERI_PRE, aclk_peri_rate);
1321eb46e717SFinley Xiao 	px30_peri_set_clk(cru_priv, HCLK_PERI_PRE, hclk_peri_rate);
1322eb46e717SFinley Xiao 	px30_pclk_pmu_set_pmuclk(priv, pclk_pmu_rate);
1323eb46e717SFinley Xiao 
1324cefa5186SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_EMMC, emmc_rate);
1325cefa5186SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_SDMMC, sdmmc_rate);
1326cefa5186SFinley Xiao 	px30_nandc_set_clk(cru_priv, nandc_rate);
1327cefa5186SFinley Xiao 
1328cefa5186SFinley Xiao 	return priv->gpll_hz;
1329cefa5186SFinley Xiao }
1330cefa5186SFinley Xiao 
1331cefa5186SFinley Xiao static ulong px30_pmuclk_get_rate(struct clk *clk)
1332cefa5186SFinley Xiao {
1333cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev);
1334cefa5186SFinley Xiao 	ulong rate = 0;
1335cefa5186SFinley Xiao 
1336cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1337cefa5186SFinley Xiao 	switch (clk->id) {
1338cefa5186SFinley Xiao 	case PLL_GPLL:
1339cefa5186SFinley Xiao 		rate = px30_gpll_get_pmuclk(priv);
1340cefa5186SFinley Xiao 		break;
1341cefa5186SFinley Xiao 	case PCLK_PMU_PRE:
1342cefa5186SFinley Xiao 		rate = px30_pclk_pmu_get_pmuclk(priv);
1343cefa5186SFinley Xiao 		break;
1344cefa5186SFinley Xiao 	default:
1345cefa5186SFinley Xiao 		return -ENOENT;
1346cefa5186SFinley Xiao 	}
1347cefa5186SFinley Xiao 
1348cefa5186SFinley Xiao 	return rate;
1349cefa5186SFinley Xiao }
1350cefa5186SFinley Xiao 
1351cefa5186SFinley Xiao static ulong px30_pmuclk_set_rate(struct clk *clk, ulong rate)
1352cefa5186SFinley Xiao {
1353cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev);
1354cefa5186SFinley Xiao 	ulong ret = 0;
1355cefa5186SFinley Xiao 
1356cefa5186SFinley Xiao 	debug("%s %ld %ld\n", __func__, clk->id, rate);
1357cefa5186SFinley Xiao 	switch (clk->id) {
1358cefa5186SFinley Xiao 	case PLL_GPLL:
1359cefa5186SFinley Xiao 		ret = px30_gpll_set_pmuclk(priv, rate);
1360cefa5186SFinley Xiao 		break;
1361cefa5186SFinley Xiao 	case PCLK_PMU_PRE:
1362cefa5186SFinley Xiao 		ret = px30_pclk_pmu_set_pmuclk(priv, rate);
1363cefa5186SFinley Xiao 		break;
1364cefa5186SFinley Xiao 	default:
1365cefa5186SFinley Xiao 		return -ENOENT;
1366cefa5186SFinley Xiao 	}
1367cefa5186SFinley Xiao 
1368cefa5186SFinley Xiao 	return ret;
1369cefa5186SFinley Xiao }
1370cefa5186SFinley Xiao 
1371cefa5186SFinley Xiao static struct clk_ops px30_pmuclk_ops = {
1372cefa5186SFinley Xiao 	.get_rate = px30_pmuclk_get_rate,
1373cefa5186SFinley Xiao 	.set_rate = px30_pmuclk_set_rate,
1374cefa5186SFinley Xiao };
1375cefa5186SFinley Xiao 
1376cefa5186SFinley Xiao static int px30_pmuclk_probe(struct udevice *dev)
1377cefa5186SFinley Xiao {
1378c4d4e4dcSFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(dev);
1379c4d4e4dcSFinley Xiao 	int ret;
1380c4d4e4dcSFinley Xiao 
1381c4d4e4dcSFinley Xiao 	priv->gpll_hz = px30_gpll_get_pmuclk(priv);
1382c4d4e4dcSFinley Xiao 
1383c4d4e4dcSFinley Xiao 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
1384c4d4e4dcSFinley Xiao 	ret = clk_set_defaults(dev);
1385c4d4e4dcSFinley Xiao 	if (ret)
1386c4d4e4dcSFinley Xiao 		debug("%s clk_set_defaults failed %d\n", __func__, ret);
1387c4d4e4dcSFinley Xiao 
1388cefa5186SFinley Xiao 	return 0;
1389cefa5186SFinley Xiao }
1390cefa5186SFinley Xiao 
1391cefa5186SFinley Xiao static int px30_pmuclk_ofdata_to_platdata(struct udevice *dev)
1392cefa5186SFinley Xiao {
1393cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(dev);
1394cefa5186SFinley Xiao 
1395cefa5186SFinley Xiao 	priv->pmucru = dev_read_addr_ptr(dev);
1396cefa5186SFinley Xiao 
1397cefa5186SFinley Xiao 	return 0;
1398cefa5186SFinley Xiao }
1399cefa5186SFinley Xiao 
1400cefa5186SFinley Xiao static const struct udevice_id px30_pmuclk_ids[] = {
1401cefa5186SFinley Xiao 	{ .compatible = "rockchip,px30-pmucru" },
1402cefa5186SFinley Xiao 	{ }
1403cefa5186SFinley Xiao };
1404cefa5186SFinley Xiao 
1405cefa5186SFinley Xiao U_BOOT_DRIVER(rockchip_px30_pmucru) = {
1406cefa5186SFinley Xiao 	.name		= "rockchip_px30_pmucru",
1407cefa5186SFinley Xiao 	.id		= UCLASS_CLK,
1408cefa5186SFinley Xiao 	.of_match	= px30_pmuclk_ids,
1409cefa5186SFinley Xiao 	.priv_auto_alloc_size = sizeof(struct px30_pmuclk_priv),
1410cefa5186SFinley Xiao 	.ofdata_to_platdata = px30_pmuclk_ofdata_to_platdata,
1411cefa5186SFinley Xiao 	.ops		= &px30_pmuclk_ops,
1412cefa5186SFinley Xiao 	.probe		= px30_pmuclk_probe,
1413cefa5186SFinley Xiao };
14147a1915c0SFinley Xiao 
14157a1915c0SFinley Xiao /**
14167a1915c0SFinley Xiao  * soc_clk_dump() - Print clock frequencies
14177a1915c0SFinley Xiao  * Returns zero on success
14187a1915c0SFinley Xiao  *
14197a1915c0SFinley Xiao  * Implementation for the clk dump command.
14207a1915c0SFinley Xiao  */
14217a1915c0SFinley Xiao int soc_clk_dump(void)
14227a1915c0SFinley Xiao {
14237a1915c0SFinley Xiao 	struct udevice *cru_dev, *pmucru_dev;
14247a1915c0SFinley Xiao 	const struct px30_clk_info *clk_dump;
14257a1915c0SFinley Xiao 	struct clk clk;
14267a1915c0SFinley Xiao 	unsigned long clk_count = ARRAY_SIZE(clks_dump);
14277a1915c0SFinley Xiao 	unsigned long rate;
14287a1915c0SFinley Xiao 	int i, ret;
14297a1915c0SFinley Xiao 
14307a1915c0SFinley Xiao 	ret = uclass_get_device_by_driver(UCLASS_CLK,
14317a1915c0SFinley Xiao 					  DM_GET_DRIVER(rockchip_px30_cru),
14327a1915c0SFinley Xiao 					  &cru_dev);
14337a1915c0SFinley Xiao 	if (ret) {
14347a1915c0SFinley Xiao 		printf("%s failed to get cru device\n", __func__);
14357a1915c0SFinley Xiao 		return ret;
14367a1915c0SFinley Xiao 	}
14377a1915c0SFinley Xiao 
14387a1915c0SFinley Xiao 	ret = uclass_get_device_by_driver(UCLASS_CLK,
14397a1915c0SFinley Xiao 					  DM_GET_DRIVER(rockchip_px30_pmucru),
14407a1915c0SFinley Xiao 					  &pmucru_dev);
14417a1915c0SFinley Xiao 	if (ret) {
14427a1915c0SFinley Xiao 		printf("%s failed to get pmucru device\n", __func__);
14437a1915c0SFinley Xiao 		return ret;
14447a1915c0SFinley Xiao 	}
14457a1915c0SFinley Xiao 
144656dd66cfSFinley Xiao 	printf("CLK:\n");
14477a1915c0SFinley Xiao 	for (i = 0; i < clk_count; i++) {
14487a1915c0SFinley Xiao 		clk_dump = &clks_dump[i];
14497a1915c0SFinley Xiao 		if (clk_dump->name) {
14507a1915c0SFinley Xiao 			clk.id = clk_dump->id;
14517a1915c0SFinley Xiao 			if (clk_dump->is_cru)
14527a1915c0SFinley Xiao 				ret = clk_request(cru_dev, &clk);
14537a1915c0SFinley Xiao 			else
14547a1915c0SFinley Xiao 				ret = clk_request(pmucru_dev, &clk);
14557a1915c0SFinley Xiao 			if (ret < 0)
14567a1915c0SFinley Xiao 				return ret;
14577a1915c0SFinley Xiao 
14587a1915c0SFinley Xiao 			rate = clk_get_rate(&clk);
14597a1915c0SFinley Xiao 			clk_free(&clk);
14607a1915c0SFinley Xiao 			if (i == 0) {
14617a1915c0SFinley Xiao 				if (rate < 0)
146256dd66cfSFinley Xiao 					printf("%s %s\n", clk_dump->name,
14637a1915c0SFinley Xiao 					       "unknown");
14647a1915c0SFinley Xiao 				else
146556dd66cfSFinley Xiao 					printf("%s %lu KHz\n", clk_dump->name,
146656dd66cfSFinley Xiao 					       rate / 1000);
14677a1915c0SFinley Xiao 			} else {
14687a1915c0SFinley Xiao 				if (rate < 0)
146956dd66cfSFinley Xiao 					printf("%s %s\n", clk_dump->name,
14707a1915c0SFinley Xiao 					       "unknown");
14717a1915c0SFinley Xiao 				else
147256dd66cfSFinley Xiao 					printf("%s %lu KHz\n", clk_dump->name,
147356dd66cfSFinley Xiao 					       rate / 1000);
14747a1915c0SFinley Xiao 			}
14757a1915c0SFinley Xiao 		}
14767a1915c0SFinley Xiao 	}
14777a1915c0SFinley Xiao 
14787a1915c0SFinley Xiao 	return 0;
14797a1915c0SFinley Xiao }
1480