xref: /rk3399_rockchip-uboot/drivers/clk/rockchip/clk_rk3036.c (revision 4112f8e0c1aa46415558a499c318e2e276bb98ea)
1aff8795cSHeiko Stübner /*
2aff8795cSHeiko Stübner  * (C) Copyright 2015 Google, Inc
3aff8795cSHeiko Stübner  *
4aff8795cSHeiko Stübner  * SPDX-License-Identifier:	GPL-2.0
5aff8795cSHeiko Stübner  */
6aff8795cSHeiko Stübner 
7aff8795cSHeiko Stübner #include <common.h>
8aff8795cSHeiko Stübner #include <clk-uclass.h>
9aff8795cSHeiko Stübner #include <dm.h>
10aff8795cSHeiko Stübner #include <errno.h>
11aff8795cSHeiko Stübner #include <syscon.h>
12aff8795cSHeiko Stübner #include <asm/io.h>
13aff8795cSHeiko Stübner #include <asm/arch/clock.h>
14aff8795cSHeiko Stübner #include <asm/arch/cru_rk3036.h>
15aff8795cSHeiko Stübner #include <asm/arch/hardware.h>
16aff8795cSHeiko Stübner #include <dm/lists.h>
17aff8795cSHeiko Stübner #include <dt-bindings/clock/rk3036-cru.h>
18abd0128eSHeiko Stübner #include <linux/log2.h>
19aff8795cSHeiko Stübner 
20aff8795cSHeiko Stübner DECLARE_GLOBAL_DATA_PTR;
21aff8795cSHeiko Stübner 
22aff8795cSHeiko Stübner enum {
23aff8795cSHeiko Stübner 	VCO_MAX_HZ	= 2400U * 1000000,
24aff8795cSHeiko Stübner 	VCO_MIN_HZ	= 600 * 1000000,
25aff8795cSHeiko Stübner 	OUTPUT_MAX_HZ	= 2400U * 1000000,
26aff8795cSHeiko Stübner 	OUTPUT_MIN_HZ	= 24 * 1000000,
27aff8795cSHeiko Stübner };
28aff8795cSHeiko Stübner 
293a1c76d9SElaine Zhang #ifndef CONFIG_SPL_BUILD
303a1c76d9SElaine Zhang #define RK3036_CLK_DUMP(_id, _name, _iscru)	\
313a1c76d9SElaine Zhang {						\
323a1c76d9SElaine Zhang 	.id = _id,				\
333a1c76d9SElaine Zhang 	.name = _name,				\
343a1c76d9SElaine Zhang 	.is_cru = _iscru,			\
353a1c76d9SElaine Zhang }
363a1c76d9SElaine Zhang 
373a1c76d9SElaine Zhang static const struct rk3036_clk_info clks_dump[] = {
383a1c76d9SElaine Zhang 	RK3036_CLK_DUMP(PLL_APLL, "apll", true),
393a1c76d9SElaine Zhang 	RK3036_CLK_DUMP(PLL_DPLL, "dpll", true),
403a1c76d9SElaine Zhang 	RK3036_CLK_DUMP(PLL_GPLL, "gpll", true),
413a1c76d9SElaine Zhang };
423a1c76d9SElaine Zhang #endif
433a1c76d9SElaine Zhang 
44aff8795cSHeiko Stübner #define RATE_TO_DIV(input_rate, output_rate) \
45aff8795cSHeiko Stübner 	((input_rate) / (output_rate) - 1);
46aff8795cSHeiko Stübner 
47aff8795cSHeiko Stübner #define DIV_TO_RATE(input_rate, div)	((input_rate) / ((div) + 1))
48aff8795cSHeiko Stübner 
49aff8795cSHeiko Stübner #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
50aff8795cSHeiko Stübner 	.refdiv = _refdiv,\
51aff8795cSHeiko Stübner 	.fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
52aff8795cSHeiko Stübner 	.postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\
53aff8795cSHeiko Stübner 	_Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\
54aff8795cSHeiko Stübner 			 OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\
55aff8795cSHeiko Stübner 			 #hz "Hz cannot be hit with PLL "\
56aff8795cSHeiko Stübner 			 "divisors on line " __stringify(__LINE__));
57aff8795cSHeiko Stübner 
586a464d9cSKever Yang /* use integer mode*/
59aff8795cSHeiko Stübner static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1);
60aff8795cSHeiko Stübner static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1);
61aff8795cSHeiko Stübner 
rkclk_set_pll(struct rk3036_cru * cru,enum rk_clk_id clk_id,const struct pll_div * div)62aff8795cSHeiko Stübner static int rkclk_set_pll(struct rk3036_cru *cru, enum rk_clk_id clk_id,
63aff8795cSHeiko Stübner 			 const struct pll_div *div)
64aff8795cSHeiko Stübner {
65aff8795cSHeiko Stübner 	int pll_id = rk_pll_id(clk_id);
66aff8795cSHeiko Stübner 	struct rk3036_pll *pll = &cru->pll[pll_id];
67aff8795cSHeiko Stübner 
68aff8795cSHeiko Stübner 	/* All PLLs have same VCO and output frequency range restrictions. */
69aff8795cSHeiko Stübner 	uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000;
70aff8795cSHeiko Stübner 	uint output_hz = vco_hz / div->postdiv1 / div->postdiv2;
71aff8795cSHeiko Stübner 
72aff8795cSHeiko Stübner 	debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, postdiv2=%d,\
73aff8795cSHeiko Stübner 		 vco=%u Hz, output=%u Hz\n",
74aff8795cSHeiko Stübner 			pll, div->fbdiv, div->refdiv, div->postdiv1,
75aff8795cSHeiko Stübner 			div->postdiv2, vco_hz, output_hz);
76aff8795cSHeiko Stübner 	assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
77aff8795cSHeiko Stübner 	       output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ);
78aff8795cSHeiko Stübner 
796a464d9cSKever Yang 	/* use integer mode */
806a464d9cSKever Yang 	rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
81aff8795cSHeiko Stübner 
8200fbb281SDavid Wu 	/* Power down */
8300fbb281SDavid Wu 	rk_setreg(&pll->con1, 1 << PLL_PD_SHIFT);
8400fbb281SDavid Wu 
85aff8795cSHeiko Stübner 	rk_clrsetreg(&pll->con0,
8637943aaeSKever Yang 		     PLL_POSTDIV1_MASK | PLL_FBDIV_MASK,
87aff8795cSHeiko Stübner 		     (div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv);
8837943aaeSKever Yang 	rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK,
89aff8795cSHeiko Stübner 		     (div->postdiv2 << PLL_POSTDIV2_SHIFT |
90aff8795cSHeiko Stübner 		     div->refdiv << PLL_REFDIV_SHIFT));
91aff8795cSHeiko Stübner 
9200fbb281SDavid Wu 	/* Power Up */
9300fbb281SDavid Wu 	rk_clrreg(&pll->con1, 1 << PLL_PD_SHIFT);
9400fbb281SDavid Wu 
95aff8795cSHeiko Stübner 	/* waiting for pll lock */
96aff8795cSHeiko Stübner 	while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT))
97aff8795cSHeiko Stübner 		udelay(1);
98aff8795cSHeiko Stübner 
99aff8795cSHeiko Stübner 	return 0;
100aff8795cSHeiko Stübner }
101aff8795cSHeiko Stübner 
rkclk_init(struct rk3036_cru * cru)102aff8795cSHeiko Stübner static void rkclk_init(struct rk3036_cru *cru)
103aff8795cSHeiko Stübner {
104aff8795cSHeiko Stübner 	u32 aclk_div;
105aff8795cSHeiko Stübner 	u32 hclk_div;
106aff8795cSHeiko Stübner 	u32 pclk_div;
107904b267dSElaine Zhang 	u32 nandc_div;
108aff8795cSHeiko Stübner 
109aff8795cSHeiko Stübner 	/* pll enter slow-mode */
110aff8795cSHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con,
11137943aaeSKever Yang 		     GPLL_MODE_MASK | APLL_MODE_MASK,
112aff8795cSHeiko Stübner 		     GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
113aff8795cSHeiko Stübner 		     APLL_MODE_SLOW << APLL_MODE_SHIFT);
114aff8795cSHeiko Stübner 
115aff8795cSHeiko Stübner 	/* init pll */
116aff8795cSHeiko Stübner 	rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg);
117aff8795cSHeiko Stübner 	rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg);
118aff8795cSHeiko Stübner 
119aff8795cSHeiko Stübner 	/*
12037943aaeSKever Yang 	 * select apll as cpu/core clock pll source and
12137943aaeSKever Yang 	 * set up dependent divisors for PERI and ACLK clocks.
122aff8795cSHeiko Stübner 	 * core hz : apll = 1:1
123aff8795cSHeiko Stübner 	 */
124aff8795cSHeiko Stübner 	aclk_div = APLL_HZ / CORE_ACLK_HZ - 1;
125f149c047SElaine Zhang 	assert((aclk_div + 1) * CORE_ACLK_HZ <= APLL_HZ && aclk_div < 0x7);
126aff8795cSHeiko Stübner 
127aff8795cSHeiko Stübner 	pclk_div = APLL_HZ / CORE_PERI_HZ - 1;
128f149c047SElaine Zhang 	assert((pclk_div + 1) * CORE_PERI_HZ <= APLL_HZ && pclk_div < 0xf);
129aff8795cSHeiko Stübner 
130aff8795cSHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[0],
13137943aaeSKever Yang 		     CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK,
132aff8795cSHeiko Stübner 		     CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
133aff8795cSHeiko Stübner 		     0 << CORE_DIV_CON_SHIFT);
134aff8795cSHeiko Stübner 
135aff8795cSHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[1],
13637943aaeSKever Yang 		     CORE_ACLK_DIV_MASK | CORE_PERI_DIV_MASK,
137aff8795cSHeiko Stübner 		     aclk_div << CORE_ACLK_DIV_SHIFT |
138aff8795cSHeiko Stübner 		     pclk_div << CORE_PERI_DIV_SHIFT);
139aff8795cSHeiko Stübner 
140aff8795cSHeiko Stübner 	/*
141f54f0bc2SKever Yang 	 * select gpll as pd_bus bus clock source and
142aff8795cSHeiko Stübner 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
143aff8795cSHeiko Stübner 	 */
1441960b010SKever Yang 	aclk_div = GPLL_HZ / BUS_ACLK_HZ - 1;
145f149c047SElaine Zhang 	assert((aclk_div + 1) * BUS_ACLK_HZ <= GPLL_HZ && aclk_div <= 0x1f);
146aff8795cSHeiko Stübner 
147f54f0bc2SKever Yang 	pclk_div = BUS_ACLK_HZ / BUS_PCLK_HZ - 1;
148f149c047SElaine Zhang 	assert((pclk_div + 1) * BUS_PCLK_HZ <= BUS_ACLK_HZ && pclk_div <= 0x7);
149aff8795cSHeiko Stübner 
150f54f0bc2SKever Yang 	hclk_div = BUS_ACLK_HZ / BUS_HCLK_HZ - 1;
151f149c047SElaine Zhang 	assert((hclk_div + 1) * BUS_HCLK_HZ <= BUS_ACLK_HZ && hclk_div <= 0x3);
152aff8795cSHeiko Stübner 
153aff8795cSHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[0],
15437943aaeSKever Yang 		     BUS_ACLK_PLL_SEL_MASK | BUS_ACLK_DIV_MASK,
1551960b010SKever Yang 		     BUS_ACLK_PLL_SEL_GPLL << BUS_ACLK_PLL_SEL_SHIFT |
15637943aaeSKever Yang 		     aclk_div << BUS_ACLK_DIV_SHIFT);
157aff8795cSHeiko Stübner 
158aff8795cSHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[1],
15937943aaeSKever Yang 		     BUS_PCLK_DIV_MASK | BUS_HCLK_DIV_MASK,
16037943aaeSKever Yang 		     pclk_div << BUS_PCLK_DIV_SHIFT |
16137943aaeSKever Yang 		     hclk_div << BUS_HCLK_DIV_SHIFT);
162aff8795cSHeiko Stübner 
163aff8795cSHeiko Stübner 	/*
16437943aaeSKever Yang 	 * select gpll as pd_peri bus clock source and
165aff8795cSHeiko Stübner 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
166aff8795cSHeiko Stübner 	 */
167aff8795cSHeiko Stübner 	aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
168f149c047SElaine Zhang 	assert((aclk_div + 1) * PERI_ACLK_HZ <= GPLL_HZ && aclk_div < 0x1f);
169aff8795cSHeiko Stübner 
170abd0128eSHeiko Stübner 	hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ);
171f149c047SElaine Zhang 	assert((1 << hclk_div) * PERI_HCLK_HZ <=
1721960b010SKever Yang 		PERI_ACLK_HZ && (hclk_div < 0x4));
173aff8795cSHeiko Stübner 
174abd0128eSHeiko Stübner 	pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ);
175f149c047SElaine Zhang 	assert((1 << pclk_div) * PERI_PCLK_HZ <=
176aff8795cSHeiko Stübner 		PERI_ACLK_HZ && pclk_div < 0x8);
177aff8795cSHeiko Stübner 
178aff8795cSHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[10],
17937943aaeSKever Yang 		     PERI_PLL_SEL_MASK | PERI_PCLK_DIV_MASK |
18037943aaeSKever Yang 		     PERI_HCLK_DIV_MASK | PERI_ACLK_DIV_MASK,
181aff8795cSHeiko Stübner 		     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
182aff8795cSHeiko Stübner 		     pclk_div << PERI_PCLK_DIV_SHIFT |
183aff8795cSHeiko Stübner 		     hclk_div << PERI_HCLK_DIV_SHIFT |
184aff8795cSHeiko Stübner 		     aclk_div << PERI_ACLK_DIV_SHIFT);
185aff8795cSHeiko Stübner 
186904b267dSElaine Zhang 	nandc_div = DIV_ROUND_UP(GPLL_HZ, 150 * 1000000);
187904b267dSElaine Zhang 
188904b267dSElaine Zhang 	rk_clrsetreg(&cru->cru_clksel_con[16],
189904b267dSElaine Zhang 		     NANDC_PLL_MASK | NANDC_DIV_MASK,
190904b267dSElaine Zhang 		     NANDC_SEL_GPLL << NANDC_PLL_SHIFT |
191904b267dSElaine Zhang 		     nandc_div << NANDC_DIV_SHIFT);
192904b267dSElaine Zhang 
193aff8795cSHeiko Stübner 	/* PLL enter normal-mode */
194aff8795cSHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con,
19537943aaeSKever Yang 		     GPLL_MODE_MASK | APLL_MODE_MASK,
196aff8795cSHeiko Stübner 		     GPLL_MODE_NORM << GPLL_MODE_SHIFT |
197aff8795cSHeiko Stübner 		     APLL_MODE_NORM << APLL_MODE_SHIFT);
198aff8795cSHeiko Stübner }
199aff8795cSHeiko Stübner 
200aff8795cSHeiko Stübner /* Get pll rate by id */
rkclk_pll_get_rate(struct rk3036_cru * cru,enum rk_clk_id clk_id)201aff8795cSHeiko Stübner static uint32_t rkclk_pll_get_rate(struct rk3036_cru *cru,
202aff8795cSHeiko Stübner 				   enum rk_clk_id clk_id)
203aff8795cSHeiko Stübner {
204aff8795cSHeiko Stübner 	uint32_t refdiv, fbdiv, postdiv1, postdiv2;
205aff8795cSHeiko Stübner 	uint32_t con;
206aff8795cSHeiko Stübner 	int pll_id = rk_pll_id(clk_id);
207aff8795cSHeiko Stübner 	struct rk3036_pll *pll = &cru->pll[pll_id];
208aff8795cSHeiko Stübner 	static u8 clk_shift[CLK_COUNT] = {
209aff8795cSHeiko Stübner 		0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, 0xff,
210aff8795cSHeiko Stübner 		GPLL_MODE_SHIFT, 0xff
211aff8795cSHeiko Stübner 	};
21237943aaeSKever Yang 	static u32 clk_mask[CLK_COUNT] = {
21337943aaeSKever Yang 		0xffffffff, APLL_MODE_MASK, DPLL_MODE_MASK, 0xffffffff,
21437943aaeSKever Yang 		GPLL_MODE_MASK, 0xffffffff
215aff8795cSHeiko Stübner 	};
216aff8795cSHeiko Stübner 	uint shift;
217aff8795cSHeiko Stübner 	uint mask;
218aff8795cSHeiko Stübner 
219aff8795cSHeiko Stübner 	con = readl(&cru->cru_mode_con);
220aff8795cSHeiko Stübner 	shift = clk_shift[clk_id];
221aff8795cSHeiko Stübner 	mask = clk_mask[clk_id];
222aff8795cSHeiko Stübner 
22337943aaeSKever Yang 	switch ((con & mask) >> shift) {
224aff8795cSHeiko Stübner 	case GPLL_MODE_SLOW:
225aff8795cSHeiko Stübner 		return OSC_HZ;
226aff8795cSHeiko Stübner 	case GPLL_MODE_NORM:
227aff8795cSHeiko Stübner 
228aff8795cSHeiko Stübner 		/* normal mode */
229aff8795cSHeiko Stübner 		con = readl(&pll->con0);
23037943aaeSKever Yang 		postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
23137943aaeSKever Yang 		fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
232aff8795cSHeiko Stübner 		con = readl(&pll->con1);
23337943aaeSKever Yang 		postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
23437943aaeSKever Yang 		refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
235aff8795cSHeiko Stübner 		return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
236aff8795cSHeiko Stübner 	case GPLL_MODE_DEEP:
237aff8795cSHeiko Stübner 	default:
238aff8795cSHeiko Stübner 		return 32768;
239aff8795cSHeiko Stübner 	}
240aff8795cSHeiko Stübner }
241aff8795cSHeiko Stübner 
rockchip_mmc_get_clk(struct rk3036_cru * cru,uint clk_general_rate,int periph)242aff8795cSHeiko Stübner static ulong rockchip_mmc_get_clk(struct rk3036_cru *cru, uint clk_general_rate,
243aff8795cSHeiko Stübner 				  int periph)
244aff8795cSHeiko Stübner {
245aff8795cSHeiko Stübner 	uint src_rate;
246aff8795cSHeiko Stübner 	uint div, mux;
247aff8795cSHeiko Stübner 	u32 con;
248aff8795cSHeiko Stübner 
249aff8795cSHeiko Stübner 	switch (periph) {
250aff8795cSHeiko Stübner 	case HCLK_EMMC:
2517f0cfe47SXu Ziyuan 	case SCLK_EMMC:
252aff8795cSHeiko Stübner 		con = readl(&cru->cru_clksel_con[12]);
25337943aaeSKever Yang 		mux = (con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT;
25437943aaeSKever Yang 		div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT;
255aff8795cSHeiko Stübner 		break;
256aff8795cSHeiko Stübner 	case HCLK_SDIO:
2577f0cfe47SXu Ziyuan 	case SCLK_SDIO:
258aff8795cSHeiko Stübner 		con = readl(&cru->cru_clksel_con[12]);
259*4112f8e0SElaine Zhang 		mux = (con & SDIO_PLL_MASK) >> SDIO_PLL_SHIFT;
260*4112f8e0SElaine Zhang 		con = readl(&cru->cru_clksel_con[11]);
261*4112f8e0SElaine Zhang 		div = (con & SDIO_DIV_MASK) >> SDIO_DIV_SHIFT;
262*4112f8e0SElaine Zhang 		break;
263*4112f8e0SElaine Zhang 	case HCLK_SDMMC:
264*4112f8e0SElaine Zhang 	case SCLK_SDMMC:
265*4112f8e0SElaine Zhang 		con = readl(&cru->cru_clksel_con[12]);
26637943aaeSKever Yang 		mux = (con & MMC0_PLL_MASK) >> MMC0_PLL_SHIFT;
267*4112f8e0SElaine Zhang 		con = readl(&cru->cru_clksel_con[11]);
26837943aaeSKever Yang 		div = (con & MMC0_DIV_MASK) >> MMC0_DIV_SHIFT;
269aff8795cSHeiko Stübner 		break;
270aff8795cSHeiko Stübner 	default:
271aff8795cSHeiko Stübner 		return -EINVAL;
272aff8795cSHeiko Stübner 	}
273aff8795cSHeiko Stübner 
274aff8795cSHeiko Stübner 	src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate;
2753a94d75dSKever Yang 	return DIV_TO_RATE(src_rate, div) / 2;
276aff8795cSHeiko Stübner }
277aff8795cSHeiko Stübner 
rockchip_mmc_set_clk(struct rk3036_cru * cru,uint clk_general_rate,int periph,uint freq)278aff8795cSHeiko Stübner static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate,
279aff8795cSHeiko Stübner 				  int periph, uint freq)
280aff8795cSHeiko Stübner {
281aff8795cSHeiko Stübner 	int src_clk_div;
282aff8795cSHeiko Stübner 	int mux;
283aff8795cSHeiko Stübner 
284aff8795cSHeiko Stübner 	debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate);
285aff8795cSHeiko Stübner 
286aff8795cSHeiko Stübner 	/* mmc clock auto divide 2 in internal */
2873a94d75dSKever Yang 	src_clk_div = DIV_ROUND_UP(clk_general_rate / 2, freq);
288aff8795cSHeiko Stübner 
289217273cdSKever Yang 	if (src_clk_div > 128) {
2903a94d75dSKever Yang 		src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, freq);
291217273cdSKever Yang 		assert(src_clk_div - 1 < 128);
292aff8795cSHeiko Stübner 		mux = EMMC_SEL_24M;
293aff8795cSHeiko Stübner 	} else {
294aff8795cSHeiko Stübner 		mux = EMMC_SEL_GPLL;
295aff8795cSHeiko Stübner 	}
296aff8795cSHeiko Stübner 
297aff8795cSHeiko Stübner 	switch (periph) {
298aff8795cSHeiko Stübner 	case HCLK_EMMC:
2997f0cfe47SXu Ziyuan 	case SCLK_EMMC:
300aff8795cSHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[12],
30137943aaeSKever Yang 			     EMMC_PLL_MASK | EMMC_DIV_MASK,
302aff8795cSHeiko Stübner 			     mux << EMMC_PLL_SHIFT |
303aff8795cSHeiko Stübner 			     (src_clk_div - 1) << EMMC_DIV_SHIFT);
304aff8795cSHeiko Stübner 		break;
305aff8795cSHeiko Stübner 	case HCLK_SDIO:
3067f0cfe47SXu Ziyuan 	case SCLK_SDIO:
307*4112f8e0SElaine Zhang 		rk_clrsetreg(&cru->cru_clksel_con[12],
308*4112f8e0SElaine Zhang 			     SDIO_PLL_MASK,
309*4112f8e0SElaine Zhang 			     SDIO_SEL_24M << SDIO_PLL_SHIFT);
310aff8795cSHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[11],
311*4112f8e0SElaine Zhang 			     SDIO_DIV_MASK,
312*4112f8e0SElaine Zhang 			     (src_clk_div - 1) << SDIO_DIV_SHIFT);
313*4112f8e0SElaine Zhang 		rk_clrsetreg(&cru->cru_clksel_con[12],
314*4112f8e0SElaine Zhang 			     SDIO_PLL_MASK,
315*4112f8e0SElaine Zhang 			     mux << SDIO_PLL_SHIFT);
316*4112f8e0SElaine Zhang 		break;
317*4112f8e0SElaine Zhang 	case HCLK_SDMMC:
318*4112f8e0SElaine Zhang 	case SCLK_SDMMC:
319*4112f8e0SElaine Zhang 		rk_clrsetreg(&cru->cru_clksel_con[12],
320*4112f8e0SElaine Zhang 			     MMC0_PLL_MASK,
321*4112f8e0SElaine Zhang 			     MMC0_SEL_24M << MMC0_PLL_SHIFT);
322*4112f8e0SElaine Zhang 		rk_clrsetreg(&cru->cru_clksel_con[11],
323*4112f8e0SElaine Zhang 			     MMC0_DIV_MASK,
324aff8795cSHeiko Stübner 			     (src_clk_div - 1) << MMC0_DIV_SHIFT);
325*4112f8e0SElaine Zhang 		rk_clrsetreg(&cru->cru_clksel_con[12],
326*4112f8e0SElaine Zhang 			     MMC0_PLL_MASK,
327*4112f8e0SElaine Zhang 			     mux << MMC0_PLL_SHIFT);
328aff8795cSHeiko Stübner 		break;
329aff8795cSHeiko Stübner 	default:
330aff8795cSHeiko Stübner 		return -EINVAL;
331aff8795cSHeiko Stübner 	}
332aff8795cSHeiko Stübner 
333aff8795cSHeiko Stübner 	return rockchip_mmc_get_clk(cru, clk_general_rate, periph);
334aff8795cSHeiko Stübner }
335aff8795cSHeiko Stübner 
rk3036_spi_get_clk(struct rk3036_cru * cru,uint clk_general_rate)336df77e7a3SElaine Zhang static ulong rk3036_spi_get_clk(struct rk3036_cru *cru, uint clk_general_rate)
337df77e7a3SElaine Zhang {
338df77e7a3SElaine Zhang 	u32 div, con;
339df77e7a3SElaine Zhang 
340df77e7a3SElaine Zhang 	con = readl(&cru->cru_clksel_con[25]);
341df77e7a3SElaine Zhang 	div = (con & SPI_DIV_MASK) >> SPI_DIV_SHIFT;
342df77e7a3SElaine Zhang 
343df77e7a3SElaine Zhang 	return DIV_TO_RATE(clk_general_rate, div);
344df77e7a3SElaine Zhang }
345df77e7a3SElaine Zhang 
rk3036_spi_set_clk(struct rk3036_cru * cru,uint clk_general_rate,ulong hz)346df77e7a3SElaine Zhang static ulong  rk3036_spi_set_clk(struct rk3036_cru *cru,
347df77e7a3SElaine Zhang 				 uint clk_general_rate,
348df77e7a3SElaine Zhang 				 ulong hz)
349df77e7a3SElaine Zhang {
350df77e7a3SElaine Zhang 	int div;
351df77e7a3SElaine Zhang 
352df77e7a3SElaine Zhang 	div = DIV_ROUND_UP(clk_general_rate, hz);
353df77e7a3SElaine Zhang 	assert(div - 1 < 128);
354df77e7a3SElaine Zhang 	rk_clrsetreg(&cru->cru_clksel_con[25],
355df77e7a3SElaine Zhang 		     SPI_PLL_SEL_MASK | SPI_DIV_MASK,
356df77e7a3SElaine Zhang 		     SPI_PLL_SEL_GPLL << SPI_PLL_SEL_SHIFT |
357df77e7a3SElaine Zhang 		     (div - 1) << SPI_DIV_SHIFT);
358df77e7a3SElaine Zhang 	return  rk3036_spi_get_clk(cru, clk_general_rate);
359df77e7a3SElaine Zhang }
360df77e7a3SElaine Zhang 
rockchip_dclk_lcdc_get_clk(struct rk3036_cru * cru,uint clk_general_rate)36115ede2a1SFinley Xiao static ulong rockchip_dclk_lcdc_get_clk(struct rk3036_cru *cru,
36215ede2a1SFinley Xiao 					uint clk_general_rate)
36315ede2a1SFinley Xiao {
36415ede2a1SFinley Xiao 	u32 con, div, sel, parent;
36515ede2a1SFinley Xiao 
36615ede2a1SFinley Xiao 	con = readl(&cru->cru_clksel_con[28]);
36715ede2a1SFinley Xiao 	div = (con & LCDC_DCLK_DIV_MASK) >> LCDC_DCLK_DIV_SHIFT;
36815ede2a1SFinley Xiao 	sel = (con & LCDC_DCLK_SEL_MASK) >> LCDC_DCLK_SEL_SHIFT;
36915ede2a1SFinley Xiao 	if (sel == LCDC_DCLK_SEL_GPLL)
37015ede2a1SFinley Xiao 		parent = clk_general_rate;
37115ede2a1SFinley Xiao 	else
37215ede2a1SFinley Xiao 		return -ENOENT;
37315ede2a1SFinley Xiao 
37415ede2a1SFinley Xiao 	return DIV_TO_RATE(parent, div);
37515ede2a1SFinley Xiao }
37615ede2a1SFinley Xiao 
rockchip_dclk_lcdc_set_clk(struct rk3036_cru * cru,uint clk_general_rate,uint freq)37715ede2a1SFinley Xiao static ulong rockchip_dclk_lcdc_set_clk(struct rk3036_cru *cru,
37815ede2a1SFinley Xiao 					uint clk_general_rate, uint freq)
37915ede2a1SFinley Xiao {
38015ede2a1SFinley Xiao 	int src_clk_div;
38115ede2a1SFinley Xiao 
38215ede2a1SFinley Xiao 	src_clk_div = DIV_ROUND_UP(clk_general_rate, freq);
38315ede2a1SFinley Xiao 	assert(src_clk_div - 1 <= 255);
38415ede2a1SFinley Xiao 
38515ede2a1SFinley Xiao 	rk_clrsetreg(&cru->cru_clksel_con[28],
38615ede2a1SFinley Xiao 		     LCDC_DCLK_SEL_MASK | LCDC_DCLK_DIV_MASK,
38715ede2a1SFinley Xiao 		     LCDC_DCLK_SEL_GPLL << LCDC_DCLK_SEL_SHIFT |
38815ede2a1SFinley Xiao 		     (src_clk_div - 1) << LCDC_DCLK_DIV_SHIFT);
38915ede2a1SFinley Xiao 
39015ede2a1SFinley Xiao 	return rockchip_dclk_lcdc_get_clk(cru, clk_general_rate);
39115ede2a1SFinley Xiao }
39215ede2a1SFinley Xiao 
rockchip_aclk_lcdc_get_clk(struct rk3036_cru * cru,uint clk_general_rate)39315ede2a1SFinley Xiao static ulong rockchip_aclk_lcdc_get_clk(struct rk3036_cru *cru,
39415ede2a1SFinley Xiao 					uint clk_general_rate)
39515ede2a1SFinley Xiao {
39615ede2a1SFinley Xiao 	u32 con, div, sel, parent;
39715ede2a1SFinley Xiao 
39815ede2a1SFinley Xiao 	con = readl(&cru->cru_clksel_con[31]);
39915ede2a1SFinley Xiao 	div = (con & LCDC_ACLK_DIV_MASK) >> LCDC_ACLK_DIV_SHIFT;
40015ede2a1SFinley Xiao 	sel = (con & LCDC_ACLK_SEL_MASK) >> LCDC_ACLK_SEL_SHIFT;
40115ede2a1SFinley Xiao 	if (sel == LCDC_ACLK_SEL_GPLL)
40215ede2a1SFinley Xiao 		parent = clk_general_rate;
40315ede2a1SFinley Xiao 	else
40415ede2a1SFinley Xiao 		return -ENOENT;
40515ede2a1SFinley Xiao 
40615ede2a1SFinley Xiao 	return DIV_TO_RATE(parent, div);
40715ede2a1SFinley Xiao }
40815ede2a1SFinley Xiao 
rockchip_aclk_lcdc_set_clk(struct rk3036_cru * cru,uint clk_general_rate,uint freq)40915ede2a1SFinley Xiao static ulong rockchip_aclk_lcdc_set_clk(struct rk3036_cru *cru,
41015ede2a1SFinley Xiao 					uint clk_general_rate, uint freq)
41115ede2a1SFinley Xiao {
41215ede2a1SFinley Xiao 	int src_clk_div;
41315ede2a1SFinley Xiao 
41415ede2a1SFinley Xiao 	src_clk_div = DIV_ROUND_UP(clk_general_rate, freq);
41515ede2a1SFinley Xiao 	assert(src_clk_div - 1 <= 31);
41615ede2a1SFinley Xiao 
41715ede2a1SFinley Xiao 	rk_clrsetreg(&cru->cru_clksel_con[31],
41815ede2a1SFinley Xiao 		     LCDC_ACLK_SEL_MASK | LCDC_ACLK_DIV_MASK,
41915ede2a1SFinley Xiao 		     LCDC_ACLK_SEL_GPLL << LCDC_ACLK_SEL_SHIFT |
42015ede2a1SFinley Xiao 		     (src_clk_div - 1) << LCDC_ACLK_DIV_SHIFT);
42115ede2a1SFinley Xiao 
42215ede2a1SFinley Xiao 	return rockchip_aclk_lcdc_get_clk(cru, clk_general_rate);
42315ede2a1SFinley Xiao }
42415ede2a1SFinley Xiao 
rk3036_peri_get_clk(struct rk3036_clk_priv * priv,ulong clk_id,uint clk_general_rate)425238c00d9SQiqi Zhang static ulong rk3036_peri_get_clk(struct rk3036_clk_priv *priv, ulong clk_id,
426238c00d9SQiqi Zhang 				 uint clk_general_rate)
427238c00d9SQiqi Zhang {
428238c00d9SQiqi Zhang 	struct rk3036_cru *cru = priv->cru;
429238c00d9SQiqi Zhang 	u32 div, con, parent;
430238c00d9SQiqi Zhang 
431238c00d9SQiqi Zhang 	switch (clk_id) {
432238c00d9SQiqi Zhang 	case ACLK_PERI:
433238c00d9SQiqi Zhang 		con = readl(&cru->cru_clksel_con[10]);
434238c00d9SQiqi Zhang 		div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT;
435238c00d9SQiqi Zhang 		parent = clk_general_rate;
436238c00d9SQiqi Zhang 		break;
437238c00d9SQiqi Zhang 	case PCLK_PWM:
438238c00d9SQiqi Zhang 		con = readl(&cru->cru_clksel_con[10]);
439238c00d9SQiqi Zhang 		div = (con & PERI_PCLK_DIV_MASK) >> PERI_PCLK_DIV_SHIFT;
440238c00d9SQiqi Zhang 		parent = rk3036_peri_get_clk(priv, ACLK_PERI, clk_general_rate);
441238c00d9SQiqi Zhang 		break;
442238c00d9SQiqi Zhang 	default:
443238c00d9SQiqi Zhang 		printf("do not support this peripheral bus\n");
444238c00d9SQiqi Zhang 		return -EINVAL;
445238c00d9SQiqi Zhang 	}
446238c00d9SQiqi Zhang 
447238c00d9SQiqi Zhang 	return DIV_TO_RATE(parent, div);
448238c00d9SQiqi Zhang }
449238c00d9SQiqi Zhang 
rk3036_peri_set_clk(struct rk3036_clk_priv * priv,ulong clk_id,uint clk_general_rate,uint hz)450238c00d9SQiqi Zhang static ulong rk3036_peri_set_clk(struct rk3036_clk_priv *priv,
451238c00d9SQiqi Zhang 				 ulong clk_id, uint clk_general_rate,
452238c00d9SQiqi Zhang 				 uint hz)
453238c00d9SQiqi Zhang {
454238c00d9SQiqi Zhang 	struct rk3036_cru *cru = priv->cru;
455238c00d9SQiqi Zhang 	int src_clk_div;
456238c00d9SQiqi Zhang 
457238c00d9SQiqi Zhang 	switch (clk_id) {
458238c00d9SQiqi Zhang 	case ACLK_PERI:
459238c00d9SQiqi Zhang 		src_clk_div = DIV_ROUND_UP(clk_general_rate, hz);
460238c00d9SQiqi Zhang 		assert(src_clk_div - 1 < 32);
461238c00d9SQiqi Zhang 		rk_clrsetreg(&cru->cru_clksel_con[10],
462238c00d9SQiqi Zhang 			     PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK,
463238c00d9SQiqi Zhang 			     PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
464238c00d9SQiqi Zhang 			     (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT);
465238c00d9SQiqi Zhang 		break;
466238c00d9SQiqi Zhang 	case PCLK_PWM:
467238c00d9SQiqi Zhang 		src_clk_div = DIV_ROUND_UP(rk3036_peri_get_clk(priv,
468238c00d9SQiqi Zhang 							       ACLK_PERI,
469238c00d9SQiqi Zhang 							       clk_general_rate),
470238c00d9SQiqi Zhang 							       hz);
471238c00d9SQiqi Zhang 		assert(src_clk_div - 1 < 8);
472238c00d9SQiqi Zhang 		rk_clrsetreg(&cru->cru_clksel_con[10],
473238c00d9SQiqi Zhang 			     PERI_PCLK_DIV_MASK,
474238c00d9SQiqi Zhang 			     (src_clk_div - 1) << PERI_PCLK_DIV_SHIFT);
475238c00d9SQiqi Zhang 		break;
476238c00d9SQiqi Zhang 	default:
477238c00d9SQiqi Zhang 		printf("do not support this peripheral bus\n");
478238c00d9SQiqi Zhang 		return -EINVAL;
479238c00d9SQiqi Zhang 	}
480238c00d9SQiqi Zhang 
481238c00d9SQiqi Zhang 	return rk3036_peri_get_clk(priv, clk_id, clk_general_rate);
482238c00d9SQiqi Zhang }
483238c00d9SQiqi Zhang 
rk3036_clk_get_rate(struct clk * clk)484aff8795cSHeiko Stübner static ulong rk3036_clk_get_rate(struct clk *clk)
485aff8795cSHeiko Stübner {
486aff8795cSHeiko Stübner 	struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
48715ede2a1SFinley Xiao 	ulong gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
488aff8795cSHeiko Stübner 
489aff8795cSHeiko Stübner 	switch (clk->id) {
490aff8795cSHeiko Stübner 	case 0 ... 63:
491aff8795cSHeiko Stübner 		return rkclk_pll_get_rate(priv->cru, clk->id);
492*4112f8e0SElaine Zhang 	case SCLK_EMMC:
493*4112f8e0SElaine Zhang 	case SCLK_SDMMC:
494*4112f8e0SElaine Zhang 	case SCLK_SDIO:
495*4112f8e0SElaine Zhang 	case HCLK_EMMC:
496*4112f8e0SElaine Zhang 	case HCLK_SDMMC:
497*4112f8e0SElaine Zhang 	case HCLK_SDIO:
498*4112f8e0SElaine Zhang 		return rockchip_mmc_get_clk(priv->cru, gclk_rate,
499*4112f8e0SElaine Zhang 					    clk->id);
50015ede2a1SFinley Xiao 	case SCLK_LCDC:
50115ede2a1SFinley Xiao 		return rockchip_dclk_lcdc_get_clk(priv->cru, gclk_rate);
50215ede2a1SFinley Xiao 	case ACLK_LCDC:
50315ede2a1SFinley Xiao 		return rockchip_aclk_lcdc_get_clk(priv->cru, gclk_rate);
504df77e7a3SElaine Zhang 	case SCLK_SPI:
505df77e7a3SElaine Zhang 		return rk3036_spi_get_clk(priv->cru, gclk_rate);
506238c00d9SQiqi Zhang 	case PCLK_PWM:
507238c00d9SQiqi Zhang 		return rk3036_peri_get_clk(priv, clk->id, gclk_rate);
508aff8795cSHeiko Stübner 	default:
509aff8795cSHeiko Stübner 		return -ENOENT;
510aff8795cSHeiko Stübner 	}
511aff8795cSHeiko Stübner }
512aff8795cSHeiko Stübner 
rk3036_clk_set_rate(struct clk * clk,ulong rate)513aff8795cSHeiko Stübner static ulong rk3036_clk_set_rate(struct clk *clk, ulong rate)
514aff8795cSHeiko Stübner {
515aff8795cSHeiko Stübner 	struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
516aff8795cSHeiko Stübner 	ulong new_rate, gclk_rate;
517aff8795cSHeiko Stübner 
518aff8795cSHeiko Stübner 	gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
519aff8795cSHeiko Stübner 	switch (clk->id) {
520aff8795cSHeiko Stübner 	case 0 ... 63:
521aff8795cSHeiko Stübner 		return 0;
522aff8795cSHeiko Stübner 	case HCLK_EMMC:
523*4112f8e0SElaine Zhang 	case HCLK_SDMMC:
524*4112f8e0SElaine Zhang 	case HCLK_SDIO:
5257f0cfe47SXu Ziyuan 	case SCLK_EMMC:
526*4112f8e0SElaine Zhang 	case SCLK_SDMMC:
527*4112f8e0SElaine Zhang 	case SCLK_SDIO:
528aff8795cSHeiko Stübner 		new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate,
529aff8795cSHeiko Stübner 						clk->id, rate);
530aff8795cSHeiko Stübner 		break;
53115ede2a1SFinley Xiao 	case SCLK_LCDC:
53215ede2a1SFinley Xiao 		new_rate = rockchip_dclk_lcdc_set_clk(priv->cru, gclk_rate,
53315ede2a1SFinley Xiao 						      rate);
53415ede2a1SFinley Xiao 		break;
53515ede2a1SFinley Xiao 	case ACLK_LCDC:
53615ede2a1SFinley Xiao 		new_rate = rockchip_aclk_lcdc_set_clk(priv->cru, gclk_rate,
53715ede2a1SFinley Xiao 						      rate);
53815ede2a1SFinley Xiao 		break;
539df77e7a3SElaine Zhang 	case SCLK_SPI:
540df77e7a3SElaine Zhang 		new_rate = rk3036_spi_set_clk(priv->cru, gclk_rate,
541df77e7a3SElaine Zhang 					      rate);
542df77e7a3SElaine Zhang 		break;
543238c00d9SQiqi Zhang 	case PCLK_PWM:
544238c00d9SQiqi Zhang 		new_rate = rk3036_peri_set_clk(priv, clk->id, gclk_rate,
545238c00d9SQiqi Zhang 					       rate);
546238c00d9SQiqi Zhang 		break;
547aff8795cSHeiko Stübner 	default:
548aff8795cSHeiko Stübner 		return -ENOENT;
549aff8795cSHeiko Stübner 	}
550aff8795cSHeiko Stübner 
551aff8795cSHeiko Stübner 	return new_rate;
552aff8795cSHeiko Stübner }
553aff8795cSHeiko Stübner 
554aff8795cSHeiko Stübner static struct clk_ops rk3036_clk_ops = {
555aff8795cSHeiko Stübner 	.get_rate	= rk3036_clk_get_rate,
556aff8795cSHeiko Stübner 	.set_rate	= rk3036_clk_set_rate,
557aff8795cSHeiko Stübner };
558aff8795cSHeiko Stübner 
rk3036_clk_ofdata_to_platdata(struct udevice * dev)559bb8e4ec3SDavid Wu static int rk3036_clk_ofdata_to_platdata(struct udevice *dev)
560bb8e4ec3SDavid Wu {
561bb8e4ec3SDavid Wu 	struct rk3036_clk_priv *priv = dev_get_priv(dev);
562bb8e4ec3SDavid Wu 
563bb8e4ec3SDavid Wu 	priv->cru = dev_read_addr_ptr(dev);
564bb8e4ec3SDavid Wu 
565bb8e4ec3SDavid Wu 	return 0;
566bb8e4ec3SDavid Wu }
567bb8e4ec3SDavid Wu 
rk3036_clk_probe(struct udevice * dev)568aff8795cSHeiko Stübner static int rk3036_clk_probe(struct udevice *dev)
569aff8795cSHeiko Stübner {
570aff8795cSHeiko Stübner 	struct rk3036_clk_priv *priv = dev_get_priv(dev);
571aff8795cSHeiko Stübner 
5723a1c76d9SElaine Zhang 	priv->sync_kernel = false;
5733a1c76d9SElaine Zhang 	if (!priv->armclk_enter_hz)
5743a1c76d9SElaine Zhang 		priv->armclk_enter_hz = rkclk_pll_get_rate(priv->cru,
5753a1c76d9SElaine Zhang 							   CLK_ARM);
576aff8795cSHeiko Stübner 	rkclk_init(priv->cru);
5773a1c76d9SElaine Zhang 	if (!priv->armclk_init_hz)
5783a1c76d9SElaine Zhang 		priv->armclk_init_hz = rkclk_pll_get_rate(priv->cru,
5793a1c76d9SElaine Zhang 							  CLK_ARM);
580aff8795cSHeiko Stübner 
581aff8795cSHeiko Stübner 	return 0;
582aff8795cSHeiko Stübner }
583aff8795cSHeiko Stübner 
rk3036_clk_bind(struct udevice * dev)584aff8795cSHeiko Stübner static int rk3036_clk_bind(struct udevice *dev)
585aff8795cSHeiko Stübner {
586aff8795cSHeiko Stübner 	int ret;
5873d555d75SElaine Zhang 	struct udevice *sys_child, *sf_child;
588fbdd1558SKever Yang 	struct sysreset_reg *priv;
5893d555d75SElaine Zhang 	struct softreset_reg *sf_priv;
590aff8795cSHeiko Stübner 
591aff8795cSHeiko Stübner 	/* The reset driver does not have a device node, so bind it here */
592fbdd1558SKever Yang 	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
593fbdd1558SKever Yang 				 &sys_child);
594fbdd1558SKever Yang 	if (ret) {
595fbdd1558SKever Yang 		debug("Warning: No sysreset driver: ret=%d\n", ret);
596fbdd1558SKever Yang 	} else {
597fbdd1558SKever Yang 		priv = malloc(sizeof(struct sysreset_reg));
598fbdd1558SKever Yang 		priv->glb_srst_fst_value = offsetof(struct rk3036_cru,
599fbdd1558SKever Yang 						    cru_glb_srst_fst_value);
600fbdd1558SKever Yang 		priv->glb_srst_snd_value = offsetof(struct rk3036_cru,
601fbdd1558SKever Yang 						    cru_glb_srst_snd_value);
602fbdd1558SKever Yang 		sys_child->priv = priv;
603fbdd1558SKever Yang 	}
604aff8795cSHeiko Stübner 
6053d555d75SElaine Zhang 	ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset",
6063d555d75SElaine Zhang 					 dev_ofnode(dev), &sf_child);
6073d555d75SElaine Zhang 	if (ret) {
6083d555d75SElaine Zhang 		debug("Warning: No rockchip reset driver: ret=%d\n", ret);
6093d555d75SElaine Zhang 	} else {
6103d555d75SElaine Zhang 		sf_priv = malloc(sizeof(struct softreset_reg));
6113d555d75SElaine Zhang 		sf_priv->sf_reset_offset = offsetof(struct rk3036_cru,
6123d555d75SElaine Zhang 						    cru_softrst_con[0]);
6133d555d75SElaine Zhang 		sf_priv->sf_reset_num = 9;
6143d555d75SElaine Zhang 		sf_child->priv = sf_priv;
6153d555d75SElaine Zhang 	}
6163d555d75SElaine Zhang 
617aff8795cSHeiko Stübner 	return 0;
618aff8795cSHeiko Stübner }
619aff8795cSHeiko Stübner 
620aff8795cSHeiko Stübner static const struct udevice_id rk3036_clk_ids[] = {
621aff8795cSHeiko Stübner 	{ .compatible = "rockchip,rk3036-cru" },
622aff8795cSHeiko Stübner 	{ }
623aff8795cSHeiko Stübner };
624aff8795cSHeiko Stübner 
62592ac73e4SSimon Glass U_BOOT_DRIVER(rockchip_rk3036_cru) = {
626aff8795cSHeiko Stübner 	.name		= "clk_rk3036",
627aff8795cSHeiko Stübner 	.id		= UCLASS_CLK,
628aff8795cSHeiko Stübner 	.of_match	= rk3036_clk_ids,
629aff8795cSHeiko Stübner 	.priv_auto_alloc_size = sizeof(struct rk3036_clk_priv),
630bb8e4ec3SDavid Wu 	.ofdata_to_platdata = rk3036_clk_ofdata_to_platdata,
631aff8795cSHeiko Stübner 	.ops		= &rk3036_clk_ops,
632aff8795cSHeiko Stübner 	.bind		= rk3036_clk_bind,
633aff8795cSHeiko Stübner 	.probe		= rk3036_clk_probe,
634aff8795cSHeiko Stübner };
6353a1c76d9SElaine Zhang 
6363a1c76d9SElaine Zhang #ifndef CONFIG_SPL_BUILD
6373a1c76d9SElaine Zhang /**
6383a1c76d9SElaine Zhang  * soc_clk_dump() - Print clock frequencies
6393a1c76d9SElaine Zhang  * Returns zero on success
6403a1c76d9SElaine Zhang  *
6413a1c76d9SElaine Zhang  * Implementation for the clk dump command.
6423a1c76d9SElaine Zhang  */
soc_clk_dump(void)6433a1c76d9SElaine Zhang int soc_clk_dump(void)
6443a1c76d9SElaine Zhang {
6453a1c76d9SElaine Zhang 	struct udevice *cru_dev;
6463a1c76d9SElaine Zhang 	struct rk3036_clk_priv *priv;
6473a1c76d9SElaine Zhang 	const struct rk3036_clk_info *clk_dump;
6483a1c76d9SElaine Zhang 	struct clk clk;
6493a1c76d9SElaine Zhang 	unsigned long clk_count = ARRAY_SIZE(clks_dump);
6503a1c76d9SElaine Zhang 	unsigned long rate;
6513a1c76d9SElaine Zhang 	int i, ret;
6523a1c76d9SElaine Zhang 
6533a1c76d9SElaine Zhang 	ret = uclass_get_device_by_driver(UCLASS_CLK,
6543a1c76d9SElaine Zhang 					  DM_GET_DRIVER(rockchip_rk3036_cru),
6553a1c76d9SElaine Zhang 					  &cru_dev);
6563a1c76d9SElaine Zhang 	if (ret) {
6573a1c76d9SElaine Zhang 		printf("%s failed to get cru device\n", __func__);
6583a1c76d9SElaine Zhang 		return ret;
6593a1c76d9SElaine Zhang 	}
6603a1c76d9SElaine Zhang 
6613a1c76d9SElaine Zhang 	priv = dev_get_priv(cru_dev);
6623a1c76d9SElaine Zhang 	printf("CLK: (%s. arm: enter %lu KHz, init %lu KHz, kernel %lu%s)\n",
6633a1c76d9SElaine Zhang 	       priv->sync_kernel ? "sync kernel" : "uboot",
6643a1c76d9SElaine Zhang 	       priv->armclk_enter_hz / 1000,
6653a1c76d9SElaine Zhang 	       priv->armclk_init_hz / 1000,
6663a1c76d9SElaine Zhang 	       priv->set_armclk_rate ? priv->armclk_hz / 1000 : 0,
6673a1c76d9SElaine Zhang 	       priv->set_armclk_rate ? " KHz" : "N/A");
6683a1c76d9SElaine Zhang 	for (i = 0; i < clk_count; i++) {
6693a1c76d9SElaine Zhang 		clk_dump = &clks_dump[i];
6703a1c76d9SElaine Zhang 		if (clk_dump->name) {
6713a1c76d9SElaine Zhang 			clk.id = clk_dump->id;
6723a1c76d9SElaine Zhang 			if (clk_dump->is_cru)
6733a1c76d9SElaine Zhang 				ret = clk_request(cru_dev, &clk);
6743a1c76d9SElaine Zhang 			if (ret < 0)
6753a1c76d9SElaine Zhang 				return ret;
6763a1c76d9SElaine Zhang 
6773a1c76d9SElaine Zhang 			rate = clk_get_rate(&clk);
6783a1c76d9SElaine Zhang 			clk_free(&clk);
6793a1c76d9SElaine Zhang 			if (i == 0) {
6803a1c76d9SElaine Zhang 				if (rate < 0)
6813a1c76d9SElaine Zhang 					printf("  %s %s\n", clk_dump->name,
6823a1c76d9SElaine Zhang 					       "unknown");
6833a1c76d9SElaine Zhang 				else
6843a1c76d9SElaine Zhang 					printf("  %s %lu KHz\n", clk_dump->name,
6853a1c76d9SElaine Zhang 					       rate / 1000);
6863a1c76d9SElaine Zhang 			} else {
6873a1c76d9SElaine Zhang 				if (rate < 0)
6883a1c76d9SElaine Zhang 					printf("  %s %s\n", clk_dump->name,
6893a1c76d9SElaine Zhang 					       "unknown");
6903a1c76d9SElaine Zhang 				else
6913a1c76d9SElaine Zhang 					printf("  %s %lu KHz\n", clk_dump->name,
6923a1c76d9SElaine Zhang 					       rate / 1000);
6933a1c76d9SElaine Zhang 			}
6943a1c76d9SElaine Zhang 		}
6953a1c76d9SElaine Zhang 	}
6963a1c76d9SElaine Zhang 
6973a1c76d9SElaine Zhang 	return 0;
6983a1c76d9SElaine Zhang }
6993a1c76d9SElaine Zhang #endif
7003a1c76d9SElaine Zhang 
701