xref: /openbmc/u-boot/drivers/clk/rockchip/clk_rk3188.c (revision f785357073be1563b2f668a1b4bb5847bfaa96f9)
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 RATE_TO_DIV(input_rate, output_rate) \
75dcdd3278SHeiko Stübner 	((input_rate) / (output_rate) - 1);
76dcdd3278SHeiko Stübner 
77dcdd3278SHeiko Stübner #define DIV_TO_RATE(input_rate, div)	((input_rate) / ((div) + 1))
78dcdd3278SHeiko Stübner 
79dcdd3278SHeiko Stübner #define PLL_DIVISORS(hz, _nr, _no) {\
80dcdd3278SHeiko Stübner 	.nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\
81dcdd3278SHeiko Stübner 	_Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
82dcdd3278SHeiko Stübner 		       (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\
83dcdd3278SHeiko Stübner 		       "divisors on line " __stringify(__LINE__));
84dcdd3278SHeiko Stübner 
85dcdd3278SHeiko Stübner /* Keep divisors as low as possible to reduce jitter and power usage */
86dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD
87dcdd3278SHeiko Stübner static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
88dcdd3278SHeiko Stübner static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
89dcdd3278SHeiko Stübner #endif
90dcdd3278SHeiko Stübner 
91dcdd3278SHeiko Stübner static int rkclk_set_pll(struct rk3188_cru *cru, enum rk_clk_id clk_id,
92dcdd3278SHeiko Stübner 			 const struct pll_div *div, bool has_bwadj)
93dcdd3278SHeiko Stübner {
94dcdd3278SHeiko Stübner 	int pll_id = rk_pll_id(clk_id);
95dcdd3278SHeiko Stübner 	struct rk3188_pll *pll = &cru->pll[pll_id];
96dcdd3278SHeiko Stübner 	/* All PLLs have same VCO and output frequency range restrictions. */
97dcdd3278SHeiko Stübner 	uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
98dcdd3278SHeiko Stübner 	uint output_hz = vco_hz / div->no;
99dcdd3278SHeiko Stübner 
100dcdd3278SHeiko Stübner 	debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
101dcdd3278SHeiko Stübner 	      (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz);
102dcdd3278SHeiko Stübner 	assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
103dcdd3278SHeiko Stübner 	       output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ &&
104dcdd3278SHeiko Stübner 	       (div->no == 1 || !(div->no % 2)));
105dcdd3278SHeiko Stübner 
106dcdd3278SHeiko Stübner 	/* enter reset */
107dcdd3278SHeiko Stübner 	rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
108dcdd3278SHeiko Stübner 
109dcdd3278SHeiko Stübner 	rk_clrsetreg(&pll->con0,
110dcdd3278SHeiko Stübner 		     CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK,
111dcdd3278SHeiko Stübner 		     ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1));
112dcdd3278SHeiko Stübner 	rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1);
113dcdd3278SHeiko Stübner 
114dcdd3278SHeiko Stübner 	if (has_bwadj)
115dcdd3278SHeiko Stübner 		rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
116dcdd3278SHeiko Stübner 
117dcdd3278SHeiko Stübner 	udelay(10);
118dcdd3278SHeiko Stübner 
119dcdd3278SHeiko Stübner 	/* return from reset */
120dcdd3278SHeiko Stübner 	rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
121dcdd3278SHeiko Stübner 
122dcdd3278SHeiko Stübner 	return 0;
123dcdd3278SHeiko Stübner }
124dcdd3278SHeiko Stübner 
125dcdd3278SHeiko Stübner static int rkclk_configure_ddr(struct rk3188_cru *cru, struct rk3188_grf *grf,
126dcdd3278SHeiko Stübner 			       unsigned int hz, bool has_bwadj)
127dcdd3278SHeiko Stübner {
128dcdd3278SHeiko Stübner 	static const struct pll_div dpll_cfg[] = {
129dcdd3278SHeiko Stübner 		{.nf = 25, .nr = 2, .no = 1},
130dcdd3278SHeiko Stübner 		{.nf = 400, .nr = 9, .no = 2},
131dcdd3278SHeiko Stübner 		{.nf = 500, .nr = 9, .no = 2},
132dcdd3278SHeiko Stübner 		{.nf = 100, .nr = 3, .no = 1},
133dcdd3278SHeiko Stübner 	};
134dcdd3278SHeiko Stübner 	int cfg;
135dcdd3278SHeiko Stübner 
136dcdd3278SHeiko Stübner 	switch (hz) {
137dcdd3278SHeiko Stübner 	case 300000000:
138dcdd3278SHeiko Stübner 		cfg = 0;
139dcdd3278SHeiko Stübner 		break;
140dcdd3278SHeiko Stübner 	case 533000000:	/* actually 533.3P MHz */
141dcdd3278SHeiko Stübner 		cfg = 1;
142dcdd3278SHeiko Stübner 		break;
143dcdd3278SHeiko Stübner 	case 666000000:	/* actually 666.6P MHz */
144dcdd3278SHeiko Stübner 		cfg = 2;
145dcdd3278SHeiko Stübner 		break;
146dcdd3278SHeiko Stübner 	case 800000000:
147dcdd3278SHeiko Stübner 		cfg = 3;
148dcdd3278SHeiko Stübner 		break;
149dcdd3278SHeiko Stübner 	default:
150dcdd3278SHeiko Stübner 		debug("Unsupported SDRAM frequency");
151dcdd3278SHeiko Stübner 		return -EINVAL;
152dcdd3278SHeiko Stübner 	}
153dcdd3278SHeiko Stübner 
154dcdd3278SHeiko Stübner 	/* pll enter slow-mode */
155dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
156dcdd3278SHeiko Stübner 		     DPLL_MODE_SLOW << DPLL_MODE_SHIFT);
157dcdd3278SHeiko Stübner 
158dcdd3278SHeiko Stübner 	rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg], has_bwadj);
159dcdd3278SHeiko Stübner 
160dcdd3278SHeiko Stübner 	/* wait for pll lock */
161dcdd3278SHeiko Stübner 	while (!(readl(&grf->soc_status0) & SOCSTS_DPLL_LOCK))
162dcdd3278SHeiko Stübner 		udelay(1);
163dcdd3278SHeiko Stübner 
164dcdd3278SHeiko Stübner 	/* PLL enter normal-mode */
165dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
166dcdd3278SHeiko Stübner 		     DPLL_MODE_NORMAL << DPLL_MODE_SHIFT);
167dcdd3278SHeiko Stübner 
168dcdd3278SHeiko Stübner 	return 0;
169dcdd3278SHeiko Stübner }
170dcdd3278SHeiko Stübner 
171*f7853570SHeiko Stübner static int rkclk_configure_cpu(struct rk3188_cru *cru, struct rk3188_grf *grf,
172*f7853570SHeiko Stübner 			      unsigned int hz, bool has_bwadj)
173*f7853570SHeiko Stübner {
174*f7853570SHeiko Stübner 	static const struct pll_div apll_cfg[] = {
175*f7853570SHeiko Stübner 		{.nf = 50, .nr = 1, .no = 2},
176*f7853570SHeiko Stübner 		{.nf = 67, .nr = 1, .no = 1},
177*f7853570SHeiko Stübner 	};
178*f7853570SHeiko Stübner 	int div_core_peri, div_aclk_core, cfg;
179*f7853570SHeiko Stübner 
180*f7853570SHeiko Stübner 	/*
181*f7853570SHeiko Stübner 	 * We support two possible frequencies, the safe 600MHz
182*f7853570SHeiko Stübner 	 * which will work with default pmic settings and will
183*f7853570SHeiko Stübner 	 * be set in SPL to get away from the 24MHz default and
184*f7853570SHeiko Stübner 	 * the maximum of 1.6Ghz, which boards can set if they
185*f7853570SHeiko Stübner 	 * were able to get pmic support for it.
186*f7853570SHeiko Stübner 	 */
187*f7853570SHeiko Stübner 	switch (hz) {
188*f7853570SHeiko Stübner 	case APLL_SAFE_HZ:
189*f7853570SHeiko Stübner 		cfg = 0;
190*f7853570SHeiko Stübner 		div_core_peri = 1;
191*f7853570SHeiko Stübner 		div_aclk_core = 3;
192*f7853570SHeiko Stübner 		break;
193*f7853570SHeiko Stübner 	case APLL_HZ:
194*f7853570SHeiko Stübner 		cfg = 1;
195*f7853570SHeiko Stübner 		div_core_peri = 2;
196*f7853570SHeiko Stübner 		div_aclk_core = 3;
197*f7853570SHeiko Stübner 		break;
198*f7853570SHeiko Stübner 	default:
199*f7853570SHeiko Stübner 		debug("Unsupported ARMCLK frequency");
200*f7853570SHeiko Stübner 		return -EINVAL;
201*f7853570SHeiko Stübner 	}
202*f7853570SHeiko Stübner 
203*f7853570SHeiko Stübner 	/* pll enter slow-mode */
204*f7853570SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK << APLL_MODE_SHIFT,
205*f7853570SHeiko Stübner 		     APLL_MODE_SLOW << APLL_MODE_SHIFT);
206*f7853570SHeiko Stübner 
207*f7853570SHeiko Stübner 	rkclk_set_pll(cru, CLK_ARM, &apll_cfg[cfg], has_bwadj);
208*f7853570SHeiko Stübner 
209*f7853570SHeiko Stübner 	/* waiting for pll lock */
210*f7853570SHeiko Stübner 	while (!(readl(&grf->soc_status0) & SOCSTS_APLL_LOCK))
211*f7853570SHeiko Stübner 		udelay(1);
212*f7853570SHeiko Stübner 
213*f7853570SHeiko Stübner 	/* Set divider for peripherals attached to the cpu core. */
214*f7853570SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[0],
215*f7853570SHeiko Stübner 		CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT,
216*f7853570SHeiko Stübner 		div_core_peri << CORE_PERI_DIV_SHIFT);
217*f7853570SHeiko Stübner 
218*f7853570SHeiko Stübner 	/* set up dependent divisor for aclk_core */
219*f7853570SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[1],
220*f7853570SHeiko Stübner 		CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT,
221*f7853570SHeiko Stübner 		div_aclk_core << CORE_ACLK_DIV_SHIFT);
222*f7853570SHeiko Stübner 
223*f7853570SHeiko Stübner 	/* PLL enter normal-mode */
224*f7853570SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK << APLL_MODE_SHIFT,
225*f7853570SHeiko Stübner 		     APLL_MODE_NORMAL << APLL_MODE_SHIFT);
226*f7853570SHeiko Stübner 
227*f7853570SHeiko Stübner 	return hz;
228*f7853570SHeiko Stübner }
229*f7853570SHeiko Stübner 
230dcdd3278SHeiko Stübner /* Get pll rate by id */
231dcdd3278SHeiko Stübner static uint32_t rkclk_pll_get_rate(struct rk3188_cru *cru,
232dcdd3278SHeiko Stübner 				   enum rk_clk_id clk_id)
233dcdd3278SHeiko Stübner {
234dcdd3278SHeiko Stübner 	uint32_t nr, no, nf;
235dcdd3278SHeiko Stübner 	uint32_t con;
236dcdd3278SHeiko Stübner 	int pll_id = rk_pll_id(clk_id);
237dcdd3278SHeiko Stübner 	struct rk3188_pll *pll = &cru->pll[pll_id];
238dcdd3278SHeiko Stübner 	static u8 clk_shift[CLK_COUNT] = {
239dcdd3278SHeiko Stübner 		0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
240dcdd3278SHeiko Stübner 		GPLL_MODE_SHIFT
241dcdd3278SHeiko Stübner 	};
242dcdd3278SHeiko Stübner 	uint shift;
243dcdd3278SHeiko Stübner 
244dcdd3278SHeiko Stübner 	con = readl(&cru->cru_mode_con);
245dcdd3278SHeiko Stübner 	shift = clk_shift[clk_id];
246dcdd3278SHeiko Stübner 	switch ((con >> shift) & APLL_MODE_MASK) {
247dcdd3278SHeiko Stübner 	case APLL_MODE_SLOW:
248dcdd3278SHeiko Stübner 		return OSC_HZ;
249dcdd3278SHeiko Stübner 	case APLL_MODE_NORMAL:
250dcdd3278SHeiko Stübner 		/* normal mode */
251dcdd3278SHeiko Stübner 		con = readl(&pll->con0);
252dcdd3278SHeiko Stübner 		no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1;
253dcdd3278SHeiko Stübner 		nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1;
254dcdd3278SHeiko Stübner 		con = readl(&pll->con1);
255dcdd3278SHeiko Stübner 		nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1;
256dcdd3278SHeiko Stübner 
257dcdd3278SHeiko Stübner 		return (24 * nf / (nr * no)) * 1000000;
258dcdd3278SHeiko Stübner 	case APLL_MODE_DEEP:
259dcdd3278SHeiko Stübner 	default:
260dcdd3278SHeiko Stübner 		return 32768;
261dcdd3278SHeiko Stübner 	}
262dcdd3278SHeiko Stübner }
263dcdd3278SHeiko Stübner 
264dcdd3278SHeiko Stübner static ulong rockchip_mmc_get_clk(struct rk3188_cru *cru, uint gclk_rate,
265dcdd3278SHeiko Stübner 				  int periph)
266dcdd3278SHeiko Stübner {
267dcdd3278SHeiko Stübner 	uint div;
268dcdd3278SHeiko Stübner 	u32 con;
269dcdd3278SHeiko Stübner 
270dcdd3278SHeiko Stübner 	switch (periph) {
271dcdd3278SHeiko Stübner 	case HCLK_EMMC:
272dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[12]);
273dcdd3278SHeiko Stübner 		div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
274dcdd3278SHeiko Stübner 		break;
275dcdd3278SHeiko Stübner 	case HCLK_SDMMC:
276dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[11]);
277dcdd3278SHeiko Stübner 		div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK;
278dcdd3278SHeiko Stübner 		break;
279dcdd3278SHeiko Stübner 	case HCLK_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 
287dcdd3278SHeiko Stübner 	return DIV_TO_RATE(gclk_rate, div);
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);
296dcdd3278SHeiko Stübner 	src_clk_div = RATE_TO_DIV(gclk_rate, freq);
297dcdd3278SHeiko Stübner 	assert(src_clk_div <= 0x3f);
298dcdd3278SHeiko Stübner 
299dcdd3278SHeiko Stübner 	switch (periph) {
300dcdd3278SHeiko Stübner 	case HCLK_EMMC:
301dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[12],
302dcdd3278SHeiko Stübner 			     EMMC_DIV_MASK << EMMC_DIV_SHIFT,
303dcdd3278SHeiko Stübner 			     src_clk_div << EMMC_DIV_SHIFT);
304dcdd3278SHeiko Stübner 		break;
305dcdd3278SHeiko Stübner 	case HCLK_SDMMC:
306dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[11],
307dcdd3278SHeiko Stübner 			     MMC0_DIV_MASK << MMC0_DIV_SHIFT,
308dcdd3278SHeiko Stübner 			     src_clk_div << MMC0_DIV_SHIFT);
309dcdd3278SHeiko Stübner 		break;
310dcdd3278SHeiko Stübner 	case HCLK_SDIO:
311dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[12],
312dcdd3278SHeiko Stübner 			     SDIO_DIV_MASK << SDIO_DIV_SHIFT,
313dcdd3278SHeiko Stübner 			     src_clk_div << SDIO_DIV_SHIFT);
314dcdd3278SHeiko Stübner 		break;
315dcdd3278SHeiko Stübner 	default:
316dcdd3278SHeiko Stübner 		return -EINVAL;
317dcdd3278SHeiko Stübner 	}
318dcdd3278SHeiko Stübner 
319dcdd3278SHeiko Stübner 	return rockchip_mmc_get_clk(cru, gclk_rate, periph);
320dcdd3278SHeiko Stübner }
321dcdd3278SHeiko Stübner 
322dcdd3278SHeiko Stübner static ulong rockchip_spi_get_clk(struct rk3188_cru *cru, uint gclk_rate,
323dcdd3278SHeiko Stübner 				  int periph)
324dcdd3278SHeiko Stübner {
325dcdd3278SHeiko Stübner 	uint div;
326dcdd3278SHeiko Stübner 	u32 con;
327dcdd3278SHeiko Stübner 
328dcdd3278SHeiko Stübner 	switch (periph) {
329dcdd3278SHeiko Stübner 	case SCLK_SPI0:
330dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[25]);
331dcdd3278SHeiko Stübner 		div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK;
332dcdd3278SHeiko Stübner 		break;
333dcdd3278SHeiko Stübner 	case SCLK_SPI1:
334dcdd3278SHeiko Stübner 		con = readl(&cru->cru_clksel_con[25]);
335dcdd3278SHeiko Stübner 		div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK;
336dcdd3278SHeiko Stübner 		break;
337dcdd3278SHeiko Stübner 	default:
338dcdd3278SHeiko Stübner 		return -EINVAL;
339dcdd3278SHeiko Stübner 	}
340dcdd3278SHeiko Stübner 
341dcdd3278SHeiko Stübner 	return DIV_TO_RATE(gclk_rate, div);
342dcdd3278SHeiko Stübner }
343dcdd3278SHeiko Stübner 
344dcdd3278SHeiko Stübner static ulong rockchip_spi_set_clk(struct rk3188_cru *cru, uint gclk_rate,
345dcdd3278SHeiko Stübner 				  int periph, uint freq)
346dcdd3278SHeiko Stübner {
347dcdd3278SHeiko Stübner 	int src_clk_div = RATE_TO_DIV(gclk_rate, freq);
348dcdd3278SHeiko Stübner 
349dcdd3278SHeiko Stübner 	switch (periph) {
350dcdd3278SHeiko Stübner 	case SCLK_SPI0:
351dcdd3278SHeiko Stübner 		assert(src_clk_div <= SPI0_DIV_MASK);
352dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[25],
353dcdd3278SHeiko Stübner 			     SPI0_DIV_MASK << SPI0_DIV_SHIFT,
354dcdd3278SHeiko Stübner 			     src_clk_div << SPI0_DIV_SHIFT);
355dcdd3278SHeiko Stübner 		break;
356dcdd3278SHeiko Stübner 	case SCLK_SPI1:
357dcdd3278SHeiko Stübner 		assert(src_clk_div <= SPI1_DIV_MASK);
358dcdd3278SHeiko Stübner 		rk_clrsetreg(&cru->cru_clksel_con[25],
359dcdd3278SHeiko Stübner 			     SPI1_DIV_MASK << SPI1_DIV_SHIFT,
360dcdd3278SHeiko Stübner 			     src_clk_div << SPI1_DIV_SHIFT);
361dcdd3278SHeiko Stübner 		break;
362dcdd3278SHeiko Stübner 	default:
363dcdd3278SHeiko Stübner 		return -EINVAL;
364dcdd3278SHeiko Stübner 	}
365dcdd3278SHeiko Stübner 
366dcdd3278SHeiko Stübner 	return rockchip_spi_get_clk(cru, gclk_rate, periph);
367dcdd3278SHeiko Stübner }
368dcdd3278SHeiko Stübner 
369dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD
370dcdd3278SHeiko Stübner static void rkclk_init(struct rk3188_cru *cru, struct rk3188_grf *grf,
371dcdd3278SHeiko Stübner 		       bool has_bwadj)
372dcdd3278SHeiko Stübner {
373dcdd3278SHeiko Stübner 	u32 aclk_div, hclk_div, pclk_div, h2p_div;
374dcdd3278SHeiko Stübner 
375dcdd3278SHeiko Stübner 	/* pll enter slow-mode */
376dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con,
377dcdd3278SHeiko Stübner 		     GPLL_MODE_MASK << GPLL_MODE_SHIFT |
378dcdd3278SHeiko Stübner 		     CPLL_MODE_MASK << CPLL_MODE_SHIFT,
379dcdd3278SHeiko Stübner 		     GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
380dcdd3278SHeiko Stübner 		     CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
381dcdd3278SHeiko Stübner 
382dcdd3278SHeiko Stübner 	/* init pll */
383dcdd3278SHeiko Stübner 	rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg, has_bwadj);
384dcdd3278SHeiko Stübner 	rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg, has_bwadj);
385dcdd3278SHeiko Stübner 
386dcdd3278SHeiko Stübner 	/* waiting for pll lock */
387dcdd3278SHeiko Stübner 	while ((readl(&grf->soc_status0) &
388dcdd3278SHeiko Stübner 			(SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) !=
389dcdd3278SHeiko Stübner 			(SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK))
390dcdd3278SHeiko Stübner 		udelay(1);
391dcdd3278SHeiko Stübner 
392dcdd3278SHeiko Stübner 	/*
393dcdd3278SHeiko Stübner 	 * cpu clock pll source selection and
394dcdd3278SHeiko Stübner 	 * reparent aclk_cpu_pre from apll to gpll
395dcdd3278SHeiko Stübner 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
396dcdd3278SHeiko Stübner 	 */
397dcdd3278SHeiko Stübner 	aclk_div = RATE_TO_DIV(GPLL_HZ, CPU_ACLK_HZ);
398dcdd3278SHeiko Stübner 	assert((aclk_div + 1) * CPU_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
399dcdd3278SHeiko Stübner 
400dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[0],
401dcdd3278SHeiko Stübner 		     CPU_ACLK_PLL_MASK << CPU_ACLK_PLL_SHIFT |
402dcdd3278SHeiko Stübner 		     A9_CPU_DIV_MASK << A9_CPU_DIV_SHIFT,
403dcdd3278SHeiko Stübner 		     CPU_ACLK_PLL_SELECT_GPLL << CPU_ACLK_PLL_SHIFT |
404dcdd3278SHeiko Stübner 		     aclk_div << A9_CPU_DIV_SHIFT);
405dcdd3278SHeiko Stübner 
406dcdd3278SHeiko Stübner 	hclk_div = ilog2(CPU_ACLK_HZ / CPU_HCLK_HZ);
407dcdd3278SHeiko Stübner 	assert((1 << hclk_div) * CPU_HCLK_HZ == CPU_ACLK_HZ && hclk_div < 0x3);
408dcdd3278SHeiko Stübner 	pclk_div = ilog2(CPU_ACLK_HZ / CPU_PCLK_HZ);
409dcdd3278SHeiko Stübner 	assert((1 << pclk_div) * CPU_PCLK_HZ == CPU_ACLK_HZ && pclk_div < 0x4);
410dcdd3278SHeiko Stübner 	h2p_div = ilog2(CPU_HCLK_HZ / CPU_H2P_HZ);
411dcdd3278SHeiko Stübner 	assert((1 << h2p_div) * CPU_H2P_HZ == CPU_HCLK_HZ && pclk_div < 0x3);
412dcdd3278SHeiko Stübner 
413dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[1],
414dcdd3278SHeiko Stübner 		     AHB2APB_DIV_MASK << AHB2APB_DIV_SHIFT |
415dcdd3278SHeiko Stübner 		     CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT |
416dcdd3278SHeiko Stübner 		     CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT,
417dcdd3278SHeiko Stübner 		     h2p_div << AHB2APB_DIV_SHIFT |
418dcdd3278SHeiko Stübner 		     pclk_div << CPU_PCLK_DIV_SHIFT |
419dcdd3278SHeiko Stübner 		     hclk_div << CPU_HCLK_DIV_SHIFT);
420dcdd3278SHeiko Stübner 
421dcdd3278SHeiko Stübner 	/*
422dcdd3278SHeiko Stübner 	 * peri clock pll source selection and
423dcdd3278SHeiko Stübner 	 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
424dcdd3278SHeiko Stübner 	 */
425dcdd3278SHeiko Stübner 	aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
426dcdd3278SHeiko Stübner 	assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
427dcdd3278SHeiko Stübner 
428dcdd3278SHeiko Stübner 	hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ);
429dcdd3278SHeiko Stübner 	assert((1 << hclk_div) * PERI_HCLK_HZ ==
430dcdd3278SHeiko Stübner 		PERI_ACLK_HZ && (hclk_div < 0x4));
431dcdd3278SHeiko Stübner 
432dcdd3278SHeiko Stübner 	pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ);
433dcdd3278SHeiko Stübner 	assert((1 << pclk_div) * PERI_PCLK_HZ ==
434dcdd3278SHeiko Stübner 		PERI_ACLK_HZ && (pclk_div < 0x4));
435dcdd3278SHeiko Stübner 
436dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_clksel_con[10],
437dcdd3278SHeiko Stübner 		     PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT |
438dcdd3278SHeiko Stübner 		     PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT |
439dcdd3278SHeiko Stübner 		     PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT,
440dcdd3278SHeiko Stübner 		     PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT |
441dcdd3278SHeiko Stübner 		     pclk_div << PERI_PCLK_DIV_SHIFT |
442dcdd3278SHeiko Stübner 		     hclk_div << PERI_HCLK_DIV_SHIFT |
443dcdd3278SHeiko Stübner 		     aclk_div << PERI_ACLK_DIV_SHIFT);
444dcdd3278SHeiko Stübner 
445dcdd3278SHeiko Stübner 	/* PLL enter normal-mode */
446dcdd3278SHeiko Stübner 	rk_clrsetreg(&cru->cru_mode_con,
447dcdd3278SHeiko Stübner 		     GPLL_MODE_MASK << GPLL_MODE_SHIFT |
448dcdd3278SHeiko Stübner 		     CPLL_MODE_MASK << CPLL_MODE_SHIFT,
449dcdd3278SHeiko Stübner 		     GPLL_MODE_NORMAL << GPLL_MODE_SHIFT |
450dcdd3278SHeiko Stübner 		     CPLL_MODE_NORMAL << CPLL_MODE_SHIFT);
451dcdd3278SHeiko Stübner 
452dcdd3278SHeiko Stübner 	rockchip_mmc_set_clk(cru, PERI_HCLK_HZ, HCLK_SDMMC, 16000000);
453dcdd3278SHeiko Stübner }
454dcdd3278SHeiko Stübner #endif
455dcdd3278SHeiko Stübner 
456dcdd3278SHeiko Stübner static ulong rk3188_clk_get_rate(struct clk *clk)
457dcdd3278SHeiko Stübner {
458dcdd3278SHeiko Stübner 	struct rk3188_clk_priv *priv = dev_get_priv(clk->dev);
459dcdd3278SHeiko Stübner 	ulong new_rate, gclk_rate;
460dcdd3278SHeiko Stübner 
461dcdd3278SHeiko Stübner 	gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
462dcdd3278SHeiko Stübner 	switch (clk->id) {
463dcdd3278SHeiko Stübner 	case 1 ... 4:
464dcdd3278SHeiko Stübner 		new_rate = rkclk_pll_get_rate(priv->cru, clk->id);
465dcdd3278SHeiko Stübner 		break;
466dcdd3278SHeiko Stübner 	case HCLK_EMMC:
467dcdd3278SHeiko Stübner 	case HCLK_SDMMC:
468dcdd3278SHeiko Stübner 	case HCLK_SDIO:
469dcdd3278SHeiko Stübner 		new_rate = rockchip_mmc_get_clk(priv->cru, PERI_HCLK_HZ,
470dcdd3278SHeiko Stübner 						clk->id);
471dcdd3278SHeiko Stübner 		break;
472dcdd3278SHeiko Stübner 	case SCLK_SPI0:
473dcdd3278SHeiko Stübner 	case SCLK_SPI1:
474dcdd3278SHeiko Stübner 		new_rate = rockchip_spi_get_clk(priv->cru, PERI_PCLK_HZ,
475dcdd3278SHeiko Stübner 						clk->id);
476dcdd3278SHeiko Stübner 		break;
477dcdd3278SHeiko Stübner 	case PCLK_I2C0:
478dcdd3278SHeiko Stübner 	case PCLK_I2C1:
479dcdd3278SHeiko Stübner 	case PCLK_I2C2:
480dcdd3278SHeiko Stübner 	case PCLK_I2C3:
481dcdd3278SHeiko Stübner 	case PCLK_I2C4:
482dcdd3278SHeiko Stübner 		return gclk_rate;
483dcdd3278SHeiko Stübner 	default:
484dcdd3278SHeiko Stübner 		return -ENOENT;
485dcdd3278SHeiko Stübner 	}
486dcdd3278SHeiko Stübner 
487dcdd3278SHeiko Stübner 	return new_rate;
488dcdd3278SHeiko Stübner }
489dcdd3278SHeiko Stübner 
490dcdd3278SHeiko Stübner static ulong rk3188_clk_set_rate(struct clk *clk, ulong rate)
491dcdd3278SHeiko Stübner {
492dcdd3278SHeiko Stübner 	struct rk3188_clk_priv *priv = dev_get_priv(clk->dev);
493dcdd3278SHeiko Stübner 	struct rk3188_cru *cru = priv->cru;
494dcdd3278SHeiko Stübner 	ulong new_rate;
495dcdd3278SHeiko Stübner 
496dcdd3278SHeiko Stübner 	switch (clk->id) {
497*f7853570SHeiko Stübner 	case PLL_APLL:
498*f7853570SHeiko Stübner 		new_rate = rkclk_configure_cpu(priv->cru, priv->grf, rate,
499*f7853570SHeiko Stübner 					       priv->has_bwadj);
500*f7853570SHeiko Stübner 		break;
501dcdd3278SHeiko Stübner 	case CLK_DDR:
502dcdd3278SHeiko Stübner 		new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate,
503dcdd3278SHeiko Stübner 					       priv->has_bwadj);
504dcdd3278SHeiko Stübner 		break;
505dcdd3278SHeiko Stübner 	case HCLK_EMMC:
506dcdd3278SHeiko Stübner 	case HCLK_SDMMC:
507dcdd3278SHeiko Stübner 	case HCLK_SDIO:
508dcdd3278SHeiko Stübner 		new_rate = rockchip_mmc_set_clk(cru, PERI_HCLK_HZ,
509dcdd3278SHeiko Stübner 						clk->id, rate);
510dcdd3278SHeiko Stübner 		break;
511dcdd3278SHeiko Stübner 	case SCLK_SPI0:
512dcdd3278SHeiko Stübner 	case SCLK_SPI1:
513dcdd3278SHeiko Stübner 		new_rate = rockchip_spi_set_clk(cru, PERI_PCLK_HZ,
514dcdd3278SHeiko Stübner 						clk->id, rate);
515dcdd3278SHeiko Stübner 		break;
516dcdd3278SHeiko Stübner 	default:
517dcdd3278SHeiko Stübner 		return -ENOENT;
518dcdd3278SHeiko Stübner 	}
519dcdd3278SHeiko Stübner 
520dcdd3278SHeiko Stübner 	return new_rate;
521dcdd3278SHeiko Stübner }
522dcdd3278SHeiko Stübner 
523dcdd3278SHeiko Stübner static struct clk_ops rk3188_clk_ops = {
524dcdd3278SHeiko Stübner 	.get_rate	= rk3188_clk_get_rate,
525dcdd3278SHeiko Stübner 	.set_rate	= rk3188_clk_set_rate,
526dcdd3278SHeiko Stübner };
527dcdd3278SHeiko Stübner 
528dcdd3278SHeiko Stübner static int rk3188_clk_ofdata_to_platdata(struct udevice *dev)
529dcdd3278SHeiko Stübner {
530dcdd3278SHeiko Stübner #if !CONFIG_IS_ENABLED(OF_PLATDATA)
531dcdd3278SHeiko Stübner 	struct rk3188_clk_priv *priv = dev_get_priv(dev);
532dcdd3278SHeiko Stübner 
533dcdd3278SHeiko Stübner 	priv->cru = (struct rk3188_cru *)dev_get_addr(dev);
534dcdd3278SHeiko Stübner #endif
535dcdd3278SHeiko Stübner 
536dcdd3278SHeiko Stübner 	return 0;
537dcdd3278SHeiko Stübner }
538dcdd3278SHeiko Stübner 
539dcdd3278SHeiko Stübner static int rk3188_clk_probe(struct udevice *dev)
540dcdd3278SHeiko Stübner {
541dcdd3278SHeiko Stübner 	struct rk3188_clk_priv *priv = dev_get_priv(dev);
542dcdd3278SHeiko Stübner 	enum rk3188_clk_type type = dev_get_driver_data(dev);
543dcdd3278SHeiko Stübner 
544dcdd3278SHeiko Stübner 	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
545dcdd3278SHeiko Stübner 	if (IS_ERR(priv->grf))
546dcdd3278SHeiko Stübner 		return PTR_ERR(priv->grf);
547dcdd3278SHeiko Stübner 	priv->has_bwadj = (type == RK3188A_CRU) ? 1 : 0;
548dcdd3278SHeiko Stübner 
549dcdd3278SHeiko Stübner #ifdef CONFIG_SPL_BUILD
550dcdd3278SHeiko Stübner #if CONFIG_IS_ENABLED(OF_PLATDATA)
551dcdd3278SHeiko Stübner 	struct rk3188_clk_plat *plat = dev_get_platdata(dev);
552dcdd3278SHeiko Stübner 
553dcdd3278SHeiko Stübner 	priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
554dcdd3278SHeiko Stübner #endif
555dcdd3278SHeiko Stübner 
556dcdd3278SHeiko Stübner 	rkclk_init(priv->cru, priv->grf, priv->has_bwadj);
557dcdd3278SHeiko Stübner #endif
558dcdd3278SHeiko Stübner 
559dcdd3278SHeiko Stübner 	return 0;
560dcdd3278SHeiko Stübner }
561dcdd3278SHeiko Stübner 
562dcdd3278SHeiko Stübner static int rk3188_clk_bind(struct udevice *dev)
563dcdd3278SHeiko Stübner {
564dcdd3278SHeiko Stübner 	int ret;
565dcdd3278SHeiko Stübner 
566dcdd3278SHeiko Stübner 	/* The reset driver does not have a device node, so bind it here */
567dcdd3278SHeiko Stübner 	ret = device_bind_driver(gd->dm_root, "rk3188_sysreset", "reset", &dev);
568dcdd3278SHeiko Stübner 	if (ret)
569dcdd3278SHeiko Stübner 		debug("Warning: No rk3188 reset driver: ret=%d\n", ret);
570dcdd3278SHeiko Stübner 
571dcdd3278SHeiko Stübner 	return 0;
572dcdd3278SHeiko Stübner }
573dcdd3278SHeiko Stübner 
574dcdd3278SHeiko Stübner static const struct udevice_id rk3188_clk_ids[] = {
575dcdd3278SHeiko Stübner 	{ .compatible = "rockchip,rk3188-cru", .data = RK3188_CRU },
576dcdd3278SHeiko Stübner 	{ .compatible = "rockchip,rk3188a-cru", .data = RK3188A_CRU },
577dcdd3278SHeiko Stübner 	{ }
578dcdd3278SHeiko Stübner };
579dcdd3278SHeiko Stübner 
580dcdd3278SHeiko Stübner U_BOOT_DRIVER(rockchip_rk3188_cru) = {
581dcdd3278SHeiko Stübner 	.name			= "rockchip_rk3188_cru",
582dcdd3278SHeiko Stübner 	.id			= UCLASS_CLK,
583dcdd3278SHeiko Stübner 	.of_match		= rk3188_clk_ids,
584dcdd3278SHeiko Stübner 	.priv_auto_alloc_size	= sizeof(struct rk3188_clk_priv),
585dcdd3278SHeiko Stübner 	.platdata_auto_alloc_size = sizeof(struct rk3188_clk_plat),
586dcdd3278SHeiko Stübner 	.ops			= &rk3188_clk_ops,
587dcdd3278SHeiko Stübner 	.bind			= rk3188_clk_bind,
588dcdd3278SHeiko Stübner 	.ofdata_to_platdata	= rk3188_clk_ofdata_to_platdata,
589dcdd3278SHeiko Stübner 	.probe			= rk3188_clk_probe,
590dcdd3278SHeiko Stübner };
591