xref: /openbmc/u-boot/drivers/clk/rockchip/clk_rk3188.c (revision 6b0c26fa05470b939be05ecf534566aef0f5a2b2)
1dcdd3278SHeiko Stübner /*
2dcdd3278SHeiko Stübner  * (C) Copyright 2015 Google, Inc
3dcdd3278SHeiko Stübner  * (C) Copyright 2016 Heiko Stuebner <heiko@sntech.de>
4dcdd3278SHeiko Stübner  *
5dcdd3278SHeiko Stübner  * SPDX-License-Identifier:	GPL-2.0
6dcdd3278SHeiko Stübner  */
7dcdd3278SHeiko Stübner 
8dcdd3278SHeiko Stübner #include <common.h>
9dcdd3278SHeiko Stübner #include <clk-uclass.h>
10dcdd3278SHeiko Stübner #include <dm.h>
11dcdd3278SHeiko Stübner #include <dt-structs.h>
12dcdd3278SHeiko Stübner #include <errno.h>
13dcdd3278SHeiko Stübner #include <mapmem.h>
14dcdd3278SHeiko Stübner #include <syscon.h>
15dcdd3278SHeiko Stübner #include <asm/io.h>
16dcdd3278SHeiko Stübner #include <asm/arch/clock.h>
17dcdd3278SHeiko Stübner #include <asm/arch/cru_rk3188.h>
18dcdd3278SHeiko Stübner #include <asm/arch/grf_rk3188.h>
19dcdd3278SHeiko Stübner #include <asm/arch/hardware.h>
20dcdd3278SHeiko Stübner #include <dt-bindings/clock/rk3188-cru.h>
21dcdd3278SHeiko Stübner #include <dm/device-internal.h>
22dcdd3278SHeiko Stübner #include <dm/lists.h>
23dcdd3278SHeiko Stübner #include <dm/uclass-internal.h>
24dcdd3278SHeiko Stübner #include <linux/log2.h>
25dcdd3278SHeiko Stübner 
26dcdd3278SHeiko Stübner DECLARE_GLOBAL_DATA_PTR;
27dcdd3278SHeiko Stübner 
28dcdd3278SHeiko Stübner enum rk3188_clk_type {
29dcdd3278SHeiko Stübner 	RK3188_CRU,
30dcdd3278SHeiko Stübner 	RK3188A_CRU,
31dcdd3278SHeiko Stübner };
32dcdd3278SHeiko Stübner 
33dcdd3278SHeiko Stübner struct rk3188_clk_plat {
34dcdd3278SHeiko Stübner #if CONFIG_IS_ENABLED(OF_PLATDATA)
35dcdd3278SHeiko Stübner 	struct dtd_rockchip_rk3188_cru dtd;
36dcdd3278SHeiko Stübner #endif
37dcdd3278SHeiko Stübner };
38dcdd3278SHeiko Stübner 
39dcdd3278SHeiko Stübner struct pll_div {
40dcdd3278SHeiko Stübner 	u32 nr;
41dcdd3278SHeiko Stübner 	u32 nf;
42dcdd3278SHeiko Stübner 	u32 no;
43dcdd3278SHeiko Stübner };
44dcdd3278SHeiko Stübner 
45dcdd3278SHeiko Stübner enum {
46dcdd3278SHeiko Stübner 	VCO_MAX_HZ	= 2200U * 1000000,
47dcdd3278SHeiko Stübner 	VCO_MIN_HZ	= 440 * 1000000,
48dcdd3278SHeiko Stübner 	OUTPUT_MAX_HZ	= 2200U * 1000000,
49dcdd3278SHeiko Stübner 	OUTPUT_MIN_HZ	= 30 * 1000000,
50dcdd3278SHeiko Stübner 	FREF_MAX_HZ	= 2200U * 1000000,
51dcdd3278SHeiko Stübner 	FREF_MIN_HZ	= 30 * 1000,
52dcdd3278SHeiko Stübner };
53dcdd3278SHeiko Stübner 
54dcdd3278SHeiko Stübner enum {
55dcdd3278SHeiko Stübner 	/* PLL CON0 */
56dcdd3278SHeiko Stübner 	PLL_OD_MASK		= 0x0f,
57dcdd3278SHeiko Stübner 
58dcdd3278SHeiko Stübner 	/* PLL CON1 */
59dcdd3278SHeiko Stübner 	PLL_NF_MASK		= 0x1fff,
60dcdd3278SHeiko Stübner 
61dcdd3278SHeiko Stübner 	/* PLL CON2 */
62dcdd3278SHeiko Stübner 	PLL_BWADJ_MASK		= 0x0fff,
63dcdd3278SHeiko Stübner 
64dcdd3278SHeiko Stübner 	/* PLL CON3 */
65dcdd3278SHeiko Stübner 	PLL_RESET_SHIFT		= 5,
66dcdd3278SHeiko Stübner 
67dcdd3278SHeiko Stübner 	/* GRF_SOC_STATUS0 */
68dcdd3278SHeiko Stübner 	SOCSTS_DPLL_LOCK	= 1 << 5,
69dcdd3278SHeiko Stübner 	SOCSTS_APLL_LOCK	= 1 << 6,
70dcdd3278SHeiko Stübner 	SOCSTS_CPLL_LOCK	= 1 << 7,
71dcdd3278SHeiko Stübner 	SOCSTS_GPLL_LOCK	= 1 << 8,
72dcdd3278SHeiko Stübner };
73dcdd3278SHeiko Stübner 
74dcdd3278SHeiko Stübner #define DIV_TO_RATE(input_rate, div)	((input_rate) / ((div) + 1))
75dcdd3278SHeiko Stübner 
76dcdd3278SHeiko Stübner #define PLL_DIVISORS(hz, _nr, _no) {\
77dcdd3278SHeiko Stübner 	.nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\
78dcdd3278SHeiko Stübner 	_Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
79dcdd3278SHeiko Stübner 		       (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\
80dcdd3278SHeiko Stübner 		       "divisors on line " __stringify(__LINE__));
81dcdd3278SHeiko Stübner 
82dcdd3278SHeiko Stübner /* Keep divisors as low as possible to reduce jitter and power usage */
83dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD
84dcdd3278SHeiko Stübner static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
85dcdd3278SHeiko Stübner static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
86dcdd3278SHeiko Stübner #endif
87dcdd3278SHeiko Stübner 
88dcdd3278SHeiko Stübner static int rkclk_set_pll(struct rk3188_cru *cru, enum rk_clk_id clk_id,
89dcdd3278SHeiko Stübner 			 const struct pll_div *div, bool has_bwadj)
90dcdd3278SHeiko Stübner {
91dcdd3278SHeiko Stübner 	int pll_id = rk_pll_id(clk_id);
92dcdd3278SHeiko Stübner 	struct rk3188_pll *pll = &cru->pll[pll_id];
93dcdd3278SHeiko Stübner 	/* All PLLs have same VCO and output frequency range restrictions. */
94dcdd3278SHeiko Stübner 	uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
95dcdd3278SHeiko Stübner 	uint output_hz = vco_hz / div->no;
96dcdd3278SHeiko Stübner 
97dcdd3278SHeiko Stübner 	debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
98dcdd3278SHeiko Stübner 	      (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz);
99dcdd3278SHeiko Stübner 	assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
100dcdd3278SHeiko Stübner 	       output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ &&
101dcdd3278SHeiko Stübner 	       (div->no == 1 || !(div->no % 2)));
102dcdd3278SHeiko Stübner 
103dcdd3278SHeiko Stübner 	/* enter reset */
104dcdd3278SHeiko Stübner 	rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
105dcdd3278SHeiko Stübner 
106dcdd3278SHeiko Stübner 	rk_clrsetreg(&pll->con0,
107dcdd3278SHeiko Stübner 		     CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK,
108dcdd3278SHeiko Stübner 		     ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1));
109dcdd3278SHeiko Stübner 	rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1);
110dcdd3278SHeiko Stübner 
111dcdd3278SHeiko Stübner 	if (has_bwadj)
112dcdd3278SHeiko Stübner 		rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
113dcdd3278SHeiko Stübner 
114dcdd3278SHeiko Stübner 	udelay(10);
115dcdd3278SHeiko Stübner 
116dcdd3278SHeiko Stübner 	/* return from reset */
117dcdd3278SHeiko Stübner 	rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
118dcdd3278SHeiko Stübner 
119dcdd3278SHeiko Stübner 	return 0;
120dcdd3278SHeiko Stübner }
121dcdd3278SHeiko Stübner 
122dcdd3278SHeiko Stübner static int rkclk_configure_ddr(struct rk3188_cru *cru, struct rk3188_grf *grf,
123dcdd3278SHeiko Stübner 			       unsigned int hz, bool has_bwadj)
124dcdd3278SHeiko Stübner {
125dcdd3278SHeiko Stübner 	static const struct pll_div dpll_cfg[] = {
126*6b0c26faSAlexander Kochetkov 		{.nf = 75, .nr = 1, .no = 6},
127dcdd3278SHeiko Stübner 		{.nf = 400, .nr = 9, .no = 2},
128dcdd3278SHeiko Stübner 		{.nf = 500, .nr = 9, .no = 2},
129dcdd3278SHeiko Stübner 		{.nf = 100, .nr = 3, .no = 1},
130dcdd3278SHeiko Stübner 	};
131dcdd3278SHeiko Stübner 	int cfg;
132dcdd3278SHeiko Stübner 
133dcdd3278SHeiko Stübner 	switch (hz) {
134dcdd3278SHeiko Stübner 	case 300000000:
135dcdd3278SHeiko Stübner 		cfg = 0;
136dcdd3278SHeiko Stübner 		break;
137dcdd3278SHeiko Stübner 	case 533000000:	/* actually 533.3P MHz */
138dcdd3278SHeiko Stübner 		cfg = 1;
139dcdd3278SHeiko Stübner 		break;
140dcdd3278SHeiko Stübner 	case 666000000:	/* actually 666.6P MHz */
141dcdd3278SHeiko Stübner 		cfg = 2;
142dcdd3278SHeiko Stübner 		break;
143dcdd3278SHeiko Stübner 	case 800000000:
144dcdd3278SHeiko Stübner 		cfg = 3;
145dcdd3278SHeiko Stübner 		break;
146dcdd3278SHeiko Stübner 	default:
147dcdd3278SHeiko Stübner 		debug("Unsupported SDRAM frequency");
148dcdd3278SHeiko Stübner 		return -EINVAL;
149dcdd3278SHeiko Stübner 	}
150dcdd3278SHeiko Stübner 
151dcdd3278SHeiko Stübner 	/* pll enter slow-mode */
152dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
153dcdd3278SHeiko Stübner 		     DPLL_MODE_SLOW << DPLL_MODE_SHIFT);
154dcdd3278SHeiko Stübner 
155dcdd3278SHeiko Stübner 	rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg], has_bwadj);
156dcdd3278SHeiko Stübner 
157dcdd3278SHeiko Stübner 	/* wait for pll lock */
158dcdd3278SHeiko Stübner 	while (!(readl(&grf->soc_status0) & SOCSTS_DPLL_LOCK))
159dcdd3278SHeiko Stübner 		udelay(1);
160dcdd3278SHeiko Stübner 
161dcdd3278SHeiko Stübner 	/* PLL enter normal-mode */
162dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
163dcdd3278SHeiko Stübner 		     DPLL_MODE_NORMAL << DPLL_MODE_SHIFT);
164dcdd3278SHeiko Stübner 
165dcdd3278SHeiko Stübner 	return 0;
166dcdd3278SHeiko Stübner }
167dcdd3278SHeiko Stübner 
168f7853570SHeiko Stübner static int rkclk_configure_cpu(struct rk3188_cru *cru, struct rk3188_grf *grf,
169f7853570SHeiko Stübner 			      unsigned int hz, bool has_bwadj)
170f7853570SHeiko Stübner {
171f7853570SHeiko Stübner 	static const struct pll_div apll_cfg[] = {
172f7853570SHeiko Stübner 		{.nf = 50, .nr = 1, .no = 2},
173f7853570SHeiko Stübner 		{.nf = 67, .nr = 1, .no = 1},
174f7853570SHeiko Stübner 	};
175f7853570SHeiko Stübner 	int div_core_peri, div_aclk_core, cfg;
176f7853570SHeiko Stübner 
177f7853570SHeiko Stübner 	/*
178f7853570SHeiko Stübner 	 * We support two possible frequencies, the safe 600MHz
179f7853570SHeiko Stübner 	 * which will work with default pmic settings and will
180f7853570SHeiko Stübner 	 * be set in SPL to get away from the 24MHz default and
181f7853570SHeiko Stübner 	 * the maximum of 1.6Ghz, which boards can set if they
182f7853570SHeiko Stübner 	 * were able to get pmic support for it.
183f7853570SHeiko Stübner 	 */
184f7853570SHeiko Stübner 	switch (hz) {
185f7853570SHeiko Stübner 	case APLL_SAFE_HZ:
186f7853570SHeiko Stübner 		cfg = 0;
187f7853570SHeiko Stübner 		div_core_peri = 1;
188f7853570SHeiko Stübner 		div_aclk_core = 3;
189f7853570SHeiko Stübner 		break;
190f7853570SHeiko Stübner 	case APLL_HZ:
191f7853570SHeiko Stübner 		cfg = 1;
192f7853570SHeiko Stübner 		div_core_peri = 2;
193f7853570SHeiko Stübner 		div_aclk_core = 3;
194f7853570SHeiko Stübner 		break;
195f7853570SHeiko Stübner 	default:
196f7853570SHeiko Stübner 		debug("Unsupported ARMCLK frequency");
197f7853570SHeiko Stübner 		return -EINVAL;
198f7853570SHeiko Stübner 	}
199f7853570SHeiko Stübner 
200f7853570SHeiko Stübner 	/* pll enter slow-mode */
201f7853570SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK << APLL_MODE_SHIFT,
202f7853570SHeiko Stübner 		     APLL_MODE_SLOW << APLL_MODE_SHIFT);
203f7853570SHeiko Stübner 
204f7853570SHeiko Stübner 	rkclk_set_pll(cru, CLK_ARM, &apll_cfg[cfg], has_bwadj);
205f7853570SHeiko Stübner 
206f7853570SHeiko Stübner 	/* waiting for pll lock */
207f7853570SHeiko Stübner 	while (!(readl(&grf->soc_status0) & SOCSTS_APLL_LOCK))
208f7853570SHeiko Stübner 		udelay(1);
209f7853570SHeiko Stübner 
210f7853570SHeiko Stübner 	/* Set divider for peripherals attached to the cpu core. */
211f7853570SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[0],
212f7853570SHeiko Stübner 		CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT,
213f7853570SHeiko Stübner 		div_core_peri << CORE_PERI_DIV_SHIFT);
214f7853570SHeiko Stübner 
215f7853570SHeiko Stübner 	/* set up dependent divisor for aclk_core */
216f7853570SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[1],
217f7853570SHeiko Stübner 		CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT,
218f7853570SHeiko Stübner 		div_aclk_core << CORE_ACLK_DIV_SHIFT);
219f7853570SHeiko Stübner 
220f7853570SHeiko Stübner 	/* PLL enter normal-mode */
221f7853570SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK << APLL_MODE_SHIFT,
222f7853570SHeiko Stübner 		     APLL_MODE_NORMAL << APLL_MODE_SHIFT);
223f7853570SHeiko Stübner 
224f7853570SHeiko Stübner 	return hz;
225f7853570SHeiko Stübner }
226f7853570SHeiko Stübner 
227dcdd3278SHeiko Stübner /* Get pll rate by id */
228dcdd3278SHeiko Stübner static uint32_t rkclk_pll_get_rate(struct rk3188_cru *cru,
229dcdd3278SHeiko Stübner 				   enum rk_clk_id clk_id)
230dcdd3278SHeiko Stübner {
231dcdd3278SHeiko Stübner 	uint32_t nr, no, nf;
232dcdd3278SHeiko Stübner 	uint32_t con;
233dcdd3278SHeiko Stübner 	int pll_id = rk_pll_id(clk_id);
234dcdd3278SHeiko Stübner 	struct rk3188_pll *pll = &cru->pll[pll_id];
235dcdd3278SHeiko Stübner 	static u8 clk_shift[CLK_COUNT] = {
236dcdd3278SHeiko Stübner 		0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
237dcdd3278SHeiko Stübner 		GPLL_MODE_SHIFT
238dcdd3278SHeiko Stübner 	};
239dcdd3278SHeiko Stübner 	uint shift;
240dcdd3278SHeiko Stübner 
241dcdd3278SHeiko Stübner 	con = readl(&cru->cru_mode_con);
242dcdd3278SHeiko Stübner 	shift = clk_shift[clk_id];
243dcdd3278SHeiko Stübner 	switch ((con >> shift) & APLL_MODE_MASK) {
244dcdd3278SHeiko Stübner 	case APLL_MODE_SLOW:
245dcdd3278SHeiko Stübner 		return OSC_HZ;
246dcdd3278SHeiko Stübner 	case APLL_MODE_NORMAL:
247dcdd3278SHeiko Stübner 		/* normal mode */
248dcdd3278SHeiko Stübner 		con = readl(&pll->con0);
249dcdd3278SHeiko Stübner 		no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1;
250dcdd3278SHeiko Stübner 		nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1;
251dcdd3278SHeiko Stübner 		con = readl(&pll->con1);
252dcdd3278SHeiko Stübner 		nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1;
253dcdd3278SHeiko Stübner 
254dcdd3278SHeiko Stübner 		return (24 * nf / (nr * no)) * 1000000;
255dcdd3278SHeiko Stübner 	case APLL_MODE_DEEP:
256dcdd3278SHeiko Stübner 	default:
257dcdd3278SHeiko Stübner 		return 32768;
258dcdd3278SHeiko Stübner 	}
259dcdd3278SHeiko Stübner }
260dcdd3278SHeiko Stübner 
261dcdd3278SHeiko Stübner static ulong rockchip_mmc_get_clk(struct rk3188_cru *cru, uint gclk_rate,
262dcdd3278SHeiko Stübner 				  int periph)
263dcdd3278SHeiko Stübner {
264dcdd3278SHeiko Stübner 	uint div;
265dcdd3278SHeiko Stübner 	u32 con;
266dcdd3278SHeiko Stübner 
267dcdd3278SHeiko Stübner 	switch (periph) {
268dcdd3278SHeiko Stübner 	case HCLK_EMMC:
2697a25a63cSXu Ziyuan 	case SCLK_EMMC:
270dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[12]);
271dcdd3278SHeiko Stübner 		div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
272dcdd3278SHeiko Stübner 		break;
273dcdd3278SHeiko Stübner 	case HCLK_SDMMC:
2747a25a63cSXu Ziyuan 	case SCLK_SDMMC:
275dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[11]);
276dcdd3278SHeiko Stübner 		div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK;
277dcdd3278SHeiko Stübner 		break;
278dcdd3278SHeiko Stübner 	case HCLK_SDIO:
2797a25a63cSXu Ziyuan 	case SCLK_SDIO:
280dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[12]);
281dcdd3278SHeiko Stübner 		div = (con >> SDIO_DIV_SHIFT) & SDIO_DIV_MASK;
282dcdd3278SHeiko Stübner 		break;
283dcdd3278SHeiko Stübner 	default:
284dcdd3278SHeiko Stübner 		return -EINVAL;
285dcdd3278SHeiko Stübner 	}
286dcdd3278SHeiko Stübner 
2873a94d75dSKever Yang 	return DIV_TO_RATE(gclk_rate, div) / 2;
288dcdd3278SHeiko Stübner }
289dcdd3278SHeiko Stübner 
290dcdd3278SHeiko Stübner static ulong rockchip_mmc_set_clk(struct rk3188_cru *cru, uint gclk_rate,
291dcdd3278SHeiko Stübner 				  int  periph, uint freq)
292dcdd3278SHeiko Stübner {
293dcdd3278SHeiko Stübner 	int src_clk_div;
294dcdd3278SHeiko Stübner 
295dcdd3278SHeiko Stübner 	debug("%s: gclk_rate=%u\n", __func__, gclk_rate);
2963a94d75dSKever Yang 	/* mmc clock defaulg div 2 internal, need provide double in cru */
297217273cdSKever Yang 	src_clk_div = DIV_ROUND_UP(gclk_rate / 2, freq) - 1;
298dcdd3278SHeiko Stübner 	assert(src_clk_div <= 0x3f);
299dcdd3278SHeiko Stübner 
300dcdd3278SHeiko Stübner 	switch (periph) {
301dcdd3278SHeiko Stübner 	case HCLK_EMMC:
3027a25a63cSXu Ziyuan 	case SCLK_EMMC:
303dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[12],
304dcdd3278SHeiko Stübner 			     EMMC_DIV_MASK << EMMC_DIV_SHIFT,
305dcdd3278SHeiko Stübner 			     src_clk_div << EMMC_DIV_SHIFT);
306dcdd3278SHeiko Stübner 		break;
307dcdd3278SHeiko Stübner 	case HCLK_SDMMC:
3087a25a63cSXu Ziyuan 	case SCLK_SDMMC:
309dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[11],
310dcdd3278SHeiko Stübner 			     MMC0_DIV_MASK << MMC0_DIV_SHIFT,
311dcdd3278SHeiko Stübner 			     src_clk_div << MMC0_DIV_SHIFT);
312dcdd3278SHeiko Stübner 		break;
313dcdd3278SHeiko Stübner 	case HCLK_SDIO:
3147a25a63cSXu Ziyuan 	case SCLK_SDIO:
315dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[12],
316dcdd3278SHeiko Stübner 			     SDIO_DIV_MASK << SDIO_DIV_SHIFT,
317dcdd3278SHeiko Stübner 			     src_clk_div << SDIO_DIV_SHIFT);
318dcdd3278SHeiko Stübner 		break;
319dcdd3278SHeiko Stübner 	default:
320dcdd3278SHeiko Stübner 		return -EINVAL;
321dcdd3278SHeiko Stübner 	}
322dcdd3278SHeiko Stübner 
323dcdd3278SHeiko Stübner 	return rockchip_mmc_get_clk(cru, gclk_rate, periph);
324dcdd3278SHeiko Stübner }
325dcdd3278SHeiko Stübner 
326dcdd3278SHeiko Stübner static ulong rockchip_spi_get_clk(struct rk3188_cru *cru, uint gclk_rate,
327dcdd3278SHeiko Stübner 				  int periph)
328dcdd3278SHeiko Stübner {
329dcdd3278SHeiko Stübner 	uint div;
330dcdd3278SHeiko Stübner 	u32 con;
331dcdd3278SHeiko Stübner 
332dcdd3278SHeiko Stübner 	switch (periph) {
333dcdd3278SHeiko Stübner 	case SCLK_SPI0:
334dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[25]);
335dcdd3278SHeiko Stübner 		div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK;
336dcdd3278SHeiko Stübner 		break;
337dcdd3278SHeiko Stübner 	case SCLK_SPI1:
338dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[25]);
339dcdd3278SHeiko Stübner 		div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK;
340dcdd3278SHeiko Stübner 		break;
341dcdd3278SHeiko Stübner 	default:
342dcdd3278SHeiko Stübner 		return -EINVAL;
343dcdd3278SHeiko Stübner 	}
344dcdd3278SHeiko Stübner 
345dcdd3278SHeiko Stübner 	return DIV_TO_RATE(gclk_rate, div);
346dcdd3278SHeiko Stübner }
347dcdd3278SHeiko Stübner 
348dcdd3278SHeiko Stübner static ulong rockchip_spi_set_clk(struct rk3188_cru *cru, uint gclk_rate,
349dcdd3278SHeiko Stübner 				  int periph, uint freq)
350dcdd3278SHeiko Stübner {
351217273cdSKever Yang 	int src_clk_div = DIV_ROUND_UP(gclk_rate, freq) - 1;
352dcdd3278SHeiko Stübner 
353217273cdSKever Yang 	assert(src_clk_div < 128);
354dcdd3278SHeiko Stübner 	switch (periph) {
355dcdd3278SHeiko Stübner 	case SCLK_SPI0:
356dcdd3278SHeiko Stübner 		assert(src_clk_div <= SPI0_DIV_MASK);
357dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[25],
358dcdd3278SHeiko Stübner 			     SPI0_DIV_MASK << SPI0_DIV_SHIFT,
359dcdd3278SHeiko Stübner 			     src_clk_div << SPI0_DIV_SHIFT);
360dcdd3278SHeiko Stübner 		break;
361dcdd3278SHeiko Stübner 	case SCLK_SPI1:
362dcdd3278SHeiko Stübner 		assert(src_clk_div <= SPI1_DIV_MASK);
363dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[25],
364dcdd3278SHeiko Stübner 			     SPI1_DIV_MASK << SPI1_DIV_SHIFT,
365dcdd3278SHeiko Stübner 			     src_clk_div << SPI1_DIV_SHIFT);
366dcdd3278SHeiko Stübner 		break;
367dcdd3278SHeiko Stübner 	default:
368dcdd3278SHeiko Stübner 		return -EINVAL;
369dcdd3278SHeiko Stübner 	}
370dcdd3278SHeiko Stübner 
371dcdd3278SHeiko Stübner 	return rockchip_spi_get_clk(cru, gclk_rate, periph);
372dcdd3278SHeiko Stübner }
373dcdd3278SHeiko Stübner 
374dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD
375dcdd3278SHeiko Stübner static void rkclk_init(struct rk3188_cru *cru, struct rk3188_grf *grf,
376dcdd3278SHeiko Stübner 		       bool has_bwadj)
377dcdd3278SHeiko Stübner {
378dcdd3278SHeiko Stübner 	u32 aclk_div, hclk_div, pclk_div, h2p_div;
379dcdd3278SHeiko Stübner 
380dcdd3278SHeiko Stübner 	/* pll enter slow-mode */
381dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con,
382dcdd3278SHeiko Stübner 		     GPLL_MODE_MASK << GPLL_MODE_SHIFT |
383dcdd3278SHeiko Stübner 		     CPLL_MODE_MASK << CPLL_MODE_SHIFT,
384dcdd3278SHeiko Stübner 		     GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
385dcdd3278SHeiko Stübner 		     CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
386dcdd3278SHeiko Stübner 
387dcdd3278SHeiko Stübner 	/* init pll */
388dcdd3278SHeiko Stübner 	rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg, has_bwadj);
389dcdd3278SHeiko Stübner 	rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg, has_bwadj);
390dcdd3278SHeiko Stübner 
391dcdd3278SHeiko Stübner 	/* waiting for pll lock */
392dcdd3278SHeiko Stübner 	while ((readl(&grf->soc_status0) &
393dcdd3278SHeiko Stübner 			(SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) !=
394dcdd3278SHeiko Stübner 			(SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK))
395dcdd3278SHeiko Stübner 		udelay(1);
396dcdd3278SHeiko Stübner 
397dcdd3278SHeiko Stübner 	/*
398dcdd3278SHeiko Stübner 	 * cpu clock pll source selection and
399dcdd3278SHeiko Stübner 	 * reparent aclk_cpu_pre from apll to gpll
400dcdd3278SHeiko Stübner 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
401dcdd3278SHeiko Stübner 	 */
402217273cdSKever Yang 	aclk_div = DIV_ROUND_UP(GPLL_HZ, CPU_ACLK_HZ) - 1;
403217273cdSKever Yang 	assert((aclk_div + 1) * CPU_ACLK_HZ == GPLL_HZ && aclk_div <= 0x1f);
404dcdd3278SHeiko Stübner 
405dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[0],
406dcdd3278SHeiko Stübner 		     CPU_ACLK_PLL_MASK << CPU_ACLK_PLL_SHIFT |
407dcdd3278SHeiko Stübner 		     A9_CPU_DIV_MASK << A9_CPU_DIV_SHIFT,
408dcdd3278SHeiko Stübner 		     CPU_ACLK_PLL_SELECT_GPLL << CPU_ACLK_PLL_SHIFT |
409dcdd3278SHeiko Stübner 		     aclk_div << A9_CPU_DIV_SHIFT);
410dcdd3278SHeiko Stübner 
411dcdd3278SHeiko Stübner 	hclk_div = ilog2(CPU_ACLK_HZ / CPU_HCLK_HZ);
412dcdd3278SHeiko Stübner 	assert((1 << hclk_div) * CPU_HCLK_HZ == CPU_ACLK_HZ && hclk_div < 0x3);
413dcdd3278SHeiko Stübner 	pclk_div = ilog2(CPU_ACLK_HZ / CPU_PCLK_HZ);
414dcdd3278SHeiko Stübner 	assert((1 << pclk_div) * CPU_PCLK_HZ == CPU_ACLK_HZ && pclk_div < 0x4);
415dcdd3278SHeiko Stübner 	h2p_div = ilog2(CPU_HCLK_HZ / CPU_H2P_HZ);
416dcdd3278SHeiko Stübner 	assert((1 << h2p_div) * CPU_H2P_HZ == CPU_HCLK_HZ && pclk_div < 0x3);
417dcdd3278SHeiko Stübner 
418dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[1],
419dcdd3278SHeiko Stübner 		     AHB2APB_DIV_MASK << AHB2APB_DIV_SHIFT |
420dcdd3278SHeiko Stübner 		     CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT |
421dcdd3278SHeiko Stübner 		     CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT,
422dcdd3278SHeiko Stübner 		     h2p_div << AHB2APB_DIV_SHIFT |
423dcdd3278SHeiko Stübner 		     pclk_div << CPU_PCLK_DIV_SHIFT |
424dcdd3278SHeiko Stübner 		     hclk_div << CPU_HCLK_DIV_SHIFT);
425dcdd3278SHeiko Stübner 
426dcdd3278SHeiko Stübner 	/*
427dcdd3278SHeiko Stübner 	 * peri clock pll source selection and
428dcdd3278SHeiko Stübner 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
429dcdd3278SHeiko Stübner 	 */
430dcdd3278SHeiko Stübner 	aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
431dcdd3278SHeiko Stübner 	assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
432dcdd3278SHeiko Stübner 
433dcdd3278SHeiko Stübner 	hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ);
434dcdd3278SHeiko Stübner 	assert((1 << hclk_div) * PERI_HCLK_HZ ==
435dcdd3278SHeiko Stübner 		PERI_ACLK_HZ && (hclk_div < 0x4));
436dcdd3278SHeiko Stübner 
437dcdd3278SHeiko Stübner 	pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ);
438dcdd3278SHeiko Stübner 	assert((1 << pclk_div) * PERI_PCLK_HZ ==
439dcdd3278SHeiko Stübner 		PERI_ACLK_HZ && (pclk_div < 0x4));
440dcdd3278SHeiko Stübner 
441dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[10],
442dcdd3278SHeiko Stübner 		     PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT |
443dcdd3278SHeiko Stübner 		     PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT |
444dcdd3278SHeiko Stübner 		     PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT,
445dcdd3278SHeiko Stübner 		     PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT |
446dcdd3278SHeiko Stübner 		     pclk_div << PERI_PCLK_DIV_SHIFT |
447dcdd3278SHeiko Stübner 		     hclk_div << PERI_HCLK_DIV_SHIFT |
448dcdd3278SHeiko Stübner 		     aclk_div << PERI_ACLK_DIV_SHIFT);
449dcdd3278SHeiko Stübner 
450dcdd3278SHeiko Stübner 	/* PLL enter normal-mode */
451dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con,
452dcdd3278SHeiko Stübner 		     GPLL_MODE_MASK << GPLL_MODE_SHIFT |
453dcdd3278SHeiko Stübner 		     CPLL_MODE_MASK << CPLL_MODE_SHIFT,
454dcdd3278SHeiko Stübner 		     GPLL_MODE_NORMAL << GPLL_MODE_SHIFT |
455dcdd3278SHeiko Stübner 		     CPLL_MODE_NORMAL << CPLL_MODE_SHIFT);
456dcdd3278SHeiko Stübner 
457dcdd3278SHeiko Stübner 	rockchip_mmc_set_clk(cru, PERI_HCLK_HZ, HCLK_SDMMC, 16000000);
458dcdd3278SHeiko Stübner }
459dcdd3278SHeiko Stübner #endif
460dcdd3278SHeiko Stübner 
461dcdd3278SHeiko Stübner static ulong rk3188_clk_get_rate(struct clk *clk)
462dcdd3278SHeiko Stübner {
463dcdd3278SHeiko Stübner 	struct rk3188_clk_priv *priv = dev_get_priv(clk->dev);
464dcdd3278SHeiko Stübner 	ulong new_rate, gclk_rate;
465dcdd3278SHeiko Stübner 
466dcdd3278SHeiko Stübner 	gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
467dcdd3278SHeiko Stübner 	switch (clk->id) {
468dcdd3278SHeiko Stübner 	case 1 ... 4:
469dcdd3278SHeiko Stübner 		new_rate = rkclk_pll_get_rate(priv->cru, clk->id);
470dcdd3278SHeiko Stübner 		break;
471dcdd3278SHeiko Stübner 	case HCLK_EMMC:
472dcdd3278SHeiko Stübner 	case HCLK_SDMMC:
473dcdd3278SHeiko Stübner 	case HCLK_SDIO:
4747a25a63cSXu Ziyuan 	case SCLK_EMMC:
4757a25a63cSXu Ziyuan 	case SCLK_SDMMC:
4767a25a63cSXu Ziyuan 	case SCLK_SDIO:
477dcdd3278SHeiko Stübner 		new_rate = rockchip_mmc_get_clk(priv->cru, PERI_HCLK_HZ,
478dcdd3278SHeiko Stübner 						clk->id);
479dcdd3278SHeiko Stübner 		break;
480dcdd3278SHeiko Stübner 	case SCLK_SPI0:
481dcdd3278SHeiko Stübner 	case SCLK_SPI1:
482dcdd3278SHeiko Stübner 		new_rate = rockchip_spi_get_clk(priv->cru, PERI_PCLK_HZ,
483dcdd3278SHeiko Stübner 						clk->id);
484dcdd3278SHeiko Stübner 		break;
485dcdd3278SHeiko Stübner 	case PCLK_I2C0:
486dcdd3278SHeiko Stübner 	case PCLK_I2C1:
487dcdd3278SHeiko Stübner 	case PCLK_I2C2:
488dcdd3278SHeiko Stübner 	case PCLK_I2C3:
489dcdd3278SHeiko Stübner 	case PCLK_I2C4:
490dcdd3278SHeiko Stübner 		return gclk_rate;
491dcdd3278SHeiko Stübner 	default:
492dcdd3278SHeiko Stübner 		return -ENOENT;
493dcdd3278SHeiko Stübner 	}
494dcdd3278SHeiko Stübner 
495dcdd3278SHeiko Stübner 	return new_rate;
496dcdd3278SHeiko Stübner }
497dcdd3278SHeiko Stübner 
498dcdd3278SHeiko Stübner static ulong rk3188_clk_set_rate(struct clk *clk, ulong rate)
499dcdd3278SHeiko Stübner {
500dcdd3278SHeiko Stübner 	struct rk3188_clk_priv *priv = dev_get_priv(clk->dev);
501dcdd3278SHeiko Stübner 	struct rk3188_cru *cru = priv->cru;
502dcdd3278SHeiko Stübner 	ulong new_rate;
503dcdd3278SHeiko Stübner 
504dcdd3278SHeiko Stübner 	switch (clk->id) {
505f7853570SHeiko Stübner 	case PLL_APLL:
506f7853570SHeiko Stübner 		new_rate = rkclk_configure_cpu(priv->cru, priv->grf, rate,
507f7853570SHeiko Stübner 					       priv->has_bwadj);
508f7853570SHeiko Stübner 		break;
509dcdd3278SHeiko Stübner 	case CLK_DDR:
510dcdd3278SHeiko Stübner 		new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate,
511dcdd3278SHeiko Stübner 					       priv->has_bwadj);
512dcdd3278SHeiko Stübner 		break;
513dcdd3278SHeiko Stübner 	case HCLK_EMMC:
514dcdd3278SHeiko Stübner 	case HCLK_SDMMC:
515dcdd3278SHeiko Stübner 	case HCLK_SDIO:
5167a25a63cSXu Ziyuan 	case SCLK_EMMC:
5177a25a63cSXu Ziyuan 	case SCLK_SDMMC:
5187a25a63cSXu Ziyuan 	case SCLK_SDIO:
519dcdd3278SHeiko Stübner 		new_rate = rockchip_mmc_set_clk(cru, PERI_HCLK_HZ,
520dcdd3278SHeiko Stübner 						clk->id, rate);
521dcdd3278SHeiko Stübner 		break;
522dcdd3278SHeiko Stübner 	case SCLK_SPI0:
523dcdd3278SHeiko Stübner 	case SCLK_SPI1:
524dcdd3278SHeiko Stübner 		new_rate = rockchip_spi_set_clk(cru, PERI_PCLK_HZ,
525dcdd3278SHeiko Stübner 						clk->id, rate);
526dcdd3278SHeiko Stübner 		break;
527dcdd3278SHeiko Stübner 	default:
528dcdd3278SHeiko Stübner 		return -ENOENT;
529dcdd3278SHeiko Stübner 	}
530dcdd3278SHeiko Stübner 
531dcdd3278SHeiko Stübner 	return new_rate;
532dcdd3278SHeiko Stübner }
533dcdd3278SHeiko Stübner 
534dcdd3278SHeiko Stübner static struct clk_ops rk3188_clk_ops = {
535dcdd3278SHeiko Stübner 	.get_rate	= rk3188_clk_get_rate,
536dcdd3278SHeiko Stübner 	.set_rate	= rk3188_clk_set_rate,
537dcdd3278SHeiko Stübner };
538dcdd3278SHeiko Stübner 
539dcdd3278SHeiko Stübner static int rk3188_clk_ofdata_to_platdata(struct udevice *dev)
540dcdd3278SHeiko Stübner {
541dcdd3278SHeiko Stübner #if !CONFIG_IS_ENABLED(OF_PLATDATA)
542dcdd3278SHeiko Stübner 	struct rk3188_clk_priv *priv = dev_get_priv(dev);
543dcdd3278SHeiko Stübner 
544aca45647SKever Yang 	priv->cru = dev_read_addr_ptr(dev);
545dcdd3278SHeiko Stübner #endif
546dcdd3278SHeiko Stübner 
547dcdd3278SHeiko Stübner 	return 0;
548dcdd3278SHeiko Stübner }
549dcdd3278SHeiko Stübner 
550dcdd3278SHeiko Stübner static int rk3188_clk_probe(struct udevice *dev)
551dcdd3278SHeiko Stübner {
552dcdd3278SHeiko Stübner 	struct rk3188_clk_priv *priv = dev_get_priv(dev);
553dcdd3278SHeiko Stübner 	enum rk3188_clk_type type = dev_get_driver_data(dev);
554dcdd3278SHeiko Stübner 
555dcdd3278SHeiko Stübner 	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
556dcdd3278SHeiko Stübner 	if (IS_ERR(priv->grf))
557dcdd3278SHeiko Stübner 		return PTR_ERR(priv->grf);
558dcdd3278SHeiko Stübner 	priv->has_bwadj = (type == RK3188A_CRU) ? 1 : 0;
559dcdd3278SHeiko Stübner 
560dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD
561dcdd3278SHeiko Stübner #if CONFIG_IS_ENABLED(OF_PLATDATA)
562dcdd3278SHeiko Stübner 	struct rk3188_clk_plat *plat = dev_get_platdata(dev);
563dcdd3278SHeiko Stübner 
564dcdd3278SHeiko Stübner 	priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
565dcdd3278SHeiko Stübner #endif
566dcdd3278SHeiko Stübner 
567dcdd3278SHeiko Stübner 	rkclk_init(priv->cru, priv->grf, priv->has_bwadj);
568dcdd3278SHeiko Stübner #endif
569dcdd3278SHeiko Stübner 
570dcdd3278SHeiko Stübner 	return 0;
571dcdd3278SHeiko Stübner }
572dcdd3278SHeiko Stübner 
573dcdd3278SHeiko Stübner static int rk3188_clk_bind(struct udevice *dev)
574dcdd3278SHeiko Stübner {
575dcdd3278SHeiko Stübner 	int ret;
576f24e36daSKever Yang 	struct udevice *sys_child;
577f24e36daSKever Yang 	struct sysreset_reg *priv;
578dcdd3278SHeiko Stübner 
579dcdd3278SHeiko Stübner 	/* The reset driver does not have a device node, so bind it here */
580f24e36daSKever Yang 	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
581f24e36daSKever Yang 				 &sys_child);
582f24e36daSKever Yang 	if (ret) {
583f24e36daSKever Yang 		debug("Warning: No sysreset driver: ret=%d\n", ret);
584f24e36daSKever Yang 	} else {
585f24e36daSKever Yang 		priv = malloc(sizeof(struct sysreset_reg));
586f24e36daSKever Yang 		priv->glb_srst_fst_value = offsetof(struct rk3188_cru,
587f24e36daSKever Yang 						    cru_glb_srst_fst_value);
588f24e36daSKever Yang 		priv->glb_srst_snd_value = offsetof(struct rk3188_cru,
589f24e36daSKever Yang 						    cru_glb_srst_snd_value);
590f24e36daSKever Yang 		sys_child->priv = priv;
591f24e36daSKever Yang 	}
592dcdd3278SHeiko Stübner 
593538f67c3SElaine Zhang #if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP)
594538f67c3SElaine Zhang 	ret = offsetof(struct rk3188_cru, cru_softrst_con[0]);
595538f67c3SElaine Zhang 	ret = rockchip_reset_bind(dev, ret, 9);
596538f67c3SElaine Zhang 	if (ret)
597538f67c3SElaine Zhang 		debug("Warning: software reset driver bind faile\n");
598538f67c3SElaine Zhang #endif
599538f67c3SElaine Zhang 
600dcdd3278SHeiko Stübner 	return 0;
601dcdd3278SHeiko Stübner }
602dcdd3278SHeiko Stübner 
603dcdd3278SHeiko Stübner static const struct udevice_id rk3188_clk_ids[] = {
604dcdd3278SHeiko Stübner 	{ .compatible = "rockchip,rk3188-cru", .data = RK3188_CRU },
605dcdd3278SHeiko Stübner 	{ .compatible = "rockchip,rk3188a-cru", .data = RK3188A_CRU },
606dcdd3278SHeiko Stübner 	{ }
607dcdd3278SHeiko Stübner };
608dcdd3278SHeiko Stübner 
609dcdd3278SHeiko Stübner U_BOOT_DRIVER(rockchip_rk3188_cru) = {
610dcdd3278SHeiko Stübner 	.name			= "rockchip_rk3188_cru",
611dcdd3278SHeiko Stübner 	.id			= UCLASS_CLK,
612dcdd3278SHeiko Stübner 	.of_match		= rk3188_clk_ids,
613dcdd3278SHeiko Stübner 	.priv_auto_alloc_size	= sizeof(struct rk3188_clk_priv),
614dcdd3278SHeiko Stübner 	.platdata_auto_alloc_size = sizeof(struct rk3188_clk_plat),
615dcdd3278SHeiko Stübner 	.ops			= &rk3188_clk_ops,
616dcdd3278SHeiko Stübner 	.bind			= rk3188_clk_bind,
617dcdd3278SHeiko Stübner 	.ofdata_to_platdata	= rk3188_clk_ofdata_to_platdata,
618dcdd3278SHeiko Stübner 	.probe			= rk3188_clk_probe,
619dcdd3278SHeiko Stübner };
620