xref: /rk3399_rockchip-uboot/drivers/clk/rockchip/clk_px30.c (revision f5b1a4f280dfab437fa37422a2537e0eef860011)
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>
1489cc3f4dSElaine Zhang #include <asm/arch/cpu.h>
15a60961a3SKever Yang #include <asm/arch/cru_px30.h>
16a60961a3SKever Yang #include <asm/arch/hardware.h>
17a60961a3SKever Yang #include <asm/io.h>
18a60961a3SKever Yang #include <dm/lists.h>
19a60961a3SKever Yang #include <dt-bindings/clock/px30-cru.h>
20a60961a3SKever Yang 
21a60961a3SKever Yang DECLARE_GLOBAL_DATA_PTR;
22a60961a3SKever Yang 
23a60961a3SKever Yang enum {
24a60961a3SKever Yang 	VCO_MAX_HZ	= 3200U * 1000000,
25a60961a3SKever Yang 	VCO_MIN_HZ	= 800 * 1000000,
26a60961a3SKever Yang 	OUTPUT_MAX_HZ	= 3200U * 1000000,
27a60961a3SKever Yang 	OUTPUT_MIN_HZ	= 24 * 1000000,
28a60961a3SKever Yang };
29a60961a3SKever Yang 
30f909d4a8SFinley Xiao #define PX30_VOP_PLL_LIMIT			600000000
31f909d4a8SFinley Xiao 
32cefa5186SFinley Xiao #define PX30_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1,	\
33cefa5186SFinley Xiao 			_postdiv2, _dsmpd, _frac)		\
34cefa5186SFinley Xiao {								\
35cefa5186SFinley Xiao 	.rate	= _rate##U,					\
36cefa5186SFinley Xiao 	.fbdiv = _fbdiv,					\
37cefa5186SFinley Xiao 	.postdiv1 = _postdiv1,					\
38cefa5186SFinley Xiao 	.refdiv = _refdiv,					\
39cefa5186SFinley Xiao 	.postdiv2 = _postdiv2,					\
40cefa5186SFinley Xiao 	.dsmpd = _dsmpd,					\
41cefa5186SFinley Xiao 	.frac = _frac,						\
42cefa5186SFinley Xiao }
43cefa5186SFinley Xiao 
4437428b92SFinley Xiao #define PX30_CPUCLK_RATE(_rate, _aclk_div, _pclk_div)		\
4537428b92SFinley Xiao {								\
4637428b92SFinley Xiao 	.rate	= _rate##U,					\
4737428b92SFinley Xiao 	.aclk_div = _aclk_div,					\
4837428b92SFinley Xiao 	.pclk_div = _pclk_div,					\
4937428b92SFinley Xiao }
5037428b92SFinley Xiao 
51a60961a3SKever Yang #define DIV_TO_RATE(input_rate, div)    ((input_rate) / ((div) + 1))
52a60961a3SKever Yang 
537a1915c0SFinley Xiao #define PX30_CLK_DUMP(_id, _name, _iscru)	\
547a1915c0SFinley Xiao {						\
557a1915c0SFinley Xiao 	.id = _id,				\
567a1915c0SFinley Xiao 	.name = _name,				\
577a1915c0SFinley Xiao 	.is_cru = _iscru,			\
587a1915c0SFinley Xiao }
597a1915c0SFinley Xiao 
60cefa5186SFinley Xiao static struct pll_rate_table px30_pll_rates[] = {
61cefa5186SFinley Xiao 	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
62cefa5186SFinley Xiao 	PX30_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
63cefa5186SFinley Xiao 	PX30_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
64cefa5186SFinley Xiao 	PX30_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0),
6537428b92SFinley Xiao 	PX30_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0),
66cefa5186SFinley Xiao 	PX30_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0),
67cefa5186SFinley Xiao 	PX30_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0),
68dd472d4fSFinley Xiao 	PX30_PLL_RATE(600000000, 1, 75, 3, 1, 1, 0),
69a60961a3SKever Yang };
70a60961a3SKever Yang 
717a1915c0SFinley Xiao static const struct px30_clk_info clks_dump[] = {
727a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_APLL, "apll", true),
737a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_DPLL, "dpll", true),
747a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_CPLL, "cpll", true),
757a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_NPLL, "npll", true),
767a1915c0SFinley Xiao 	PX30_CLK_DUMP(PLL_GPLL, "gpll", false),
777a1915c0SFinley Xiao 	PX30_CLK_DUMP(ACLK_BUS_PRE, "aclk_bus", true),
787a1915c0SFinley Xiao 	PX30_CLK_DUMP(HCLK_BUS_PRE, "hclk_bus", true),
797a1915c0SFinley Xiao 	PX30_CLK_DUMP(PCLK_BUS_PRE, "pclk_bus", true),
807a1915c0SFinley Xiao 	PX30_CLK_DUMP(ACLK_PERI_PRE, "aclk_peri", true),
817a1915c0SFinley Xiao 	PX30_CLK_DUMP(HCLK_PERI_PRE, "hclk_peri", true),
827a1915c0SFinley Xiao 	PX30_CLK_DUMP(PCLK_PMU_PRE, "pclk_pmu", false),
837a1915c0SFinley Xiao };
847a1915c0SFinley Xiao 
8537428b92SFinley Xiao static struct cpu_rate_table px30_cpu_rates[] = {
8637428b92SFinley Xiao 	PX30_CPUCLK_RATE(1200000000, 1, 5),
8737428b92SFinley Xiao 	PX30_CPUCLK_RATE(1008000000, 1, 5),
8837428b92SFinley Xiao 	PX30_CPUCLK_RATE(816000000, 1, 3),
8937428b92SFinley Xiao 	PX30_CPUCLK_RATE(600000000, 1, 3),
90c111479fSJoseph Chen 	PX30_CPUCLK_RATE(408000000, 1, 1),
9137428b92SFinley Xiao };
9237428b92SFinley Xiao 
93bcefd077SFinley Xiao static u8 pll_mode_shift[PLL_COUNT] = {
94bcefd077SFinley Xiao 	APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
95bcefd077SFinley Xiao 	NPLL_MODE_SHIFT, GPLL_MODE_SHIFT
96bcefd077SFinley Xiao };
97bcefd077SFinley Xiao static u32 pll_mode_mask[PLL_COUNT] = {
98bcefd077SFinley Xiao 	APLL_MODE_MASK, DPLL_MODE_MASK, CPLL_MODE_MASK,
99bcefd077SFinley Xiao 	NPLL_MODE_MASK, GPLL_MODE_MASK
100bcefd077SFinley Xiao };
101bcefd077SFinley Xiao 
102cefa5186SFinley Xiao static struct pll_rate_table auto_table;
103cefa5186SFinley Xiao 
104d101530aSFinley Xiao static ulong px30_clk_get_pll_rate(struct px30_clk_priv *priv,
105d101530aSFinley Xiao 				   enum px30_pll_id pll_id);
106d101530aSFinley Xiao 
pll_clk_set_by_auto(u32 drate)107cefa5186SFinley Xiao static struct pll_rate_table *pll_clk_set_by_auto(u32 drate)
108cefa5186SFinley Xiao {
109cefa5186SFinley Xiao 	struct pll_rate_table *rate = &auto_table;
110cefa5186SFinley Xiao 	u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0;
111cefa5186SFinley Xiao 	u32 postdiv1, postdiv2 = 1;
112cefa5186SFinley Xiao 	u32 fref_khz;
113cefa5186SFinley Xiao 	u32 diff_khz, best_diff_khz;
114cefa5186SFinley Xiao 	const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16;
115cefa5186SFinley Xiao 	const u32 max_postdiv1 = 7, max_postdiv2 = 7;
116cefa5186SFinley Xiao 	u32 vco_khz;
117cefa5186SFinley Xiao 	u32 rate_khz = drate / KHz;
118cefa5186SFinley Xiao 
119cefa5186SFinley Xiao 	if (!drate) {
120cefa5186SFinley Xiao 		printf("%s: the frequency can't be 0 Hz\n", __func__);
121cefa5186SFinley Xiao 		return NULL;
122cefa5186SFinley Xiao 	}
123cefa5186SFinley Xiao 
124cefa5186SFinley Xiao 	postdiv1 = DIV_ROUND_UP(VCO_MIN_HZ / 1000, rate_khz);
125cefa5186SFinley Xiao 	if (postdiv1 > max_postdiv1) {
126cefa5186SFinley Xiao 		postdiv2 = DIV_ROUND_UP(postdiv1, max_postdiv1);
127cefa5186SFinley Xiao 		postdiv1 = DIV_ROUND_UP(postdiv1, postdiv2);
128cefa5186SFinley Xiao 	}
129cefa5186SFinley Xiao 
130cefa5186SFinley Xiao 	vco_khz = rate_khz * postdiv1 * postdiv2;
131cefa5186SFinley Xiao 
132cefa5186SFinley Xiao 	if (vco_khz < (VCO_MIN_HZ / KHz) || vco_khz > (VCO_MAX_HZ / KHz) ||
133cefa5186SFinley Xiao 	    postdiv2 > max_postdiv2) {
134cefa5186SFinley Xiao 		printf("%s: Cannot find out a supported VCO for Freq (%uHz)\n",
135cefa5186SFinley Xiao 		       __func__, rate_khz);
136cefa5186SFinley Xiao 		return NULL;
137cefa5186SFinley Xiao 	}
138cefa5186SFinley Xiao 
139cefa5186SFinley Xiao 	rate->postdiv1 = postdiv1;
140cefa5186SFinley Xiao 	rate->postdiv2 = postdiv2;
141cefa5186SFinley Xiao 
142cefa5186SFinley Xiao 	best_diff_khz = vco_khz;
143cefa5186SFinley Xiao 	for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) {
144cefa5186SFinley Xiao 		fref_khz = ref_khz / refdiv;
145cefa5186SFinley Xiao 
146cefa5186SFinley Xiao 		fbdiv = vco_khz / fref_khz;
147cefa5186SFinley Xiao 		if ((fbdiv >= max_fbdiv) || (fbdiv <= min_fbdiv))
148cefa5186SFinley Xiao 			continue;
149cefa5186SFinley Xiao 		diff_khz = vco_khz - fbdiv * fref_khz;
150cefa5186SFinley Xiao 		if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) {
151cefa5186SFinley Xiao 			fbdiv++;
152cefa5186SFinley Xiao 			diff_khz = fref_khz - diff_khz;
153cefa5186SFinley Xiao 		}
154cefa5186SFinley Xiao 
155cefa5186SFinley Xiao 		if (diff_khz >= best_diff_khz)
156cefa5186SFinley Xiao 			continue;
157cefa5186SFinley Xiao 
158cefa5186SFinley Xiao 		best_diff_khz = diff_khz;
159cefa5186SFinley Xiao 		rate->refdiv = refdiv;
160cefa5186SFinley Xiao 		rate->fbdiv = fbdiv;
161cefa5186SFinley Xiao 	}
162cefa5186SFinley Xiao 
163cefa5186SFinley Xiao 	if (best_diff_khz > 4 * (MHz / KHz)) {
164cefa5186SFinley Xiao 		printf("%s: Failed to match output frequency %u bestis %u Hz\n",
165cefa5186SFinley Xiao 		       __func__, rate_khz,
166cefa5186SFinley Xiao 		       best_diff_khz * KHz);
167cefa5186SFinley Xiao 		return NULL;
168cefa5186SFinley Xiao 	}
169cefa5186SFinley Xiao 
170cefa5186SFinley Xiao 	return rate;
171cefa5186SFinley Xiao }
172cefa5186SFinley Xiao 
get_pll_settings(unsigned long rate)173cefa5186SFinley Xiao static const struct pll_rate_table *get_pll_settings(unsigned long rate)
174cefa5186SFinley Xiao {
175cefa5186SFinley Xiao 	unsigned int rate_count = ARRAY_SIZE(px30_pll_rates);
176cefa5186SFinley Xiao 	int i;
177cefa5186SFinley Xiao 
178cefa5186SFinley Xiao 	for (i = 0; i < rate_count; i++) {
179cefa5186SFinley Xiao 		if (rate == px30_pll_rates[i].rate)
180cefa5186SFinley Xiao 			return &px30_pll_rates[i];
181cefa5186SFinley Xiao 	}
182cefa5186SFinley Xiao 
183cefa5186SFinley Xiao 	return pll_clk_set_by_auto(rate);
184cefa5186SFinley Xiao }
185a60961a3SKever Yang 
get_cpu_settings(unsigned long rate)18637428b92SFinley Xiao static const struct cpu_rate_table *get_cpu_settings(unsigned long rate)
18737428b92SFinley Xiao {
18837428b92SFinley Xiao 	unsigned int rate_count = ARRAY_SIZE(px30_cpu_rates);
18937428b92SFinley Xiao 	int i;
19037428b92SFinley Xiao 
19137428b92SFinley Xiao 	for (i = 0; i < rate_count; i++) {
19237428b92SFinley Xiao 		if (rate == px30_cpu_rates[i].rate)
19337428b92SFinley Xiao 			return &px30_cpu_rates[i];
19437428b92SFinley Xiao 	}
19537428b92SFinley Xiao 
19637428b92SFinley Xiao 	return NULL;
19737428b92SFinley Xiao }
19837428b92SFinley Xiao 
199a60961a3SKever Yang /*
200a60961a3SKever Yang  * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
201a60961a3SKever Yang  * Formulas also embedded within the Fractional PLL Verilog model:
202a60961a3SKever Yang  * If DSMPD = 1 (DSM is disabled, "integer mode")
203a60961a3SKever Yang  * FOUTVCO = FREF / REFDIV * FBDIV
204a60961a3SKever Yang  * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
205a60961a3SKever Yang  * Where:
206a60961a3SKever Yang  * FOUTVCO = Fractional PLL non-divided output frequency
207a60961a3SKever Yang  * FOUTPOSTDIV = Fractional PLL divided output frequency
208a60961a3SKever Yang  *               (output of second post divider)
209a60961a3SKever Yang  * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
210a60961a3SKever Yang  * REFDIV = Fractional PLL input reference clock divider
211a60961a3SKever Yang  * FBDIV = Integer value programmed into feedback divide
212a60961a3SKever Yang  *
213a60961a3SKever Yang  */
rkclk_set_pll(struct px30_pll * pll,unsigned int * mode,enum px30_pll_id pll_id,unsigned long drate)214cefa5186SFinley Xiao static int rkclk_set_pll(struct px30_pll *pll, unsigned int *mode,
215cefa5186SFinley Xiao 			 enum px30_pll_id pll_id,
216cefa5186SFinley Xiao 			 unsigned long drate)
217a60961a3SKever Yang {
218cefa5186SFinley Xiao 	const struct pll_rate_table *rate;
219cefa5186SFinley Xiao 	uint vco_hz, output_hz;
220db235eb5SFinley Xiao 
221cefa5186SFinley Xiao 	rate = get_pll_settings(drate);
222cefa5186SFinley Xiao 	if (!rate) {
223cefa5186SFinley Xiao 		printf("%s unsupport rate\n", __func__);
224cefa5186SFinley Xiao 		return -EINVAL;
225cefa5186SFinley Xiao 	}
226cefa5186SFinley Xiao 
227cefa5186SFinley Xiao 	/* All PLLs have same VCO and output frequency range restrictions. */
228cefa5186SFinley Xiao 	vco_hz = OSC_HZ / 1000 * rate->fbdiv / rate->refdiv * 1000;
229cefa5186SFinley Xiao 	output_hz = vco_hz / rate->postdiv1 / rate->postdiv2;
230a60961a3SKever Yang 
231a60961a3SKever Yang 	debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n",
232cefa5186SFinley Xiao 	      pll, rate->fbdiv, rate->refdiv, rate->postdiv1,
233cefa5186SFinley Xiao 	      rate->postdiv2, vco_hz, output_hz);
234a60961a3SKever Yang 	assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
235a60961a3SKever Yang 	       output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ);
236a60961a3SKever Yang 
237db235eb5SFinley Xiao 	/*
238db235eb5SFinley Xiao 	 * When power on or changing PLL setting,
239db235eb5SFinley Xiao 	 * we must force PLL into slow mode to ensure output stable clock.
240db235eb5SFinley Xiao 	 */
241bcefd077SFinley Xiao 	rk_clrsetreg(mode, pll_mode_mask[pll_id],
242bcefd077SFinley Xiao 		     PLLMUX_FROM_XIN24M << pll_mode_shift[pll_id]);
243db235eb5SFinley Xiao 
244a60961a3SKever Yang 	/* use integer mode */
245a60961a3SKever Yang 	rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
246a60961a3SKever Yang 	/* Power down */
247a60961a3SKever Yang 	rk_setreg(&pll->con1, 1 << PLL_PD_SHIFT);
248a60961a3SKever Yang 
249a60961a3SKever Yang 	rk_clrsetreg(&pll->con0,
250a60961a3SKever Yang 		     PLL_POSTDIV1_MASK | PLL_FBDIV_MASK,
251cefa5186SFinley Xiao 		     (rate->postdiv1 << PLL_POSTDIV1_SHIFT) | rate->fbdiv);
252a60961a3SKever Yang 	rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK,
253cefa5186SFinley Xiao 		     (rate->postdiv2 << PLL_POSTDIV2_SHIFT |
254cefa5186SFinley Xiao 		     rate->refdiv << PLL_REFDIV_SHIFT));
255a60961a3SKever Yang 
256a60961a3SKever Yang 	/* Power Up */
257a60961a3SKever Yang 	rk_clrreg(&pll->con1, 1 << PLL_PD_SHIFT);
258a60961a3SKever Yang 
259a60961a3SKever Yang 	/* waiting for pll lock */
2606fb52eadSFinley Xiao 	while (!(readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)))
261a60961a3SKever Yang 		udelay(1);
262a60961a3SKever Yang 
263bcefd077SFinley Xiao 	rk_clrsetreg(mode, pll_mode_mask[pll_id],
264bcefd077SFinley Xiao 		     PLLMUX_FROM_PLL << pll_mode_shift[pll_id]);
265db235eb5SFinley Xiao 
266cefa5186SFinley Xiao 	return 0;
267a60961a3SKever Yang }
268a60961a3SKever Yang 
rkclk_pll_get_rate(struct px30_pll * pll,unsigned int * mode,enum px30_pll_id pll_id)269cefa5186SFinley Xiao static uint32_t rkclk_pll_get_rate(struct px30_pll *pll, unsigned int *mode,
27030f1f38dSFinley Xiao 				   enum px30_pll_id pll_id)
27130f1f38dSFinley Xiao {
27230f1f38dSFinley Xiao 	u32 refdiv, fbdiv, postdiv1, postdiv2;
273cefa5186SFinley Xiao 	u32 con, shift, mask;
27430f1f38dSFinley Xiao 
275cefa5186SFinley Xiao 	con = readl(mode);
276bcefd077SFinley Xiao 	shift = pll_mode_shift[pll_id];
277bcefd077SFinley Xiao 	mask = pll_mode_mask[pll_id];
27830f1f38dSFinley Xiao 
27930f1f38dSFinley Xiao 	switch ((con & mask) >> shift) {
28030f1f38dSFinley Xiao 	case PLLMUX_FROM_XIN24M:
28130f1f38dSFinley Xiao 		return OSC_HZ;
28230f1f38dSFinley Xiao 	case PLLMUX_FROM_PLL:
28330f1f38dSFinley Xiao 		/* normal mode */
28430f1f38dSFinley Xiao 		con = readl(&pll->con0);
28530f1f38dSFinley Xiao 		postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
28630f1f38dSFinley Xiao 		fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
28730f1f38dSFinley Xiao 		con = readl(&pll->con1);
28830f1f38dSFinley Xiao 		postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
28930f1f38dSFinley Xiao 		refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
29030f1f38dSFinley Xiao 		return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
29130f1f38dSFinley Xiao 	case PLLMUX_FROM_RTC32K:
29230f1f38dSFinley Xiao 	default:
29330f1f38dSFinley Xiao 		return 32768;
29430f1f38dSFinley Xiao 	}
29530f1f38dSFinley Xiao }
29630f1f38dSFinley Xiao 
px30_i2c_get_clk(struct px30_clk_priv * priv,ulong clk_id)297cefa5186SFinley Xiao static ulong px30_i2c_get_clk(struct px30_clk_priv *priv, ulong clk_id)
29830f1f38dSFinley Xiao {
299cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
300a60961a3SKever Yang 	u32 div, con;
301a60961a3SKever Yang 
302a60961a3SKever Yang 	switch (clk_id) {
303a60961a3SKever Yang 	case SCLK_I2C0:
304f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[49]);
305f67f522bSFinley Xiao 		div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
306a60961a3SKever Yang 		break;
307a60961a3SKever Yang 	case SCLK_I2C1:
308f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[49]);
309f67f522bSFinley Xiao 		div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
310a60961a3SKever Yang 		break;
311a60961a3SKever Yang 	case SCLK_I2C2:
312f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[50]);
313f67f522bSFinley Xiao 		div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
314a60961a3SKever Yang 		break;
315a60961a3SKever Yang 	case SCLK_I2C3:
316f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[50]);
317f67f522bSFinley Xiao 		div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
318a60961a3SKever Yang 		break;
319a60961a3SKever Yang 	default:
320a60961a3SKever Yang 		printf("do not support this i2c bus\n");
321a60961a3SKever Yang 		return -EINVAL;
322a60961a3SKever Yang 	}
323a60961a3SKever Yang 
324cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
325a60961a3SKever Yang }
326a60961a3SKever Yang 
px30_i2c_set_clk(struct px30_clk_priv * priv,ulong clk_id,uint hz)327cefa5186SFinley Xiao static ulong px30_i2c_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
328a60961a3SKever Yang {
329cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
330a60961a3SKever Yang 	int src_clk_div;
331a60961a3SKever Yang 
332cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
333cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 127);
334a60961a3SKever Yang 
335a60961a3SKever Yang 	switch (clk_id) {
336a60961a3SKever Yang 	case SCLK_I2C0:
337f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[49],
338f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT |
339f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT,
340f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT |
341f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT);
342a60961a3SKever Yang 		break;
343a60961a3SKever Yang 	case SCLK_I2C1:
344f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[49],
345f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT |
346f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT,
347f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT |
348f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT);
349a60961a3SKever Yang 		break;
350a60961a3SKever Yang 	case SCLK_I2C2:
351f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[50],
352f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT |
353f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT,
354f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT |
355f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT);
356a60961a3SKever Yang 		break;
357a60961a3SKever Yang 	case SCLK_I2C3:
358f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[50],
359f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT |
360f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT,
361f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT |
362f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT);
363a60961a3SKever Yang 		break;
364a60961a3SKever Yang 	default:
365a60961a3SKever Yang 		printf("do not support this i2c bus\n");
366a60961a3SKever Yang 		return -EINVAL;
367a60961a3SKever Yang 	}
368a60961a3SKever Yang 
369cefa5186SFinley Xiao 	return px30_i2c_get_clk(priv, clk_id);
370a60961a3SKever Yang }
371a60961a3SKever Yang 
37295f26412SSugar Zhang /*
37395f26412SSugar Zhang  * calculate best rational approximation for a given fraction
37495f26412SSugar Zhang  * taking into account restricted register size, e.g. to find
37595f26412SSugar Zhang  * appropriate values for a pll with 5 bit denominator and
37695f26412SSugar Zhang  * 8 bit numerator register fields, trying to set up with a
37795f26412SSugar Zhang  * frequency ratio of 3.1415, one would say:
37895f26412SSugar Zhang  *
37995f26412SSugar Zhang  * rational_best_approximation(31415, 10000,
38095f26412SSugar Zhang  *		(1 << 8) - 1, (1 << 5) - 1, &n, &d);
38195f26412SSugar Zhang  *
38295f26412SSugar Zhang  * you may look at given_numerator as a fixed point number,
38395f26412SSugar Zhang  * with the fractional part size described in given_denominator.
38495f26412SSugar Zhang  *
38595f26412SSugar Zhang  * for theoretical background, see:
38695f26412SSugar Zhang  * http://en.wikipedia.org/wiki/Continued_fraction
38795f26412SSugar Zhang  */
rational_best_approximation(unsigned long given_numerator,unsigned long given_denominator,unsigned long max_numerator,unsigned long max_denominator,unsigned long * best_numerator,unsigned long * best_denominator)38895f26412SSugar Zhang static void rational_best_approximation(
38995f26412SSugar Zhang 	unsigned long given_numerator, unsigned long given_denominator,
39095f26412SSugar Zhang 	unsigned long max_numerator, unsigned long max_denominator,
39195f26412SSugar Zhang 	unsigned long *best_numerator, unsigned long *best_denominator)
39295f26412SSugar Zhang {
39395f26412SSugar Zhang 	unsigned long n, d, n0, d0, n1, d1;
39495f26412SSugar Zhang 
39595f26412SSugar Zhang 	n = given_numerator;
39695f26412SSugar Zhang 	d = given_denominator;
39795f26412SSugar Zhang 	n0 = 0;
39895f26412SSugar Zhang 	d1 = 0;
39995f26412SSugar Zhang 	n1 = 1;
40095f26412SSugar Zhang 	d0 = 1;
40195f26412SSugar Zhang 	for (;;) {
40295f26412SSugar Zhang 		unsigned long t, a;
40395f26412SSugar Zhang 
40495f26412SSugar Zhang 		if (n1 > max_numerator || d1 > max_denominator) {
40595f26412SSugar Zhang 			n1 = n0;
40695f26412SSugar Zhang 			d1 = d0;
40795f26412SSugar Zhang 			break;
40895f26412SSugar Zhang 		}
40995f26412SSugar Zhang 		if (d == 0)
41095f26412SSugar Zhang 			break;
41195f26412SSugar Zhang 		t = d;
41295f26412SSugar Zhang 		a = n / d;
41395f26412SSugar Zhang 		d = n % d;
41495f26412SSugar Zhang 		n = t;
41595f26412SSugar Zhang 		t = n0 + a * n1;
41695f26412SSugar Zhang 		n0 = n1;
41795f26412SSugar Zhang 		n1 = t;
41895f26412SSugar Zhang 		t = d0 + a * d1;
41995f26412SSugar Zhang 		d0 = d1;
42095f26412SSugar Zhang 		d1 = t;
42195f26412SSugar Zhang 	}
42295f26412SSugar Zhang 	*best_numerator = n1;
42395f26412SSugar Zhang 	*best_denominator = d1;
42495f26412SSugar Zhang }
42595f26412SSugar Zhang 
px30_i2s_get_clk(struct px30_clk_priv * priv,ulong clk_id)42695f26412SSugar Zhang static ulong px30_i2s_get_clk(struct px30_clk_priv *priv, ulong clk_id)
42795f26412SSugar Zhang {
42895f26412SSugar Zhang 	u32 con, fracdiv, gate;
42995f26412SSugar Zhang 	u32 clk_src = GPLL_HZ / 2;
43095f26412SSugar Zhang 	unsigned long m, n;
43195f26412SSugar Zhang 	struct px30_cru *cru = priv->cru;
43295f26412SSugar Zhang 
43395f26412SSugar Zhang 	switch (clk_id) {
43495f26412SSugar Zhang 	case SCLK_I2S1:
43595f26412SSugar Zhang 		con = readl(&cru->clksel_con[30]);
43695f26412SSugar Zhang 		fracdiv = readl(&cru->clksel_con[31]);
43795f26412SSugar Zhang 		gate = readl(&cru->clkgate_con[10]);
4389936e5ddSWyon Bi 		n = fracdiv & CLK_I2S1_FRAC_NUMERATOR_MASK;
4399936e5ddSWyon Bi 		n >>= CLK_I2S1_FRAC_NUMERATOR_SHIFT;
4409936e5ddSWyon Bi 		m = fracdiv & CLK_I2S1_FRAC_DENOMINATOR_MASK;
4419936e5ddSWyon Bi 		m >>= CLK_I2S1_FRAC_DENOMINATOR_SHIFT;
44295f26412SSugar Zhang 		debug("con30: 0x%x, gate: 0x%x, frac: 0x%x\n",
44395f26412SSugar Zhang 		      con, gate, fracdiv);
44495f26412SSugar Zhang 		break;
44595f26412SSugar Zhang 	default:
44695f26412SSugar Zhang 		printf("do not support this i2s bus\n");
44795f26412SSugar Zhang 		return -EINVAL;
44895f26412SSugar Zhang 	}
44995f26412SSugar Zhang 
45095f26412SSugar Zhang 	return clk_src * n / m;
45195f26412SSugar Zhang }
45295f26412SSugar Zhang 
px30_i2s_set_clk(struct px30_clk_priv * priv,ulong clk_id,uint hz)45395f26412SSugar Zhang static ulong px30_i2s_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
45495f26412SSugar Zhang {
45595f26412SSugar Zhang 	u32 clk_src;
45695f26412SSugar Zhang 	unsigned long m, n, val;
45795f26412SSugar Zhang 	struct px30_cru *cru = priv->cru;
45895f26412SSugar Zhang 
45995f26412SSugar Zhang 	clk_src = GPLL_HZ / 2;
46095f26412SSugar Zhang 	rational_best_approximation(hz, clk_src,
46195f26412SSugar Zhang 				    GENMASK(16 - 1, 0),
46295f26412SSugar Zhang 				    GENMASK(16 - 1, 0),
46395f26412SSugar Zhang 				    &m, &n);
46495f26412SSugar Zhang 	switch (clk_id) {
46595f26412SSugar Zhang 	case SCLK_I2S1:
46695f26412SSugar Zhang 		rk_clrsetreg(&cru->clksel_con[30],
46795f26412SSugar Zhang 			     CLK_I2S1_PLL_SEL_MASK, CLK_I2S1_PLL_SEL_GPLL);
46895f26412SSugar Zhang 		rk_clrsetreg(&cru->clksel_con[30],
46995f26412SSugar Zhang 			     CLK_I2S1_DIV_CON_MASK, 0x1);
47095f26412SSugar Zhang 		rk_clrsetreg(&cru->clksel_con[30],
47195f26412SSugar Zhang 			     CLK_I2S1_SEL_MASK, CLK_I2S1_SEL_FRAC);
47295f26412SSugar Zhang 		val = m << CLK_I2S1_FRAC_NUMERATOR_SHIFT | n;
47395f26412SSugar Zhang 		writel(val, &cru->clksel_con[31]);
47495f26412SSugar Zhang 		rk_clrsetreg(&cru->clkgate_con[10],
47595f26412SSugar Zhang 			     CLK_I2S1_OUT_MCLK_PAD_MASK,
47695f26412SSugar Zhang 			     CLK_I2S1_OUT_MCLK_PAD_ENABLE);
47795f26412SSugar Zhang 		break;
47895f26412SSugar Zhang 	default:
47995f26412SSugar Zhang 		printf("do not support this i2s bus\n");
48095f26412SSugar Zhang 		return -EINVAL;
48195f26412SSugar Zhang 	}
48295f26412SSugar Zhang 
48395f26412SSugar Zhang 	return px30_i2s_get_clk(priv, clk_id);
48495f26412SSugar Zhang }
48595f26412SSugar Zhang 
px30_i2s1_mclk_get_clk(struct px30_clk_priv * priv,ulong clk_id)486a9cbfff9SWyon Bi static ulong px30_i2s1_mclk_get_clk(struct px30_clk_priv *priv, ulong clk_id)
487a9cbfff9SWyon Bi {
488a9cbfff9SWyon Bi 	struct px30_cru *cru = priv->cru;
489a9cbfff9SWyon Bi 	u32 con;
490a9cbfff9SWyon Bi 
491a9cbfff9SWyon Bi 	con = readl(&cru->clksel_con[30]);
492a9cbfff9SWyon Bi 
493a9cbfff9SWyon Bi 	if (con & CLK_I2S1_OUT_SEL_MASK)
494a9cbfff9SWyon Bi 		return 12000000;
495a9cbfff9SWyon Bi 
496a9cbfff9SWyon Bi 	return px30_i2s_get_clk(priv, SCLK_I2S1);
497a9cbfff9SWyon Bi }
498a9cbfff9SWyon Bi 
px30_i2s1_mclk_set_clk(struct px30_clk_priv * priv,ulong clk_id,ulong hz)499a9cbfff9SWyon Bi static ulong px30_i2s1_mclk_set_clk(struct px30_clk_priv *priv, ulong clk_id,
500a9cbfff9SWyon Bi 				    ulong hz)
501a9cbfff9SWyon Bi {
502a9cbfff9SWyon Bi 	struct px30_cru *cru = priv->cru;
503a9cbfff9SWyon Bi 
504a9cbfff9SWyon Bi 	if (hz == 12000000) {
505a9cbfff9SWyon Bi 		rk_clrsetreg(&cru->clksel_con[30], CLK_I2S1_OUT_SEL_MASK,
506a9cbfff9SWyon Bi 			     CLK_I2S1_OUT_SEL_OSC);
507a9cbfff9SWyon Bi 	} else {
508*f5b1a4f2SJianqun Xu 		px30_i2s_set_clk(priv, SCLK_I2S1, hz);
509a9cbfff9SWyon Bi 		rk_clrsetreg(&cru->clksel_con[30], CLK_I2S1_OUT_SEL_MASK,
510a9cbfff9SWyon Bi 			     CLK_I2S1_OUT_SEL_I2S1);
511a9cbfff9SWyon Bi 	}
512a9cbfff9SWyon Bi 
513a9cbfff9SWyon Bi 	rk_clrsetreg(&cru->clkgate_con[10], CLK_I2S1_OUT_MCLK_PAD_MASK,
514a9cbfff9SWyon Bi 		     CLK_I2S1_OUT_MCLK_PAD_ENABLE);
515a9cbfff9SWyon Bi 
516a9cbfff9SWyon Bi 	return px30_i2s1_mclk_get_clk(priv, clk_id);
517a9cbfff9SWyon Bi }
518a9cbfff9SWyon Bi 
px30_nandc_get_clk(struct px30_clk_priv * priv)519cefa5186SFinley Xiao static ulong px30_nandc_get_clk(struct px30_clk_priv *priv)
520a60961a3SKever Yang {
521cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
522cefa5186SFinley Xiao 	u32 div, con;
523cefa5186SFinley Xiao 
524cefa5186SFinley Xiao 	con = readl(&cru->clksel_con[15]);
525cefa5186SFinley Xiao 	div = (con & NANDC_DIV_MASK) >> NANDC_DIV_SHIFT;
526cefa5186SFinley Xiao 
52760a1199eSYifeng Zhao 	return DIV_TO_RATE(priv->gpll_hz, div);
528cefa5186SFinley Xiao }
529cefa5186SFinley Xiao 
px30_nandc_set_clk(struct px30_clk_priv * priv,ulong set_rate)530cefa5186SFinley Xiao static ulong px30_nandc_set_clk(struct px30_clk_priv *priv,
531cefa5186SFinley Xiao 				ulong set_rate)
532cefa5186SFinley Xiao {
533cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
534cefa5186SFinley Xiao 	int src_clk_div;
535cefa5186SFinley Xiao 
536cefa5186SFinley Xiao 	/* Select nandc source from GPLL by default */
537cefa5186SFinley Xiao 	/* nandc clock defaulg div 2 internal, need provide double in cru */
53860a1199eSYifeng Zhao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, set_rate);
539cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 31);
540cefa5186SFinley Xiao 
541cefa5186SFinley Xiao 	rk_clrsetreg(&cru->clksel_con[15],
542cefa5186SFinley Xiao 		     NANDC_CLK_SEL_MASK | NANDC_PLL_MASK |
543cefa5186SFinley Xiao 		     NANDC_DIV_MASK,
544cefa5186SFinley Xiao 		     NANDC_CLK_SEL_NANDC << NANDC_CLK_SEL_SHIFT |
545cefa5186SFinley Xiao 		     NANDC_SEL_GPLL << NANDC_PLL_SHIFT |
546cefa5186SFinley Xiao 		     (src_clk_div - 1) << NANDC_DIV_SHIFT);
547cefa5186SFinley Xiao 
548cefa5186SFinley Xiao 	return px30_nandc_get_clk(priv);
549cefa5186SFinley Xiao }
550cefa5186SFinley Xiao 
px30_mmc_get_clk(struct px30_clk_priv * priv,uint clk_id)551cefa5186SFinley Xiao static ulong px30_mmc_get_clk(struct px30_clk_priv *priv, uint clk_id)
552cefa5186SFinley Xiao {
553cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
554a60961a3SKever Yang 	u32 div, con, con_id;
555a60961a3SKever Yang 
556a60961a3SKever Yang 	switch (clk_id) {
557a60961a3SKever Yang 	case HCLK_SDMMC:
558a60961a3SKever Yang 	case SCLK_SDMMC:
559a60961a3SKever Yang 		con_id = 16;
560a60961a3SKever Yang 		break;
561a60961a3SKever Yang 	case HCLK_EMMC:
562a60961a3SKever Yang 	case SCLK_EMMC:
56396f1b3d9SKever Yang 	case SCLK_EMMC_SAMPLE:
564a60961a3SKever Yang 		con_id = 20;
565a60961a3SKever Yang 		break;
566a60961a3SKever Yang 	default:
567a60961a3SKever Yang 		return -EINVAL;
568a60961a3SKever Yang 	}
569a60961a3SKever Yang 
570a60961a3SKever Yang 	con = readl(&cru->clksel_con[con_id]);
571a60961a3SKever Yang 	div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT;
572a60961a3SKever Yang 
573a60961a3SKever Yang 	if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT
574a60961a3SKever Yang 	    == EMMC_SEL_24M)
575a60961a3SKever Yang 		return DIV_TO_RATE(OSC_HZ, div) / 2;
576a60961a3SKever Yang 	else
577cefa5186SFinley Xiao 		return DIV_TO_RATE(priv->gpll_hz, div) / 2;
578a60961a3SKever Yang 
579a60961a3SKever Yang }
580a60961a3SKever Yang 
px30_mmc_set_clk(struct px30_clk_priv * priv,ulong clk_id,ulong set_rate)581cefa5186SFinley Xiao static ulong px30_mmc_set_clk(struct px30_clk_priv *priv,
582a60961a3SKever Yang 			      ulong clk_id, ulong set_rate)
583a60961a3SKever Yang {
584cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
585a60961a3SKever Yang 	int src_clk_div;
586a60961a3SKever Yang 	u32 con_id;
587a60961a3SKever Yang 
588a60961a3SKever Yang 	switch (clk_id) {
589a60961a3SKever Yang 	case HCLK_SDMMC:
590a60961a3SKever Yang 	case SCLK_SDMMC:
591a60961a3SKever Yang 		con_id = 16;
592a60961a3SKever Yang 		break;
593a60961a3SKever Yang 	case HCLK_EMMC:
594a60961a3SKever Yang 	case SCLK_EMMC:
595a60961a3SKever Yang 		con_id = 20;
596a60961a3SKever Yang 		break;
597a60961a3SKever Yang 	default:
598a60961a3SKever Yang 		return -EINVAL;
599a60961a3SKever Yang 	}
600cefa5186SFinley Xiao 
601a60961a3SKever Yang 	/* Select clk_sdmmc/emmc source from GPLL by default */
602a60961a3SKever Yang 	/* mmc clock defaulg div 2 internal, need provide double in cru */
603cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz / 2, set_rate);
604a60961a3SKever Yang 
605a60961a3SKever Yang 	if (src_clk_div > 127) {
606a60961a3SKever Yang 		/* use 24MHz source for 400KHz clock */
607a60961a3SKever Yang 		src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate);
608a60961a3SKever Yang 		rk_clrsetreg(&cru->clksel_con[con_id],
609a60961a3SKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
610a60961a3SKever Yang 			     EMMC_SEL_24M << EMMC_PLL_SHIFT |
611a60961a3SKever Yang 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
612a60961a3SKever Yang 	} else {
613a60961a3SKever Yang 		rk_clrsetreg(&cru->clksel_con[con_id],
614a60961a3SKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
615a60961a3SKever Yang 			     EMMC_SEL_GPLL << EMMC_PLL_SHIFT |
616a60961a3SKever Yang 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
617a60961a3SKever Yang 	}
618a60961a3SKever Yang 	rk_clrsetreg(&cru->clksel_con[con_id +1], EMMC_CLK_SEL_MASK,
619a60961a3SKever Yang 		     EMMC_CLK_SEL_EMMC);
620a60961a3SKever Yang 
621cefa5186SFinley Xiao 	return px30_mmc_get_clk(priv, clk_id);
622a60961a3SKever Yang }
623a60961a3SKever Yang 
px30_sfc_get_clk(struct px30_clk_priv * priv,uint clk_id)6249bc02da5SElaine Zhang static ulong px30_sfc_get_clk(struct px30_clk_priv *priv, uint clk_id)
6259bc02da5SElaine Zhang {
6269bc02da5SElaine Zhang 	struct px30_cru *cru = priv->cru;
6279bc02da5SElaine Zhang 	u32 div, con;
6289bc02da5SElaine Zhang 
6299bc02da5SElaine Zhang 	con = readl(&cru->clksel_con[22]);
6309bc02da5SElaine Zhang 	div = (con & SFC_DIV_CON_MASK) >> SFC_DIV_CON_SHIFT;
6319bc02da5SElaine Zhang 
6329bc02da5SElaine Zhang 	return DIV_TO_RATE(priv->gpll_hz, div);
6339bc02da5SElaine Zhang }
6349bc02da5SElaine Zhang 
px30_sfc_set_clk(struct px30_clk_priv * priv,ulong clk_id,ulong set_rate)6359bc02da5SElaine Zhang static ulong px30_sfc_set_clk(struct px30_clk_priv *priv,
6369bc02da5SElaine Zhang 			      ulong clk_id, ulong set_rate)
6379bc02da5SElaine Zhang {
6389bc02da5SElaine Zhang 	struct px30_cru *cru = priv->cru;
6399bc02da5SElaine Zhang 	int src_clk_div;
6409bc02da5SElaine Zhang 
6419bc02da5SElaine Zhang 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, set_rate);
6429bc02da5SElaine Zhang 	rk_clrsetreg(&cru->clksel_con[22],
6439bc02da5SElaine Zhang 		     SFC_PLL_SEL_MASK | SFC_DIV_CON_MASK,
6449bc02da5SElaine Zhang 		     0 << SFC_PLL_SEL_SHIFT |
6459bc02da5SElaine Zhang 		     (src_clk_div - 1) << SFC_DIV_CON_SHIFT);
6469bc02da5SElaine Zhang 
6479bc02da5SElaine Zhang 	return px30_sfc_get_clk(priv, clk_id);
6489bc02da5SElaine Zhang }
6499bc02da5SElaine Zhang 
px30_pwm_get_clk(struct px30_clk_priv * priv,ulong clk_id)650cefa5186SFinley Xiao static ulong px30_pwm_get_clk(struct px30_clk_priv *priv, ulong clk_id)
651a60961a3SKever Yang {
652cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
653a60961a3SKever Yang 	u32 div, con;
654a60961a3SKever Yang 
655f67f522bSFinley Xiao 	switch (clk_id) {
656f67f522bSFinley Xiao 	case SCLK_PWM0:
657f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[52]);
658f67f522bSFinley Xiao 		div = con >> CLK_PWM0_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK;
659f67f522bSFinley Xiao 		break;
660f67f522bSFinley Xiao 	case SCLK_PWM1:
661f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[52]);
662f67f522bSFinley Xiao 		div = con >> CLK_PWM1_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK;
663f67f522bSFinley Xiao 		break;
664f67f522bSFinley Xiao 	default:
665f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
666f67f522bSFinley Xiao 		return -EINVAL;
667f67f522bSFinley Xiao 	}
668f67f522bSFinley Xiao 
669cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
670a60961a3SKever Yang }
671a60961a3SKever Yang 
px30_pwm_set_clk(struct px30_clk_priv * priv,ulong clk_id,uint hz)672cefa5186SFinley Xiao static ulong px30_pwm_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
673a60961a3SKever Yang {
674cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
675f67f522bSFinley Xiao 	int src_clk_div;
676a60961a3SKever Yang 
677cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
678cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 127);
679f67f522bSFinley Xiao 
680f67f522bSFinley Xiao 	switch (clk_id) {
681f67f522bSFinley Xiao 	case SCLK_PWM0:
682f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[52],
683f67f522bSFinley Xiao 			     CLK_PWM_DIV_CON_MASK << CLK_PWM0_DIV_CON_SHIFT |
684f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_MASK << CLK_PWM0_PLL_SEL_SHIFT,
685f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_PWM0_DIV_CON_SHIFT |
686f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_GPLL << CLK_PWM0_PLL_SEL_SHIFT);
687f67f522bSFinley Xiao 		break;
688f67f522bSFinley Xiao 	case SCLK_PWM1:
689f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[52],
690f67f522bSFinley Xiao 			     CLK_PWM_DIV_CON_MASK << CLK_PWM1_DIV_CON_SHIFT |
691f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_MASK << CLK_PWM1_PLL_SEL_SHIFT,
692f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_PWM1_DIV_CON_SHIFT |
693f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_GPLL << CLK_PWM1_PLL_SEL_SHIFT);
694f67f522bSFinley Xiao 		break;
695f67f522bSFinley Xiao 	default:
696f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
697f67f522bSFinley Xiao 		return -EINVAL;
698f67f522bSFinley Xiao 	}
699f67f522bSFinley Xiao 
700cefa5186SFinley Xiao 	return px30_pwm_get_clk(priv, clk_id);
701a60961a3SKever Yang }
702a60961a3SKever Yang 
px30_saradc_get_clk(struct px30_clk_priv * priv)703cefa5186SFinley Xiao static ulong px30_saradc_get_clk(struct px30_clk_priv *priv)
704a60961a3SKever Yang {
705cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
706f67f522bSFinley Xiao 	u32 div, con;
707f67f522bSFinley Xiao 
708f67f522bSFinley Xiao 	con = readl(&cru->clksel_con[55]);
709f9157291SFinley Xiao 	div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK;
710a60961a3SKever Yang 
711a60961a3SKever Yang 	return DIV_TO_RATE(OSC_HZ, div);
712a60961a3SKever Yang }
713a60961a3SKever Yang 
px30_saradc_set_clk(struct px30_clk_priv * priv,uint hz)714cefa5186SFinley Xiao static ulong px30_saradc_set_clk(struct px30_clk_priv *priv, uint hz)
715a60961a3SKever Yang {
716cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
717a60961a3SKever Yang 	int src_clk_div;
718a60961a3SKever Yang 
719cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz);
720cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 2047);
721a60961a3SKever Yang 
722f67f522bSFinley Xiao 	rk_clrsetreg(&cru->clksel_con[55],
723f67f522bSFinley Xiao 		     CLK_SARADC_DIV_CON_MASK,
724fce7cb7bSFinley Xiao 		     (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT);
725f67f522bSFinley Xiao 
726cefa5186SFinley Xiao 	return px30_saradc_get_clk(priv);
727f67f522bSFinley Xiao }
728f67f522bSFinley Xiao 
px30_tsadc_get_clk(struct px30_clk_priv * priv)729cb3c37fcSElaine Zhang static ulong px30_tsadc_get_clk(struct px30_clk_priv *priv)
730cb3c37fcSElaine Zhang {
731cb3c37fcSElaine Zhang 	struct px30_cru *cru = priv->cru;
732cb3c37fcSElaine Zhang 	u32 div, con;
733cb3c37fcSElaine Zhang 
734cb3c37fcSElaine Zhang 	con = readl(&cru->clksel_con[54]);
735cb3c37fcSElaine Zhang 	div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK;
736cb3c37fcSElaine Zhang 
737cb3c37fcSElaine Zhang 	return DIV_TO_RATE(OSC_HZ, div);
738cb3c37fcSElaine Zhang }
739cb3c37fcSElaine Zhang 
px30_tsadc_set_clk(struct px30_clk_priv * priv,uint hz)740cb3c37fcSElaine Zhang static ulong px30_tsadc_set_clk(struct px30_clk_priv *priv, uint hz)
741cb3c37fcSElaine Zhang {
742cb3c37fcSElaine Zhang 	struct px30_cru *cru = priv->cru;
743cb3c37fcSElaine Zhang 	int src_clk_div;
744cb3c37fcSElaine Zhang 
745cb3c37fcSElaine Zhang 	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz);
746cb3c37fcSElaine Zhang 	assert(src_clk_div - 1 <= 2047);
747cb3c37fcSElaine Zhang 
748cb3c37fcSElaine Zhang 	rk_clrsetreg(&cru->clksel_con[54],
749cb3c37fcSElaine Zhang 		     CLK_SARADC_DIV_CON_MASK,
750cb3c37fcSElaine Zhang 		     (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT);
751cb3c37fcSElaine Zhang 
752cb3c37fcSElaine Zhang 	return px30_tsadc_get_clk(priv);
753cb3c37fcSElaine Zhang }
754cb3c37fcSElaine Zhang 
px30_spi_get_clk(struct px30_clk_priv * priv,ulong clk_id)755cefa5186SFinley Xiao static ulong px30_spi_get_clk(struct px30_clk_priv *priv, ulong clk_id)
756f67f522bSFinley Xiao {
757cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
758f67f522bSFinley Xiao 	u32 div, con;
759f67f522bSFinley Xiao 
760f67f522bSFinley Xiao 	switch (clk_id) {
761fce7cb7bSFinley Xiao 	case SCLK_SPI0:
762f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[53]);
763f67f522bSFinley Xiao 		div = con >> CLK_SPI0_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK;
764f67f522bSFinley Xiao 		break;
765fce7cb7bSFinley Xiao 	case SCLK_SPI1:
766f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[53]);
767f67f522bSFinley Xiao 		div = con >> CLK_SPI1_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK;
768f67f522bSFinley Xiao 		break;
769f67f522bSFinley Xiao 	default:
770f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
771f67f522bSFinley Xiao 		return -EINVAL;
772f67f522bSFinley Xiao 	}
773f67f522bSFinley Xiao 
774cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
775f67f522bSFinley Xiao }
776f67f522bSFinley Xiao 
px30_spi_set_clk(struct px30_clk_priv * priv,ulong clk_id,uint hz)777cefa5186SFinley Xiao static ulong px30_spi_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
778f67f522bSFinley Xiao {
779cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
780f67f522bSFinley Xiao 	int src_clk_div;
781f67f522bSFinley Xiao 
782cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
783cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 127);
784f67f522bSFinley Xiao 
785f67f522bSFinley Xiao 	switch (clk_id) {
786f67f522bSFinley Xiao 	case SCLK_SPI0:
787f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[53],
788f67f522bSFinley Xiao 			     CLK_SPI_DIV_CON_MASK << CLK_SPI0_DIV_CON_SHIFT |
789f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_MASK << CLK_SPI0_PLL_SEL_SHIFT,
790f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_SPI0_DIV_CON_SHIFT |
791f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_GPLL << CLK_SPI0_PLL_SEL_SHIFT);
792f67f522bSFinley Xiao 		break;
793f67f522bSFinley Xiao 	case SCLK_SPI1:
794f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[53],
795f67f522bSFinley Xiao 			     CLK_SPI_DIV_CON_MASK << CLK_SPI1_DIV_CON_SHIFT |
796f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_MASK << CLK_SPI1_PLL_SEL_SHIFT,
797f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_SPI1_DIV_CON_SHIFT |
798f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_GPLL << CLK_SPI1_PLL_SEL_SHIFT);
799f67f522bSFinley Xiao 		break;
800f67f522bSFinley Xiao 	default:
801f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
802f67f522bSFinley Xiao 		return -EINVAL;
803f67f522bSFinley Xiao 	}
804f67f522bSFinley Xiao 
805cefa5186SFinley Xiao 	return px30_spi_get_clk(priv, clk_id);
806a60961a3SKever Yang }
807a60961a3SKever Yang 
px30_vop_get_clk(struct px30_clk_priv * priv,ulong clk_id)808cefa5186SFinley Xiao static ulong px30_vop_get_clk(struct px30_clk_priv *priv, ulong clk_id)
80930f1f38dSFinley Xiao {
810cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
81130f1f38dSFinley Xiao 	u32 div, con, parent;
81230f1f38dSFinley Xiao 
81330f1f38dSFinley Xiao 	switch (clk_id) {
81430f1f38dSFinley Xiao 	case ACLK_VOPB:
815c996ae8aSFinley Xiao 	case ACLK_VOPL:
81630f1f38dSFinley Xiao 		con = readl(&cru->clksel_con[3]);
81730f1f38dSFinley Xiao 		div = con & ACLK_VO_DIV_MASK;
818cefa5186SFinley Xiao 		parent = priv->gpll_hz;
81930f1f38dSFinley Xiao 		break;
82030f1f38dSFinley Xiao 	case DCLK_VOPB:
82130f1f38dSFinley Xiao 		con = readl(&cru->clksel_con[5]);
82230f1f38dSFinley Xiao 		div = con & DCLK_VOPB_DIV_MASK;
823cefa5186SFinley Xiao 		parent = rkclk_pll_get_rate(&cru->pll[CPLL], &cru->mode, CPLL);
82430f1f38dSFinley Xiao 		break;
825c996ae8aSFinley Xiao 	case DCLK_VOPL:
826c996ae8aSFinley Xiao 		con = readl(&cru->clksel_con[8]);
827c996ae8aSFinley Xiao 		div = con & DCLK_VOPL_DIV_MASK;
828c996ae8aSFinley Xiao 		parent = rkclk_pll_get_rate(&cru->pll[NPLL], &cru->mode, NPLL);
829c996ae8aSFinley Xiao 		break;
83030f1f38dSFinley Xiao 	default:
83130f1f38dSFinley Xiao 		return -ENOENT;
83230f1f38dSFinley Xiao 	}
83330f1f38dSFinley Xiao 
83430f1f38dSFinley Xiao 	return DIV_TO_RATE(parent, div);
83530f1f38dSFinley Xiao }
83630f1f38dSFinley Xiao 
px30_vop_set_clk(struct px30_clk_priv * priv,ulong clk_id,uint hz)837cefa5186SFinley Xiao static ulong px30_vop_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
83830f1f38dSFinley Xiao {
839cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
840d101530aSFinley Xiao 	ulong npll_hz;
84130f1f38dSFinley Xiao 	int src_clk_div;
84230f1f38dSFinley Xiao 
84330f1f38dSFinley Xiao 	switch (clk_id) {
84430f1f38dSFinley Xiao 	case ACLK_VOPB:
845d101530aSFinley Xiao 	case ACLK_VOPL:
846f909d4a8SFinley Xiao 		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
847f909d4a8SFinley Xiao 		assert(src_clk_div - 1 <= 31);
84830f1f38dSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[3],
84930f1f38dSFinley Xiao 			     ACLK_VO_PLL_MASK | ACLK_VO_DIV_MASK,
85030f1f38dSFinley Xiao 			     ACLK_VO_SEL_GPLL << ACLK_VO_PLL_SHIFT |
85130f1f38dSFinley Xiao 			     (src_clk_div - 1) << ACLK_VO_DIV_SHIFT);
85230f1f38dSFinley Xiao 		break;
85330f1f38dSFinley Xiao 	case DCLK_VOPB:
854fda8d873SElaine Zhang 		if (hz < PX30_VOP_PLL_LIMIT) {
855f909d4a8SFinley Xiao 			src_clk_div = DIV_ROUND_UP(PX30_VOP_PLL_LIMIT, hz);
856fda8d873SElaine Zhang 			if (src_clk_div % 2)
857fda8d873SElaine Zhang 				src_clk_div = src_clk_div - 1;
858fda8d873SElaine Zhang 		} else {
859f909d4a8SFinley Xiao 			src_clk_div = 1;
860fda8d873SElaine Zhang 		}
861f909d4a8SFinley Xiao 		assert(src_clk_div - 1 <= 255);
862f909d4a8SFinley Xiao 		rkclk_set_pll(&cru->pll[CPLL], &cru->mode, CPLL, hz * src_clk_div);
86330f1f38dSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[5],
86430f1f38dSFinley Xiao 			     DCLK_VOPB_SEL_MASK | DCLK_VOPB_PLL_SEL_MASK |
86530f1f38dSFinley Xiao 			     DCLK_VOPB_DIV_MASK,
86630f1f38dSFinley Xiao 			     DCLK_VOPB_SEL_DIVOUT << DCLK_VOPB_SEL_SHIFT |
86730f1f38dSFinley Xiao 			     DCLK_VOPB_PLL_SEL_CPLL << DCLK_VOPB_PLL_SEL_SHIFT |
868f909d4a8SFinley Xiao 			     (src_clk_div - 1) << DCLK_VOPB_DIV_SHIFT);
86930f1f38dSFinley Xiao 		break;
870d101530aSFinley Xiao 	case DCLK_VOPL:
871d101530aSFinley Xiao 		npll_hz = px30_clk_get_pll_rate(priv, NPLL);
872d101530aSFinley Xiao 		if (npll_hz >= PX30_VOP_PLL_LIMIT && npll_hz >= hz && npll_hz % hz == 0) {
873d101530aSFinley Xiao 			src_clk_div = npll_hz / hz;
874d101530aSFinley Xiao 			assert(src_clk_div - 1 <= 255);
875d101530aSFinley Xiao 		} else {
876fda8d873SElaine Zhang 			if (hz < PX30_VOP_PLL_LIMIT) {
877d101530aSFinley Xiao 				src_clk_div = DIV_ROUND_UP(PX30_VOP_PLL_LIMIT, hz);
878fda8d873SElaine Zhang 				if (src_clk_div % 2)
879fda8d873SElaine Zhang 					src_clk_div = src_clk_div - 1;
880fda8d873SElaine Zhang 			} else {
881d101530aSFinley Xiao 				src_clk_div = 1;
882fda8d873SElaine Zhang 			}
883d101530aSFinley Xiao 			assert(src_clk_div - 1 <= 255);
884d101530aSFinley Xiao 			rkclk_set_pll(&cru->pll[NPLL], &cru->mode, NPLL, hz * src_clk_div);
885d101530aSFinley Xiao 		}
886c996ae8aSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[8],
887d101530aSFinley Xiao 			     DCLK_VOPL_SEL_MASK | DCLK_VOPL_PLL_SEL_MASK |
888d101530aSFinley Xiao 			     DCLK_VOPL_DIV_MASK,
889d101530aSFinley Xiao 			     DCLK_VOPL_SEL_DIVOUT << DCLK_VOPL_SEL_SHIFT |
890d101530aSFinley Xiao 			     DCLK_VOPL_PLL_SEL_NPLL << DCLK_VOPL_PLL_SEL_SHIFT |
891d101530aSFinley Xiao 			     (src_clk_div - 1) << DCLK_VOPL_DIV_SHIFT);
892d101530aSFinley Xiao 		break;
89330f1f38dSFinley Xiao 	default:
89430f1f38dSFinley Xiao 		printf("do not support this vop freq\n");
89530f1f38dSFinley Xiao 		return -EINVAL;
89630f1f38dSFinley Xiao 	}
89730f1f38dSFinley Xiao 
898cefa5186SFinley Xiao 	return px30_vop_get_clk(priv, clk_id);
89930f1f38dSFinley Xiao }
90030f1f38dSFinley Xiao 
px30_bus_get_clk(struct px30_clk_priv * priv,ulong clk_id)901cefa5186SFinley Xiao static ulong px30_bus_get_clk(struct px30_clk_priv *priv, ulong clk_id)
902df8f8a42SFinley Xiao {
903cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
904df8f8a42SFinley Xiao 	u32 div, con, parent;
905df8f8a42SFinley Xiao 
906df8f8a42SFinley Xiao 	switch (clk_id) {
907df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
908df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[23]);
909df8f8a42SFinley Xiao 		div = (con & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT;
910cefa5186SFinley Xiao 		parent = priv->gpll_hz;
911df8f8a42SFinley Xiao 		break;
912df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
913df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[24]);
914df8f8a42SFinley Xiao 		div = (con & BUS_HCLK_DIV_MASK) >> BUS_HCLK_DIV_SHIFT;
915cefa5186SFinley Xiao 		parent = priv->gpll_hz;
916df8f8a42SFinley Xiao 		break;
917df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
9188afd7ff1SElaine Zhang 	case PCLK_WDT_NS:
919cefa5186SFinley Xiao 		parent = px30_bus_get_clk(priv, ACLK_BUS_PRE);
920df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[24]);
921df8f8a42SFinley Xiao 		div = (con & BUS_PCLK_DIV_MASK) >> BUS_PCLK_DIV_SHIFT;
922df8f8a42SFinley Xiao 		break;
923df8f8a42SFinley Xiao 	default:
924df8f8a42SFinley Xiao 		return -ENOENT;
925df8f8a42SFinley Xiao 	}
926df8f8a42SFinley Xiao 
927df8f8a42SFinley Xiao 	return DIV_TO_RATE(parent, div);
928df8f8a42SFinley Xiao }
929df8f8a42SFinley Xiao 
px30_bus_set_clk(struct px30_clk_priv * priv,ulong clk_id,ulong hz)930cefa5186SFinley Xiao static ulong px30_bus_set_clk(struct px30_clk_priv *priv, ulong clk_id,
931cefa5186SFinley Xiao 			      ulong hz)
932df8f8a42SFinley Xiao {
933cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
934df8f8a42SFinley Xiao 	int src_clk_div;
935df8f8a42SFinley Xiao 
936df8f8a42SFinley Xiao 	/*
937df8f8a42SFinley Xiao 	 * select gpll as pd_bus bus clock source and
938df8f8a42SFinley Xiao 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
939df8f8a42SFinley Xiao 	 */
940df8f8a42SFinley Xiao 	switch (clk_id) {
941df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
942cefa5186SFinley Xiao 		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
943cb981eeaSFinley Xiao 		assert(src_clk_div - 1 <= 31);
944df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[23],
945df8f8a42SFinley Xiao 			     BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK,
946df8f8a42SFinley Xiao 			     BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT |
947df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_ACLK_DIV_SHIFT);
948df8f8a42SFinley Xiao 		break;
949df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
950cefa5186SFinley Xiao 		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
951cb981eeaSFinley Xiao 		assert(src_clk_div - 1 <= 31);
952df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[24],
953df8f8a42SFinley Xiao 			     BUS_PLL_SEL_MASK | BUS_HCLK_DIV_MASK,
954df8f8a42SFinley Xiao 			     BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT |
955df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_HCLK_DIV_SHIFT);
956df8f8a42SFinley Xiao 		break;
957df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
958cefa5186SFinley Xiao 		src_clk_div =
959cefa5186SFinley Xiao 			DIV_ROUND_UP(px30_bus_get_clk(priv, ACLK_BUS_PRE), hz);
960cb981eeaSFinley Xiao 		assert(src_clk_div - 1 <= 3);
961df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[24],
962df8f8a42SFinley Xiao 			     BUS_PCLK_DIV_MASK,
963df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_PCLK_DIV_SHIFT);
964df8f8a42SFinley Xiao 		break;
965df8f8a42SFinley Xiao 	default:
966df8f8a42SFinley Xiao 		printf("do not support this bus freq\n");
967df8f8a42SFinley Xiao 		return -EINVAL;
968df8f8a42SFinley Xiao 	}
969df8f8a42SFinley Xiao 
970cefa5186SFinley Xiao 	return px30_bus_get_clk(priv, clk_id);
971df8f8a42SFinley Xiao }
972df8f8a42SFinley Xiao 
px30_peri_get_clk(struct px30_clk_priv * priv,ulong clk_id)973cefa5186SFinley Xiao static ulong px30_peri_get_clk(struct px30_clk_priv *priv, ulong clk_id)
974df8f8a42SFinley Xiao {
975cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
976df8f8a42SFinley Xiao 	u32 div, con, parent;
977df8f8a42SFinley Xiao 
978df8f8a42SFinley Xiao 	switch (clk_id) {
979df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
980df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[14]);
981df8f8a42SFinley Xiao 		div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT;
982cefa5186SFinley Xiao 		parent = priv->gpll_hz;
983df8f8a42SFinley Xiao 		break;
984df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
985df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[14]);
986df8f8a42SFinley Xiao 		div = (con & PERI_HCLK_DIV_MASK) >> PERI_HCLK_DIV_SHIFT;
987cefa5186SFinley Xiao 		parent = priv->gpll_hz;
988df8f8a42SFinley Xiao 		break;
989df8f8a42SFinley Xiao 	default:
990df8f8a42SFinley Xiao 		return -ENOENT;
991df8f8a42SFinley Xiao 	}
992df8f8a42SFinley Xiao 
993df8f8a42SFinley Xiao 	return DIV_TO_RATE(parent, div);
994df8f8a42SFinley Xiao }
995df8f8a42SFinley Xiao 
px30_peri_set_clk(struct px30_clk_priv * priv,ulong clk_id,ulong hz)996cefa5186SFinley Xiao static ulong px30_peri_set_clk(struct px30_clk_priv *priv, ulong clk_id,
997cefa5186SFinley Xiao 			       ulong hz)
998df8f8a42SFinley Xiao {
999cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
1000df8f8a42SFinley Xiao 	int src_clk_div;
1001df8f8a42SFinley Xiao 
1002cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
1003cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 31);
1004df8f8a42SFinley Xiao 
1005df8f8a42SFinley Xiao 	/*
1006df8f8a42SFinley Xiao 	 * select gpll as pd_peri bus clock source and
1007df8f8a42SFinley Xiao 	 * set up dependent divisors for HCLK and ACLK clocks.
1008df8f8a42SFinley Xiao 	 */
1009df8f8a42SFinley Xiao 	switch (clk_id) {
1010df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
1011df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[14],
1012df8f8a42SFinley Xiao 			     PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK,
1013df8f8a42SFinley Xiao 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
1014df8f8a42SFinley Xiao 			     (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT);
1015df8f8a42SFinley Xiao 		break;
1016df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
1017df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[14],
1018df8f8a42SFinley Xiao 			     PERI_PLL_SEL_MASK | PERI_HCLK_DIV_MASK,
1019df8f8a42SFinley Xiao 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
1020df8f8a42SFinley Xiao 			     (src_clk_div - 1) << PERI_HCLK_DIV_SHIFT);
1021df8f8a42SFinley Xiao 		break;
1022df8f8a42SFinley Xiao 	default:
1023df8f8a42SFinley Xiao 		printf("do not support this peri freq\n");
1024df8f8a42SFinley Xiao 		return -EINVAL;
1025df8f8a42SFinley Xiao 	}
1026df8f8a42SFinley Xiao 
1027cefa5186SFinley Xiao 	return px30_peri_get_clk(priv, clk_id);
1028cefa5186SFinley Xiao }
1029cefa5186SFinley Xiao 
px30_otp_get_clk(struct px30_clk_priv * priv,ulong clk_id)103089cc3f4dSElaine Zhang static ulong px30_otp_get_clk(struct px30_clk_priv *priv, ulong clk_id)
103189cc3f4dSElaine Zhang {
103289cc3f4dSElaine Zhang 	struct px30_cru *cru = priv->cru;
103389cc3f4dSElaine Zhang 	u32 src, div, con, parent;
103489cc3f4dSElaine Zhang 
103589cc3f4dSElaine Zhang 	if (soc_is_px30s()) {
103689cc3f4dSElaine Zhang 		con = readl(&cru->clksel_con[56]);
103789cc3f4dSElaine Zhang 		src = (con & CLK_OTP_S_SEL_MASK) >> CLK_OTP_S_SEL_SHIFT;
103889cc3f4dSElaine Zhang 		div = (con & CLK_OTP_S_DIV_CON_MASK) >> CLK_OTP_S_DIV_CON_SHIFT;
103989cc3f4dSElaine Zhang 		if (src)
104089cc3f4dSElaine Zhang 			return DIV_TO_RATE(priv->gpll_hz, div);
104189cc3f4dSElaine Zhang 		else
104289cc3f4dSElaine Zhang 			return DIV_TO_RATE(OSC_HZ, div);
104389cc3f4dSElaine Zhang 	}
104489cc3f4dSElaine Zhang 
104589cc3f4dSElaine Zhang 	switch (clk_id) {
104689cc3f4dSElaine Zhang 	case SCLK_OTP:
104789cc3f4dSElaine Zhang 		con = readl(&cru->clksel_con[56]);
104889cc3f4dSElaine Zhang 		div = (con & CLK_OTP_DIV_CON_MASK) >> CLK_OTP_DIV_CON_SHIFT;
104989cc3f4dSElaine Zhang 		parent = OSC_HZ;
105089cc3f4dSElaine Zhang 		break;
105189cc3f4dSElaine Zhang 	case SCLK_OTP_USR:
105289cc3f4dSElaine Zhang 		con = readl(&cru->clksel_con[56]);
105389cc3f4dSElaine Zhang 		div = (con & CLK_OTP_USR_DIV_CON_MASK) >>
105489cc3f4dSElaine Zhang 		      CLK_OTP_USR_DIV_CON_SHIFT;
105589cc3f4dSElaine Zhang 		parent = px30_otp_get_clk(priv, SCLK_OTP);
105689cc3f4dSElaine Zhang 		break;
105789cc3f4dSElaine Zhang 	default:
105889cc3f4dSElaine Zhang 		return -ENOENT;
105989cc3f4dSElaine Zhang 	}
106089cc3f4dSElaine Zhang 
106189cc3f4dSElaine Zhang 	return DIV_TO_RATE(parent, div);
106289cc3f4dSElaine Zhang }
106389cc3f4dSElaine Zhang 
px30_otp_set_clk(struct px30_clk_priv * priv,ulong clk_id,ulong hz)106489cc3f4dSElaine Zhang static ulong px30_otp_set_clk(struct px30_clk_priv *priv, ulong clk_id,
106589cc3f4dSElaine Zhang 			      ulong hz)
106689cc3f4dSElaine Zhang {
106789cc3f4dSElaine Zhang 	struct px30_cru *cru = priv->cru;
106889cc3f4dSElaine Zhang 	u32 src, div, parent;
106989cc3f4dSElaine Zhang 
107089cc3f4dSElaine Zhang 	if (soc_is_px30s()) {
107189cc3f4dSElaine Zhang 		if ((OSC_HZ % hz) == 0) {
107289cc3f4dSElaine Zhang 			src = 0;
107389cc3f4dSElaine Zhang 			parent = OSC_HZ;
107489cc3f4dSElaine Zhang 		} else {
107589cc3f4dSElaine Zhang 			src = 1;
107689cc3f4dSElaine Zhang 			parent = priv->gpll_hz;
107789cc3f4dSElaine Zhang 		}
107889cc3f4dSElaine Zhang 		div = DIV_ROUND_UP(parent, hz);
107989cc3f4dSElaine Zhang 		rk_clrsetreg(&cru->clksel_con[56],
108089cc3f4dSElaine Zhang 			     CLK_OTP_S_SEL_MASK | CLK_OTP_S_DIV_CON_MASK,
108189cc3f4dSElaine Zhang 			     src << CLK_OTP_S_SEL_SHIFT |
108289cc3f4dSElaine Zhang 			     (div - 1) << CLK_OTP_S_DIV_CON_SHIFT);
108389cc3f4dSElaine Zhang 		return px30_otp_get_clk(priv, clk_id);
108489cc3f4dSElaine Zhang 	}
108589cc3f4dSElaine Zhang 
108689cc3f4dSElaine Zhang 	switch (clk_id) {
108789cc3f4dSElaine Zhang 	case SCLK_OTP:
108889cc3f4dSElaine Zhang 		div = DIV_ROUND_UP(OSC_HZ, hz);
108989cc3f4dSElaine Zhang 		rk_clrsetreg(&cru->clksel_con[56],
109089cc3f4dSElaine Zhang 			     CLK_OTP_DIV_CON_MASK,
109189cc3f4dSElaine Zhang 			     (div - 1) << CLK_OTP_DIV_CON_SHIFT);
109289cc3f4dSElaine Zhang 		break;
109389cc3f4dSElaine Zhang 	case SCLK_OTP_USR:
109489cc3f4dSElaine Zhang 		div = DIV_ROUND_UP(px30_otp_get_clk(priv, SCLK_OTP), hz);
109589cc3f4dSElaine Zhang 		rk_clrsetreg(&cru->clksel_con[56],
109689cc3f4dSElaine Zhang 			     CLK_OTP_USR_DIV_CON_MASK,
109789cc3f4dSElaine Zhang 			     (div - 1) << CLK_OTP_USR_DIV_CON_SHIFT);
109889cc3f4dSElaine Zhang 		break;
109989cc3f4dSElaine Zhang 	default:
110089cc3f4dSElaine Zhang 		printf("do not support this peri freq\n");
110189cc3f4dSElaine Zhang 		return -EINVAL;
110289cc3f4dSElaine Zhang 	}
110389cc3f4dSElaine Zhang 
110489cc3f4dSElaine Zhang 	return px30_otp_get_clk(priv, clk_id);
110589cc3f4dSElaine Zhang }
110689cc3f4dSElaine Zhang 
px30_crypto_get_clk(struct px30_clk_priv * priv,ulong clk_id)110751d1c6b1SElaine Zhang static ulong px30_crypto_get_clk(struct px30_clk_priv *priv, ulong clk_id)
110851d1c6b1SElaine Zhang {
110951d1c6b1SElaine Zhang 	struct px30_cru *cru = priv->cru;
111051d1c6b1SElaine Zhang 	u32 div, con, parent;
111151d1c6b1SElaine Zhang 
111251d1c6b1SElaine Zhang 	switch (clk_id) {
111351d1c6b1SElaine Zhang 	case SCLK_CRYPTO:
111451d1c6b1SElaine Zhang 		con = readl(&cru->clksel_con[25]);
111551d1c6b1SElaine Zhang 		div = (con & CRYPTO_DIV_MASK) >> CRYPTO_DIV_SHIFT;
111651d1c6b1SElaine Zhang 		parent = priv->gpll_hz;
111751d1c6b1SElaine Zhang 		break;
111851d1c6b1SElaine Zhang 	case SCLK_CRYPTO_APK:
111951d1c6b1SElaine Zhang 		con = readl(&cru->clksel_con[25]);
112051d1c6b1SElaine Zhang 		div = (con & CRYPTO_APK_DIV_MASK) >> CRYPTO_APK_DIV_SHIFT;
112151d1c6b1SElaine Zhang 		parent = priv->gpll_hz;
112251d1c6b1SElaine Zhang 		break;
112351d1c6b1SElaine Zhang 	default:
112451d1c6b1SElaine Zhang 		return -ENOENT;
112551d1c6b1SElaine Zhang 	}
112651d1c6b1SElaine Zhang 
112751d1c6b1SElaine Zhang 	return DIV_TO_RATE(parent, div);
112851d1c6b1SElaine Zhang }
112951d1c6b1SElaine Zhang 
px30_crypto_set_clk(struct px30_clk_priv * priv,ulong clk_id,ulong hz)113051d1c6b1SElaine Zhang static ulong px30_crypto_set_clk(struct px30_clk_priv *priv, ulong clk_id,
113151d1c6b1SElaine Zhang 				 ulong hz)
113251d1c6b1SElaine Zhang {
113351d1c6b1SElaine Zhang 	struct px30_cru *cru = priv->cru;
113451d1c6b1SElaine Zhang 	int src_clk_div;
113551d1c6b1SElaine Zhang 
113651d1c6b1SElaine Zhang 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
113751d1c6b1SElaine Zhang 	assert(src_clk_div - 1 <= 31);
113851d1c6b1SElaine Zhang 
113951d1c6b1SElaine Zhang 	/*
114051d1c6b1SElaine Zhang 	 * select gpll as crypto clock source and
114151d1c6b1SElaine Zhang 	 * set up dependent divisors for crypto clocks.
114251d1c6b1SElaine Zhang 	 */
114351d1c6b1SElaine Zhang 	switch (clk_id) {
114451d1c6b1SElaine Zhang 	case SCLK_CRYPTO:
114551d1c6b1SElaine Zhang 		rk_clrsetreg(&cru->clksel_con[25],
114651d1c6b1SElaine Zhang 			     CRYPTO_PLL_SEL_MASK | CRYPTO_DIV_MASK,
114751d1c6b1SElaine Zhang 			     CRYPTO_PLL_SEL_GPLL << CRYPTO_PLL_SEL_SHIFT |
114851d1c6b1SElaine Zhang 			     (src_clk_div - 1) << CRYPTO_DIV_SHIFT);
114951d1c6b1SElaine Zhang 		break;
115051d1c6b1SElaine Zhang 	case SCLK_CRYPTO_APK:
115151d1c6b1SElaine Zhang 		rk_clrsetreg(&cru->clksel_con[25],
115251d1c6b1SElaine Zhang 			     CRYPTO_APK_PLL_SEL_MASK | CRYPTO_APK_DIV_MASK,
115351d1c6b1SElaine Zhang 			     CRYPTO_PLL_SEL_GPLL << CRYPTO_APK_SEL_SHIFT |
115451d1c6b1SElaine Zhang 			     (src_clk_div - 1) << CRYPTO_APK_DIV_SHIFT);
115551d1c6b1SElaine Zhang 		break;
115651d1c6b1SElaine Zhang 	default:
115751d1c6b1SElaine Zhang 		printf("do not support this peri freq\n");
115851d1c6b1SElaine Zhang 		return -EINVAL;
115951d1c6b1SElaine Zhang 	}
116051d1c6b1SElaine Zhang 
116151d1c6b1SElaine Zhang 	return px30_crypto_get_clk(priv, clk_id);
116251d1c6b1SElaine Zhang }
1163152682edSWyon Bi 
1164ec073f31SJason Zhu #ifndef CONFIG_SPL_BUILD
px30_mac_set_clk(struct clk * clk,uint hz)116522d359b8SElaine Zhang static ulong px30_mac_set_clk(struct clk *clk, uint hz)
116622d359b8SElaine Zhang {
116722d359b8SElaine Zhang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
116822d359b8SElaine Zhang 	struct px30_cru *cru = priv->cru;
116922d359b8SElaine Zhang 	u32 con = readl(&cru->clksel_con[22]);
117022d359b8SElaine Zhang 	ulong pll_rate;
117122d359b8SElaine Zhang 	u8 div;
117222d359b8SElaine Zhang 
117322d359b8SElaine Zhang 	if ((con >> GMAC_PLL_SEL_SHIFT) & GMAC_PLL_SEL_CPLL)
117422d359b8SElaine Zhang 		pll_rate = px30_clk_get_pll_rate(priv, CPLL);
117522d359b8SElaine Zhang 	else if ((con >> GMAC_PLL_SEL_SHIFT) & GMAC_PLL_SEL_NPLL)
117622d359b8SElaine Zhang 		pll_rate = px30_clk_get_pll_rate(priv, NPLL);
117722d359b8SElaine Zhang 	else
117822d359b8SElaine Zhang 		pll_rate = priv->gpll_hz;
117922d359b8SElaine Zhang 
118022d359b8SElaine Zhang 	/*default set 50MHZ for gmac*/
118122d359b8SElaine Zhang 	if (!hz)
118222d359b8SElaine Zhang 		hz = 50000000;
118322d359b8SElaine Zhang 
118422d359b8SElaine Zhang 	div = DIV_ROUND_UP(pll_rate, hz) - 1;
118522d359b8SElaine Zhang 	assert(div < 32);
118622d359b8SElaine Zhang 	rk_clrsetreg(&cru->clksel_con[22], CLK_GMAC_DIV_MASK,
118722d359b8SElaine Zhang 		     div << CLK_GMAC_DIV_SHIFT);
118822d359b8SElaine Zhang 
118922d359b8SElaine Zhang 	return DIV_TO_RATE(pll_rate, div);
119022d359b8SElaine Zhang }
119122d359b8SElaine Zhang 
px30_mac_set_speed_clk(struct clk * clk,uint hz)119222d359b8SElaine Zhang static int px30_mac_set_speed_clk(struct clk *clk, uint hz)
119322d359b8SElaine Zhang {
119422d359b8SElaine Zhang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
119522d359b8SElaine Zhang 	struct px30_cru *cru = priv->cru;
119622d359b8SElaine Zhang 
119722d359b8SElaine Zhang 	if (hz != 2500000 && hz != 25000000) {
119822d359b8SElaine Zhang 		debug("Unsupported mac speed:%d\n", hz);
119922d359b8SElaine Zhang 		return -EINVAL;
120022d359b8SElaine Zhang 	}
120122d359b8SElaine Zhang 
120222d359b8SElaine Zhang 	rk_clrsetreg(&cru->clksel_con[23], RMII_CLK_SEL_MASK,
120322d359b8SElaine Zhang 		     ((hz == 2500000) ? 0 : 1) << RMII_CLK_SEL_SHIFT);
120422d359b8SElaine Zhang 
120522d359b8SElaine Zhang 	return 0;
120622d359b8SElaine Zhang }
120722d359b8SElaine Zhang 
120851d1c6b1SElaine Zhang #endif
120951d1c6b1SElaine Zhang 
px30_clk_get_gpll_rate(ulong * rate)1210cefa5186SFinley Xiao static int px30_clk_get_gpll_rate(ulong *rate)
1211cefa5186SFinley Xiao {
1212cefa5186SFinley Xiao 	struct udevice *pmucru_dev;
1213cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv;
1214cefa5186SFinley Xiao 	int ret;
1215cefa5186SFinley Xiao 
1216cefa5186SFinley Xiao 	ret = uclass_get_device_by_driver(UCLASS_CLK,
1217cefa5186SFinley Xiao 					  DM_GET_DRIVER(rockchip_px30_pmucru),
1218cefa5186SFinley Xiao 					  &pmucru_dev);
1219cefa5186SFinley Xiao 	if (ret) {
1220cefa5186SFinley Xiao 		printf("%s: could not find pmucru device\n", __func__);
1221cefa5186SFinley Xiao 		return ret;
1222cefa5186SFinley Xiao 	}
1223cefa5186SFinley Xiao 	priv = dev_get_priv(pmucru_dev);
1224c4d4e4dcSFinley Xiao 	*rate =  priv->gpll_hz;
1225cefa5186SFinley Xiao 
1226cefa5186SFinley Xiao 	return 0;
1227df8f8a42SFinley Xiao }
1228df8f8a42SFinley Xiao 
px30_clk_get_pll_rate(struct px30_clk_priv * priv,enum px30_pll_id pll_id)12298b1aed51SFinley Xiao static ulong px30_clk_get_pll_rate(struct px30_clk_priv *priv,
12308b1aed51SFinley Xiao 				   enum px30_pll_id pll_id)
12318b1aed51SFinley Xiao {
12328b1aed51SFinley Xiao 	struct px30_cru *cru = priv->cru;
12338b1aed51SFinley Xiao 
12348b1aed51SFinley Xiao 	return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id);
12358b1aed51SFinley Xiao }
12368b1aed51SFinley Xiao 
px30_clk_set_pll_rate(struct px30_clk_priv * priv,enum px30_pll_id pll_id,ulong hz)1237a221d6e6SFinley Xiao static ulong px30_clk_set_pll_rate(struct px30_clk_priv *priv,
1238a221d6e6SFinley Xiao 				   enum px30_pll_id pll_id, ulong hz)
1239a221d6e6SFinley Xiao {
1240a221d6e6SFinley Xiao 	struct px30_cru *cru = priv->cru;
1241a221d6e6SFinley Xiao 
1242a221d6e6SFinley Xiao 	if (rkclk_set_pll(&cru->pll[pll_id], &cru->mode, pll_id, hz))
1243a221d6e6SFinley Xiao 		return -EINVAL;
1244a221d6e6SFinley Xiao 	return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id);
1245a221d6e6SFinley Xiao }
1246a221d6e6SFinley Xiao 
px30_armclk_set_clk(struct px30_clk_priv * priv,ulong hz)124737428b92SFinley Xiao static ulong px30_armclk_set_clk(struct px30_clk_priv *priv, ulong hz)
124837428b92SFinley Xiao {
124937428b92SFinley Xiao 	struct px30_cru *cru = priv->cru;
125037428b92SFinley Xiao 	const struct cpu_rate_table *rate;
125137428b92SFinley Xiao 	ulong old_rate;
125237428b92SFinley Xiao 
125337428b92SFinley Xiao 	rate = get_cpu_settings(hz);
125437428b92SFinley Xiao 	if (!rate) {
125537428b92SFinley Xiao 		printf("%s unsupport rate\n", __func__);
125637428b92SFinley Xiao 		return -EINVAL;
125737428b92SFinley Xiao 	}
125837428b92SFinley Xiao 
125937428b92SFinley Xiao 	/*
126037428b92SFinley Xiao 	 * select apll as cpu/core clock pll source and
126137428b92SFinley Xiao 	 * set up dependent divisors for PERI and ACLK clocks.
126237428b92SFinley Xiao 	 * core hz : apll = 1:1
126337428b92SFinley Xiao 	 */
126437428b92SFinley Xiao 	old_rate = px30_clk_get_pll_rate(priv, APLL);
126537428b92SFinley Xiao 	if (old_rate > hz) {
126637428b92SFinley Xiao 		if (rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, hz))
126737428b92SFinley Xiao 			return -EINVAL;
126837428b92SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[0],
126937428b92SFinley Xiao 			     CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK |
127037428b92SFinley Xiao 			     CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK,
127137428b92SFinley Xiao 			     rate->aclk_div << CORE_ACLK_DIV_SHIFT |
127237428b92SFinley Xiao 			     rate->pclk_div << CORE_DBG_DIV_SHIFT |
127337428b92SFinley Xiao 			     CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
127437428b92SFinley Xiao 			     0 << CORE_DIV_CON_SHIFT);
127537428b92SFinley Xiao 	} else if (old_rate < hz) {
127637428b92SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[0],
127737428b92SFinley Xiao 			     CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK |
127837428b92SFinley Xiao 			     CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK,
127937428b92SFinley Xiao 			     rate->aclk_div << CORE_ACLK_DIV_SHIFT |
128037428b92SFinley Xiao 			     rate->pclk_div << CORE_DBG_DIV_SHIFT |
128137428b92SFinley Xiao 			     CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
128237428b92SFinley Xiao 			     0 << CORE_DIV_CON_SHIFT);
128337428b92SFinley Xiao 		if (rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, hz))
128437428b92SFinley Xiao 			return -EINVAL;
128537428b92SFinley Xiao 	}
128637428b92SFinley Xiao 
128737428b92SFinley Xiao 	return px30_clk_get_pll_rate(priv, APLL);
128837428b92SFinley Xiao }
128937428b92SFinley Xiao 
px30_clk_get_rate(struct clk * clk)1290a60961a3SKever Yang static ulong px30_clk_get_rate(struct clk *clk)
1291a60961a3SKever Yang {
1292a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
1293a60961a3SKever Yang 	ulong rate = 0;
1294a60961a3SKever Yang 
1295c4d4e4dcSFinley Xiao 	if (!priv->gpll_hz && clk->id > ARMCLK) {
1296c4d4e4dcSFinley Xiao 		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
1297c4d4e4dcSFinley Xiao 		return -ENOENT;
1298cefa5186SFinley Xiao 	}
1299cefa5186SFinley Xiao 
1300cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1301a60961a3SKever Yang 	switch (clk->id) {
13028b1aed51SFinley Xiao 	case PLL_APLL:
13038b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, APLL);
13048b1aed51SFinley Xiao 		break;
13058b1aed51SFinley Xiao 	case PLL_DPLL:
13068b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, DPLL);
13078b1aed51SFinley Xiao 		break;
13088b1aed51SFinley Xiao 	case PLL_CPLL:
13098b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, CPLL);
13108b1aed51SFinley Xiao 		break;
13118b1aed51SFinley Xiao 	case PLL_NPLL:
13128b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, NPLL);
13138b1aed51SFinley Xiao 		break;
131437428b92SFinley Xiao 	case ARMCLK:
131537428b92SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, APLL);
131637428b92SFinley Xiao 		break;
1317a60961a3SKever Yang 	case HCLK_SDMMC:
1318a60961a3SKever Yang 	case HCLK_EMMC:
1319a60961a3SKever Yang 	case SCLK_SDMMC:
1320a60961a3SKever Yang 	case SCLK_EMMC:
132196f1b3d9SKever Yang 	case SCLK_EMMC_SAMPLE:
1322cefa5186SFinley Xiao 		rate = px30_mmc_get_clk(priv, clk->id);
1323a60961a3SKever Yang 		break;
13249bc02da5SElaine Zhang 	case SCLK_SFC:
13259bc02da5SElaine Zhang 		rate = px30_sfc_get_clk(priv, clk->id);
13269bc02da5SElaine Zhang 		break;
1327a60961a3SKever Yang 	case SCLK_I2C0:
1328a60961a3SKever Yang 	case SCLK_I2C1:
1329a60961a3SKever Yang 	case SCLK_I2C2:
1330a60961a3SKever Yang 	case SCLK_I2C3:
1331cefa5186SFinley Xiao 		rate = px30_i2c_get_clk(priv, clk->id);
1332a60961a3SKever Yang 		break;
133395f26412SSugar Zhang 	case SCLK_I2S1:
133495f26412SSugar Zhang 		rate = px30_i2s_get_clk(priv, clk->id);
133595f26412SSugar Zhang 		break;
1336a9cbfff9SWyon Bi 	case SCLK_I2S1_OUT:
1337a9cbfff9SWyon Bi 		rate = px30_i2s1_mclk_get_clk(priv, clk->id);
1338a9cbfff9SWyon Bi 		break;
1339a60961a3SKever Yang 	case SCLK_PWM0:
1340fce7cb7bSFinley Xiao 	case SCLK_PWM1:
1341cefa5186SFinley Xiao 		rate = px30_pwm_get_clk(priv, clk->id);
1342a60961a3SKever Yang 		break;
1343a60961a3SKever Yang 	case SCLK_SARADC:
1344cefa5186SFinley Xiao 		rate = px30_saradc_get_clk(priv);
1345a60961a3SKever Yang 		break;
1346cb3c37fcSElaine Zhang 	case SCLK_TSADC:
1347cb3c37fcSElaine Zhang 		rate = px30_tsadc_get_clk(priv);
1348cb3c37fcSElaine Zhang 		break;
1349f67f522bSFinley Xiao 	case SCLK_SPI0:
1350f67f522bSFinley Xiao 	case SCLK_SPI1:
1351cefa5186SFinley Xiao 		rate = px30_spi_get_clk(priv, clk->id);
1352f67f522bSFinley Xiao 		break;
135330f1f38dSFinley Xiao 	case ACLK_VOPB:
1354d101530aSFinley Xiao 	case ACLK_VOPL:
135530f1f38dSFinley Xiao 	case DCLK_VOPB:
1356d101530aSFinley Xiao 	case DCLK_VOPL:
1357cefa5186SFinley Xiao 		rate = px30_vop_get_clk(priv, clk->id);
135830f1f38dSFinley Xiao 		break;
1359df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
1360df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
1361df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
13628afd7ff1SElaine Zhang 	case PCLK_WDT_NS:
1363cefa5186SFinley Xiao 		rate = px30_bus_get_clk(priv, clk->id);
1364df8f8a42SFinley Xiao 		break;
1365df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
1366df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
1367cefa5186SFinley Xiao 		rate = px30_peri_get_clk(priv, clk->id);
1368df8f8a42SFinley Xiao 		break;
136989cc3f4dSElaine Zhang 	case SCLK_OTP:
137089cc3f4dSElaine Zhang 	case SCLK_OTP_USR:
137189cc3f4dSElaine Zhang 		rate = px30_otp_get_clk(priv, clk->id);
137289cc3f4dSElaine Zhang 		break;
137351d1c6b1SElaine Zhang 	case SCLK_CRYPTO:
137451d1c6b1SElaine Zhang 	case SCLK_CRYPTO_APK:
137551d1c6b1SElaine Zhang 		rate = px30_crypto_get_clk(priv, clk->id);
137651d1c6b1SElaine Zhang 		break;
1377a60961a3SKever Yang 	default:
1378a60961a3SKever Yang 		return -ENOENT;
1379a60961a3SKever Yang 	}
1380a60961a3SKever Yang 
1381a60961a3SKever Yang 	return rate;
1382a60961a3SKever Yang }
1383a60961a3SKever Yang 
px30_clk_set_rate(struct clk * clk,ulong rate)1384a60961a3SKever Yang static ulong px30_clk_set_rate(struct clk *clk, ulong rate)
1385a60961a3SKever Yang {
1386a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
1387a60961a3SKever Yang 	ulong ret = 0;
1388a60961a3SKever Yang 
1389c4d4e4dcSFinley Xiao 	if (!priv->gpll_hz && clk->id > ARMCLK) {
1390c4d4e4dcSFinley Xiao 		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
1391c4d4e4dcSFinley Xiao 		return -ENOENT;
1392cefa5186SFinley Xiao 	}
1393cefa5186SFinley Xiao 
1394c4867301SKever Yang 	debug("%s %ld %ld\n", __func__, clk->id, rate);
1395a60961a3SKever Yang 	switch (clk->id) {
1396a221d6e6SFinley Xiao 	case PLL_NPLL:
1397a221d6e6SFinley Xiao 		ret = px30_clk_set_pll_rate(priv, NPLL, rate);
1398a221d6e6SFinley Xiao 		break;
139937428b92SFinley Xiao 	case ARMCLK:
140037428b92SFinley Xiao 		if (priv->armclk_hz)
140137428b92SFinley Xiao 			px30_armclk_set_clk(priv, rate);
140237428b92SFinley Xiao 		priv->armclk_hz = rate;
140337428b92SFinley Xiao 		break;
1404a60961a3SKever Yang 	case HCLK_SDMMC:
1405a60961a3SKever Yang 	case HCLK_EMMC:
1406a60961a3SKever Yang 	case SCLK_SDMMC:
1407a60961a3SKever Yang 	case SCLK_EMMC:
1408cefa5186SFinley Xiao 		ret = px30_mmc_set_clk(priv, clk->id, rate);
1409a60961a3SKever Yang 		break;
14109bc02da5SElaine Zhang 	case SCLK_SFC:
14119bc02da5SElaine Zhang 		ret = px30_sfc_set_clk(priv, clk->id, rate);
14129bc02da5SElaine Zhang 		break;
1413a60961a3SKever Yang 	case SCLK_I2C0:
1414a60961a3SKever Yang 	case SCLK_I2C1:
1415a60961a3SKever Yang 	case SCLK_I2C2:
1416a60961a3SKever Yang 	case SCLK_I2C3:
1417cefa5186SFinley Xiao 		ret = px30_i2c_set_clk(priv, clk->id, rate);
1418a60961a3SKever Yang 		break;
141995f26412SSugar Zhang 	case SCLK_I2S1:
142095f26412SSugar Zhang 		ret = px30_i2s_set_clk(priv, clk->id, rate);
142195f26412SSugar Zhang 		break;
1422a9cbfff9SWyon Bi 	case SCLK_I2S1_OUT:
1423a9cbfff9SWyon Bi 		ret = px30_i2s1_mclk_set_clk(priv, clk->id, rate);
1424a9cbfff9SWyon Bi 		break;
1425a60961a3SKever Yang 	case SCLK_PWM0:
1426f67f522bSFinley Xiao 	case SCLK_PWM1:
1427cefa5186SFinley Xiao 		ret = px30_pwm_set_clk(priv, clk->id, rate);
1428a60961a3SKever Yang 		break;
1429a60961a3SKever Yang 	case SCLK_SARADC:
1430cefa5186SFinley Xiao 		ret = px30_saradc_set_clk(priv, rate);
1431a60961a3SKever Yang 		break;
1432cb3c37fcSElaine Zhang 	case SCLK_TSADC:
1433cb3c37fcSElaine Zhang 		ret = px30_tsadc_set_clk(priv, rate);
1434cb3c37fcSElaine Zhang 		break;
1435f67f522bSFinley Xiao 	case SCLK_SPI0:
1436f67f522bSFinley Xiao 	case SCLK_SPI1:
1437cefa5186SFinley Xiao 		ret = px30_spi_set_clk(priv, clk->id, rate);
1438f67f522bSFinley Xiao 		break;
143930f1f38dSFinley Xiao 	case ACLK_VOPB:
1440d101530aSFinley Xiao 	case ACLK_VOPL:
144130f1f38dSFinley Xiao 	case DCLK_VOPB:
1442d101530aSFinley Xiao 	case DCLK_VOPL:
1443cefa5186SFinley Xiao 		ret = px30_vop_set_clk(priv, clk->id, rate);
144430f1f38dSFinley Xiao 		break;
1445df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
1446df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
1447df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
1448cefa5186SFinley Xiao 		ret = px30_bus_set_clk(priv, clk->id, rate);
1449df8f8a42SFinley Xiao 		break;
1450df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
1451df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
1452cefa5186SFinley Xiao 		ret = px30_peri_set_clk(priv, clk->id, rate);
1453df8f8a42SFinley Xiao 		break;
145489cc3f4dSElaine Zhang 	case SCLK_OTP:
145589cc3f4dSElaine Zhang 	case SCLK_OTP_USR:
145689cc3f4dSElaine Zhang 		ret = px30_otp_set_clk(priv, clk->id, rate);
145789cc3f4dSElaine Zhang 		break;
145851d1c6b1SElaine Zhang 	case SCLK_CRYPTO:
145951d1c6b1SElaine Zhang 	case SCLK_CRYPTO_APK:
146051d1c6b1SElaine Zhang 		ret = px30_crypto_set_clk(priv, clk->id, rate);
146151d1c6b1SElaine Zhang 		break;
1462ec073f31SJason Zhu #ifndef CONFIG_SPL_BUILD
146322d359b8SElaine Zhang 	case SCLK_GMAC:
146422d359b8SElaine Zhang 	case SCLK_GMAC_SRC:
146522d359b8SElaine Zhang 		ret = px30_mac_set_clk(clk, rate);
146622d359b8SElaine Zhang 		break;
146722d359b8SElaine Zhang 	case SCLK_GMAC_RMII:
146822d359b8SElaine Zhang 		ret = px30_mac_set_speed_clk(clk, rate);
146922d359b8SElaine Zhang 		break;
147051d1c6b1SElaine Zhang #endif
1471a60961a3SKever Yang 	default:
1472a60961a3SKever Yang 		return -ENOENT;
1473a60961a3SKever Yang 	}
1474a60961a3SKever Yang 
1475a60961a3SKever Yang 	return ret;
1476a60961a3SKever Yang }
1477a60961a3SKever Yang 
1478a60961a3SKever Yang #define ROCKCHIP_MMC_DELAY_SEL		BIT(10)
1479a60961a3SKever Yang #define ROCKCHIP_MMC_DEGREE_MASK	0x3
1480a60961a3SKever Yang #define ROCKCHIP_MMC_DELAYNUM_OFFSET	2
1481a60961a3SKever Yang #define ROCKCHIP_MMC_DELAYNUM_MASK	(0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
1482a60961a3SKever Yang 
1483a60961a3SKever Yang #define PSECS_PER_SEC 1000000000000LL
1484a60961a3SKever Yang /*
1485a60961a3SKever Yang  * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
1486a60961a3SKever Yang  * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
1487a60961a3SKever Yang  */
1488a60961a3SKever Yang #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
1489a60961a3SKever Yang 
rockchip_mmc_get_phase(struct clk * clk)1490a60961a3SKever Yang int rockchip_mmc_get_phase(struct clk *clk)
1491a60961a3SKever Yang {
1492a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
1493a60961a3SKever Yang 	struct px30_cru *cru = priv->cru;
1494a60961a3SKever Yang 	u32 raw_value, delay_num;
1495a60961a3SKever Yang 	u16 degrees = 0;
1496a60961a3SKever Yang 	ulong rate;
1497a60961a3SKever Yang 
1498a60961a3SKever Yang 	rate = px30_clk_get_rate(clk);
1499a60961a3SKever Yang 
1500a60961a3SKever Yang 	if (rate < 0)
1501a60961a3SKever Yang 		return rate;
1502a60961a3SKever Yang 
1503a60961a3SKever Yang 	if (clk->id == SCLK_EMMC_SAMPLE)
1504a60961a3SKever Yang 		raw_value = readl(&cru->emmc_con[1]);
1505a60961a3SKever Yang 	else
1506a60961a3SKever Yang 		raw_value = readl(&cru->sdmmc_con[1]);
1507a60961a3SKever Yang 
150896f1b3d9SKever Yang 	raw_value >>= 1;
1509a60961a3SKever Yang 	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
1510a60961a3SKever Yang 
1511a60961a3SKever Yang 	if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
1512a60961a3SKever Yang 		/* degrees/delaynum * 10000 */
1513a60961a3SKever Yang 		unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
1514a60961a3SKever Yang 					36 * (rate / 1000000);
1515a60961a3SKever Yang 
1516a60961a3SKever Yang 		delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
1517a60961a3SKever Yang 		delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
1518a60961a3SKever Yang 		degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
1519a60961a3SKever Yang 	}
1520a60961a3SKever Yang 
1521a60961a3SKever Yang 	return degrees % 360;
1522a60961a3SKever Yang }
1523a60961a3SKever Yang 
rockchip_mmc_set_phase(struct clk * clk,u32 degrees)1524a60961a3SKever Yang int rockchip_mmc_set_phase(struct clk *clk, u32 degrees)
1525a60961a3SKever Yang {
1526a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
1527a60961a3SKever Yang 	struct px30_cru *cru = priv->cru;
1528a60961a3SKever Yang 	u8 nineties, remainder, delay_num;
1529a60961a3SKever Yang 	u32 raw_value, delay;
1530a60961a3SKever Yang 	ulong rate;
1531a60961a3SKever Yang 
1532a60961a3SKever Yang 	rate = px30_clk_get_rate(clk);
1533a60961a3SKever Yang 
1534a60961a3SKever Yang 	if (rate < 0)
1535a60961a3SKever Yang 		return rate;
1536a60961a3SKever Yang 
1537a60961a3SKever Yang 	nineties = degrees / 90;
1538a60961a3SKever Yang 	remainder = (degrees % 90);
1539a60961a3SKever Yang 
1540a60961a3SKever Yang 	/*
1541a60961a3SKever Yang 	 * Convert to delay; do a little extra work to make sure we
1542a60961a3SKever Yang 	 * don't overflow 32-bit / 64-bit numbers.
1543a60961a3SKever Yang 	 */
1544a60961a3SKever Yang 	delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
1545a60961a3SKever Yang 	delay *= remainder;
1546a60961a3SKever Yang 	delay = DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 *
1547a60961a3SKever Yang 				(ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
1548a60961a3SKever Yang 
1549a60961a3SKever Yang 	delay_num = (u8)min_t(u32, delay, 255);
1550a60961a3SKever Yang 
1551a60961a3SKever Yang 	raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
1552a60961a3SKever Yang 	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
1553a60961a3SKever Yang 	raw_value |= nineties;
1554a60961a3SKever Yang 
155596f1b3d9SKever Yang 	raw_value <<= 1;
1556a60961a3SKever Yang 	if (clk->id == SCLK_EMMC_SAMPLE)
1557a60961a3SKever Yang 		writel(raw_value | 0xffff0000, &cru->emmc_con[1]);
1558a60961a3SKever Yang 	else
1559a60961a3SKever Yang 		writel(raw_value | 0xffff0000, &cru->sdmmc_con[1]);
1560a60961a3SKever Yang 
1561a60961a3SKever Yang 	debug("mmc set_phase(%d) delay_nums=%u reg=%#x actual_degrees=%d\n",
1562a60961a3SKever Yang 	      degrees, delay_num, raw_value, rockchip_mmc_get_phase(clk));
1563a60961a3SKever Yang 
1564a60961a3SKever Yang 	return 0;
1565a60961a3SKever Yang }
1566a60961a3SKever Yang 
px30_clk_get_phase(struct clk * clk)1567a60961a3SKever Yang static int px30_clk_get_phase(struct clk *clk)
1568a60961a3SKever Yang {
1569a60961a3SKever Yang 	int ret;
1570eb46e717SFinley Xiao 
1571cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1572a60961a3SKever Yang 	switch (clk->id) {
1573a60961a3SKever Yang 	case SCLK_EMMC_SAMPLE:
1574a60961a3SKever Yang 	case SCLK_SDMMC_SAMPLE:
1575a60961a3SKever Yang 		ret = rockchip_mmc_get_phase(clk);
1576a60961a3SKever Yang 		break;
1577a60961a3SKever Yang 	default:
1578a60961a3SKever Yang 		return -ENOENT;
1579a60961a3SKever Yang 	}
1580a60961a3SKever Yang 
1581a60961a3SKever Yang 	return ret;
1582a60961a3SKever Yang }
1583a60961a3SKever Yang 
px30_clk_set_phase(struct clk * clk,int degrees)1584a60961a3SKever Yang static int px30_clk_set_phase(struct clk *clk, int degrees)
1585a60961a3SKever Yang {
1586a60961a3SKever Yang 	int ret;
1587a60961a3SKever Yang 
1588cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1589a60961a3SKever Yang 	switch (clk->id) {
1590a60961a3SKever Yang 	case SCLK_EMMC_SAMPLE:
1591a60961a3SKever Yang 	case SCLK_SDMMC_SAMPLE:
1592a60961a3SKever Yang 		ret = rockchip_mmc_set_phase(clk, degrees);
1593a60961a3SKever Yang 		break;
1594a60961a3SKever Yang 	default:
1595a60961a3SKever Yang 		return -ENOENT;
1596a60961a3SKever Yang 	}
1597a60961a3SKever Yang 
1598a60961a3SKever Yang 	return ret;
1599a60961a3SKever Yang }
1600a60961a3SKever Yang 
160122d359b8SElaine Zhang #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
px30_gmac_set_parent(struct clk * clk,struct clk * parent)160222d359b8SElaine Zhang static int px30_gmac_set_parent(struct clk *clk, struct clk *parent)
160322d359b8SElaine Zhang {
160422d359b8SElaine Zhang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
160522d359b8SElaine Zhang 	struct px30_cru *cru = priv->cru;
160622d359b8SElaine Zhang 
160722d359b8SElaine Zhang 	if (parent->id == SCLK_GMAC_SRC) {
160822d359b8SElaine Zhang 		debug("%s: switching GAMC to SCLK_GMAC_SRC\n", __func__);
160922d359b8SElaine Zhang 		rk_clrsetreg(&cru->clksel_con[23], RMII_EXTCLK_SEL_MASK,
161022d359b8SElaine Zhang 			     RMII_EXTCLK_SEL_INT << RMII_EXTCLK_SEL_SHIFT);
161122d359b8SElaine Zhang 	} else {
161222d359b8SElaine Zhang 		debug("%s: switching GMAC to external clock\n", __func__);
161322d359b8SElaine Zhang 		rk_clrsetreg(&cru->clksel_con[23], RMII_EXTCLK_SEL_MASK,
161422d359b8SElaine Zhang 			     RMII_EXTCLK_SEL_EXT << RMII_EXTCLK_SEL_SHIFT);
161522d359b8SElaine Zhang 	}
161622d359b8SElaine Zhang 	return 0;
161722d359b8SElaine Zhang }
161822d359b8SElaine Zhang 
px30_clk_set_parent(struct clk * clk,struct clk * parent)161922d359b8SElaine Zhang static int px30_clk_set_parent(struct clk *clk, struct clk *parent)
162022d359b8SElaine Zhang {
162122d359b8SElaine Zhang 	switch (clk->id) {
162222d359b8SElaine Zhang 	case SCLK_GMAC:
162322d359b8SElaine Zhang 		return px30_gmac_set_parent(clk, parent);
162422d359b8SElaine Zhang 	default:
162522d359b8SElaine Zhang 		return -ENOENT;
162622d359b8SElaine Zhang 	}
162722d359b8SElaine Zhang }
162822d359b8SElaine Zhang #endif
162922d359b8SElaine Zhang 
1630a60961a3SKever Yang static struct clk_ops px30_clk_ops = {
1631a60961a3SKever Yang 	.get_rate = px30_clk_get_rate,
1632a60961a3SKever Yang 	.set_rate = px30_clk_set_rate,
1633a60961a3SKever Yang 	.get_phase	= px30_clk_get_phase,
1634a60961a3SKever Yang 	.set_phase	= px30_clk_set_phase,
163522d359b8SElaine Zhang #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
163622d359b8SElaine Zhang 	.set_parent	= px30_clk_set_parent,
163722d359b8SElaine Zhang #endif
1638a60961a3SKever Yang };
1639a60961a3SKever Yang 
px30_clk_probe(struct udevice * dev)1640a60961a3SKever Yang static int px30_clk_probe(struct udevice *dev)
1641a60961a3SKever Yang {
1642a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(dev);
1643c4d4e4dcSFinley Xiao 	int ret;
1644a60961a3SKever Yang 
1645dfce0096SElaine Zhang 	priv->sync_kernel = false;
1646dfce0096SElaine Zhang 	if (!priv->armclk_enter_hz) {
1647dfce0096SElaine Zhang 		priv->armclk_enter_hz = px30_clk_get_pll_rate(priv, APLL);
1648dfce0096SElaine Zhang 		priv->armclk_init_hz = priv->armclk_enter_hz;
1649dfce0096SElaine Zhang 	}
1650c4d4e4dcSFinley Xiao 	if (px30_clk_get_pll_rate(priv, APLL) != APLL_HZ) {
1651c4d4e4dcSFinley Xiao 		ret = px30_armclk_set_clk(priv, APLL_HZ);
1652c4d4e4dcSFinley Xiao 		if (ret < 0)
1653c4d4e4dcSFinley Xiao 			printf("%s failed to set armclk rate\n", __func__);
1654dfce0096SElaine Zhang 		priv->armclk_init_hz = APLL_HZ;
1655c4d4e4dcSFinley Xiao 	}
1656dd472d4fSFinley Xiao 
1657c4d4e4dcSFinley Xiao 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
1658c4d4e4dcSFinley Xiao 	ret = clk_set_defaults(dev);
1659c4d4e4dcSFinley Xiao 	if (ret)
1660c4d4e4dcSFinley Xiao 		debug("%s clk_set_defaults failed %d\n", __func__, ret);
1661dfce0096SElaine Zhang 	else
1662dfce0096SElaine Zhang 		priv->sync_kernel = true;
1663c4d4e4dcSFinley Xiao 
1664c4d4e4dcSFinley Xiao 	if (!priv->gpll_hz) {
1665c4d4e4dcSFinley Xiao 		ret = px30_clk_get_gpll_rate(&priv->gpll_hz);
1666c4d4e4dcSFinley Xiao 		if (ret) {
1667c4d4e4dcSFinley Xiao 			printf("%s failed to get gpll rate\n", __func__);
1668c4d4e4dcSFinley Xiao 			return ret;
1669c4d4e4dcSFinley Xiao 		}
1670c4d4e4dcSFinley Xiao 	}
1671a60961a3SKever Yang 
1672a60961a3SKever Yang 	return 0;
1673a60961a3SKever Yang }
1674a60961a3SKever Yang 
px30_clk_ofdata_to_platdata(struct udevice * dev)1675a60961a3SKever Yang static int px30_clk_ofdata_to_platdata(struct udevice *dev)
1676a60961a3SKever Yang {
1677a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(dev);
1678a60961a3SKever Yang 
16794203970bSKever Yang 	priv->cru = dev_read_addr_ptr(dev);
1680a60961a3SKever Yang 
1681a60961a3SKever Yang 	return 0;
1682a60961a3SKever Yang }
1683a60961a3SKever Yang 
px30_clk_bind(struct udevice * dev)1684a60961a3SKever Yang static int px30_clk_bind(struct udevice *dev)
1685a60961a3SKever Yang {
1686a60961a3SKever Yang 	int ret;
1687a60961a3SKever Yang 	struct udevice *sys_child, *sf_child;
1688a60961a3SKever Yang 	struct sysreset_reg *priv;
1689a60961a3SKever Yang 	struct softreset_reg *sf_priv;
1690a60961a3SKever Yang 
1691a60961a3SKever Yang 	/* The reset driver does not have a device node, so bind it here */
1692a60961a3SKever Yang 	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
1693a60961a3SKever Yang 				 &sys_child);
1694a60961a3SKever Yang 	if (ret) {
1695a60961a3SKever Yang 		debug("Warning: No sysreset driver: ret=%d\n", ret);
1696a60961a3SKever Yang 	} else {
1697a60961a3SKever Yang 		priv = malloc(sizeof(struct sysreset_reg));
1698a60961a3SKever Yang 		priv->glb_srst_fst_value = offsetof(struct px30_cru,
1699a60961a3SKever Yang 						    glb_srst_fst);
1700a60961a3SKever Yang 		priv->glb_srst_snd_value = offsetof(struct px30_cru,
1701a60961a3SKever Yang 						    glb_srst_snd);
1702a60961a3SKever Yang 		sys_child->priv = priv;
1703a60961a3SKever Yang 	}
1704a60961a3SKever Yang 
1705a60961a3SKever Yang 	ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset",
1706a60961a3SKever Yang 					 dev_ofnode(dev), &sf_child);
1707a60961a3SKever Yang 	if (ret) {
1708a60961a3SKever Yang 		debug("Warning: No rockchip reset driver: ret=%d\n", ret);
1709a60961a3SKever Yang 	} else {
1710a60961a3SKever Yang 		sf_priv = malloc(sizeof(struct softreset_reg));
1711a60961a3SKever Yang 		sf_priv->sf_reset_offset = offsetof(struct px30_cru,
1712a60961a3SKever Yang 						    softrst_con[0]);
1713a60961a3SKever Yang 		sf_priv->sf_reset_num = 12;
1714a60961a3SKever Yang 		sf_child->priv = sf_priv;
1715a60961a3SKever Yang 	}
1716a60961a3SKever Yang 
1717a60961a3SKever Yang 	return 0;
1718a60961a3SKever Yang }
1719a60961a3SKever Yang 
1720a60961a3SKever Yang static const struct udevice_id px30_clk_ids[] = {
1721a60961a3SKever Yang 	{ .compatible = "rockchip,px30-cru" },
1722a60961a3SKever Yang 	{ }
1723a60961a3SKever Yang };
1724a60961a3SKever Yang 
1725a60961a3SKever Yang U_BOOT_DRIVER(rockchip_px30_cru) = {
1726a60961a3SKever Yang 	.name		= "rockchip_px30_cru",
1727a60961a3SKever Yang 	.id		= UCLASS_CLK,
1728a60961a3SKever Yang 	.of_match	= px30_clk_ids,
1729a60961a3SKever Yang 	.priv_auto_alloc_size = sizeof(struct px30_clk_priv),
1730a60961a3SKever Yang 	.ofdata_to_platdata = px30_clk_ofdata_to_platdata,
1731a60961a3SKever Yang 	.ops		= &px30_clk_ops,
1732a60961a3SKever Yang 	.bind		= px30_clk_bind,
1733a60961a3SKever Yang 	.probe		= px30_clk_probe,
1734a60961a3SKever Yang };
1735cefa5186SFinley Xiao 
px30_pclk_pmu_get_pmuclk(struct px30_pmuclk_priv * priv)1736cefa5186SFinley Xiao static ulong px30_pclk_pmu_get_pmuclk(struct px30_pmuclk_priv *priv)
1737cefa5186SFinley Xiao {
1738cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1739cefa5186SFinley Xiao 	u32 div, con;
1740cefa5186SFinley Xiao 
1741cefa5186SFinley Xiao 	con = readl(&pmucru->pmu_clksel_con[0]);
1742cefa5186SFinley Xiao 	div = (con & CLK_PMU_PCLK_DIV_MASK) >> CLK_PMU_PCLK_DIV_SHIFT;
1743cefa5186SFinley Xiao 
1744cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
1745cefa5186SFinley Xiao }
1746cefa5186SFinley Xiao 
px30_pclk_pmu_set_pmuclk(struct px30_pmuclk_priv * priv,ulong hz)1747cefa5186SFinley Xiao static ulong px30_pclk_pmu_set_pmuclk(struct px30_pmuclk_priv *priv, ulong hz)
1748cefa5186SFinley Xiao {
1749cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1750cefa5186SFinley Xiao 	int src_clk_div;
1751cefa5186SFinley Xiao 
1752cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
1753cb981eeaSFinley Xiao 	assert(src_clk_div - 1 <= 31);
1754cefa5186SFinley Xiao 
1755cefa5186SFinley Xiao 	rk_clrsetreg(&pmucru->pmu_clksel_con[0],
1756cefa5186SFinley Xiao 		     CLK_PMU_PCLK_DIV_MASK,
1757cefa5186SFinley Xiao 		     (src_clk_div - 1) << CLK_PMU_PCLK_DIV_SHIFT);
1758cefa5186SFinley Xiao 
1759cefa5186SFinley Xiao 	return px30_pclk_pmu_get_pmuclk(priv);
1760cefa5186SFinley Xiao }
1761cefa5186SFinley Xiao 
px30_gpll_get_pmuclk(struct px30_pmuclk_priv * priv)1762cefa5186SFinley Xiao static ulong px30_gpll_get_pmuclk(struct px30_pmuclk_priv *priv)
1763cefa5186SFinley Xiao {
1764cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1765cefa5186SFinley Xiao 
1766cefa5186SFinley Xiao 	return rkclk_pll_get_rate(&pmucru->pll, &pmucru->pmu_mode, GPLL);
1767cefa5186SFinley Xiao }
1768cefa5186SFinley Xiao 
px30_gpll_set_pmuclk(struct px30_pmuclk_priv * priv,ulong hz)1769cefa5186SFinley Xiao static ulong px30_gpll_set_pmuclk(struct px30_pmuclk_priv *priv, ulong hz)
1770cefa5186SFinley Xiao {
1771cefa5186SFinley Xiao 	struct udevice *cru_dev;
1772cefa5186SFinley Xiao 	struct px30_clk_priv *cru_priv;
1773cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1774cefa5186SFinley Xiao 	u32 div;
17759bc02da5SElaine Zhang 	ulong emmc_rate, sdmmc_rate, nandc_rate, sfc_rate;
1776eb46e717SFinley Xiao 	ulong aclk_bus_rate, hclk_bus_rate, pclk_bus_rate;
1777eb46e717SFinley Xiao 	ulong aclk_peri_rate, hclk_peri_rate, pclk_pmu_rate;
1778cefa5186SFinley Xiao 	int ret;
1779cefa5186SFinley Xiao 
1780cefa5186SFinley Xiao 	ret = uclass_get_device_by_name(UCLASS_CLK,
1781cefa5186SFinley Xiao 					"clock-controller@ff2b0000",
1782cefa5186SFinley Xiao 					 &cru_dev);
1783cefa5186SFinley Xiao 	if (ret) {
1784cefa5186SFinley Xiao 		printf("%s failed to get cru device\n", __func__);
1785cefa5186SFinley Xiao 		return ret;
1786cefa5186SFinley Xiao 	}
1787cefa5186SFinley Xiao 	cru_priv = dev_get_priv(cru_dev);
1788bf97d0d6SFinley Xiao 
1789bf97d0d6SFinley Xiao 	if (priv->gpll_hz == hz)
1790bf97d0d6SFinley Xiao 		return priv->gpll_hz;
1791bf97d0d6SFinley Xiao 
1792cefa5186SFinley Xiao 	cru_priv->gpll_hz = priv->gpll_hz;
1793cefa5186SFinley Xiao 	div = DIV_ROUND_UP(hz, priv->gpll_hz);
1794cefa5186SFinley Xiao 
1795eb46e717SFinley Xiao 	/* save clock rate */
1796eb46e717SFinley Xiao 	aclk_bus_rate = px30_bus_get_clk(cru_priv, ACLK_BUS_PRE);
1797eb46e717SFinley Xiao 	hclk_bus_rate = px30_bus_get_clk(cru_priv, HCLK_BUS_PRE);
1798eb46e717SFinley Xiao 	pclk_bus_rate = px30_bus_get_clk(cru_priv, PCLK_BUS_PRE);
1799eb46e717SFinley Xiao 	aclk_peri_rate = px30_peri_get_clk(cru_priv, ACLK_PERI_PRE);
1800eb46e717SFinley Xiao 	hclk_peri_rate = px30_peri_get_clk(cru_priv, HCLK_PERI_PRE);
1801eb46e717SFinley Xiao 	pclk_pmu_rate = px30_pclk_pmu_get_pmuclk(priv);
1802eb46e717SFinley Xiao 	debug("%s aclk_bus=%lu, hclk_bus=%lu, pclk_bus=%lu\n", __func__,
1803eb46e717SFinley Xiao 	      aclk_bus_rate, hclk_bus_rate, pclk_bus_rate);
1804eb46e717SFinley Xiao 	debug("%s aclk_peri=%lu, hclk_peri=%lu, pclk_pmu=%lu\n", __func__,
1805eb46e717SFinley Xiao 	      aclk_peri_rate, hclk_peri_rate, pclk_pmu_rate);
1806cefa5186SFinley Xiao 	emmc_rate = px30_mmc_get_clk(cru_priv, SCLK_EMMC);
1807cefa5186SFinley Xiao 	sdmmc_rate = px30_mmc_get_clk(cru_priv, SCLK_SDMMC);
1808cefa5186SFinley Xiao 	nandc_rate = px30_nandc_get_clk(cru_priv);
18099bc02da5SElaine Zhang 	sfc_rate = px30_sfc_get_clk(cru_priv, SCLK_SFC);
18109bc02da5SElaine Zhang 	debug("%s emmc=%lu, sdmmc=%lu, nandc=%lu sfc=%lu\n", __func__,
18119bc02da5SElaine Zhang 	      emmc_rate, sdmmc_rate, nandc_rate, sfc_rate);
1812eb46e717SFinley Xiao 
18134a9de4c9SFinley Xiao 	/* avoid rate too large, reduce rate first */
1814eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, ACLK_BUS_PRE, aclk_bus_rate / div);
1815eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, HCLK_BUS_PRE, hclk_bus_rate / div);
1816eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, PCLK_BUS_PRE, pclk_bus_rate / div);
1817eb46e717SFinley Xiao 	px30_peri_set_clk(cru_priv, ACLK_PERI_PRE, aclk_peri_rate / div);
1818eb46e717SFinley Xiao 	px30_peri_set_clk(cru_priv, HCLK_PERI_PRE, hclk_peri_rate / div);
1819eb46e717SFinley Xiao 	px30_pclk_pmu_set_pmuclk(priv, pclk_pmu_rate / div);
1820eb46e717SFinley Xiao 
18214a9de4c9SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_EMMC, emmc_rate / div);
18224a9de4c9SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_SDMMC, sdmmc_rate / div);
18234a9de4c9SFinley Xiao 	px30_nandc_set_clk(cru_priv, nandc_rate / div);
18249bc02da5SElaine Zhang 	px30_sfc_set_clk(cru_priv, SCLK_SFC, sfc_rate / div);
1825cefa5186SFinley Xiao 
1826eb46e717SFinley Xiao 	/* change gpll rate */
1827cefa5186SFinley Xiao 	rkclk_set_pll(&pmucru->pll, &pmucru->pmu_mode, GPLL, hz);
1828cefa5186SFinley Xiao 	priv->gpll_hz = px30_gpll_get_pmuclk(priv);
1829cefa5186SFinley Xiao 	cru_priv->gpll_hz = priv->gpll_hz;
1830cefa5186SFinley Xiao 
1831eb46e717SFinley Xiao 	/* restore clock rate */
1832eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, ACLK_BUS_PRE, aclk_bus_rate);
1833eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, HCLK_BUS_PRE, hclk_bus_rate);
1834eb46e717SFinley Xiao 	px30_bus_set_clk(cru_priv, PCLK_BUS_PRE, pclk_bus_rate);
1835eb46e717SFinley Xiao 	px30_peri_set_clk(cru_priv, ACLK_PERI_PRE, aclk_peri_rate);
1836eb46e717SFinley Xiao 	px30_peri_set_clk(cru_priv, HCLK_PERI_PRE, hclk_peri_rate);
1837eb46e717SFinley Xiao 	px30_pclk_pmu_set_pmuclk(priv, pclk_pmu_rate);
1838eb46e717SFinley Xiao 
1839cefa5186SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_EMMC, emmc_rate);
1840cefa5186SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_SDMMC, sdmmc_rate);
1841cefa5186SFinley Xiao 	px30_nandc_set_clk(cru_priv, nandc_rate);
18429bc02da5SElaine Zhang 	px30_sfc_set_clk(cru_priv, SCLK_SFC, sfc_rate);
1843cefa5186SFinley Xiao 
1844cefa5186SFinley Xiao 	return priv->gpll_hz;
1845cefa5186SFinley Xiao }
1846cefa5186SFinley Xiao 
px30_pmuclk_get_rate(struct clk * clk)1847cefa5186SFinley Xiao static ulong px30_pmuclk_get_rate(struct clk *clk)
1848cefa5186SFinley Xiao {
1849cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev);
1850cefa5186SFinley Xiao 	ulong rate = 0;
1851cefa5186SFinley Xiao 
1852cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1853cefa5186SFinley Xiao 	switch (clk->id) {
1854cefa5186SFinley Xiao 	case PLL_GPLL:
1855cefa5186SFinley Xiao 		rate = px30_gpll_get_pmuclk(priv);
1856cefa5186SFinley Xiao 		break;
1857cefa5186SFinley Xiao 	case PCLK_PMU_PRE:
1858cefa5186SFinley Xiao 		rate = px30_pclk_pmu_get_pmuclk(priv);
1859cefa5186SFinley Xiao 		break;
1860cefa5186SFinley Xiao 	default:
1861cefa5186SFinley Xiao 		return -ENOENT;
1862cefa5186SFinley Xiao 	}
1863cefa5186SFinley Xiao 
1864cefa5186SFinley Xiao 	return rate;
1865cefa5186SFinley Xiao }
1866cefa5186SFinley Xiao 
px30_pmuclk_set_rate(struct clk * clk,ulong rate)1867cefa5186SFinley Xiao static ulong px30_pmuclk_set_rate(struct clk *clk, ulong rate)
1868cefa5186SFinley Xiao {
1869cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev);
1870cefa5186SFinley Xiao 	ulong ret = 0;
1871cefa5186SFinley Xiao 
1872cefa5186SFinley Xiao 	debug("%s %ld %ld\n", __func__, clk->id, rate);
1873cefa5186SFinley Xiao 	switch (clk->id) {
1874cefa5186SFinley Xiao 	case PLL_GPLL:
1875cefa5186SFinley Xiao 		ret = px30_gpll_set_pmuclk(priv, rate);
1876cefa5186SFinley Xiao 		break;
1877cefa5186SFinley Xiao 	case PCLK_PMU_PRE:
1878cefa5186SFinley Xiao 		ret = px30_pclk_pmu_set_pmuclk(priv, rate);
1879cefa5186SFinley Xiao 		break;
1880cefa5186SFinley Xiao 	default:
1881cefa5186SFinley Xiao 		return -ENOENT;
1882cefa5186SFinley Xiao 	}
1883cefa5186SFinley Xiao 
1884cefa5186SFinley Xiao 	return ret;
1885cefa5186SFinley Xiao }
1886cefa5186SFinley Xiao 
1887cefa5186SFinley Xiao static struct clk_ops px30_pmuclk_ops = {
1888cefa5186SFinley Xiao 	.get_rate = px30_pmuclk_get_rate,
1889cefa5186SFinley Xiao 	.set_rate = px30_pmuclk_set_rate,
1890cefa5186SFinley Xiao };
1891cefa5186SFinley Xiao 
px30_clk_init(struct px30_pmuclk_priv * priv)1892fe784db3SFinley Xiao static void px30_clk_init(struct px30_pmuclk_priv *priv)
1893fe784db3SFinley Xiao {
1894fe784db3SFinley Xiao 	struct udevice *cru_dev;
1895fe784db3SFinley Xiao 	struct px30_clk_priv *cru_priv;
189645484bdcSFinley Xiao 	ulong npll_hz;
1897fe784db3SFinley Xiao 	int ret;
1898fe784db3SFinley Xiao 
1899*f5b1a4f2SJianqun Xu 	ret = uclass_get_device_by_name(UCLASS_CLK,
1900*f5b1a4f2SJianqun Xu 					"clock-controller@ff2b0000", &cru_dev);
1901*f5b1a4f2SJianqun Xu 	if (ret) {
1902*f5b1a4f2SJianqun Xu 		printf("%s failed to get cru device\n", __func__);
1903*f5b1a4f2SJianqun Xu 		return;
1904*f5b1a4f2SJianqun Xu 	}
1905*f5b1a4f2SJianqun Xu 
1906*f5b1a4f2SJianqun Xu 	cru_priv = dev_get_priv(cru_dev);
1907*f5b1a4f2SJianqun Xu 
1908*f5b1a4f2SJianqun Xu 	/* Source from xin_osc0_half before gpll rate. */
1909*f5b1a4f2SJianqun Xu 	px30_i2s1_mclk_set_clk(cru_priv, SCLK_I2S1_OUT, 12000000);
1910*f5b1a4f2SJianqun Xu 
1911fe784db3SFinley Xiao 	priv->gpll_hz = px30_gpll_get_pmuclk(priv);
1912fe784db3SFinley Xiao 	if (priv->gpll_hz != GPLL_HZ) {
1913fe784db3SFinley Xiao 		ret = px30_gpll_set_pmuclk(priv, GPLL_HZ);
1914fe784db3SFinley Xiao 		if (ret < 0)
1915fe784db3SFinley Xiao 			printf("%s failed to set gpll rate\n", __func__);
1916fe784db3SFinley Xiao 	}
1917fe784db3SFinley Xiao 
1918fe784db3SFinley Xiao 	cru_priv->gpll_hz = priv->gpll_hz;
1919fe784db3SFinley Xiao 
192045484bdcSFinley Xiao 	npll_hz = px30_clk_get_pll_rate(cru_priv, NPLL);
192145484bdcSFinley Xiao 	if (npll_hz != NPLL_HZ) {
192245484bdcSFinley Xiao 		ret = px30_clk_set_pll_rate(cru_priv, NPLL, NPLL_HZ);
192345484bdcSFinley Xiao 		if (ret < 0)
192445484bdcSFinley Xiao 			printf("%s failed to set npll rate\n", __func__);
192545484bdcSFinley Xiao 	}
192645484bdcSFinley Xiao 
1927fe784db3SFinley Xiao 	px30_bus_set_clk(cru_priv, ACLK_BUS_PRE, ACLK_BUS_HZ);
1928fe784db3SFinley Xiao 	px30_bus_set_clk(cru_priv, HCLK_BUS_PRE, HCLK_BUS_HZ);
1929fe784db3SFinley Xiao 	px30_bus_set_clk(cru_priv, PCLK_BUS_PRE, PCLK_BUS_HZ);
1930fe784db3SFinley Xiao 	px30_peri_set_clk(cru_priv, ACLK_PERI_PRE, ACLK_PERI_HZ);
1931fe784db3SFinley Xiao 	px30_peri_set_clk(cru_priv, HCLK_PERI_PRE, HCLK_PERI_HZ);
1932fe784db3SFinley Xiao 	px30_pclk_pmu_set_pmuclk(priv, PCLK_PMU_HZ);
1933*f5b1a4f2SJianqun Xu 
1934*f5b1a4f2SJianqun Xu 	/* Source from gpll as default set. */
1935*f5b1a4f2SJianqun Xu 	px30_i2s1_mclk_set_clk(cru_priv, SCLK_I2S1_OUT, 11289600);
1936fe784db3SFinley Xiao }
1937fe784db3SFinley Xiao 
px30_pmuclk_probe(struct udevice * dev)1938cefa5186SFinley Xiao static int px30_pmuclk_probe(struct udevice *dev)
1939cefa5186SFinley Xiao {
1940c4d4e4dcSFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(dev);
1941c4d4e4dcSFinley Xiao 	int ret;
1942c4d4e4dcSFinley Xiao 
1943fe784db3SFinley Xiao 	px30_clk_init(priv);
1944c4d4e4dcSFinley Xiao 
1945c4d4e4dcSFinley Xiao 	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
1946c4d4e4dcSFinley Xiao 	ret = clk_set_defaults(dev);
1947c4d4e4dcSFinley Xiao 	if (ret)
1948c4d4e4dcSFinley Xiao 		debug("%s clk_set_defaults failed %d\n", __func__, ret);
1949c4d4e4dcSFinley Xiao 
1950cefa5186SFinley Xiao 	return 0;
1951cefa5186SFinley Xiao }
1952cefa5186SFinley Xiao 
px30_pmuclk_ofdata_to_platdata(struct udevice * dev)1953cefa5186SFinley Xiao static int px30_pmuclk_ofdata_to_platdata(struct udevice *dev)
1954cefa5186SFinley Xiao {
1955cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(dev);
1956cefa5186SFinley Xiao 
1957cefa5186SFinley Xiao 	priv->pmucru = dev_read_addr_ptr(dev);
1958cefa5186SFinley Xiao 
1959cefa5186SFinley Xiao 	return 0;
1960cefa5186SFinley Xiao }
1961cefa5186SFinley Xiao 
1962cefa5186SFinley Xiao static const struct udevice_id px30_pmuclk_ids[] = {
1963cefa5186SFinley Xiao 	{ .compatible = "rockchip,px30-pmucru" },
1964cefa5186SFinley Xiao 	{ }
1965cefa5186SFinley Xiao };
1966cefa5186SFinley Xiao 
1967cefa5186SFinley Xiao U_BOOT_DRIVER(rockchip_px30_pmucru) = {
1968cefa5186SFinley Xiao 	.name		= "rockchip_px30_pmucru",
1969cefa5186SFinley Xiao 	.id		= UCLASS_CLK,
1970cefa5186SFinley Xiao 	.of_match	= px30_pmuclk_ids,
1971cefa5186SFinley Xiao 	.priv_auto_alloc_size = sizeof(struct px30_pmuclk_priv),
1972cefa5186SFinley Xiao 	.ofdata_to_platdata = px30_pmuclk_ofdata_to_platdata,
1973cefa5186SFinley Xiao 	.ops		= &px30_pmuclk_ops,
1974cefa5186SFinley Xiao 	.probe		= px30_pmuclk_probe,
1975cefa5186SFinley Xiao };
19767a1915c0SFinley Xiao 
19777a1915c0SFinley Xiao /**
19787a1915c0SFinley Xiao  * soc_clk_dump() - Print clock frequencies
19797a1915c0SFinley Xiao  * Returns zero on success
19807a1915c0SFinley Xiao  *
19817a1915c0SFinley Xiao  * Implementation for the clk dump command.
19827a1915c0SFinley Xiao  */
soc_clk_dump(void)19837a1915c0SFinley Xiao int soc_clk_dump(void)
19847a1915c0SFinley Xiao {
19857a1915c0SFinley Xiao 	struct udevice *cru_dev, *pmucru_dev;
1986dfce0096SElaine Zhang 	struct px30_clk_priv *priv;
19877a1915c0SFinley Xiao 	const struct px30_clk_info *clk_dump;
19887a1915c0SFinley Xiao 	struct clk clk;
19897a1915c0SFinley Xiao 	unsigned long clk_count = ARRAY_SIZE(clks_dump);
19907a1915c0SFinley Xiao 	unsigned long rate;
19917a1915c0SFinley Xiao 	int i, ret;
19927a1915c0SFinley Xiao 
19937a1915c0SFinley Xiao 	ret = uclass_get_device_by_driver(UCLASS_CLK,
19947a1915c0SFinley Xiao 					  DM_GET_DRIVER(rockchip_px30_cru),
19957a1915c0SFinley Xiao 					  &cru_dev);
19967a1915c0SFinley Xiao 	if (ret) {
19977a1915c0SFinley Xiao 		printf("%s failed to get cru device\n", __func__);
19987a1915c0SFinley Xiao 		return ret;
19997a1915c0SFinley Xiao 	}
20007a1915c0SFinley Xiao 
20017a1915c0SFinley Xiao 	ret = uclass_get_device_by_driver(UCLASS_CLK,
20027a1915c0SFinley Xiao 					  DM_GET_DRIVER(rockchip_px30_pmucru),
20037a1915c0SFinley Xiao 					  &pmucru_dev);
20047a1915c0SFinley Xiao 	if (ret) {
20057a1915c0SFinley Xiao 		printf("%s failed to get pmucru device\n", __func__);
20067a1915c0SFinley Xiao 		return ret;
20077a1915c0SFinley Xiao 	}
20087a1915c0SFinley Xiao 
2009dfce0096SElaine Zhang 	priv = dev_get_priv(cru_dev);
2010dfce0096SElaine Zhang 	printf("CLK: (%s. arm: enter %lu KHz, init %lu KHz, kernel %lu%s)\n",
2011dfce0096SElaine Zhang 	       priv->sync_kernel ? "sync kernel" : "uboot",
2012dfce0096SElaine Zhang 	       priv->armclk_enter_hz / 1000,
2013dfce0096SElaine Zhang 	       priv->armclk_init_hz / 1000,
2014dfce0096SElaine Zhang 	       priv->set_armclk_rate ? priv->armclk_hz / 1000 : 0,
2015dfce0096SElaine Zhang 	       priv->set_armclk_rate ? " KHz" : "N/A");
20167a1915c0SFinley Xiao 	for (i = 0; i < clk_count; i++) {
20177a1915c0SFinley Xiao 		clk_dump = &clks_dump[i];
20187a1915c0SFinley Xiao 		if (clk_dump->name) {
20197a1915c0SFinley Xiao 			clk.id = clk_dump->id;
20207a1915c0SFinley Xiao 			if (clk_dump->is_cru)
20217a1915c0SFinley Xiao 				ret = clk_request(cru_dev, &clk);
20227a1915c0SFinley Xiao 			else
20237a1915c0SFinley Xiao 				ret = clk_request(pmucru_dev, &clk);
20247a1915c0SFinley Xiao 			if (ret < 0)
20257a1915c0SFinley Xiao 				return ret;
20267a1915c0SFinley Xiao 
20277a1915c0SFinley Xiao 			rate = clk_get_rate(&clk);
20287a1915c0SFinley Xiao 			clk_free(&clk);
20297a1915c0SFinley Xiao 			if (i == 0) {
20307a1915c0SFinley Xiao 				if (rate < 0)
203156dd66cfSFinley Xiao 					printf("  %s %s\n", clk_dump->name,
20327a1915c0SFinley Xiao 					       "unknown");
20337a1915c0SFinley Xiao 				else
203456dd66cfSFinley Xiao 					printf("  %s %lu KHz\n", clk_dump->name,
203556dd66cfSFinley Xiao 					       rate / 1000);
20367a1915c0SFinley Xiao 			} else {
20377a1915c0SFinley Xiao 				if (rate < 0)
203856dd66cfSFinley Xiao 					printf("  %s %s\n", clk_dump->name,
20397a1915c0SFinley Xiao 					       "unknown");
20407a1915c0SFinley Xiao 				else
204156dd66cfSFinley Xiao 					printf("  %s %lu KHz\n", clk_dump->name,
204256dd66cfSFinley Xiao 					       rate / 1000);
20437a1915c0SFinley Xiao 			}
20447a1915c0SFinley Xiao 		}
20457a1915c0SFinley Xiao 	}
20467a1915c0SFinley Xiao 
20477a1915c0SFinley Xiao 	return 0;
20487a1915c0SFinley Xiao }
2049