xref: /rk3399_rockchip-uboot/drivers/clk/rockchip/clk_px30.c (revision df8f8a42aad07a4e0ce72cceba3bc5e6966bd542)
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 
29a60961a3SKever Yang #define DIV_TO_RATE(input_rate, div)    ((input_rate) / ((div) + 1))
30a60961a3SKever Yang 
31a60961a3SKever Yang #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
32a60961a3SKever Yang 	.refdiv = _refdiv,\
33a60961a3SKever Yang 	.fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
34a60961a3SKever Yang 	.postdiv1 = _postdiv1, .postdiv2 = _postdiv2};
3577ecce68SFinley Xiao static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 1, 1);
36a60961a3SKever Yang 
37a60961a3SKever Yang static const struct pll_div apll_816_cfg = PLL_DIVISORS(816 * MHz, 1, 2, 1);
38a60961a3SKever Yang static const struct pll_div apll_600_cfg = PLL_DIVISORS(600 * MHz, 1, 3, 1);
39a60961a3SKever Yang 
40a60961a3SKever Yang static const struct pll_div *apll_cfgs[] = {
41a60961a3SKever Yang 	[APLL_816_MHZ] = &apll_816_cfg,
42a60961a3SKever Yang 	[APLL_600_MHZ] = &apll_600_cfg,
43a60961a3SKever Yang };
44a60961a3SKever Yang 
45bcefd077SFinley Xiao static u8 pll_mode_shift[PLL_COUNT] = {
46bcefd077SFinley Xiao 	APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
47bcefd077SFinley Xiao 	NPLL_MODE_SHIFT, GPLL_MODE_SHIFT
48bcefd077SFinley Xiao };
49bcefd077SFinley Xiao static u32 pll_mode_mask[PLL_COUNT] = {
50bcefd077SFinley Xiao 	APLL_MODE_MASK, DPLL_MODE_MASK, CPLL_MODE_MASK,
51bcefd077SFinley Xiao 	NPLL_MODE_MASK, GPLL_MODE_MASK
52bcefd077SFinley Xiao };
53bcefd077SFinley Xiao 
54a60961a3SKever Yang /*
55a60961a3SKever Yang  *  the div restructions of pll in integer mode, these are defined in
56a60961a3SKever Yang  *  * CRU_*PLL_CON0 or PMUCRU_*PLL_CON0
57a60961a3SKever Yang  */
58a60961a3SKever Yang #define PLL_DIV_MIN	16
59a60961a3SKever Yang #define PLL_DIV_MAX	3200
60a60961a3SKever Yang 
61a60961a3SKever Yang /*
62a60961a3SKever Yang  * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
63a60961a3SKever Yang  * Formulas also embedded within the Fractional PLL Verilog model:
64a60961a3SKever Yang  * If DSMPD = 1 (DSM is disabled, "integer mode")
65a60961a3SKever Yang  * FOUTVCO = FREF / REFDIV * FBDIV
66a60961a3SKever Yang  * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
67a60961a3SKever Yang  * Where:
68a60961a3SKever Yang  * FOUTVCO = Fractional PLL non-divided output frequency
69a60961a3SKever Yang  * FOUTPOSTDIV = Fractional PLL divided output frequency
70a60961a3SKever Yang  *               (output of second post divider)
71a60961a3SKever Yang  * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
72a60961a3SKever Yang  * REFDIV = Fractional PLL input reference clock divider
73a60961a3SKever Yang  * FBDIV = Integer value programmed into feedback divide
74a60961a3SKever Yang  *
75a60961a3SKever Yang  */
76db235eb5SFinley Xiao static void rkclk_set_pll(struct px30_cru *cru, enum px30_pll_id pll_id,
77db235eb5SFinley Xiao 			  const struct pll_div *div)
78a60961a3SKever Yang {
79db235eb5SFinley Xiao 	struct px30_pll *pll;
80db235eb5SFinley Xiao 	unsigned int *mode;
81a60961a3SKever Yang 	/* All PLLs have same VCO and output frequency range restrictions. */
82a60961a3SKever Yang 	uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000;
83a60961a3SKever Yang 	uint output_hz = vco_hz / div->postdiv1 / div->postdiv2;
84db235eb5SFinley Xiao 
85db235eb5SFinley Xiao 	if (pll_id == GPLL) {
86db235eb5SFinley Xiao 		pll = &cru->gpll;
87db235eb5SFinley Xiao 		mode = &cru->pmu_mode;
88db235eb5SFinley Xiao 	} else {
89db235eb5SFinley Xiao 		pll = &cru->pll[pll_id];
90db235eb5SFinley Xiao 		mode = &cru->mode;
91db235eb5SFinley Xiao 	};
92a60961a3SKever Yang 
93a60961a3SKever Yang 	debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n",
94a60961a3SKever Yang 	      pll, div->fbdiv, div->refdiv, div->postdiv1,
95a60961a3SKever Yang 	      div->postdiv2, vco_hz, output_hz);
96a60961a3SKever Yang 	assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
97a60961a3SKever Yang 	       output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ);
98a60961a3SKever Yang 
99db235eb5SFinley Xiao 	/*
100db235eb5SFinley Xiao 	 * When power on or changing PLL setting,
101db235eb5SFinley Xiao 	 * we must force PLL into slow mode to ensure output stable clock.
102db235eb5SFinley Xiao 	 */
103bcefd077SFinley Xiao 	rk_clrsetreg(mode, pll_mode_mask[pll_id],
104bcefd077SFinley Xiao 		     PLLMUX_FROM_XIN24M << pll_mode_shift[pll_id]);
105db235eb5SFinley Xiao 
106a60961a3SKever Yang 	/* use integer mode */
107a60961a3SKever Yang 	rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
108a60961a3SKever Yang 	/* Power down */
109a60961a3SKever Yang 	rk_setreg(&pll->con1, 1 << PLL_PD_SHIFT);
110a60961a3SKever Yang 
111a60961a3SKever Yang 	rk_clrsetreg(&pll->con0,
112a60961a3SKever Yang 		     PLL_POSTDIV1_MASK | PLL_FBDIV_MASK,
113a60961a3SKever Yang 		     (div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv);
114a60961a3SKever Yang 	rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK,
115a60961a3SKever Yang 		     (div->postdiv2 << PLL_POSTDIV2_SHIFT |
116a60961a3SKever Yang 		     div->refdiv << PLL_REFDIV_SHIFT));
117a60961a3SKever Yang 
118a60961a3SKever Yang 	/* Power Up */
119a60961a3SKever Yang 	rk_clrreg(&pll->con1, 1 << PLL_PD_SHIFT);
120a60961a3SKever Yang 
121a60961a3SKever Yang 	/* waiting for pll lock */
1226fb52eadSFinley Xiao 	while (!(readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT)))
123a60961a3SKever Yang 		udelay(1);
124a60961a3SKever Yang 
125bcefd077SFinley Xiao 	rk_clrsetreg(mode, pll_mode_mask[pll_id],
126bcefd077SFinley Xiao 		     PLLMUX_FROM_PLL << pll_mode_shift[pll_id]);
127db235eb5SFinley Xiao 
128a60961a3SKever Yang 	return;
129a60961a3SKever Yang }
130a60961a3SKever Yang 
13130f1f38dSFinley Xiao static uint32_t rkclk_pll_get_rate(struct px30_cru *cru,
13230f1f38dSFinley Xiao 				   enum px30_pll_id pll_id)
13330f1f38dSFinley Xiao {
13430f1f38dSFinley Xiao 	u32 refdiv, fbdiv, postdiv1, postdiv2;
13530f1f38dSFinley Xiao 	u32 con;
13630f1f38dSFinley Xiao 	struct px30_pll *pll;
13730f1f38dSFinley Xiao 	uint shift;
13830f1f38dSFinley Xiao 	uint mask;
13930f1f38dSFinley Xiao 
14030f1f38dSFinley Xiao 	if (pll_id == GPLL) {
14130f1f38dSFinley Xiao 		pll = &cru->gpll;
14230f1f38dSFinley Xiao 		con = readl(&cru->pmu_mode);
14330f1f38dSFinley Xiao 	} else {
14430f1f38dSFinley Xiao 		pll = &cru->pll[pll_id];
14530f1f38dSFinley Xiao 		con = readl(&cru->mode);
14630f1f38dSFinley Xiao 	}
14730f1f38dSFinley Xiao 
148bcefd077SFinley Xiao 	shift = pll_mode_shift[pll_id];
149bcefd077SFinley Xiao 	mask = pll_mode_mask[pll_id];
15030f1f38dSFinley Xiao 
15130f1f38dSFinley Xiao 	switch ((con & mask) >> shift) {
15230f1f38dSFinley Xiao 	case PLLMUX_FROM_XIN24M:
15330f1f38dSFinley Xiao 		return OSC_HZ;
15430f1f38dSFinley Xiao 	case PLLMUX_FROM_PLL:
15530f1f38dSFinley Xiao 		/* normal mode */
15630f1f38dSFinley Xiao 		con = readl(&pll->con0);
15730f1f38dSFinley Xiao 		postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
15830f1f38dSFinley Xiao 		fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
15930f1f38dSFinley Xiao 		con = readl(&pll->con1);
16030f1f38dSFinley Xiao 		postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
16130f1f38dSFinley Xiao 		refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
16230f1f38dSFinley Xiao 		return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
16330f1f38dSFinley Xiao 	case PLLMUX_FROM_RTC32K:
16430f1f38dSFinley Xiao 	default:
16530f1f38dSFinley Xiao 		return 32768;
16630f1f38dSFinley Xiao 	}
16730f1f38dSFinley Xiao }
16830f1f38dSFinley Xiao 
16930f1f38dSFinley Xiao static int pll_para_config(u32 freq_hz, struct pll_div *div)
17030f1f38dSFinley Xiao {
17130f1f38dSFinley Xiao 	u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0;
17230f1f38dSFinley Xiao 	u32 postdiv1, postdiv2 = 1;
17330f1f38dSFinley Xiao 	u32 fref_khz;
17430f1f38dSFinley Xiao 	u32 diff_khz, best_diff_khz;
17530f1f38dSFinley Xiao 	const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16;
17630f1f38dSFinley Xiao 	const u32 max_postdiv1 = 7, max_postdiv2 = 7;
17730f1f38dSFinley Xiao 	u32 vco_khz;
17830f1f38dSFinley Xiao 	u32 freq_khz = freq_hz / KHz;
17930f1f38dSFinley Xiao 
18030f1f38dSFinley Xiao 	if (!freq_hz) {
18130f1f38dSFinley Xiao 		printf("%s: the frequency can't be 0 Hz\n", __func__);
18230f1f38dSFinley Xiao 		return -1;
18330f1f38dSFinley Xiao 	}
18430f1f38dSFinley Xiao 
18530f1f38dSFinley Xiao 	postdiv1 = DIV_ROUND_UP(VCO_MIN_HZ / 1000, freq_khz);
18630f1f38dSFinley Xiao 	if (postdiv1 > max_postdiv1) {
18730f1f38dSFinley Xiao 		postdiv2 = DIV_ROUND_UP(postdiv1, max_postdiv1);
18830f1f38dSFinley Xiao 		postdiv1 = DIV_ROUND_UP(postdiv1, postdiv2);
18930f1f38dSFinley Xiao 	}
19030f1f38dSFinley Xiao 
19130f1f38dSFinley Xiao 	vco_khz = freq_khz * postdiv1 * postdiv2;
19230f1f38dSFinley Xiao 
19330f1f38dSFinley Xiao 	if (vco_khz < (VCO_MIN_HZ / KHz) || vco_khz > (VCO_MAX_HZ / KHz) ||
19430f1f38dSFinley Xiao 	    postdiv2 > max_postdiv2) {
19530f1f38dSFinley Xiao 		printf("%s: Cannot find out a supported VCO for Freq (%uHz)\n",
19630f1f38dSFinley Xiao 		       __func__, freq_hz);
19730f1f38dSFinley Xiao 		return -1;
19830f1f38dSFinley Xiao 	}
19930f1f38dSFinley Xiao 
20030f1f38dSFinley Xiao 	div->postdiv1 = postdiv1;
20130f1f38dSFinley Xiao 	div->postdiv2 = postdiv2;
20230f1f38dSFinley Xiao 
20330f1f38dSFinley Xiao 	best_diff_khz = vco_khz;
20430f1f38dSFinley Xiao 	for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) {
20530f1f38dSFinley Xiao 		fref_khz = ref_khz / refdiv;
20630f1f38dSFinley Xiao 
20730f1f38dSFinley Xiao 		fbdiv = vco_khz / fref_khz;
20830f1f38dSFinley Xiao 		if ((fbdiv >= max_fbdiv) || (fbdiv <= min_fbdiv))
20930f1f38dSFinley Xiao 			continue;
21030f1f38dSFinley Xiao 		diff_khz = vco_khz - fbdiv * fref_khz;
21130f1f38dSFinley Xiao 		if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) {
21230f1f38dSFinley Xiao 			fbdiv++;
21330f1f38dSFinley Xiao 			diff_khz = fref_khz - diff_khz;
21430f1f38dSFinley Xiao 		}
21530f1f38dSFinley Xiao 
21630f1f38dSFinley Xiao 		if (diff_khz >= best_diff_khz)
21730f1f38dSFinley Xiao 			continue;
21830f1f38dSFinley Xiao 
21930f1f38dSFinley Xiao 		best_diff_khz = diff_khz;
22030f1f38dSFinley Xiao 		div->refdiv = refdiv;
22130f1f38dSFinley Xiao 		div->fbdiv = fbdiv;
22230f1f38dSFinley Xiao 	}
22330f1f38dSFinley Xiao 
22430f1f38dSFinley Xiao 	if (best_diff_khz > 4 * (MHz / KHz)) {
22530f1f38dSFinley Xiao 		printf("%s: Failed to match output frequency %u bestis %u Hz\n",
22630f1f38dSFinley Xiao 		       __func__, freq_hz,
22730f1f38dSFinley Xiao 		       best_diff_khz * KHz);
22830f1f38dSFinley Xiao 		return -1;
22930f1f38dSFinley Xiao 	}
23030f1f38dSFinley Xiao 	return 0;
23130f1f38dSFinley Xiao }
23230f1f38dSFinley Xiao 
233a60961a3SKever Yang static void rkclk_init(struct px30_cru *cru)
234a60961a3SKever Yang {
235a60961a3SKever Yang 	u32 aclk_div;
236a60961a3SKever Yang 
237a60961a3SKever Yang 	/* init pll */
238db235eb5SFinley Xiao 	rkclk_set_pll(cru, APLL, apll_cfgs[APLL_816_MHZ]);
239db235eb5SFinley Xiao 	rkclk_set_pll(cru, GPLL, &gpll_init_cfg);
240a60961a3SKever Yang 
241a60961a3SKever Yang 	/*
242a60961a3SKever Yang 	 * select apll as cpu/core clock pll source and
243a60961a3SKever Yang 	 * set up dependent divisors for PERI and ACLK clocks.
244a60961a3SKever Yang 	 * core hz : apll = 1:1
245a60961a3SKever Yang 	 */
246a60961a3SKever Yang 	aclk_div = APLL_HZ / CORE_ACLK_HZ - 1;
247a60961a3SKever Yang 	rk_clrsetreg(&cru->clksel_con[0],
248a60961a3SKever Yang 		     CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK |
249a60961a3SKever Yang 		     CORE_ACLK_DIV_MASK,
250a60961a3SKever Yang 		     aclk_div << CORE_ACLK_DIV_SHIFT |
251a60961a3SKever Yang 		     CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
252a60961a3SKever Yang 		     0 << CORE_DIV_CON_SHIFT);
253a60961a3SKever Yang }
254a60961a3SKever Yang 
255a60961a3SKever Yang static ulong px30_i2c_get_clk(struct px30_cru *cru, ulong clk_id)
256a60961a3SKever Yang {
257a60961a3SKever Yang 	u32 div, con;
258a60961a3SKever Yang 
259a60961a3SKever Yang 	switch (clk_id) {
260a60961a3SKever Yang 	case SCLK_I2C0:
261f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[49]);
262f67f522bSFinley Xiao 		div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
263a60961a3SKever Yang 		break;
264a60961a3SKever Yang 	case SCLK_I2C1:
265f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[49]);
266f67f522bSFinley Xiao 		div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
267a60961a3SKever Yang 		break;
268a60961a3SKever Yang 	case SCLK_I2C2:
269f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[50]);
270f67f522bSFinley Xiao 		div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
271a60961a3SKever Yang 		break;
272a60961a3SKever Yang 	case SCLK_I2C3:
273f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[50]);
274f67f522bSFinley Xiao 		div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK;
275a60961a3SKever Yang 		break;
276a60961a3SKever Yang 	default:
277a60961a3SKever Yang 		printf("do not support this i2c bus\n");
278a60961a3SKever Yang 		return -EINVAL;
279a60961a3SKever Yang 	}
280a60961a3SKever Yang 
281a60961a3SKever Yang 	return DIV_TO_RATE(GPLL_HZ, div);
282a60961a3SKever Yang }
283a60961a3SKever Yang 
284a60961a3SKever Yang static ulong px30_i2c_set_clk(struct px30_cru *cru, ulong clk_id, uint hz)
285a60961a3SKever Yang {
286a60961a3SKever Yang 	int src_clk_div;
287a60961a3SKever Yang 
288a60961a3SKever Yang 	src_clk_div = GPLL_HZ / hz;
289a60961a3SKever Yang 	assert(src_clk_div - 1 < 127);
290a60961a3SKever Yang 
291a60961a3SKever Yang 	switch (clk_id) {
292a60961a3SKever Yang 	case SCLK_I2C0:
293f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[49],
294f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT |
295f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT,
296f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT |
297f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT);
298a60961a3SKever Yang 		break;
299a60961a3SKever Yang 	case SCLK_I2C1:
300f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[49],
301f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT |
302f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT,
303f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT |
304f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT);
305a60961a3SKever Yang 		break;
306a60961a3SKever Yang 	case SCLK_I2C2:
307f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[50],
308f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT |
309f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT,
310f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT |
311f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT);
312a60961a3SKever Yang 		break;
313a60961a3SKever Yang 	case SCLK_I2C3:
314f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[50],
315f67f522bSFinley Xiao 			     CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT |
316f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT,
317f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT |
318f67f522bSFinley Xiao 			     CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT);
319a60961a3SKever Yang 		break;
320a60961a3SKever Yang 	default:
321a60961a3SKever Yang 		printf("do not support this i2c bus\n");
322a60961a3SKever Yang 		return -EINVAL;
323a60961a3SKever Yang 	}
324a60961a3SKever Yang 
325fce7cb7bSFinley Xiao 	return px30_i2c_get_clk(cru, clk_id);
326a60961a3SKever Yang }
327a60961a3SKever Yang 
328a60961a3SKever Yang static ulong px30_mmc_get_clk(struct px30_cru *cru, uint clk_id)
329a60961a3SKever Yang {
330a60961a3SKever Yang 	u32 div, con, con_id;
331a60961a3SKever Yang 
332a60961a3SKever Yang 	switch (clk_id) {
333a60961a3SKever Yang 	case HCLK_SDMMC:
334a60961a3SKever Yang 	case SCLK_SDMMC:
335a60961a3SKever Yang 		con_id = 16;
336a60961a3SKever Yang 		break;
337a60961a3SKever Yang 	case HCLK_EMMC:
338a60961a3SKever Yang 	case SCLK_EMMC:
33996f1b3d9SKever Yang 	case SCLK_EMMC_SAMPLE:
340a60961a3SKever Yang 		con_id = 20;
341a60961a3SKever Yang 		break;
342a60961a3SKever Yang 	default:
343a60961a3SKever Yang 		return -EINVAL;
344a60961a3SKever Yang 	}
345a60961a3SKever Yang 
346a60961a3SKever Yang 	con = readl(&cru->clksel_con[con_id]);
347a60961a3SKever Yang 	div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT;
348a60961a3SKever Yang 
349a60961a3SKever Yang 	if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT
350a60961a3SKever Yang 	    == EMMC_SEL_24M)
351a60961a3SKever Yang 		return DIV_TO_RATE(OSC_HZ, div) / 2;
352a60961a3SKever Yang 	else
353a60961a3SKever Yang 		return DIV_TO_RATE(GPLL_HZ, div) / 2;
354a60961a3SKever Yang 
355a60961a3SKever Yang }
356a60961a3SKever Yang 
357a60961a3SKever Yang static ulong px30_mmc_set_clk(struct px30_cru *cru,
358a60961a3SKever Yang 				ulong clk_id, ulong set_rate)
359a60961a3SKever Yang {
360a60961a3SKever Yang 	int src_clk_div;
361a60961a3SKever Yang 	u32 con_id;
362a60961a3SKever Yang 
363c4867301SKever Yang 	debug("%s %ld %ld\n", __func__, clk_id, set_rate);
364a60961a3SKever Yang 	switch (clk_id) {
365a60961a3SKever Yang 	case HCLK_SDMMC:
366a60961a3SKever Yang 	case SCLK_SDMMC:
367a60961a3SKever Yang 		con_id = 16;
368a60961a3SKever Yang 		break;
369a60961a3SKever Yang 	case HCLK_EMMC:
370a60961a3SKever Yang 	case SCLK_EMMC:
371a60961a3SKever Yang 		con_id = 20;
372a60961a3SKever Yang 		break;
373a60961a3SKever Yang 	default:
374a60961a3SKever Yang 		return -EINVAL;
375a60961a3SKever Yang 	}
376a60961a3SKever Yang 	/* Select clk_sdmmc/emmc source from GPLL by default */
377a60961a3SKever Yang 	/* mmc clock defaulg div 2 internal, need provide double in cru */
378a60961a3SKever Yang 	src_clk_div = DIV_ROUND_UP(GPLL_HZ / 2, set_rate);
379a60961a3SKever Yang 
380a60961a3SKever Yang 	if (src_clk_div > 127) {
381a60961a3SKever Yang 		/* use 24MHz source for 400KHz clock */
382a60961a3SKever Yang 		src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate);
383a60961a3SKever Yang 		rk_clrsetreg(&cru->clksel_con[con_id],
384a60961a3SKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
385a60961a3SKever Yang 			     EMMC_SEL_24M << EMMC_PLL_SHIFT |
386a60961a3SKever Yang 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
387a60961a3SKever Yang 	} else {
388a60961a3SKever Yang 		rk_clrsetreg(&cru->clksel_con[con_id],
389a60961a3SKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
390a60961a3SKever Yang 			     EMMC_SEL_GPLL << EMMC_PLL_SHIFT |
391a60961a3SKever Yang 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
392a60961a3SKever Yang 	}
393a60961a3SKever Yang 	rk_clrsetreg(&cru->clksel_con[con_id +1], EMMC_CLK_SEL_MASK,
394a60961a3SKever Yang 		     EMMC_CLK_SEL_EMMC);
395a60961a3SKever Yang 
396a60961a3SKever Yang 	return px30_mmc_get_clk(cru, clk_id);
397a60961a3SKever Yang }
398a60961a3SKever Yang 
399f67f522bSFinley Xiao static ulong px30_pwm_get_clk(struct px30_cru *cru, ulong clk_id)
400a60961a3SKever Yang {
401a60961a3SKever Yang 	u32 div, con;
402a60961a3SKever Yang 
403f67f522bSFinley Xiao 	switch (clk_id) {
404f67f522bSFinley Xiao 	case SCLK_PWM0:
405f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[52]);
406f67f522bSFinley Xiao 		div = con >> CLK_PWM0_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK;
407f67f522bSFinley Xiao 		break;
408f67f522bSFinley Xiao 	case SCLK_PWM1:
409f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[52]);
410f67f522bSFinley Xiao 		div = con >> CLK_PWM1_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK;
411f67f522bSFinley Xiao 		break;
412f67f522bSFinley Xiao 	default:
413f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
414f67f522bSFinley Xiao 		return -EINVAL;
415f67f522bSFinley Xiao 	}
416f67f522bSFinley Xiao 
417a60961a3SKever Yang 	return DIV_TO_RATE(GPLL_HZ, div);
418a60961a3SKever Yang }
419a60961a3SKever Yang 
420f67f522bSFinley Xiao static ulong px30_pwm_set_clk(struct px30_cru *cru, ulong clk_id, uint hz)
421a60961a3SKever Yang {
422f67f522bSFinley Xiao 	int src_clk_div;
423a60961a3SKever Yang 
424f67f522bSFinley Xiao 	src_clk_div = GPLL_HZ / hz;
425f67f522bSFinley Xiao 	assert(src_clk_div - 1 < 127);
426f67f522bSFinley Xiao 
427f67f522bSFinley Xiao 	switch (clk_id) {
428f67f522bSFinley Xiao 	case SCLK_PWM0:
429f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[52],
430f67f522bSFinley Xiao 			     CLK_PWM_DIV_CON_MASK << CLK_PWM0_DIV_CON_SHIFT |
431f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_MASK << CLK_PWM0_PLL_SEL_SHIFT,
432f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_PWM0_DIV_CON_SHIFT |
433f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_GPLL << CLK_PWM0_PLL_SEL_SHIFT);
434f67f522bSFinley Xiao 		break;
435f67f522bSFinley Xiao 	case SCLK_PWM1:
436f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[52],
437f67f522bSFinley Xiao 			     CLK_PWM_DIV_CON_MASK << CLK_PWM1_DIV_CON_SHIFT |
438f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_MASK << CLK_PWM1_PLL_SEL_SHIFT,
439f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_PWM1_DIV_CON_SHIFT |
440f67f522bSFinley Xiao 			     CLK_PWM_PLL_SEL_GPLL << CLK_PWM1_PLL_SEL_SHIFT);
441f67f522bSFinley Xiao 		break;
442f67f522bSFinley Xiao 	default:
443f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
444f67f522bSFinley Xiao 		return -EINVAL;
445f67f522bSFinley Xiao 	}
446f67f522bSFinley Xiao 
447fce7cb7bSFinley Xiao 	return px30_pwm_get_clk(cru, clk_id);
448a60961a3SKever Yang }
449a60961a3SKever Yang 
450a60961a3SKever Yang static ulong px30_saradc_get_clk(struct px30_cru *cru)
451a60961a3SKever Yang {
452f67f522bSFinley Xiao 	u32 div, con;
453f67f522bSFinley Xiao 
454f67f522bSFinley Xiao 	con = readl(&cru->clksel_con[55]);
455f9157291SFinley Xiao 	div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK;
456a60961a3SKever Yang 
457a60961a3SKever Yang 	return DIV_TO_RATE(OSC_HZ, div);
458a60961a3SKever Yang }
459a60961a3SKever Yang 
460a60961a3SKever Yang static ulong px30_saradc_set_clk(struct px30_cru *cru, uint hz)
461a60961a3SKever Yang {
462a60961a3SKever Yang 	int src_clk_div;
463a60961a3SKever Yang 
464f67f522bSFinley Xiao 	src_clk_div = OSC_HZ / hz;
465f67f522bSFinley Xiao 	assert(src_clk_div - 1 < 2047);
466a60961a3SKever Yang 
467f67f522bSFinley Xiao 	rk_clrsetreg(&cru->clksel_con[55],
468f67f522bSFinley Xiao 		     CLK_SARADC_DIV_CON_MASK,
469fce7cb7bSFinley Xiao 		     (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT);
470f67f522bSFinley Xiao 
471fce7cb7bSFinley Xiao 	return px30_saradc_get_clk(cru);
472f67f522bSFinley Xiao }
473f67f522bSFinley Xiao 
474f67f522bSFinley Xiao static ulong px30_spi_get_clk(struct px30_cru *cru, ulong clk_id)
475f67f522bSFinley Xiao {
476f67f522bSFinley Xiao 	u32 div, con;
477f67f522bSFinley Xiao 
478f67f522bSFinley Xiao 	switch (clk_id) {
479fce7cb7bSFinley Xiao 	case SCLK_SPI0:
480f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[53]);
481f67f522bSFinley Xiao 		div = con >> CLK_SPI0_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK;
482f67f522bSFinley Xiao 		break;
483fce7cb7bSFinley Xiao 	case SCLK_SPI1:
484f67f522bSFinley Xiao 		con = readl(&cru->clksel_con[53]);
485f67f522bSFinley Xiao 		div = con >> CLK_SPI1_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK;
486f67f522bSFinley Xiao 		break;
487f67f522bSFinley Xiao 	default:
488f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
489f67f522bSFinley Xiao 		return -EINVAL;
490f67f522bSFinley Xiao 	}
491f67f522bSFinley Xiao 
492f67f522bSFinley Xiao 	return DIV_TO_RATE(GPLL_HZ, div);
493f67f522bSFinley Xiao }
494f67f522bSFinley Xiao 
495f67f522bSFinley Xiao static ulong px30_spi_set_clk(struct px30_cru *cru, ulong clk_id, uint hz)
496f67f522bSFinley Xiao {
497f67f522bSFinley Xiao 	int src_clk_div;
498f67f522bSFinley Xiao 
499f67f522bSFinley Xiao 	src_clk_div = GPLL_HZ / hz;
500f67f522bSFinley Xiao 	assert(src_clk_div - 1 < 127);
501f67f522bSFinley Xiao 
502f67f522bSFinley Xiao 	switch (clk_id) {
503f67f522bSFinley Xiao 	case SCLK_SPI0:
504f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[53],
505f67f522bSFinley Xiao 			     CLK_SPI_DIV_CON_MASK << CLK_SPI0_DIV_CON_SHIFT |
506f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_MASK << CLK_SPI0_PLL_SEL_SHIFT,
507f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_SPI0_DIV_CON_SHIFT |
508f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_GPLL << CLK_SPI0_PLL_SEL_SHIFT);
509f67f522bSFinley Xiao 		break;
510f67f522bSFinley Xiao 	case SCLK_SPI1:
511f67f522bSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[53],
512f67f522bSFinley Xiao 			     CLK_SPI_DIV_CON_MASK << CLK_SPI1_DIV_CON_SHIFT |
513f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_MASK << CLK_SPI1_PLL_SEL_SHIFT,
514f67f522bSFinley Xiao 			     (src_clk_div - 1) << CLK_SPI1_DIV_CON_SHIFT |
515f67f522bSFinley Xiao 			     CLK_SPI_PLL_SEL_GPLL << CLK_SPI1_PLL_SEL_SHIFT);
516f67f522bSFinley Xiao 		break;
517f67f522bSFinley Xiao 	default:
518f67f522bSFinley Xiao 		printf("do not support this pwm bus\n");
519f67f522bSFinley Xiao 		return -EINVAL;
520f67f522bSFinley Xiao 	}
521f67f522bSFinley Xiao 
522fce7cb7bSFinley Xiao 	return px30_spi_get_clk(cru, clk_id);
523a60961a3SKever Yang }
524a60961a3SKever Yang 
52530f1f38dSFinley Xiao static ulong px30_vop_get_clk(struct px30_cru *cru, ulong clk_id)
52630f1f38dSFinley Xiao {
52730f1f38dSFinley Xiao 	u32 div, con, parent;
52830f1f38dSFinley Xiao 
52930f1f38dSFinley Xiao 	switch (clk_id) {
53030f1f38dSFinley Xiao 	case ACLK_VOPB:
53130f1f38dSFinley Xiao 		con = readl(&cru->clksel_con[3]);
53230f1f38dSFinley Xiao 		div = con & ACLK_VO_DIV_MASK;
53330f1f38dSFinley Xiao 		parent = GPLL_HZ;
53430f1f38dSFinley Xiao 		break;
53530f1f38dSFinley Xiao 	case DCLK_VOPB:
53630f1f38dSFinley Xiao 		con = readl(&cru->clksel_con[5]);
53730f1f38dSFinley Xiao 		div = con & DCLK_VOPB_DIV_MASK;
53830f1f38dSFinley Xiao 		parent = rkclk_pll_get_rate(cru, CPLL);
53930f1f38dSFinley Xiao 		break;
54030f1f38dSFinley Xiao 	default:
54130f1f38dSFinley Xiao 		return -ENOENT;
54230f1f38dSFinley Xiao 	}
54330f1f38dSFinley Xiao 
54430f1f38dSFinley Xiao 	return DIV_TO_RATE(parent, div);
54530f1f38dSFinley Xiao }
54630f1f38dSFinley Xiao 
54730f1f38dSFinley Xiao static ulong px30_vop_set_clk(struct px30_cru *cru, ulong clk_id, uint hz)
54830f1f38dSFinley Xiao {
54930f1f38dSFinley Xiao 	int src_clk_div;
55030f1f38dSFinley Xiao 	struct pll_div cpll_config = {0};
55130f1f38dSFinley Xiao 
55230f1f38dSFinley Xiao 	src_clk_div = GPLL_HZ / hz;
55330f1f38dSFinley Xiao 	assert(src_clk_div - 1 < 31);
55430f1f38dSFinley Xiao 
55530f1f38dSFinley Xiao 	switch (clk_id) {
55630f1f38dSFinley Xiao 	case ACLK_VOPB:
55730f1f38dSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[3],
55830f1f38dSFinley Xiao 			     ACLK_VO_PLL_MASK | ACLK_VO_DIV_MASK,
55930f1f38dSFinley Xiao 			     ACLK_VO_SEL_GPLL << ACLK_VO_PLL_SHIFT |
56030f1f38dSFinley Xiao 			     (src_clk_div - 1) << ACLK_VO_DIV_SHIFT);
56130f1f38dSFinley Xiao 		break;
56230f1f38dSFinley Xiao 	case DCLK_VOPB:
56330f1f38dSFinley Xiao 		/*
56430f1f38dSFinley Xiao 		 * vopb dclk source from cpll, and equals to
56530f1f38dSFinley Xiao 		 * cpll(means div == 1)
56630f1f38dSFinley Xiao 		 */
56730f1f38dSFinley Xiao 		if (pll_para_config(hz, &cpll_config))
56830f1f38dSFinley Xiao 			return -1;
56930f1f38dSFinley Xiao 		rkclk_set_pll(cru, CPLL, &cpll_config);
57030f1f38dSFinley Xiao 
57130f1f38dSFinley Xiao 		rk_clrsetreg(&cru->clksel_con[5],
57230f1f38dSFinley Xiao 			     DCLK_VOPB_SEL_MASK | DCLK_VOPB_PLL_SEL_MASK |
57330f1f38dSFinley Xiao 			     DCLK_VOPB_DIV_MASK,
57430f1f38dSFinley Xiao 			     DCLK_VOPB_SEL_DIVOUT << DCLK_VOPB_SEL_SHIFT |
57530f1f38dSFinley Xiao 			     DCLK_VOPB_PLL_SEL_CPLL << DCLK_VOPB_PLL_SEL_SHIFT |
57630f1f38dSFinley Xiao 			     (1 - 1) << DCLK_VOPB_DIV_SHIFT);
57730f1f38dSFinley Xiao 		break;
57830f1f38dSFinley Xiao 	default:
57930f1f38dSFinley Xiao 		printf("do not support this vop freq\n");
58030f1f38dSFinley Xiao 		return -EINVAL;
58130f1f38dSFinley Xiao 	}
58230f1f38dSFinley Xiao 
58330f1f38dSFinley Xiao 	return hz;
58430f1f38dSFinley Xiao }
58530f1f38dSFinley Xiao 
586*df8f8a42SFinley Xiao static ulong px30_bus_get_clk(struct px30_cru *cru, ulong clk_id)
587*df8f8a42SFinley Xiao {
588*df8f8a42SFinley Xiao 	u32 div, con, parent;
589*df8f8a42SFinley Xiao 
590*df8f8a42SFinley Xiao 	switch (clk_id) {
591*df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
592*df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[23]);
593*df8f8a42SFinley Xiao 		div = (con & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT;
594*df8f8a42SFinley Xiao 		parent = GPLL_HZ;
595*df8f8a42SFinley Xiao 		break;
596*df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
597*df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[24]);
598*df8f8a42SFinley Xiao 		div = (con & BUS_HCLK_DIV_MASK) >> BUS_HCLK_DIV_SHIFT;
599*df8f8a42SFinley Xiao 		parent = GPLL_HZ;
600*df8f8a42SFinley Xiao 		break;
601*df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
602*df8f8a42SFinley Xiao 		parent = px30_bus_get_clk(cru, ACLK_BUS_PRE);
603*df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[24]);
604*df8f8a42SFinley Xiao 		div = (con & BUS_PCLK_DIV_MASK) >> BUS_PCLK_DIV_SHIFT;
605*df8f8a42SFinley Xiao 		break;
606*df8f8a42SFinley Xiao 	default:
607*df8f8a42SFinley Xiao 		return -ENOENT;
608*df8f8a42SFinley Xiao 	}
609*df8f8a42SFinley Xiao 
610*df8f8a42SFinley Xiao 	return DIV_TO_RATE(parent, div);
611*df8f8a42SFinley Xiao }
612*df8f8a42SFinley Xiao 
613*df8f8a42SFinley Xiao static ulong px30_bus_set_clk(struct px30_cru *cru, ulong clk_id, ulong hz)
614*df8f8a42SFinley Xiao {
615*df8f8a42SFinley Xiao 	int src_clk_div;
616*df8f8a42SFinley Xiao 
617*df8f8a42SFinley Xiao 	/*
618*df8f8a42SFinley Xiao 	 * select gpll as pd_bus bus clock source and
619*df8f8a42SFinley Xiao 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
620*df8f8a42SFinley Xiao 	 */
621*df8f8a42SFinley Xiao 	switch (clk_id) {
622*df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
623*df8f8a42SFinley Xiao 		src_clk_div = GPLL_HZ / hz;
624*df8f8a42SFinley Xiao 		assert(src_clk_div - 1 < 31);
625*df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[23],
626*df8f8a42SFinley Xiao 			     BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK,
627*df8f8a42SFinley Xiao 			     BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT |
628*df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_ACLK_DIV_SHIFT);
629*df8f8a42SFinley Xiao 		break;
630*df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
631*df8f8a42SFinley Xiao 		src_clk_div = GPLL_HZ / hz;
632*df8f8a42SFinley Xiao 		assert(src_clk_div - 1 < 31);
633*df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[24],
634*df8f8a42SFinley Xiao 			     BUS_PLL_SEL_MASK | BUS_HCLK_DIV_MASK,
635*df8f8a42SFinley Xiao 			     BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT |
636*df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_HCLK_DIV_SHIFT);
637*df8f8a42SFinley Xiao 		break;
638*df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
639*df8f8a42SFinley Xiao 		src_clk_div = px30_bus_get_clk(cru, ACLK_BUS_PRE) / hz;
640*df8f8a42SFinley Xiao 		assert(src_clk_div - 1 < 3);
641*df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[24],
642*df8f8a42SFinley Xiao 			     BUS_PCLK_DIV_MASK,
643*df8f8a42SFinley Xiao 			     (src_clk_div - 1) << BUS_PCLK_DIV_SHIFT);
644*df8f8a42SFinley Xiao 		break;
645*df8f8a42SFinley Xiao 	default:
646*df8f8a42SFinley Xiao 		printf("do not support this bus freq\n");
647*df8f8a42SFinley Xiao 		return -EINVAL;
648*df8f8a42SFinley Xiao 	}
649*df8f8a42SFinley Xiao 
650*df8f8a42SFinley Xiao 	return px30_bus_get_clk(cru, clk_id);
651*df8f8a42SFinley Xiao }
652*df8f8a42SFinley Xiao 
653*df8f8a42SFinley Xiao static ulong px30_peri_get_clk(struct px30_cru *cru, ulong clk_id)
654*df8f8a42SFinley Xiao {
655*df8f8a42SFinley Xiao 	u32 div, con, parent;
656*df8f8a42SFinley Xiao 
657*df8f8a42SFinley Xiao 	switch (clk_id) {
658*df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
659*df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[14]);
660*df8f8a42SFinley Xiao 		div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT;
661*df8f8a42SFinley Xiao 		parent = GPLL_HZ;
662*df8f8a42SFinley Xiao 		break;
663*df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
664*df8f8a42SFinley Xiao 		con = readl(&cru->clksel_con[14]);
665*df8f8a42SFinley Xiao 		div = (con & PERI_HCLK_DIV_MASK) >> PERI_HCLK_DIV_SHIFT;
666*df8f8a42SFinley Xiao 		parent = GPLL_HZ;
667*df8f8a42SFinley Xiao 		break;
668*df8f8a42SFinley Xiao 	default:
669*df8f8a42SFinley Xiao 		return -ENOENT;
670*df8f8a42SFinley Xiao 	}
671*df8f8a42SFinley Xiao 
672*df8f8a42SFinley Xiao 	return DIV_TO_RATE(parent, div);
673*df8f8a42SFinley Xiao }
674*df8f8a42SFinley Xiao 
675*df8f8a42SFinley Xiao static ulong px30_peri_set_clk(struct px30_cru *cru, ulong clk_id, ulong hz)
676*df8f8a42SFinley Xiao {
677*df8f8a42SFinley Xiao 	int src_clk_div;
678*df8f8a42SFinley Xiao 
679*df8f8a42SFinley Xiao 	src_clk_div = GPLL_HZ / hz;
680*df8f8a42SFinley Xiao 	assert(src_clk_div - 1 < 31);
681*df8f8a42SFinley Xiao 
682*df8f8a42SFinley Xiao 	/*
683*df8f8a42SFinley Xiao 	 * select gpll as pd_peri bus clock source and
684*df8f8a42SFinley Xiao 	 * set up dependent divisors for HCLK and ACLK clocks.
685*df8f8a42SFinley Xiao 	 */
686*df8f8a42SFinley Xiao 	switch (clk_id) {
687*df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
688*df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[14],
689*df8f8a42SFinley Xiao 			     PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK,
690*df8f8a42SFinley Xiao 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
691*df8f8a42SFinley Xiao 			     (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT);
692*df8f8a42SFinley Xiao 		break;
693*df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
694*df8f8a42SFinley Xiao 		rk_clrsetreg(&cru->clksel_con[14],
695*df8f8a42SFinley Xiao 			     PERI_PLL_SEL_MASK | PERI_HCLK_DIV_MASK,
696*df8f8a42SFinley Xiao 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
697*df8f8a42SFinley Xiao 			     (src_clk_div - 1) << PERI_HCLK_DIV_SHIFT);
698*df8f8a42SFinley Xiao 		break;
699*df8f8a42SFinley Xiao 	default:
700*df8f8a42SFinley Xiao 		printf("do not support this peri freq\n");
701*df8f8a42SFinley Xiao 		return -EINVAL;
702*df8f8a42SFinley Xiao 	}
703*df8f8a42SFinley Xiao 
704*df8f8a42SFinley Xiao 	return px30_peri_get_clk(cru, clk_id);
705*df8f8a42SFinley Xiao }
706*df8f8a42SFinley Xiao 
707a60961a3SKever Yang static ulong px30_clk_get_rate(struct clk *clk)
708a60961a3SKever Yang {
709a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
710a60961a3SKever Yang 	ulong rate = 0;
711a60961a3SKever Yang 
712a60961a3SKever Yang 	switch (clk->id) {
713a60961a3SKever Yang 	case 0 ... 15:
714a60961a3SKever Yang 		return 0;
715a60961a3SKever Yang 	case HCLK_SDMMC:
716a60961a3SKever Yang 	case HCLK_EMMC:
717a60961a3SKever Yang 	case SCLK_SDMMC:
718a60961a3SKever Yang 	case SCLK_EMMC:
71996f1b3d9SKever Yang 	case SCLK_EMMC_SAMPLE:
720a60961a3SKever Yang 		rate = px30_mmc_get_clk(priv->cru, clk->id);
721a60961a3SKever Yang 		break;
722a60961a3SKever Yang 	case SCLK_I2C0:
723a60961a3SKever Yang 	case SCLK_I2C1:
724a60961a3SKever Yang 	case SCLK_I2C2:
725a60961a3SKever Yang 	case SCLK_I2C3:
726a60961a3SKever Yang 		rate = px30_i2c_get_clk(priv->cru, clk->id);
727a60961a3SKever Yang 		break;
728a60961a3SKever Yang 	case SCLK_PWM0:
729fce7cb7bSFinley Xiao 	case SCLK_PWM1:
730f67f522bSFinley Xiao 		rate = px30_pwm_get_clk(priv->cru, clk->id);
731a60961a3SKever Yang 		break;
732a60961a3SKever Yang 	case SCLK_SARADC:
733a60961a3SKever Yang 		rate = px30_saradc_get_clk(priv->cru);
734a60961a3SKever Yang 		break;
735f67f522bSFinley Xiao 	case SCLK_SPI0:
736f67f522bSFinley Xiao 	case SCLK_SPI1:
737f67f522bSFinley Xiao 		rate = px30_spi_get_clk(priv->cru, clk->id);
738f67f522bSFinley Xiao 		break;
73930f1f38dSFinley Xiao 	case ACLK_VOPB:
74030f1f38dSFinley Xiao 	case DCLK_VOPB:
74130f1f38dSFinley Xiao 		rate = px30_vop_get_clk(priv->cru, clk->id);
74230f1f38dSFinley Xiao 		break;
743*df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
744*df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
745*df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
746*df8f8a42SFinley Xiao 		rate = px30_bus_get_clk(priv->cru, clk->id);
747*df8f8a42SFinley Xiao 		break;
748*df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
749*df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
750*df8f8a42SFinley Xiao 		rate = px30_peri_get_clk(priv->cru, clk->id);
751*df8f8a42SFinley Xiao 		break;
752a60961a3SKever Yang 	default:
753a60961a3SKever Yang 		return -ENOENT;
754a60961a3SKever Yang 	}
755a60961a3SKever Yang 
756a60961a3SKever Yang 	return rate;
757a60961a3SKever Yang }
758a60961a3SKever Yang 
759a60961a3SKever Yang static ulong px30_clk_set_rate(struct clk *clk, ulong rate)
760a60961a3SKever Yang {
761a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
762a60961a3SKever Yang 	ulong ret = 0;
763a60961a3SKever Yang 
764c4867301SKever Yang 	debug("%s %ld %ld\n", __func__, clk->id, rate);
765a60961a3SKever Yang 	switch (clk->id) {
766a60961a3SKever Yang 	case 0 ... 15:
767a60961a3SKever Yang 		return 0;
768a60961a3SKever Yang 	case HCLK_SDMMC:
769a60961a3SKever Yang 	case HCLK_EMMC:
770a60961a3SKever Yang 	case SCLK_SDMMC:
771a60961a3SKever Yang 	case SCLK_EMMC:
772a60961a3SKever Yang 		ret = px30_mmc_set_clk(priv->cru, clk->id, rate);
773a60961a3SKever Yang 		break;
774a60961a3SKever Yang 	case SCLK_I2C0:
775a60961a3SKever Yang 	case SCLK_I2C1:
776a60961a3SKever Yang 	case SCLK_I2C2:
777a60961a3SKever Yang 	case SCLK_I2C3:
778a60961a3SKever Yang 		ret = px30_i2c_set_clk(priv->cru, clk->id, rate);
779a60961a3SKever Yang 		break;
780a60961a3SKever Yang 	case SCLK_PWM0:
781f67f522bSFinley Xiao 	case SCLK_PWM1:
782f67f522bSFinley Xiao 		ret = px30_pwm_set_clk(priv->cru, clk->id, rate);
783a60961a3SKever Yang 		break;
784a60961a3SKever Yang 	case SCLK_SARADC:
785a60961a3SKever Yang 		ret = px30_saradc_set_clk(priv->cru, rate);
786a60961a3SKever Yang 		break;
787f67f522bSFinley Xiao 	case SCLK_SPI0:
788f67f522bSFinley Xiao 	case SCLK_SPI1:
789f67f522bSFinley Xiao 		ret = px30_spi_set_clk(priv->cru, clk->id, rate);
790f67f522bSFinley Xiao 		break;
79130f1f38dSFinley Xiao 	case ACLK_VOPB:
79230f1f38dSFinley Xiao 	case DCLK_VOPB:
79330f1f38dSFinley Xiao 		ret = px30_vop_set_clk(priv->cru, clk->id, rate);
79430f1f38dSFinley Xiao 		break;
795*df8f8a42SFinley Xiao 	case ACLK_BUS_PRE:
796*df8f8a42SFinley Xiao 	case HCLK_BUS_PRE:
797*df8f8a42SFinley Xiao 	case PCLK_BUS_PRE:
798*df8f8a42SFinley Xiao 		ret = px30_bus_set_clk(priv->cru, clk->id, rate);
799*df8f8a42SFinley Xiao 		break;
800*df8f8a42SFinley Xiao 	case ACLK_PERI_PRE:
801*df8f8a42SFinley Xiao 	case HCLK_PERI_PRE:
802*df8f8a42SFinley Xiao 		ret = px30_peri_set_clk(priv->cru, clk->id, rate);
803*df8f8a42SFinley Xiao 		break;
804a60961a3SKever Yang 	default:
805a60961a3SKever Yang 		return -ENOENT;
806a60961a3SKever Yang 	}
807a60961a3SKever Yang 
808a60961a3SKever Yang 	return ret;
809a60961a3SKever Yang }
810a60961a3SKever Yang 
811a60961a3SKever Yang #define ROCKCHIP_MMC_DELAY_SEL		BIT(10)
812a60961a3SKever Yang #define ROCKCHIP_MMC_DEGREE_MASK	0x3
813a60961a3SKever Yang #define ROCKCHIP_MMC_DELAYNUM_OFFSET	2
814a60961a3SKever Yang #define ROCKCHIP_MMC_DELAYNUM_MASK	(0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
815a60961a3SKever Yang 
816a60961a3SKever Yang #define PSECS_PER_SEC 1000000000000LL
817a60961a3SKever Yang /*
818a60961a3SKever Yang  * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
819a60961a3SKever Yang  * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
820a60961a3SKever Yang  */
821a60961a3SKever Yang #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
822a60961a3SKever Yang 
823a60961a3SKever Yang int rockchip_mmc_get_phase(struct clk *clk)
824a60961a3SKever Yang {
825a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
826a60961a3SKever Yang 	struct px30_cru *cru = priv->cru;
827a60961a3SKever Yang 	u32 raw_value, delay_num;
828a60961a3SKever Yang 	u16 degrees = 0;
829a60961a3SKever Yang 	ulong rate;
830a60961a3SKever Yang 
831a60961a3SKever Yang 	rate = px30_clk_get_rate(clk);
832a60961a3SKever Yang 
833a60961a3SKever Yang 	if (rate < 0)
834a60961a3SKever Yang 		return rate;
835a60961a3SKever Yang 
836a60961a3SKever Yang 	if (clk->id == SCLK_EMMC_SAMPLE)
837a60961a3SKever Yang 		raw_value = readl(&cru->emmc_con[1]);
838a60961a3SKever Yang 	else
839a60961a3SKever Yang 		raw_value = readl(&cru->sdmmc_con[1]);
840a60961a3SKever Yang 
84196f1b3d9SKever Yang 	raw_value >>= 1;
842a60961a3SKever Yang 	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
843a60961a3SKever Yang 
844a60961a3SKever Yang 	if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
845a60961a3SKever Yang 		/* degrees/delaynum * 10000 */
846a60961a3SKever Yang 		unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
847a60961a3SKever Yang 					36 * (rate / 1000000);
848a60961a3SKever Yang 
849a60961a3SKever Yang 		delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
850a60961a3SKever Yang 		delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
851a60961a3SKever Yang 		degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
852a60961a3SKever Yang 	}
853a60961a3SKever Yang 
854a60961a3SKever Yang 	return degrees % 360;
855a60961a3SKever Yang }
856a60961a3SKever Yang 
857a60961a3SKever Yang int rockchip_mmc_set_phase(struct clk *clk, u32 degrees)
858a60961a3SKever Yang {
859a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(clk->dev);
860a60961a3SKever Yang 	struct px30_cru *cru = priv->cru;
861a60961a3SKever Yang 	u8 nineties, remainder, delay_num;
862a60961a3SKever Yang 	u32 raw_value, delay;
863a60961a3SKever Yang 	ulong rate;
864a60961a3SKever Yang 
865a60961a3SKever Yang 	rate = px30_clk_get_rate(clk);
866a60961a3SKever Yang 
867a60961a3SKever Yang 	if (rate < 0)
868a60961a3SKever Yang 		return rate;
869a60961a3SKever Yang 
870a60961a3SKever Yang 	nineties = degrees / 90;
871a60961a3SKever Yang 	remainder = (degrees % 90);
872a60961a3SKever Yang 
873a60961a3SKever Yang 	/*
874a60961a3SKever Yang 	 * Convert to delay; do a little extra work to make sure we
875a60961a3SKever Yang 	 * don't overflow 32-bit / 64-bit numbers.
876a60961a3SKever Yang 	 */
877a60961a3SKever Yang 	delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
878a60961a3SKever Yang 	delay *= remainder;
879a60961a3SKever Yang 	delay = DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 *
880a60961a3SKever Yang 				(ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
881a60961a3SKever Yang 
882a60961a3SKever Yang 	delay_num = (u8)min_t(u32, delay, 255);
883a60961a3SKever Yang 
884a60961a3SKever Yang 	raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
885a60961a3SKever Yang 	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
886a60961a3SKever Yang 	raw_value |= nineties;
887a60961a3SKever Yang 
88896f1b3d9SKever Yang 	raw_value <<= 1;
889a60961a3SKever Yang 	if (clk->id == SCLK_EMMC_SAMPLE)
890a60961a3SKever Yang 		writel(raw_value | 0xffff0000, &cru->emmc_con[1]);
891a60961a3SKever Yang 	else
892a60961a3SKever Yang 		writel(raw_value | 0xffff0000, &cru->sdmmc_con[1]);
893a60961a3SKever Yang 
894a60961a3SKever Yang 	debug("mmc set_phase(%d) delay_nums=%u reg=%#x actual_degrees=%d\n",
895a60961a3SKever Yang 	      degrees, delay_num, raw_value, rockchip_mmc_get_phase(clk));
896a60961a3SKever Yang 
897a60961a3SKever Yang 	return 0;
898a60961a3SKever Yang }
899a60961a3SKever Yang 
900a60961a3SKever Yang static int px30_clk_get_phase(struct clk *clk)
901a60961a3SKever Yang {
902a60961a3SKever Yang 	int ret;
903a60961a3SKever Yang 
904a60961a3SKever Yang 	switch (clk->id) {
905a60961a3SKever Yang 	case SCLK_EMMC_SAMPLE:
906a60961a3SKever Yang 	case SCLK_SDMMC_SAMPLE:
907a60961a3SKever Yang 		ret = rockchip_mmc_get_phase(clk);
908a60961a3SKever Yang 		break;
909a60961a3SKever Yang 	default:
910a60961a3SKever Yang 		return -ENOENT;
911a60961a3SKever Yang 	}
912a60961a3SKever Yang 
913a60961a3SKever Yang 	return ret;
914a60961a3SKever Yang }
915a60961a3SKever Yang 
916a60961a3SKever Yang static int px30_clk_set_phase(struct clk *clk, int degrees)
917a60961a3SKever Yang {
918a60961a3SKever Yang 	int ret;
919a60961a3SKever Yang 
920a60961a3SKever Yang 	switch (clk->id) {
921a60961a3SKever Yang 	case SCLK_EMMC_SAMPLE:
922a60961a3SKever Yang 	case SCLK_SDMMC_SAMPLE:
923a60961a3SKever Yang 		ret = rockchip_mmc_set_phase(clk, degrees);
924a60961a3SKever Yang 		break;
925a60961a3SKever Yang 	default:
926a60961a3SKever Yang 		return -ENOENT;
927a60961a3SKever Yang 	}
928a60961a3SKever Yang 
929a60961a3SKever Yang 	return ret;
930a60961a3SKever Yang }
931a60961a3SKever Yang 
932a60961a3SKever Yang static struct clk_ops px30_clk_ops = {
933a60961a3SKever Yang 	.get_rate = px30_clk_get_rate,
934a60961a3SKever Yang 	.set_rate = px30_clk_set_rate,
935a60961a3SKever Yang 	.get_phase	= px30_clk_get_phase,
936a60961a3SKever Yang 	.set_phase	= px30_clk_set_phase,
937a60961a3SKever Yang };
938a60961a3SKever Yang 
939a60961a3SKever Yang static int px30_clk_probe(struct udevice *dev)
940a60961a3SKever Yang {
941a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(dev);
9420dc8896cSKever Yang 	u32 reg = readl(&priv->cru->clksel_con[23]);
943a60961a3SKever Yang 
9440dc8896cSKever Yang 	/* Only do the rkclk_init() one time for boot up */
9450dc8896cSKever Yang 	if (((reg & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT) !=
9460dc8896cSKever Yang 	    (GPLL_HZ / BUS_ACLK_HZ - 1))
947a60961a3SKever Yang 		rkclk_init(priv->cru);
948a60961a3SKever Yang 
949a60961a3SKever Yang 	return 0;
950a60961a3SKever Yang }
951a60961a3SKever Yang 
952a60961a3SKever Yang static int px30_clk_ofdata_to_platdata(struct udevice *dev)
953a60961a3SKever Yang {
954a60961a3SKever Yang 	struct px30_clk_priv *priv = dev_get_priv(dev);
955a60961a3SKever Yang 
9564203970bSKever Yang 	priv->cru = dev_read_addr_ptr(dev);
957a60961a3SKever Yang 
958a60961a3SKever Yang 	return 0;
959a60961a3SKever Yang }
960a60961a3SKever Yang 
961a60961a3SKever Yang static int px30_clk_bind(struct udevice *dev)
962a60961a3SKever Yang {
963a60961a3SKever Yang 	int ret;
964a60961a3SKever Yang 	struct udevice *sys_child, *sf_child;
965a60961a3SKever Yang 	struct sysreset_reg *priv;
966a60961a3SKever Yang 	struct softreset_reg *sf_priv;
967a60961a3SKever Yang 
968a60961a3SKever Yang 	/* The reset driver does not have a device node, so bind it here */
969a60961a3SKever Yang 	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
970a60961a3SKever Yang 				 &sys_child);
971a60961a3SKever Yang 	if (ret) {
972a60961a3SKever Yang 		debug("Warning: No sysreset driver: ret=%d\n", ret);
973a60961a3SKever Yang 	} else {
974a60961a3SKever Yang 		priv = malloc(sizeof(struct sysreset_reg));
975a60961a3SKever Yang 		priv->glb_srst_fst_value = offsetof(struct px30_cru,
976a60961a3SKever Yang 						    glb_srst_fst);
977a60961a3SKever Yang 		priv->glb_srst_snd_value = offsetof(struct px30_cru,
978a60961a3SKever Yang 						    glb_srst_snd);
979a60961a3SKever Yang 		sys_child->priv = priv;
980a60961a3SKever Yang 	}
981a60961a3SKever Yang 
982a60961a3SKever Yang 	ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset",
983a60961a3SKever Yang 					 dev_ofnode(dev), &sf_child);
984a60961a3SKever Yang 	if (ret) {
985a60961a3SKever Yang 		debug("Warning: No rockchip reset driver: ret=%d\n", ret);
986a60961a3SKever Yang 	} else {
987a60961a3SKever Yang 		sf_priv = malloc(sizeof(struct softreset_reg));
988a60961a3SKever Yang 		sf_priv->sf_reset_offset = offsetof(struct px30_cru,
989a60961a3SKever Yang 						    softrst_con[0]);
990a60961a3SKever Yang 		sf_priv->sf_reset_num = 12;
991a60961a3SKever Yang 		sf_child->priv = sf_priv;
992a60961a3SKever Yang 	}
993a60961a3SKever Yang 
994a60961a3SKever Yang 	return 0;
995a60961a3SKever Yang }
996a60961a3SKever Yang 
997a60961a3SKever Yang static const struct udevice_id px30_clk_ids[] = {
998a60961a3SKever Yang 	{ .compatible = "rockchip,px30-cru" },
999a60961a3SKever Yang 	{ }
1000a60961a3SKever Yang };
1001a60961a3SKever Yang 
1002a60961a3SKever Yang U_BOOT_DRIVER(rockchip_px30_cru) = {
1003a60961a3SKever Yang 	.name		= "rockchip_px30_cru",
1004a60961a3SKever Yang 	.id		= UCLASS_CLK,
1005a60961a3SKever Yang 	.of_match	= px30_clk_ids,
1006a60961a3SKever Yang 	.priv_auto_alloc_size = sizeof(struct px30_clk_priv),
1007a60961a3SKever Yang 	.ofdata_to_platdata = px30_clk_ofdata_to_platdata,
1008a60961a3SKever Yang 	.ops		= &px30_clk_ops,
1009a60961a3SKever Yang 	.bind		= px30_clk_bind,
1010a60961a3SKever Yang 	.probe		= px30_clk_probe,
1011a60961a3SKever Yang };
1012