xref: /rk3399_rockchip-uboot/drivers/clk/rockchip/clk_px30.c (revision 8b1aed51a630d15c96a7adfd99c2ea7bf2c3d834)
1a60961a3SKever Yang /*
2a60961a3SKever Yang  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3a60961a3SKever Yang  *
4a60961a3SKever Yang  * SPDX-License-Identifier:	GPL-2.0
5a60961a3SKever Yang  */
6a60961a3SKever Yang 
7a60961a3SKever Yang #include <common.h>
8a60961a3SKever Yang #include <bitfield.h>
9a60961a3SKever Yang #include <clk-uclass.h>
10a60961a3SKever Yang #include <dm.h>
11a60961a3SKever Yang #include <errno.h>
12a60961a3SKever Yang #include <syscon.h>
13a60961a3SKever Yang #include <asm/arch/clock.h>
14a60961a3SKever Yang #include <asm/arch/cru_px30.h>
15a60961a3SKever Yang #include <asm/arch/hardware.h>
16a60961a3SKever Yang #include <asm/io.h>
17a60961a3SKever Yang #include <dm/lists.h>
18a60961a3SKever Yang #include <dt-bindings/clock/px30-cru.h>
19a60961a3SKever Yang 
20a60961a3SKever Yang DECLARE_GLOBAL_DATA_PTR;
21a60961a3SKever Yang 
22a60961a3SKever Yang enum {
23a60961a3SKever Yang 	VCO_MAX_HZ	= 3200U * 1000000,
24a60961a3SKever Yang 	VCO_MIN_HZ	= 800 * 1000000,
25a60961a3SKever Yang 	OUTPUT_MAX_HZ	= 3200U * 1000000,
26a60961a3SKever Yang 	OUTPUT_MIN_HZ	= 24 * 1000000,
27a60961a3SKever Yang };
28a60961a3SKever Yang 
29cefa5186SFinley Xiao #define PX30_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1,	\
30cefa5186SFinley Xiao 			_postdiv2, _dsmpd, _frac)		\
31cefa5186SFinley Xiao {								\
32cefa5186SFinley Xiao 	.rate	= _rate##U,					\
33cefa5186SFinley Xiao 	.fbdiv = _fbdiv,					\
34cefa5186SFinley Xiao 	.postdiv1 = _postdiv1,					\
35cefa5186SFinley Xiao 	.refdiv = _refdiv,					\
36cefa5186SFinley Xiao 	.postdiv2 = _postdiv2,					\
37cefa5186SFinley Xiao 	.dsmpd = _dsmpd,					\
38cefa5186SFinley Xiao 	.frac = _frac,						\
39cefa5186SFinley Xiao }
40cefa5186SFinley Xiao 
41a60961a3SKever Yang #define DIV_TO_RATE(input_rate, div)    ((input_rate) / ((div) + 1))
42a60961a3SKever Yang 
43cefa5186SFinley Xiao static struct pll_rate_table px30_pll_rates[] = {
44cefa5186SFinley Xiao 	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
45cefa5186SFinley Xiao 	PX30_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
46cefa5186SFinley Xiao 	PX30_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
47cefa5186SFinley Xiao 	PX30_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0),
48cefa5186SFinley Xiao 	PX30_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0),
49cefa5186SFinley Xiao 	PX30_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0),
50a60961a3SKever Yang };
51a60961a3SKever Yang 
52bcefd077SFinley Xiao static u8 pll_mode_shift[PLL_COUNT] = {
53bcefd077SFinley Xiao 	APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
54bcefd077SFinley Xiao 	NPLL_MODE_SHIFT, GPLL_MODE_SHIFT
55bcefd077SFinley Xiao };
56bcefd077SFinley Xiao static u32 pll_mode_mask[PLL_COUNT] = {
57bcefd077SFinley Xiao 	APLL_MODE_MASK, DPLL_MODE_MASK, CPLL_MODE_MASK,
58bcefd077SFinley Xiao 	NPLL_MODE_MASK, GPLL_MODE_MASK
59bcefd077SFinley Xiao };
60bcefd077SFinley Xiao 
61cefa5186SFinley Xiao static struct pll_rate_table auto_table;
62cefa5186SFinley Xiao 
63cefa5186SFinley Xiao static struct pll_rate_table *pll_clk_set_by_auto(u32 drate)
64cefa5186SFinley Xiao {
65cefa5186SFinley Xiao 	struct pll_rate_table *rate = &auto_table;
66cefa5186SFinley Xiao 	u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0;
67cefa5186SFinley Xiao 	u32 postdiv1, postdiv2 = 1;
68cefa5186SFinley Xiao 	u32 fref_khz;
69cefa5186SFinley Xiao 	u32 diff_khz, best_diff_khz;
70cefa5186SFinley Xiao 	const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16;
71cefa5186SFinley Xiao 	const u32 max_postdiv1 = 7, max_postdiv2 = 7;
72cefa5186SFinley Xiao 	u32 vco_khz;
73cefa5186SFinley Xiao 	u32 rate_khz = drate / KHz;
74cefa5186SFinley Xiao 
75cefa5186SFinley Xiao 	if (!drate) {
76cefa5186SFinley Xiao 		printf("%s: the frequency can't be 0 Hz\n", __func__);
77cefa5186SFinley Xiao 		return NULL;
78cefa5186SFinley Xiao 	}
79cefa5186SFinley Xiao 
80cefa5186SFinley Xiao 	postdiv1 = DIV_ROUND_UP(VCO_MIN_HZ / 1000, rate_khz);
81cefa5186SFinley Xiao 	if (postdiv1 > max_postdiv1) {
82cefa5186SFinley Xiao 		postdiv2 = DIV_ROUND_UP(postdiv1, max_postdiv1);
83cefa5186SFinley Xiao 		postdiv1 = DIV_ROUND_UP(postdiv1, postdiv2);
84cefa5186SFinley Xiao 	}
85cefa5186SFinley Xiao 
86cefa5186SFinley Xiao 	vco_khz = rate_khz * postdiv1 * postdiv2;
87cefa5186SFinley Xiao 
88cefa5186SFinley Xiao 	if (vco_khz < (VCO_MIN_HZ / KHz) || vco_khz > (VCO_MAX_HZ / KHz) ||
89cefa5186SFinley Xiao 	    postdiv2 > max_postdiv2) {
90cefa5186SFinley Xiao 		printf("%s: Cannot find out a supported VCO for Freq (%uHz)\n",
91cefa5186SFinley Xiao 		       __func__, rate_khz);
92cefa5186SFinley Xiao 		return NULL;
93cefa5186SFinley Xiao 	}
94cefa5186SFinley Xiao 
95cefa5186SFinley Xiao 	rate->postdiv1 = postdiv1;
96cefa5186SFinley Xiao 	rate->postdiv2 = postdiv2;
97cefa5186SFinley Xiao 
98cefa5186SFinley Xiao 	best_diff_khz = vco_khz;
99cefa5186SFinley Xiao 	for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) {
100cefa5186SFinley Xiao 		fref_khz = ref_khz / refdiv;
101cefa5186SFinley Xiao 
102cefa5186SFinley Xiao 		fbdiv = vco_khz / fref_khz;
103cefa5186SFinley Xiao 		if ((fbdiv >= max_fbdiv) || (fbdiv <= min_fbdiv))
104cefa5186SFinley Xiao 			continue;
105cefa5186SFinley Xiao 		diff_khz = vco_khz - fbdiv * fref_khz;
106cefa5186SFinley Xiao 		if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) {
107cefa5186SFinley Xiao 			fbdiv++;
108cefa5186SFinley Xiao 			diff_khz = fref_khz - diff_khz;
109cefa5186SFinley Xiao 		}
110cefa5186SFinley Xiao 
111cefa5186SFinley Xiao 		if (diff_khz >= best_diff_khz)
112cefa5186SFinley Xiao 			continue;
113cefa5186SFinley Xiao 
114cefa5186SFinley Xiao 		best_diff_khz = diff_khz;
115cefa5186SFinley Xiao 		rate->refdiv = refdiv;
116cefa5186SFinley Xiao 		rate->fbdiv = fbdiv;
117cefa5186SFinley Xiao 	}
118cefa5186SFinley Xiao 
119cefa5186SFinley Xiao 	if (best_diff_khz > 4 * (MHz / KHz)) {
120cefa5186SFinley Xiao 		printf("%s: Failed to match output frequency %u bestis %u Hz\n",
121cefa5186SFinley Xiao 		       __func__, rate_khz,
122cefa5186SFinley Xiao 		       best_diff_khz * KHz);
123cefa5186SFinley Xiao 		return NULL;
124cefa5186SFinley Xiao 	}
125cefa5186SFinley Xiao 
126cefa5186SFinley Xiao 	return rate;
127cefa5186SFinley Xiao }
128cefa5186SFinley Xiao 
129cefa5186SFinley Xiao static const struct pll_rate_table *get_pll_settings(unsigned long rate)
130cefa5186SFinley Xiao {
131cefa5186SFinley Xiao 	unsigned int rate_count = ARRAY_SIZE(px30_pll_rates);
132cefa5186SFinley Xiao 	int i;
133cefa5186SFinley Xiao 
134cefa5186SFinley Xiao 	for (i = 0; i < rate_count; i++) {
135cefa5186SFinley Xiao 		if (rate == px30_pll_rates[i].rate)
136cefa5186SFinley Xiao 			return &px30_pll_rates[i];
137cefa5186SFinley Xiao 	}
138cefa5186SFinley Xiao 
139cefa5186SFinley Xiao 	return pll_clk_set_by_auto(rate);
140cefa5186SFinley Xiao }
141a60961a3SKever Yang 
142a60961a3SKever Yang /*
143a60961a3SKever Yang  * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
144a60961a3SKever Yang  * Formulas also embedded within the Fractional PLL Verilog model:
145a60961a3SKever Yang  * If DSMPD = 1 (DSM is disabled, "integer mode")
146a60961a3SKever Yang  * FOUTVCO = FREF / REFDIV * FBDIV
147a60961a3SKever Yang  * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
148a60961a3SKever Yang  * Where:
149a60961a3SKever Yang  * FOUTVCO = Fractional PLL non-divided output frequency
150a60961a3SKever Yang  * FOUTPOSTDIV = Fractional PLL divided output frequency
151a60961a3SKever Yang  *               (output of second post divider)
152a60961a3SKever Yang  * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
153a60961a3SKever Yang  * REFDIV = Fractional PLL input reference clock divider
154a60961a3SKever Yang  * FBDIV = Integer value programmed into feedback divide
155a60961a3SKever Yang  *
156a60961a3SKever Yang  */
157cefa5186SFinley Xiao static int rkclk_set_pll(struct px30_pll *pll, unsigned int *mode,
158cefa5186SFinley Xiao 			 enum px30_pll_id pll_id,
159cefa5186SFinley Xiao 			 unsigned long drate)
160a60961a3SKever Yang {
161cefa5186SFinley Xiao 	const struct pll_rate_table *rate;
162cefa5186SFinley Xiao 	uint vco_hz, output_hz;
163db235eb5SFinley Xiao 
164cefa5186SFinley Xiao 	rate = get_pll_settings(drate);
165cefa5186SFinley Xiao 	if (!rate) {
166cefa5186SFinley Xiao 		printf("%s unsupport rate\n", __func__);
167cefa5186SFinley Xiao 		return -EINVAL;
168cefa5186SFinley Xiao 	}
169cefa5186SFinley Xiao 
170cefa5186SFinley Xiao 	/* All PLLs have same VCO and output frequency range restrictions. */
171cefa5186SFinley Xiao 	vco_hz = OSC_HZ / 1000 * rate->fbdiv / rate->refdiv * 1000;
172cefa5186SFinley Xiao 	output_hz = vco_hz / rate->postdiv1 / rate->postdiv2;
173a60961a3SKever Yang 
174a60961a3SKever Yang 	debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n",
175cefa5186SFinley Xiao 	      pll, rate->fbdiv, rate->refdiv, rate->postdiv1,
176cefa5186SFinley Xiao 	      rate->postdiv2, vco_hz, output_hz);
177a60961a3SKever Yang 	assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
178a60961a3SKever Yang 	       output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ);
179a60961a3SKever Yang 
180db235eb5SFinley Xiao 	/*
181db235eb5SFinley Xiao 	 * When power on or changing PLL setting,
182db235eb5SFinley Xiao 	 * we must force PLL into slow mode to ensure output stable clock.
183db235eb5SFinley Xiao 	 */
184bcefd077SFinley Xiao 	rk_clrsetreg(mode, pll_mode_mask[pll_id],
185bcefd077SFinley Xiao 		     PLLMUX_FROM_XIN24M << pll_mode_shift[pll_id]);
186db235eb5SFinley Xiao 
187a60961a3SKever Yang 	/* use integer mode */
188a60961a3SKever Yang 	rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
189a60961a3SKever Yang 	/* Power down */
190a60961a3SKever Yang 	rk_setreg(&pll->con1, 1 << PLL_PD_SHIFT);
191a60961a3SKever Yang 
192a60961a3SKever Yang 	rk_clrsetreg(&pll->con0,
193a60961a3SKever Yang 		     PLL_POSTDIV1_MASK | PLL_FBDIV_MASK,
194cefa5186SFinley Xiao 		     (rate->postdiv1 << PLL_POSTDIV1_SHIFT) | rate->fbdiv);
195a60961a3SKever Yang 	rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK,
196cefa5186SFinley Xiao 		     (rate->postdiv2 << PLL_POSTDIV2_SHIFT |
197cefa5186SFinley Xiao 		     rate->refdiv << PLL_REFDIV_SHIFT));
198a60961a3SKever Yang 
199a60961a3SKever Yang 	/* Power Up */
200a60961a3SKever Yang 	rk_clrreg(&pll->con1, 1 << PLL_PD_SHIFT);
201a60961a3SKever Yang 
202a60961a3SKever Yang 	/* waiting for pll lock */
2036fb52eadSFinley Xiao 	while (!(readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)))
204a60961a3SKever Yang 		udelay(1);
205a60961a3SKever Yang 
206bcefd077SFinley Xiao 	rk_clrsetreg(mode, pll_mode_mask[pll_id],
207bcefd077SFinley Xiao 		     PLLMUX_FROM_PLL << pll_mode_shift[pll_id]);
208db235eb5SFinley Xiao 
209cefa5186SFinley Xiao 	return 0;
210a60961a3SKever Yang }
211a60961a3SKever Yang 
212cefa5186SFinley Xiao static uint32_t rkclk_pll_get_rate(struct px30_pll *pll, unsigned int *mode,
21330f1f38dSFinley Xiao 				   enum px30_pll_id pll_id)
21430f1f38dSFinley Xiao {
21530f1f38dSFinley Xiao 	u32 refdiv, fbdiv, postdiv1, postdiv2;
216cefa5186SFinley Xiao 	u32 con, shift, mask;
21730f1f38dSFinley Xiao 
218cefa5186SFinley Xiao 	con = readl(mode);
219bcefd077SFinley Xiao 	shift = pll_mode_shift[pll_id];
220bcefd077SFinley Xiao 	mask = pll_mode_mask[pll_id];
22130f1f38dSFinley Xiao 
22230f1f38dSFinley Xiao 	switch ((con & mask) >> shift) {
22330f1f38dSFinley Xiao 	case PLLMUX_FROM_XIN24M:
22430f1f38dSFinley Xiao 		return OSC_HZ;
22530f1f38dSFinley Xiao 	case PLLMUX_FROM_PLL:
22630f1f38dSFinley Xiao 		/* normal mode */
22730f1f38dSFinley Xiao 		con = readl(&pll->con0);
22830f1f38dSFinley Xiao 		postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
22930f1f38dSFinley Xiao 		fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
23030f1f38dSFinley Xiao 		con = readl(&pll->con1);
23130f1f38dSFinley Xiao 		postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
23230f1f38dSFinley Xiao 		refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
23330f1f38dSFinley Xiao 		return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
23430f1f38dSFinley Xiao 	case PLLMUX_FROM_RTC32K:
23530f1f38dSFinley Xiao 	default:
23630f1f38dSFinley Xiao 		return 32768;
23730f1f38dSFinley Xiao 	}
23830f1f38dSFinley Xiao }
23930f1f38dSFinley Xiao 
240cefa5186SFinley Xiao static ulong px30_i2c_get_clk(struct px30_clk_priv *priv, ulong clk_id)
24130f1f38dSFinley Xiao {
242cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
243a60961a3SKever Yang 	u32 div, con;
244a60961a3SKever Yang 
245a60961a3SKever Yang 	switch (clk_id) {
246a60961a3SKever Yang 	case SCLK_I2C0:
247f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[49]);
248f67f522bSFinley Xiao 		div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
249a60961a3SKever Yang 		break;
250a60961a3SKever Yang 	case SCLK_I2C1:
251f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[49]);
252f67f522bSFinley Xiao 		div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
253a60961a3SKever Yang 		break;
254a60961a3SKever Yang 	case SCLK_I2C2:
255f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[50]);
256f67f522bSFinley Xiao 		div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
257a60961a3SKever Yang 		break;
258a60961a3SKever Yang 	case SCLK_I2C3:
259f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[50]);
260f67f522bSFinley Xiao 		div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
261a60961a3SKever Yang 		break;
262a60961a3SKever Yang 	default:
263a60961a3SKever Yang 		printf("do not support this i2c bus\n");
264a60961a3SKever Yang 		return -EINVAL;
265a60961a3SKever Yang 	}
266a60961a3SKever Yang 
267cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
268a60961a3SKever Yang }
269a60961a3SKever Yang 
270cefa5186SFinley Xiao static ulong px30_i2c_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
271a60961a3SKever Yang {
272cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
273a60961a3SKever Yang 	int src_clk_div;
274a60961a3SKever Yang 
275cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
276a60961a3SKever Yang 	assert(src_clk_div - 1 < 127);
277a60961a3SKever Yang 
278a60961a3SKever Yang 	switch (clk_id) {
279a60961a3SKever Yang 	case SCLK_I2C0:
280f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[49],
281f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT |
282f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT,
283f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT |
284f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT);
285a60961a3SKever Yang 		break;
286a60961a3SKever Yang 	case SCLK_I2C1:
287f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[49],
288f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT |
289f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT,
290f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT |
291f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT);
292a60961a3SKever Yang 		break;
293a60961a3SKever Yang 	case SCLK_I2C2:
294f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[50],
295f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT |
296f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT,
297f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT |
298f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT);
299a60961a3SKever Yang 		break;
300a60961a3SKever Yang 	case SCLK_I2C3:
301f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[50],
302f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT |
303f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT,
304f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT |
305f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT);
306a60961a3SKever Yang 		break;
307a60961a3SKever Yang 	default:
308a60961a3SKever Yang 		printf("do not support this i2c bus\n");
309a60961a3SKever Yang 		return -EINVAL;
310a60961a3SKever Yang 	}
311a60961a3SKever Yang 
312cefa5186SFinley Xiao 	return px30_i2c_get_clk(priv, clk_id);
313a60961a3SKever Yang }
314a60961a3SKever Yang 
315cefa5186SFinley Xiao static ulong px30_nandc_get_clk(struct px30_clk_priv *priv)
316a60961a3SKever Yang {
317cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
318cefa5186SFinley Xiao 	u32 div, con;
319cefa5186SFinley Xiao 
320cefa5186SFinley Xiao 	con = readl(&cru->clksel_con[15]);
321cefa5186SFinley Xiao 	div = (con & NANDC_DIV_MASK) >> NANDC_DIV_SHIFT;
322cefa5186SFinley Xiao 
323cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div) / 2;
324cefa5186SFinley Xiao }
325cefa5186SFinley Xiao 
326cefa5186SFinley Xiao static ulong px30_nandc_set_clk(struct px30_clk_priv *priv,
327cefa5186SFinley Xiao 				ulong set_rate)
328cefa5186SFinley Xiao {
329cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
330cefa5186SFinley Xiao 	int src_clk_div;
331cefa5186SFinley Xiao 
332cefa5186SFinley Xiao 	/* Select nandc source from GPLL by default */
333cefa5186SFinley Xiao 	/* nandc clock defaulg div 2 internal, need provide double in cru */
334cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz / 2, set_rate);
335cefa5186SFinley Xiao 	assert(src_clk_div - 1 < 31);
336cefa5186SFinley Xiao 
337cefa5186SFinley Xiao 	rk_clrsetreg(&cru->clksel_con[15],
338cefa5186SFinley Xiao 		     NANDC_CLK_SEL_MASK | NANDC_PLL_MASK |
339cefa5186SFinley Xiao 		     NANDC_DIV_MASK,
340cefa5186SFinley Xiao 		     NANDC_CLK_SEL_NANDC << NANDC_CLK_SEL_SHIFT |
341cefa5186SFinley Xiao 		     NANDC_SEL_GPLL << NANDC_PLL_SHIFT |
342cefa5186SFinley Xiao 		     (src_clk_div - 1) << NANDC_DIV_SHIFT);
343cefa5186SFinley Xiao 
344cefa5186SFinley Xiao 	return px30_nandc_get_clk(priv);
345cefa5186SFinley Xiao }
346cefa5186SFinley Xiao 
347cefa5186SFinley Xiao static ulong px30_mmc_get_clk(struct px30_clk_priv *priv, uint clk_id)
348cefa5186SFinley Xiao {
349cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
350a60961a3SKever Yang 	u32 div, con, con_id;
351a60961a3SKever Yang 
352a60961a3SKever Yang 	switch (clk_id) {
353a60961a3SKever Yang 	case HCLK_SDMMC:
354a60961a3SKever Yang 	case SCLK_SDMMC:
355a60961a3SKever Yang 		con_id = 16;
356a60961a3SKever Yang 		break;
357a60961a3SKever Yang 	case HCLK_EMMC:
358a60961a3SKever Yang 	case SCLK_EMMC:
35996f1b3d9SKever Yang 	case SCLK_EMMC_SAMPLE:
360a60961a3SKever Yang 		con_id = 20;
361a60961a3SKever Yang 		break;
362a60961a3SKever Yang 	default:
363a60961a3SKever Yang 		return -EINVAL;
364a60961a3SKever Yang 	}
365a60961a3SKever Yang 
366a60961a3SKever Yang 	con = readl(&cru->clksel_con[con_id]);
367a60961a3SKever Yang 	div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT;
368a60961a3SKever Yang 
369a60961a3SKever Yang 	if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT
370a60961a3SKever Yang 	    == EMMC_SEL_24M)
371a60961a3SKever Yang 		return DIV_TO_RATE(OSC_HZ, div) / 2;
372a60961a3SKever Yang 	else
373cefa5186SFinley Xiao 		return DIV_TO_RATE(priv->gpll_hz, div) / 2;
374a60961a3SKever Yang 
375a60961a3SKever Yang }
376a60961a3SKever Yang 
377cefa5186SFinley Xiao static ulong px30_mmc_set_clk(struct px30_clk_priv *priv,
378a60961a3SKever Yang 			      ulong clk_id, ulong set_rate)
379a60961a3SKever Yang {
380cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
381a60961a3SKever Yang 	int src_clk_div;
382a60961a3SKever Yang 	u32 con_id;
383a60961a3SKever Yang 
384a60961a3SKever Yang 	switch (clk_id) {
385a60961a3SKever Yang 	case HCLK_SDMMC:
386a60961a3SKever Yang 	case SCLK_SDMMC:
387a60961a3SKever Yang 		con_id = 16;
388a60961a3SKever Yang 		break;
389a60961a3SKever Yang 	case HCLK_EMMC:
390a60961a3SKever Yang 	case SCLK_EMMC:
391a60961a3SKever Yang 		con_id = 20;
392a60961a3SKever Yang 		break;
393a60961a3SKever Yang 	default:
394a60961a3SKever Yang 		return -EINVAL;
395a60961a3SKever Yang 	}
396cefa5186SFinley Xiao 
397a60961a3SKever Yang 	/* Select clk_sdmmc/emmc source from GPLL by default */
398a60961a3SKever Yang 	/* mmc clock defaulg div 2 internal, need provide double in cru */
399cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz / 2, set_rate);
400a60961a3SKever Yang 
401a60961a3SKever Yang 	if (src_clk_div > 127) {
402a60961a3SKever Yang 		/* use 24MHz source for 400KHz clock */
403a60961a3SKever Yang 		src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate);
404a60961a3SKever Yang 		rk_clrsetreg(&cru->clksel_con[con_id],
405a60961a3SKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
406a60961a3SKever Yang 			     EMMC_SEL_24M << EMMC_PLL_SHIFT |
407a60961a3SKever Yang 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
408a60961a3SKever Yang 	} else {
409a60961a3SKever Yang 		rk_clrsetreg(&cru->clksel_con[con_id],
410a60961a3SKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
411a60961a3SKever Yang 			     EMMC_SEL_GPLL << EMMC_PLL_SHIFT |
412a60961a3SKever Yang 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
413a60961a3SKever Yang 	}
414a60961a3SKever Yang 	rk_clrsetreg(&cru->clksel_con[con_id +1], EMMC_CLK_SEL_MASK,
415a60961a3SKever Yang 		     EMMC_CLK_SEL_EMMC);
416a60961a3SKever Yang 
417cefa5186SFinley Xiao 	return px30_mmc_get_clk(priv, clk_id);
418a60961a3SKever Yang }
419a60961a3SKever Yang 
420cefa5186SFinley Xiao static ulong px30_pwm_get_clk(struct px30_clk_priv *priv, ulong clk_id)
421a60961a3SKever Yang {
422cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
423a60961a3SKever Yang 	u32 div, con;
424a60961a3SKever Yang 
425f67f522bSFinley Xiao 	switch (clk_id) {
426f67f522bSFinley Xiao 	case SCLK_PWM0:
427f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[52]);
428f67f522bSFinley Xiao 		div = con >> CLK_PWM0_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK;
429f67f522bSFinley Xiao 		break;
430f67f522bSFinley Xiao 	case SCLK_PWM1:
431f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[52]);
432f67f522bSFinley Xiao 		div = con >> CLK_PWM1_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK;
433f67f522bSFinley Xiao 		break;
434f67f522bSFinley Xiao 	default:
435f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
436f67f522bSFinley Xiao 		return -EINVAL;
437f67f522bSFinley Xiao 	}
438f67f522bSFinley Xiao 
439cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
440a60961a3SKever Yang }
441a60961a3SKever Yang 
442cefa5186SFinley Xiao static ulong px30_pwm_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
443a60961a3SKever Yang {
444cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
445f67f522bSFinley Xiao 	int src_clk_div;
446a60961a3SKever Yang 
447cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
448f67f522bSFinley Xiao 	assert(src_clk_div - 1 < 127);
449f67f522bSFinley Xiao 
450f67f522bSFinley Xiao 	switch (clk_id) {
451f67f522bSFinley Xiao 	case SCLK_PWM0:
452f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[52],
453f67f522bSFinley Xiao 			     CLK_PWM_DIV_CON_MASK << CLK_PWM0_DIV_CON_SHIFT |
454f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_MASK << CLK_PWM0_PLL_SEL_SHIFT,
455f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_PWM0_DIV_CON_SHIFT |
456f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_GPLL << CLK_PWM0_PLL_SEL_SHIFT);
457f67f522bSFinley Xiao 		break;
458f67f522bSFinley Xiao 	case SCLK_PWM1:
459f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[52],
460f67f522bSFinley Xiao 			     CLK_PWM_DIV_CON_MASK << CLK_PWM1_DIV_CON_SHIFT |
461f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_MASK << CLK_PWM1_PLL_SEL_SHIFT,
462f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_PWM1_DIV_CON_SHIFT |
463f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_GPLL << CLK_PWM1_PLL_SEL_SHIFT);
464f67f522bSFinley Xiao 		break;
465f67f522bSFinley Xiao 	default:
466f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
467f67f522bSFinley Xiao 		return -EINVAL;
468f67f522bSFinley Xiao 	}
469f67f522bSFinley Xiao 
470cefa5186SFinley Xiao 	return px30_pwm_get_clk(priv, clk_id);
471a60961a3SKever Yang }
472a60961a3SKever Yang 
473cefa5186SFinley Xiao static ulong px30_saradc_get_clk(struct px30_clk_priv *priv)
474a60961a3SKever Yang {
475cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
476f67f522bSFinley Xiao 	u32 div, con;
477f67f522bSFinley Xiao 
478f67f522bSFinley Xiao 	con = readl(&cru->clksel_con[55]);
479f9157291SFinley Xiao 	div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK;
480a60961a3SKever Yang 
481a60961a3SKever Yang 	return DIV_TO_RATE(OSC_HZ, div);
482a60961a3SKever Yang }
483a60961a3SKever Yang 
484cefa5186SFinley Xiao static ulong px30_saradc_set_clk(struct px30_clk_priv *priv, uint hz)
485a60961a3SKever Yang {
486cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
487a60961a3SKever Yang 	int src_clk_div;
488a60961a3SKever Yang 
489cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(OSC_HZ, hz);
490f67f522bSFinley Xiao 	assert(src_clk_div - 1 < 2047);
491a60961a3SKever Yang 
492f67f522bSFinley Xiao 	rk_clrsetreg(&cru->clksel_con[55],
493f67f522bSFinley Xiao 		     CLK_SARADC_DIV_CON_MASK,
494fce7cb7bSFinley Xiao 		     (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT);
495f67f522bSFinley Xiao 
496cefa5186SFinley Xiao 	return px30_saradc_get_clk(priv);
497f67f522bSFinley Xiao }
498f67f522bSFinley Xiao 
499cefa5186SFinley Xiao static ulong px30_spi_get_clk(struct px30_clk_priv *priv, ulong clk_id)
500f67f522bSFinley Xiao {
501cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
502f67f522bSFinley Xiao 	u32 div, con;
503f67f522bSFinley Xiao 
504f67f522bSFinley Xiao 	switch (clk_id) {
505fce7cb7bSFinley Xiao 	case SCLK_SPI0:
506f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[53]);
507f67f522bSFinley Xiao 		div = con >> CLK_SPI0_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK;
508f67f522bSFinley Xiao 		break;
509fce7cb7bSFinley Xiao 	case SCLK_SPI1:
510f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[53]);
511f67f522bSFinley Xiao 		div = con >> CLK_SPI1_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK;
512f67f522bSFinley Xiao 		break;
513f67f522bSFinley Xiao 	default:
514f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
515f67f522bSFinley Xiao 		return -EINVAL;
516f67f522bSFinley Xiao 	}
517f67f522bSFinley Xiao 
518cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
519f67f522bSFinley Xiao }
520f67f522bSFinley Xiao 
521cefa5186SFinley Xiao static ulong px30_spi_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
522f67f522bSFinley Xiao {
523cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
524f67f522bSFinley Xiao 	int src_clk_div;
525f67f522bSFinley Xiao 
526cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
527f67f522bSFinley Xiao 	assert(src_clk_div - 1 < 127);
528f67f522bSFinley Xiao 
529f67f522bSFinley Xiao 	switch (clk_id) {
530f67f522bSFinley Xiao 	case SCLK_SPI0:
531f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[53],
532f67f522bSFinley Xiao 			     CLK_SPI_DIV_CON_MASK << CLK_SPI0_DIV_CON_SHIFT |
533f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_MASK << CLK_SPI0_PLL_SEL_SHIFT,
534f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_SPI0_DIV_CON_SHIFT |
535f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_GPLL << CLK_SPI0_PLL_SEL_SHIFT);
536f67f522bSFinley Xiao 		break;
537f67f522bSFinley Xiao 	case SCLK_SPI1:
538f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[53],
539f67f522bSFinley Xiao 			     CLK_SPI_DIV_CON_MASK << CLK_SPI1_DIV_CON_SHIFT |
540f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_MASK << CLK_SPI1_PLL_SEL_SHIFT,
541f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_SPI1_DIV_CON_SHIFT |
542f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_GPLL << CLK_SPI1_PLL_SEL_SHIFT);
543f67f522bSFinley Xiao 		break;
544f67f522bSFinley Xiao 	default:
545f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
546f67f522bSFinley Xiao 		return -EINVAL;
547f67f522bSFinley Xiao 	}
548f67f522bSFinley Xiao 
549cefa5186SFinley Xiao 	return px30_spi_get_clk(priv, clk_id);
550a60961a3SKever Yang }
551a60961a3SKever Yang 
552cefa5186SFinley Xiao static ulong px30_vop_get_clk(struct px30_clk_priv *priv, ulong clk_id)
55330f1f38dSFinley Xiao {
554cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
55530f1f38dSFinley Xiao 	u32 div, con, parent;
55630f1f38dSFinley Xiao 
55730f1f38dSFinley Xiao 	switch (clk_id) {
55830f1f38dSFinley Xiao 	case ACLK_VOPB:
55930f1f38dSFinley Xiao 		con = readl(&cru->clksel_con[3]);
56030f1f38dSFinley Xiao 		div = con & ACLK_VO_DIV_MASK;
561cefa5186SFinley Xiao 		parent = priv->gpll_hz;
56230f1f38dSFinley Xiao 		break;
56330f1f38dSFinley Xiao 	case DCLK_VOPB:
56430f1f38dSFinley Xiao 		con = readl(&cru->clksel_con[5]);
56530f1f38dSFinley Xiao 		div = con & DCLK_VOPB_DIV_MASK;
566cefa5186SFinley Xiao 		parent = rkclk_pll_get_rate(&cru->pll[CPLL], &cru->mode, CPLL);
56730f1f38dSFinley Xiao 		break;
56830f1f38dSFinley Xiao 	default:
56930f1f38dSFinley Xiao 		return -ENOENT;
57030f1f38dSFinley Xiao 	}
57130f1f38dSFinley Xiao 
57230f1f38dSFinley Xiao 	return DIV_TO_RATE(parent, div);
57330f1f38dSFinley Xiao }
57430f1f38dSFinley Xiao 
575cefa5186SFinley Xiao static ulong px30_vop_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz)
57630f1f38dSFinley Xiao {
577cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
57830f1f38dSFinley Xiao 	int src_clk_div;
57930f1f38dSFinley Xiao 
580cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
58130f1f38dSFinley Xiao 	assert(src_clk_div - 1 < 31);
58230f1f38dSFinley Xiao 
58330f1f38dSFinley Xiao 	switch (clk_id) {
58430f1f38dSFinley Xiao 	case ACLK_VOPB:
58530f1f38dSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[3],
58630f1f38dSFinley Xiao 			     ACLK_VO_PLL_MASK | ACLK_VO_DIV_MASK,
58730f1f38dSFinley Xiao 			     ACLK_VO_SEL_GPLL << ACLK_VO_PLL_SHIFT |
58830f1f38dSFinley Xiao 			     (src_clk_div - 1) << ACLK_VO_DIV_SHIFT);
58930f1f38dSFinley Xiao 		break;
59030f1f38dSFinley Xiao 	case DCLK_VOPB:
59130f1f38dSFinley Xiao 		/*
59230f1f38dSFinley Xiao 		 * vopb dclk source from cpll, and equals to
59330f1f38dSFinley Xiao 		 * cpll(means div == 1)
59430f1f38dSFinley Xiao 		 */
595cefa5186SFinley Xiao 		rkclk_set_pll(&cru->pll[CPLL], &cru->mode, CPLL, hz);
59630f1f38dSFinley Xiao 
59730f1f38dSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[5],
59830f1f38dSFinley Xiao 			     DCLK_VOPB_SEL_MASK | DCLK_VOPB_PLL_SEL_MASK |
59930f1f38dSFinley Xiao 			     DCLK_VOPB_DIV_MASK,
60030f1f38dSFinley Xiao 			     DCLK_VOPB_SEL_DIVOUT << DCLK_VOPB_SEL_SHIFT |
60130f1f38dSFinley Xiao 			     DCLK_VOPB_PLL_SEL_CPLL << DCLK_VOPB_PLL_SEL_SHIFT |
60230f1f38dSFinley Xiao 			     (1 - 1) << DCLK_VOPB_DIV_SHIFT);
60330f1f38dSFinley Xiao 		break;
60430f1f38dSFinley Xiao 	default:
60530f1f38dSFinley Xiao 		printf("do not support this vop freq\n");
60630f1f38dSFinley Xiao 		return -EINVAL;
60730f1f38dSFinley Xiao 	}
60830f1f38dSFinley Xiao 
609cefa5186SFinley Xiao 	return px30_vop_get_clk(priv, clk_id);
61030f1f38dSFinley Xiao }
61130f1f38dSFinley Xiao 
612cefa5186SFinley Xiao static ulong px30_bus_get_clk(struct px30_clk_priv *priv, ulong clk_id)
613df8f8a42SFinley Xiao {
614cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
615df8f8a42SFinley Xiao 	u32 div, con, parent;
616df8f8a42SFinley Xiao 
617df8f8a42SFinley Xiao 	switch (clk_id) {
618df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
619df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[23]);
620df8f8a42SFinley Xiao 		div = (con & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT;
621cefa5186SFinley Xiao 		parent = priv->gpll_hz;
622df8f8a42SFinley Xiao 		break;
623df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
624df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[24]);
625df8f8a42SFinley Xiao 		div = (con & BUS_HCLK_DIV_MASK) >> BUS_HCLK_DIV_SHIFT;
626cefa5186SFinley Xiao 		parent = priv->gpll_hz;
627df8f8a42SFinley Xiao 		break;
628df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
629cefa5186SFinley Xiao 		parent = px30_bus_get_clk(priv, ACLK_BUS_PRE);
630df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[24]);
631df8f8a42SFinley Xiao 		div = (con & BUS_PCLK_DIV_MASK) >> BUS_PCLK_DIV_SHIFT;
632df8f8a42SFinley Xiao 		break;
633df8f8a42SFinley Xiao 	default:
634df8f8a42SFinley Xiao 		return -ENOENT;
635df8f8a42SFinley Xiao 	}
636df8f8a42SFinley Xiao 
637df8f8a42SFinley Xiao 	return DIV_TO_RATE(parent, div);
638df8f8a42SFinley Xiao }
639df8f8a42SFinley Xiao 
640cefa5186SFinley Xiao static ulong px30_bus_set_clk(struct px30_clk_priv *priv, ulong clk_id,
641cefa5186SFinley Xiao 			      ulong hz)
642df8f8a42SFinley Xiao {
643cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
644df8f8a42SFinley Xiao 	int src_clk_div;
645df8f8a42SFinley Xiao 
646df8f8a42SFinley Xiao 	/*
647df8f8a42SFinley Xiao 	 * select gpll as pd_bus bus clock source and
648df8f8a42SFinley Xiao 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
649df8f8a42SFinley Xiao 	 */
650df8f8a42SFinley Xiao 	switch (clk_id) {
651df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
652cefa5186SFinley Xiao 		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
653df8f8a42SFinley Xiao 		assert(src_clk_div - 1 < 31);
654df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[23],
655df8f8a42SFinley Xiao 			     BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK,
656df8f8a42SFinley Xiao 			     BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT |
657df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_ACLK_DIV_SHIFT);
658df8f8a42SFinley Xiao 		break;
659df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
660cefa5186SFinley Xiao 		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
661df8f8a42SFinley Xiao 		assert(src_clk_div - 1 < 31);
662df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[24],
663df8f8a42SFinley Xiao 			     BUS_PLL_SEL_MASK | BUS_HCLK_DIV_MASK,
664df8f8a42SFinley Xiao 			     BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT |
665df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_HCLK_DIV_SHIFT);
666df8f8a42SFinley Xiao 		break;
667df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
668cefa5186SFinley Xiao 		src_clk_div =
669cefa5186SFinley Xiao 			DIV_ROUND_UP(px30_bus_get_clk(priv, ACLK_BUS_PRE), hz);
670df8f8a42SFinley Xiao 		assert(src_clk_div - 1 < 3);
671df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[24],
672df8f8a42SFinley Xiao 			     BUS_PCLK_DIV_MASK,
673df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_PCLK_DIV_SHIFT);
674df8f8a42SFinley Xiao 		break;
675df8f8a42SFinley Xiao 	default:
676df8f8a42SFinley Xiao 		printf("do not support this bus freq\n");
677df8f8a42SFinley Xiao 		return -EINVAL;
678df8f8a42SFinley Xiao 	}
679df8f8a42SFinley Xiao 
680cefa5186SFinley Xiao 	return px30_bus_get_clk(priv, clk_id);
681df8f8a42SFinley Xiao }
682df8f8a42SFinley Xiao 
683cefa5186SFinley Xiao static ulong px30_peri_get_clk(struct px30_clk_priv *priv, ulong clk_id)
684df8f8a42SFinley Xiao {
685cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
686df8f8a42SFinley Xiao 	u32 div, con, parent;
687df8f8a42SFinley Xiao 
688df8f8a42SFinley Xiao 	switch (clk_id) {
689df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
690df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[14]);
691df8f8a42SFinley Xiao 		div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT;
692cefa5186SFinley Xiao 		parent = priv->gpll_hz;
693df8f8a42SFinley Xiao 		break;
694df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
695df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[14]);
696df8f8a42SFinley Xiao 		div = (con & PERI_HCLK_DIV_MASK) >> PERI_HCLK_DIV_SHIFT;
697cefa5186SFinley Xiao 		parent = priv->gpll_hz;
698df8f8a42SFinley Xiao 		break;
699df8f8a42SFinley Xiao 	default:
700df8f8a42SFinley Xiao 		return -ENOENT;
701df8f8a42SFinley Xiao 	}
702df8f8a42SFinley Xiao 
703df8f8a42SFinley Xiao 	return DIV_TO_RATE(parent, div);
704df8f8a42SFinley Xiao }
705df8f8a42SFinley Xiao 
706cefa5186SFinley Xiao static ulong px30_peri_set_clk(struct px30_clk_priv *priv, ulong clk_id,
707cefa5186SFinley Xiao 			       ulong hz)
708df8f8a42SFinley Xiao {
709cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
710df8f8a42SFinley Xiao 	int src_clk_div;
711df8f8a42SFinley Xiao 
712cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
713df8f8a42SFinley Xiao 	assert(src_clk_div - 1 < 31);
714df8f8a42SFinley Xiao 
715df8f8a42SFinley Xiao 	/*
716df8f8a42SFinley Xiao 	 * select gpll as pd_peri bus clock source and
717df8f8a42SFinley Xiao 	 * set up dependent divisors for HCLK and ACLK clocks.
718df8f8a42SFinley Xiao 	 */
719df8f8a42SFinley Xiao 	switch (clk_id) {
720df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
721df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[14],
722df8f8a42SFinley Xiao 			     PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK,
723df8f8a42SFinley Xiao 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
724df8f8a42SFinley Xiao 			     (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT);
725df8f8a42SFinley Xiao 		break;
726df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
727df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[14],
728df8f8a42SFinley Xiao 			     PERI_PLL_SEL_MASK | PERI_HCLK_DIV_MASK,
729df8f8a42SFinley Xiao 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
730df8f8a42SFinley Xiao 			     (src_clk_div - 1) << PERI_HCLK_DIV_SHIFT);
731df8f8a42SFinley Xiao 		break;
732df8f8a42SFinley Xiao 	default:
733df8f8a42SFinley Xiao 		printf("do not support this peri freq\n");
734df8f8a42SFinley Xiao 		return -EINVAL;
735df8f8a42SFinley Xiao 	}
736df8f8a42SFinley Xiao 
737cefa5186SFinley Xiao 	return px30_peri_get_clk(priv, clk_id);
738cefa5186SFinley Xiao }
739cefa5186SFinley Xiao 
740cefa5186SFinley Xiao static int px30_clk_get_gpll_rate(ulong *rate)
741cefa5186SFinley Xiao {
742cefa5186SFinley Xiao 	struct udevice *pmucru_dev;
743cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv;
744cefa5186SFinley Xiao 	struct px30_pmucru *pmucru;
745cefa5186SFinley Xiao 	int ret;
746cefa5186SFinley Xiao 
747cefa5186SFinley Xiao 	ret = uclass_get_device_by_driver(UCLASS_CLK,
748cefa5186SFinley Xiao 					  DM_GET_DRIVER(rockchip_px30_pmucru),
749cefa5186SFinley Xiao 					  &pmucru_dev);
750cefa5186SFinley Xiao 	if (ret) {
751cefa5186SFinley Xiao 		printf("%s: could not find pmucru device\n", __func__);
752cefa5186SFinley Xiao 		return ret;
753cefa5186SFinley Xiao 	}
754cefa5186SFinley Xiao 	priv = dev_get_priv(pmucru_dev);
755cefa5186SFinley Xiao 	pmucru = priv->pmucru;
756cefa5186SFinley Xiao 	*rate =  rkclk_pll_get_rate(&pmucru->pll, &pmucru->pmu_mode, GPLL);
757cefa5186SFinley Xiao 
758cefa5186SFinley Xiao 	return 0;
759df8f8a42SFinley Xiao }
760df8f8a42SFinley Xiao 
761*8b1aed51SFinley Xiao static ulong px30_clk_get_pll_rate(struct px30_clk_priv *priv,
762*8b1aed51SFinley Xiao 				   enum px30_pll_id pll_id)
763*8b1aed51SFinley Xiao {
764*8b1aed51SFinley Xiao 	struct px30_cru *cru = priv->cru;
765*8b1aed51SFinley Xiao 
766*8b1aed51SFinley Xiao 	return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id);
767*8b1aed51SFinley Xiao }
768*8b1aed51SFinley Xiao 
769a60961a3SKever Yang static ulong px30_clk_get_rate(struct clk *clk)
770a60961a3SKever Yang {
771a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
772a60961a3SKever Yang 	ulong rate = 0;
773cefa5186SFinley Xiao 	int ret;
774a60961a3SKever Yang 
775cefa5186SFinley Xiao 	if (!priv->gpll_hz) {
776cefa5186SFinley Xiao 		ret = px30_clk_get_gpll_rate(&priv->gpll_hz);
777cefa5186SFinley Xiao 		if (ret) {
778cefa5186SFinley Xiao 			printf("%s failed to get gpll rate\n", __func__);
779cefa5186SFinley Xiao 			return ret;
780cefa5186SFinley Xiao 		}
781cefa5186SFinley Xiao 		debug("%s gpll=%lu\n", __func__, priv->gpll_hz);
782cefa5186SFinley Xiao 	}
783cefa5186SFinley Xiao 
784cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
785a60961a3SKever Yang 	switch (clk->id) {
786*8b1aed51SFinley Xiao 	case PLL_APLL:
787*8b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, APLL);
788*8b1aed51SFinley Xiao 		break;
789*8b1aed51SFinley Xiao 	case PLL_DPLL:
790*8b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, DPLL);
791*8b1aed51SFinley Xiao 		break;
792*8b1aed51SFinley Xiao 	case PLL_CPLL:
793*8b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, CPLL);
794*8b1aed51SFinley Xiao 		break;
795*8b1aed51SFinley Xiao 	case PLL_NPLL:
796*8b1aed51SFinley Xiao 		rate = px30_clk_get_pll_rate(priv, NPLL);
797*8b1aed51SFinley Xiao 		break;
798a60961a3SKever Yang 	case HCLK_SDMMC:
799a60961a3SKever Yang 	case HCLK_EMMC:
800a60961a3SKever Yang 	case SCLK_SDMMC:
801a60961a3SKever Yang 	case SCLK_EMMC:
80296f1b3d9SKever Yang 	case SCLK_EMMC_SAMPLE:
803cefa5186SFinley Xiao 		rate = px30_mmc_get_clk(priv, clk->id);
804a60961a3SKever Yang 		break;
805a60961a3SKever Yang 	case SCLK_I2C0:
806a60961a3SKever Yang 	case SCLK_I2C1:
807a60961a3SKever Yang 	case SCLK_I2C2:
808a60961a3SKever Yang 	case SCLK_I2C3:
809cefa5186SFinley Xiao 		rate = px30_i2c_get_clk(priv, clk->id);
810a60961a3SKever Yang 		break;
811a60961a3SKever Yang 	case SCLK_PWM0:
812fce7cb7bSFinley Xiao 	case SCLK_PWM1:
813cefa5186SFinley Xiao 		rate = px30_pwm_get_clk(priv, clk->id);
814a60961a3SKever Yang 		break;
815a60961a3SKever Yang 	case SCLK_SARADC:
816cefa5186SFinley Xiao 		rate = px30_saradc_get_clk(priv);
817a60961a3SKever Yang 		break;
818f67f522bSFinley Xiao 	case SCLK_SPI0:
819f67f522bSFinley Xiao 	case SCLK_SPI1:
820cefa5186SFinley Xiao 		rate = px30_spi_get_clk(priv, clk->id);
821f67f522bSFinley Xiao 		break;
82230f1f38dSFinley Xiao 	case ACLK_VOPB:
82330f1f38dSFinley Xiao 	case DCLK_VOPB:
824cefa5186SFinley Xiao 		rate = px30_vop_get_clk(priv, clk->id);
82530f1f38dSFinley Xiao 		break;
826df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
827df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
828df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
829cefa5186SFinley Xiao 		rate = px30_bus_get_clk(priv, clk->id);
830df8f8a42SFinley Xiao 		break;
831df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
832df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
833cefa5186SFinley Xiao 		rate = px30_peri_get_clk(priv, clk->id);
834df8f8a42SFinley Xiao 		break;
835a60961a3SKever Yang 	default:
836a60961a3SKever Yang 		return -ENOENT;
837a60961a3SKever Yang 	}
838a60961a3SKever Yang 
839a60961a3SKever Yang 	return rate;
840a60961a3SKever Yang }
841a60961a3SKever Yang 
842a60961a3SKever Yang static ulong px30_clk_set_rate(struct clk *clk, ulong rate)
843a60961a3SKever Yang {
844a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
845a60961a3SKever Yang 	ulong ret = 0;
846a60961a3SKever Yang 
847cefa5186SFinley Xiao 	if (!priv->gpll_hz) {
848cefa5186SFinley Xiao 		ret = px30_clk_get_gpll_rate(&priv->gpll_hz);
849cefa5186SFinley Xiao 		if (ret) {
850cefa5186SFinley Xiao 			printf("%s failed to get gpll rate\n", __func__);
851cefa5186SFinley Xiao 			return ret;
852cefa5186SFinley Xiao 		}
853cefa5186SFinley Xiao 		debug("%s gpll=%lu\n", __func__, priv->gpll_hz);
854cefa5186SFinley Xiao 	}
855cefa5186SFinley Xiao 
856c4867301SKever Yang 	debug("%s %ld %ld\n", __func__, clk->id, rate);
857a60961a3SKever Yang 	switch (clk->id) {
858a60961a3SKever Yang 	case 0 ... 15:
859a60961a3SKever Yang 		return 0;
860a60961a3SKever Yang 	case HCLK_SDMMC:
861a60961a3SKever Yang 	case HCLK_EMMC:
862a60961a3SKever Yang 	case SCLK_SDMMC:
863a60961a3SKever Yang 	case SCLK_EMMC:
864cefa5186SFinley Xiao 		ret = px30_mmc_set_clk(priv, clk->id, rate);
865a60961a3SKever Yang 		break;
866a60961a3SKever Yang 	case SCLK_I2C0:
867a60961a3SKever Yang 	case SCLK_I2C1:
868a60961a3SKever Yang 	case SCLK_I2C2:
869a60961a3SKever Yang 	case SCLK_I2C3:
870cefa5186SFinley Xiao 		ret = px30_i2c_set_clk(priv, clk->id, rate);
871a60961a3SKever Yang 		break;
872a60961a3SKever Yang 	case SCLK_PWM0:
873f67f522bSFinley Xiao 	case SCLK_PWM1:
874cefa5186SFinley Xiao 		ret = px30_pwm_set_clk(priv, clk->id, rate);
875a60961a3SKever Yang 		break;
876a60961a3SKever Yang 	case SCLK_SARADC:
877cefa5186SFinley Xiao 		ret = px30_saradc_set_clk(priv, rate);
878a60961a3SKever Yang 		break;
879f67f522bSFinley Xiao 	case SCLK_SPI0:
880f67f522bSFinley Xiao 	case SCLK_SPI1:
881cefa5186SFinley Xiao 		ret = px30_spi_set_clk(priv, clk->id, rate);
882f67f522bSFinley Xiao 		break;
88330f1f38dSFinley Xiao 	case ACLK_VOPB:
88430f1f38dSFinley Xiao 	case DCLK_VOPB:
885cefa5186SFinley Xiao 		ret = px30_vop_set_clk(priv, clk->id, rate);
88630f1f38dSFinley Xiao 		break;
887df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
888df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
889df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
890cefa5186SFinley Xiao 		ret = px30_bus_set_clk(priv, clk->id, rate);
891df8f8a42SFinley Xiao 		break;
892df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
893df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
894cefa5186SFinley Xiao 		ret = px30_peri_set_clk(priv, clk->id, rate);
895df8f8a42SFinley Xiao 		break;
896a60961a3SKever Yang 	default:
897a60961a3SKever Yang 		return -ENOENT;
898a60961a3SKever Yang 	}
899a60961a3SKever Yang 
900a60961a3SKever Yang 	return ret;
901a60961a3SKever Yang }
902a60961a3SKever Yang 
903a60961a3SKever Yang #define ROCKCHIP_MMC_DELAY_SEL		BIT(10)
904a60961a3SKever Yang #define ROCKCHIP_MMC_DEGREE_MASK	0x3
905a60961a3SKever Yang #define ROCKCHIP_MMC_DELAYNUM_OFFSET	2
906a60961a3SKever Yang #define ROCKCHIP_MMC_DELAYNUM_MASK	(0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
907a60961a3SKever Yang 
908a60961a3SKever Yang #define PSECS_PER_SEC 1000000000000LL
909a60961a3SKever Yang /*
910a60961a3SKever Yang  * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
911a60961a3SKever Yang  * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
912a60961a3SKever Yang  */
913a60961a3SKever Yang #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
914a60961a3SKever Yang 
915a60961a3SKever Yang int rockchip_mmc_get_phase(struct clk *clk)
916a60961a3SKever Yang {
917a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
918a60961a3SKever Yang 	struct px30_cru *cru = priv->cru;
919a60961a3SKever Yang 	u32 raw_value, delay_num;
920a60961a3SKever Yang 	u16 degrees = 0;
921a60961a3SKever Yang 	ulong rate;
922a60961a3SKever Yang 
923a60961a3SKever Yang 	rate = px30_clk_get_rate(clk);
924a60961a3SKever Yang 
925a60961a3SKever Yang 	if (rate < 0)
926a60961a3SKever Yang 		return rate;
927a60961a3SKever Yang 
928a60961a3SKever Yang 	if (clk->id == SCLK_EMMC_SAMPLE)
929a60961a3SKever Yang 		raw_value = readl(&cru->emmc_con[1]);
930a60961a3SKever Yang 	else
931a60961a3SKever Yang 		raw_value = readl(&cru->sdmmc_con[1]);
932a60961a3SKever Yang 
93396f1b3d9SKever Yang 	raw_value >>= 1;
934a60961a3SKever Yang 	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
935a60961a3SKever Yang 
936a60961a3SKever Yang 	if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
937a60961a3SKever Yang 		/* degrees/delaynum * 10000 */
938a60961a3SKever Yang 		unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
939a60961a3SKever Yang 					36 * (rate / 1000000);
940a60961a3SKever Yang 
941a60961a3SKever Yang 		delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
942a60961a3SKever Yang 		delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
943a60961a3SKever Yang 		degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
944a60961a3SKever Yang 	}
945a60961a3SKever Yang 
946a60961a3SKever Yang 	return degrees % 360;
947a60961a3SKever Yang }
948a60961a3SKever Yang 
949a60961a3SKever Yang int rockchip_mmc_set_phase(struct clk *clk, u32 degrees)
950a60961a3SKever Yang {
951a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
952a60961a3SKever Yang 	struct px30_cru *cru = priv->cru;
953a60961a3SKever Yang 	u8 nineties, remainder, delay_num;
954a60961a3SKever Yang 	u32 raw_value, delay;
955a60961a3SKever Yang 	ulong rate;
956a60961a3SKever Yang 
957a60961a3SKever Yang 	rate = px30_clk_get_rate(clk);
958a60961a3SKever Yang 
959a60961a3SKever Yang 	if (rate < 0)
960a60961a3SKever Yang 		return rate;
961a60961a3SKever Yang 
962a60961a3SKever Yang 	nineties = degrees / 90;
963a60961a3SKever Yang 	remainder = (degrees % 90);
964a60961a3SKever Yang 
965a60961a3SKever Yang 	/*
966a60961a3SKever Yang 	 * Convert to delay; do a little extra work to make sure we
967a60961a3SKever Yang 	 * don't overflow 32-bit / 64-bit numbers.
968a60961a3SKever Yang 	 */
969a60961a3SKever Yang 	delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
970a60961a3SKever Yang 	delay *= remainder;
971a60961a3SKever Yang 	delay = DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 *
972a60961a3SKever Yang 				(ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
973a60961a3SKever Yang 
974a60961a3SKever Yang 	delay_num = (u8)min_t(u32, delay, 255);
975a60961a3SKever Yang 
976a60961a3SKever Yang 	raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
977a60961a3SKever Yang 	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
978a60961a3SKever Yang 	raw_value |= nineties;
979a60961a3SKever Yang 
98096f1b3d9SKever Yang 	raw_value <<= 1;
981a60961a3SKever Yang 	if (clk->id == SCLK_EMMC_SAMPLE)
982a60961a3SKever Yang 		writel(raw_value | 0xffff0000, &cru->emmc_con[1]);
983a60961a3SKever Yang 	else
984a60961a3SKever Yang 		writel(raw_value | 0xffff0000, &cru->sdmmc_con[1]);
985a60961a3SKever Yang 
986a60961a3SKever Yang 	debug("mmc set_phase(%d) delay_nums=%u reg=%#x actual_degrees=%d\n",
987a60961a3SKever Yang 	      degrees, delay_num, raw_value, rockchip_mmc_get_phase(clk));
988a60961a3SKever Yang 
989a60961a3SKever Yang 	return 0;
990a60961a3SKever Yang }
991a60961a3SKever Yang 
992a60961a3SKever Yang static int px30_clk_get_phase(struct clk *clk)
993a60961a3SKever Yang {
994a60961a3SKever Yang 	int ret;
995cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
996a60961a3SKever Yang 	switch (clk->id) {
997a60961a3SKever Yang 	case SCLK_EMMC_SAMPLE:
998a60961a3SKever Yang 	case SCLK_SDMMC_SAMPLE:
999a60961a3SKever Yang 		ret = rockchip_mmc_get_phase(clk);
1000a60961a3SKever Yang 		break;
1001a60961a3SKever Yang 	default:
1002a60961a3SKever Yang 		return -ENOENT;
1003a60961a3SKever Yang 	}
1004a60961a3SKever Yang 
1005a60961a3SKever Yang 	return ret;
1006a60961a3SKever Yang }
1007a60961a3SKever Yang 
1008a60961a3SKever Yang static int px30_clk_set_phase(struct clk *clk, int degrees)
1009a60961a3SKever Yang {
1010a60961a3SKever Yang 	int ret;
1011a60961a3SKever Yang 
1012cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1013a60961a3SKever Yang 	switch (clk->id) {
1014a60961a3SKever Yang 	case SCLK_EMMC_SAMPLE:
1015a60961a3SKever Yang 	case SCLK_SDMMC_SAMPLE:
1016a60961a3SKever Yang 		ret = rockchip_mmc_set_phase(clk, degrees);
1017a60961a3SKever Yang 		break;
1018a60961a3SKever Yang 	default:
1019a60961a3SKever Yang 		return -ENOENT;
1020a60961a3SKever Yang 	}
1021a60961a3SKever Yang 
1022a60961a3SKever Yang 	return ret;
1023a60961a3SKever Yang }
1024a60961a3SKever Yang 
1025a60961a3SKever Yang static struct clk_ops px30_clk_ops = {
1026a60961a3SKever Yang 	.get_rate = px30_clk_get_rate,
1027a60961a3SKever Yang 	.set_rate = px30_clk_set_rate,
1028a60961a3SKever Yang 	.get_phase	= px30_clk_get_phase,
1029a60961a3SKever Yang 	.set_phase	= px30_clk_set_phase,
1030a60961a3SKever Yang };
1031a60961a3SKever Yang 
1032a60961a3SKever Yang static int px30_clk_probe(struct udevice *dev)
1033a60961a3SKever Yang {
1034a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(dev);
1035cefa5186SFinley Xiao 	struct px30_cru *cru = priv->cru;
1036cefa5186SFinley Xiao 	u32 aclk_div;
1037a60961a3SKever Yang 
1038cefa5186SFinley Xiao 	/* init pll */
1039cefa5186SFinley Xiao 	rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, APLL_HZ);
1040cefa5186SFinley Xiao 	/*
1041cefa5186SFinley Xiao 	 * select apll as cpu/core clock pll source and
1042cefa5186SFinley Xiao 	 * set up dependent divisors for PERI and ACLK clocks.
1043cefa5186SFinley Xiao 	 * core hz : apll = 1:1
1044cefa5186SFinley Xiao 	 */
1045cefa5186SFinley Xiao 	aclk_div = APLL_HZ / CORE_ACLK_HZ - 1;
1046cefa5186SFinley Xiao 	rk_clrsetreg(&cru->clksel_con[0],
1047cefa5186SFinley Xiao 		     CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK |
1048cefa5186SFinley Xiao 		     CORE_ACLK_DIV_MASK,
1049cefa5186SFinley Xiao 		     aclk_div << CORE_ACLK_DIV_SHIFT |
1050cefa5186SFinley Xiao 		     CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
1051cefa5186SFinley Xiao 		     0 << CORE_DIV_CON_SHIFT);
1052a60961a3SKever Yang 
1053a60961a3SKever Yang 	return 0;
1054a60961a3SKever Yang }
1055a60961a3SKever Yang 
1056a60961a3SKever Yang static int px30_clk_ofdata_to_platdata(struct udevice *dev)
1057a60961a3SKever Yang {
1058a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(dev);
1059a60961a3SKever Yang 
10604203970bSKever Yang 	priv->cru = dev_read_addr_ptr(dev);
1061a60961a3SKever Yang 
1062a60961a3SKever Yang 	return 0;
1063a60961a3SKever Yang }
1064a60961a3SKever Yang 
1065a60961a3SKever Yang static int px30_clk_bind(struct udevice *dev)
1066a60961a3SKever Yang {
1067a60961a3SKever Yang 	int ret;
1068a60961a3SKever Yang 	struct udevice *sys_child, *sf_child;
1069a60961a3SKever Yang 	struct sysreset_reg *priv;
1070a60961a3SKever Yang 	struct softreset_reg *sf_priv;
1071a60961a3SKever Yang 
1072a60961a3SKever Yang 	/* The reset driver does not have a device node, so bind it here */
1073a60961a3SKever Yang 	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
1074a60961a3SKever Yang 				 &sys_child);
1075a60961a3SKever Yang 	if (ret) {
1076a60961a3SKever Yang 		debug("Warning: No sysreset driver: ret=%d\n", ret);
1077a60961a3SKever Yang 	} else {
1078a60961a3SKever Yang 		priv = malloc(sizeof(struct sysreset_reg));
1079a60961a3SKever Yang 		priv->glb_srst_fst_value = offsetof(struct px30_cru,
1080a60961a3SKever Yang 						    glb_srst_fst);
1081a60961a3SKever Yang 		priv->glb_srst_snd_value = offsetof(struct px30_cru,
1082a60961a3SKever Yang 						    glb_srst_snd);
1083a60961a3SKever Yang 		sys_child->priv = priv;
1084a60961a3SKever Yang 	}
1085a60961a3SKever Yang 
1086a60961a3SKever Yang 	ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset",
1087a60961a3SKever Yang 					 dev_ofnode(dev), &sf_child);
1088a60961a3SKever Yang 	if (ret) {
1089a60961a3SKever Yang 		debug("Warning: No rockchip reset driver: ret=%d\n", ret);
1090a60961a3SKever Yang 	} else {
1091a60961a3SKever Yang 		sf_priv = malloc(sizeof(struct softreset_reg));
1092a60961a3SKever Yang 		sf_priv->sf_reset_offset = offsetof(struct px30_cru,
1093a60961a3SKever Yang 						    softrst_con[0]);
1094a60961a3SKever Yang 		sf_priv->sf_reset_num = 12;
1095a60961a3SKever Yang 		sf_child->priv = sf_priv;
1096a60961a3SKever Yang 	}
1097a60961a3SKever Yang 
1098a60961a3SKever Yang 	return 0;
1099a60961a3SKever Yang }
1100a60961a3SKever Yang 
1101a60961a3SKever Yang static const struct udevice_id px30_clk_ids[] = {
1102a60961a3SKever Yang 	{ .compatible = "rockchip,px30-cru" },
1103a60961a3SKever Yang 	{ }
1104a60961a3SKever Yang };
1105a60961a3SKever Yang 
1106a60961a3SKever Yang U_BOOT_DRIVER(rockchip_px30_cru) = {
1107a60961a3SKever Yang 	.name		= "rockchip_px30_cru",
1108a60961a3SKever Yang 	.id		= UCLASS_CLK,
1109a60961a3SKever Yang 	.of_match	= px30_clk_ids,
1110a60961a3SKever Yang 	.priv_auto_alloc_size = sizeof(struct px30_clk_priv),
1111a60961a3SKever Yang 	.ofdata_to_platdata = px30_clk_ofdata_to_platdata,
1112a60961a3SKever Yang 	.ops		= &px30_clk_ops,
1113a60961a3SKever Yang 	.bind		= px30_clk_bind,
1114a60961a3SKever Yang 	.probe		= px30_clk_probe,
1115a60961a3SKever Yang };
1116cefa5186SFinley Xiao 
1117cefa5186SFinley Xiao static ulong px30_pclk_pmu_get_pmuclk(struct px30_pmuclk_priv *priv)
1118cefa5186SFinley Xiao {
1119cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1120cefa5186SFinley Xiao 	u32 div, con;
1121cefa5186SFinley Xiao 
1122cefa5186SFinley Xiao 	con = readl(&pmucru->pmu_clksel_con[0]);
1123cefa5186SFinley Xiao 	div = (con & CLK_PMU_PCLK_DIV_MASK) >> CLK_PMU_PCLK_DIV_SHIFT;
1124cefa5186SFinley Xiao 
1125cefa5186SFinley Xiao 	return DIV_TO_RATE(priv->gpll_hz, div);
1126cefa5186SFinley Xiao }
1127cefa5186SFinley Xiao 
1128cefa5186SFinley Xiao static ulong px30_pclk_pmu_set_pmuclk(struct px30_pmuclk_priv *priv, ulong hz)
1129cefa5186SFinley Xiao {
1130cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1131cefa5186SFinley Xiao 	int src_clk_div;
1132cefa5186SFinley Xiao 
1133cefa5186SFinley Xiao 	src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz);
1134cefa5186SFinley Xiao 	assert(src_clk_div - 1 < 31);
1135cefa5186SFinley Xiao 
1136cefa5186SFinley Xiao 	rk_clrsetreg(&pmucru->pmu_clksel_con[0],
1137cefa5186SFinley Xiao 		     CLK_PMU_PCLK_DIV_MASK,
1138cefa5186SFinley Xiao 		     (src_clk_div - 1) << CLK_PMU_PCLK_DIV_SHIFT);
1139cefa5186SFinley Xiao 
1140cefa5186SFinley Xiao 	return px30_pclk_pmu_get_pmuclk(priv);
1141cefa5186SFinley Xiao }
1142cefa5186SFinley Xiao 
1143cefa5186SFinley Xiao static ulong px30_gpll_get_pmuclk(struct px30_pmuclk_priv *priv)
1144cefa5186SFinley Xiao {
1145cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1146cefa5186SFinley Xiao 
1147cefa5186SFinley Xiao 	return rkclk_pll_get_rate(&pmucru->pll, &pmucru->pmu_mode, GPLL);
1148cefa5186SFinley Xiao }
1149cefa5186SFinley Xiao 
1150cefa5186SFinley Xiao static ulong px30_gpll_set_pmuclk(struct px30_pmuclk_priv *priv, ulong hz)
1151cefa5186SFinley Xiao {
1152cefa5186SFinley Xiao 	struct udevice *cru_dev;
1153cefa5186SFinley Xiao 	struct px30_clk_priv *cru_priv;
1154cefa5186SFinley Xiao 	struct px30_pmucru *pmucru = priv->pmucru;
1155cefa5186SFinley Xiao 	u32 div;
1156cefa5186SFinley Xiao 	ulong emmc_rate, sdmmc_rate, nandc_rate;
1157cefa5186SFinley Xiao 	int ret;
1158cefa5186SFinley Xiao 
1159cefa5186SFinley Xiao 	priv->gpll_hz = px30_gpll_get_pmuclk(priv);
1160cefa5186SFinley Xiao 
1161cefa5186SFinley Xiao 	ret = uclass_get_device_by_name(UCLASS_CLK,
1162cefa5186SFinley Xiao 					"clock-controller@ff2b0000",
1163cefa5186SFinley Xiao 					 &cru_dev);
1164cefa5186SFinley Xiao 	if (ret) {
1165cefa5186SFinley Xiao 		printf("%s failed to get cru device\n", __func__);
1166cefa5186SFinley Xiao 		return ret;
1167cefa5186SFinley Xiao 	}
1168cefa5186SFinley Xiao 	cru_priv = dev_get_priv(cru_dev);
1169cefa5186SFinley Xiao 	cru_priv->gpll_hz = priv->gpll_hz;
1170cefa5186SFinley Xiao 
1171cefa5186SFinley Xiao 	div = DIV_ROUND_UP(hz, priv->gpll_hz);
1172cefa5186SFinley Xiao 
1173cefa5186SFinley Xiao 	/*
1174cefa5186SFinley Xiao 	 * avoid bus and peri clock rate too large, reduce rate first.
1175cefa5186SFinley Xiao 	 * they will be assigned by clk_set_defaults.
1176cefa5186SFinley Xiao 	 */
1177cefa5186SFinley Xiao 	px30_bus_set_clk(cru_priv, ACLK_BUS_PRE,
1178cefa5186SFinley Xiao 			 px30_bus_get_clk(cru_priv, ACLK_BUS_PRE) / div);
1179cefa5186SFinley Xiao 	px30_bus_set_clk(cru_priv, HCLK_BUS_PRE,
1180cefa5186SFinley Xiao 			 px30_bus_get_clk(cru_priv, HCLK_BUS_PRE) / div);
1181cefa5186SFinley Xiao 	px30_bus_set_clk(cru_priv, PCLK_BUS_PRE,
1182cefa5186SFinley Xiao 			 px30_bus_get_clk(cru_priv, PCLK_BUS_PRE) / div);
1183cefa5186SFinley Xiao 	px30_peri_set_clk(cru_priv, ACLK_PERI_PRE,
1184cefa5186SFinley Xiao 			  px30_bus_get_clk(cru_priv, ACLK_PERI_PRE) / div);
1185cefa5186SFinley Xiao 	px30_peri_set_clk(cru_priv, HCLK_PERI_PRE,
1186cefa5186SFinley Xiao 			  px30_bus_get_clk(cru_priv, HCLK_PERI_PRE) / div);
1187cefa5186SFinley Xiao 	px30_pclk_pmu_set_pmuclk(priv, px30_pclk_pmu_get_pmuclk(priv) / div);
1188cefa5186SFinley Xiao 
1189cefa5186SFinley Xiao 	/*
1190cefa5186SFinley Xiao 	 * save emmc, sdmmc and nandc clock rate,
1191cefa5186SFinley Xiao 	 * nandc clock rate should less than or equal to 150Mhz.
1192cefa5186SFinley Xiao 	 */
1193cefa5186SFinley Xiao 	emmc_rate = px30_mmc_get_clk(cru_priv, SCLK_EMMC);
1194cefa5186SFinley Xiao 	sdmmc_rate = px30_mmc_get_clk(cru_priv, SCLK_SDMMC);
1195cefa5186SFinley Xiao 	nandc_rate = px30_nandc_get_clk(cru_priv);
1196cefa5186SFinley Xiao 	debug("%s emmc=%lu, sdmmc=%lu, nandc=%lu\n", __func__, emmc_rate,
1197cefa5186SFinley Xiao 	      sdmmc_rate, nandc_rate);
1198cefa5186SFinley Xiao 
1199cefa5186SFinley Xiao 	rkclk_set_pll(&pmucru->pll, &pmucru->pmu_mode, GPLL, hz);
1200cefa5186SFinley Xiao 	priv->gpll_hz = px30_gpll_get_pmuclk(priv);
1201cefa5186SFinley Xiao 	cru_priv->gpll_hz = priv->gpll_hz;
1202cefa5186SFinley Xiao 
1203cefa5186SFinley Xiao 	/* restore emmc, sdmmc and nandc clock rate */
1204cefa5186SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_EMMC, emmc_rate);
1205cefa5186SFinley Xiao 	px30_mmc_set_clk(cru_priv, SCLK_SDMMC, sdmmc_rate);
1206cefa5186SFinley Xiao 	px30_nandc_set_clk(cru_priv, nandc_rate);
1207cefa5186SFinley Xiao 
1208cefa5186SFinley Xiao 	return priv->gpll_hz;
1209cefa5186SFinley Xiao }
1210cefa5186SFinley Xiao 
1211cefa5186SFinley Xiao static ulong px30_pmuclk_get_rate(struct clk *clk)
1212cefa5186SFinley Xiao {
1213cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev);
1214cefa5186SFinley Xiao 	ulong rate = 0;
1215cefa5186SFinley Xiao 
1216cefa5186SFinley Xiao 	debug("%s %ld\n", __func__, clk->id);
1217cefa5186SFinley Xiao 	switch (clk->id) {
1218cefa5186SFinley Xiao 	case PLL_GPLL:
1219cefa5186SFinley Xiao 		rate = px30_gpll_get_pmuclk(priv);
1220cefa5186SFinley Xiao 		break;
1221cefa5186SFinley Xiao 	case PCLK_PMU_PRE:
1222cefa5186SFinley Xiao 		rate = px30_pclk_pmu_get_pmuclk(priv);
1223cefa5186SFinley Xiao 		break;
1224cefa5186SFinley Xiao 	default:
1225cefa5186SFinley Xiao 		return -ENOENT;
1226cefa5186SFinley Xiao 	}
1227cefa5186SFinley Xiao 
1228cefa5186SFinley Xiao 	return rate;
1229cefa5186SFinley Xiao }
1230cefa5186SFinley Xiao 
1231cefa5186SFinley Xiao static ulong px30_pmuclk_set_rate(struct clk *clk, ulong rate)
1232cefa5186SFinley Xiao {
1233cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev);
1234cefa5186SFinley Xiao 	ulong ret = 0;
1235cefa5186SFinley Xiao 
1236cefa5186SFinley Xiao 	debug("%s %ld %ld\n", __func__, clk->id, rate);
1237cefa5186SFinley Xiao 	switch (clk->id) {
1238cefa5186SFinley Xiao 	case PLL_GPLL:
1239cefa5186SFinley Xiao 		ret = px30_gpll_set_pmuclk(priv, rate);
1240cefa5186SFinley Xiao 		break;
1241cefa5186SFinley Xiao 	case PCLK_PMU_PRE:
1242cefa5186SFinley Xiao 		ret = px30_pclk_pmu_set_pmuclk(priv, rate);
1243cefa5186SFinley Xiao 		break;
1244cefa5186SFinley Xiao 	default:
1245cefa5186SFinley Xiao 		return -ENOENT;
1246cefa5186SFinley Xiao 	}
1247cefa5186SFinley Xiao 
1248cefa5186SFinley Xiao 	return ret;
1249cefa5186SFinley Xiao }
1250cefa5186SFinley Xiao 
1251cefa5186SFinley Xiao static struct clk_ops px30_pmuclk_ops = {
1252cefa5186SFinley Xiao 	.get_rate = px30_pmuclk_get_rate,
1253cefa5186SFinley Xiao 	.set_rate = px30_pmuclk_set_rate,
1254cefa5186SFinley Xiao };
1255cefa5186SFinley Xiao 
1256cefa5186SFinley Xiao static int px30_pmuclk_probe(struct udevice *dev)
1257cefa5186SFinley Xiao {
1258cefa5186SFinley Xiao 	return 0;
1259cefa5186SFinley Xiao }
1260cefa5186SFinley Xiao 
1261cefa5186SFinley Xiao static int px30_pmuclk_ofdata_to_platdata(struct udevice *dev)
1262cefa5186SFinley Xiao {
1263cefa5186SFinley Xiao 	struct px30_pmuclk_priv *priv = dev_get_priv(dev);
1264cefa5186SFinley Xiao 
1265cefa5186SFinley Xiao 	priv->pmucru = dev_read_addr_ptr(dev);
1266cefa5186SFinley Xiao 
1267cefa5186SFinley Xiao 	return 0;
1268cefa5186SFinley Xiao }
1269cefa5186SFinley Xiao 
1270cefa5186SFinley Xiao static const struct udevice_id px30_pmuclk_ids[] = {
1271cefa5186SFinley Xiao 	{ .compatible = "rockchip,px30-pmucru" },
1272cefa5186SFinley Xiao 	{ }
1273cefa5186SFinley Xiao };
1274cefa5186SFinley Xiao 
1275cefa5186SFinley Xiao U_BOOT_DRIVER(rockchip_px30_pmucru) = {
1276cefa5186SFinley Xiao 	.name		= "rockchip_px30_pmucru",
1277cefa5186SFinley Xiao 	.id		= UCLASS_CLK,
1278cefa5186SFinley Xiao 	.of_match	= px30_pmuclk_ids,
1279cefa5186SFinley Xiao 	.priv_auto_alloc_size = sizeof(struct px30_pmuclk_priv),
1280cefa5186SFinley Xiao 	.ofdata_to_platdata = px30_pmuclk_ofdata_to_platdata,
1281cefa5186SFinley Xiao 	.ops		= &px30_pmuclk_ops,
1282cefa5186SFinley Xiao 	.probe		= px30_pmuclk_probe,
1283cefa5186SFinley Xiao };
1284