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>
8b6e52402SDavid Wu #include <bitfield.h>
9aff8795cSHeiko Stübner #include <clk-uclass.h>
10aff8795cSHeiko Stübner #include <dm.h>
11aff8795cSHeiko Stübner #include <dt-structs.h>
12aff8795cSHeiko Stübner #include <errno.h>
13aff8795cSHeiko Stübner #include <mapmem.h>
14aff8795cSHeiko Stübner #include <syscon.h>
15aff8795cSHeiko Stübner #include <asm/io.h>
16aff8795cSHeiko Stübner #include <asm/arch/clock.h>
17aff8795cSHeiko Stübner #include <asm/arch/cru_rk3288.h>
18aff8795cSHeiko Stübner #include <asm/arch/grf_rk3288.h>
19aff8795cSHeiko Stübner #include <asm/arch/hardware.h>
20aff8795cSHeiko Stübner #include <dt-bindings/clock/rk3288-cru.h>
21aff8795cSHeiko Stübner #include <dm/device-internal.h>
22aff8795cSHeiko Stübner #include <dm/lists.h>
23aff8795cSHeiko Stübner #include <dm/uclass-internal.h>
24abd0128eSHeiko Stübner #include <linux/log2.h>
25aff8795cSHeiko Stübner
26aff8795cSHeiko Stübner DECLARE_GLOBAL_DATA_PTR;
27aff8795cSHeiko Stübner
28aff8795cSHeiko Stübner struct rk3288_clk_plat {
29aff8795cSHeiko Stübner #if CONFIG_IS_ENABLED(OF_PLATDATA)
30aff8795cSHeiko Stübner struct dtd_rockchip_rk3288_cru dtd;
31aff8795cSHeiko Stübner #endif
32aff8795cSHeiko Stübner };
33aff8795cSHeiko Stübner
34aff8795cSHeiko Stübner struct pll_div {
357c7fff39SElaine Zhang ulong rate;
36aff8795cSHeiko Stübner u32 nr;
37aff8795cSHeiko Stübner u32 nf;
38aff8795cSHeiko Stübner u32 no;
397c7fff39SElaine Zhang u32 nb;
407c7fff39SElaine Zhang };
417c7fff39SElaine Zhang
427c7fff39SElaine Zhang #define RK3288_PLL_RATE(_rate, _nr, _nf, _no, _nb) \
437c7fff39SElaine Zhang { \
447c7fff39SElaine Zhang .rate = _rate##U, \
457c7fff39SElaine Zhang .nr = _nr, \
467c7fff39SElaine Zhang .nf = _nf, \
477c7fff39SElaine Zhang .no = _no, \
487c7fff39SElaine Zhang .nb = _nb, \
497c7fff39SElaine Zhang }
507c7fff39SElaine Zhang
517c7fff39SElaine Zhang static struct pll_div rk3288_pll_rates[] = {
527c7fff39SElaine Zhang /* _mhz, _nr, _nf, _no, _nb */
537c7fff39SElaine Zhang RK3288_PLL_RATE(1188000000, 1, 99, 2, 16),
547c7fff39SElaine Zhang RK3288_PLL_RATE(594000000, 1, 99, 4, 16),
557c7fff39SElaine Zhang RK3288_PLL_RATE(297000000, 1, 99, 8, 16),
56aff8795cSHeiko Stübner };
57aff8795cSHeiko Stübner
58ec0307efSElaine Zhang #ifndef CONFIG_SPL_BUILD
59ec0307efSElaine Zhang #define RK3288_CLK_DUMP(_id, _name, _iscru) \
60ec0307efSElaine Zhang { \
61ec0307efSElaine Zhang .id = _id, \
62ec0307efSElaine Zhang .name = _name, \
63ec0307efSElaine Zhang .is_cru = _iscru, \
64ec0307efSElaine Zhang }
65ec0307efSElaine Zhang
66ec0307efSElaine Zhang static const struct rk3288_clk_info clks_dump[] = {
67ec0307efSElaine Zhang RK3288_CLK_DUMP(PLL_APLL, "apll", true),
68ec0307efSElaine Zhang RK3288_CLK_DUMP(PLL_DPLL, "dpll", true),
69ec0307efSElaine Zhang RK3288_CLK_DUMP(PLL_CPLL, "cpll", true),
70ec0307efSElaine Zhang RK3288_CLK_DUMP(PLL_GPLL, "gpll", true),
71ec0307efSElaine Zhang RK3288_CLK_DUMP(PLL_NPLL, "npll", true),
72ec0307efSElaine Zhang RK3288_CLK_DUMP(ACLK_CPU, "aclk_bus", true),
73ec0307efSElaine Zhang };
74ec0307efSElaine Zhang #endif
75ec0307efSElaine Zhang
76aff8795cSHeiko Stübner enum {
77aff8795cSHeiko Stübner VCO_MAX_HZ = 2200U * 1000000,
78aff8795cSHeiko Stübner VCO_MIN_HZ = 440 * 1000000,
79aff8795cSHeiko Stübner OUTPUT_MAX_HZ = 2200U * 1000000,
80aff8795cSHeiko Stübner OUTPUT_MIN_HZ = 27500000,
81aff8795cSHeiko Stübner FREF_MAX_HZ = 2200U * 1000000,
82aff8795cSHeiko Stübner FREF_MIN_HZ = 269 * 1000,
83aff8795cSHeiko Stübner };
84aff8795cSHeiko Stübner
85aff8795cSHeiko Stübner enum {
86aff8795cSHeiko Stübner /* PLL CON0 */
87aff8795cSHeiko Stübner PLL_OD_MASK = 0x0f,
88aff8795cSHeiko Stübner
89aff8795cSHeiko Stübner /* PLL CON1 */
90aff8795cSHeiko Stübner PLL_NF_MASK = 0x1fff,
91aff8795cSHeiko Stübner
92aff8795cSHeiko Stübner /* PLL CON2 */
93aff8795cSHeiko Stübner PLL_BWADJ_MASK = 0x0fff,
94aff8795cSHeiko Stübner
95aff8795cSHeiko Stübner /* PLL CON3 */
96aff8795cSHeiko Stübner PLL_RESET_SHIFT = 5,
97aff8795cSHeiko Stübner
98aff8795cSHeiko Stübner /* CLKSEL0 */
99aff8795cSHeiko Stübner CORE_SEL_PLL_SHIFT = 15,
100b223c1aeSSimon Glass CORE_SEL_PLL_MASK = 1 << CORE_SEL_PLL_SHIFT,
101aff8795cSHeiko Stübner A17_DIV_SHIFT = 8,
102b223c1aeSSimon Glass A17_DIV_MASK = 0x1f << A17_DIV_SHIFT,
103aff8795cSHeiko Stübner MP_DIV_SHIFT = 4,
104b223c1aeSSimon Glass MP_DIV_MASK = 0xf << MP_DIV_SHIFT,
105aff8795cSHeiko Stübner M0_DIV_SHIFT = 0,
106b223c1aeSSimon Glass M0_DIV_MASK = 0xf << M0_DIV_SHIFT,
107aff8795cSHeiko Stübner
108aff8795cSHeiko Stübner /* CLKSEL1: pd bus clk pll sel: codec or general */
1098d52d662SWyon Bi PD_BUS_SEL_PLL_SHIFT = 15,
1108d52d662SWyon Bi PD_BUS_SEL_PLL_MASK = 1 << PD_BUS_SEL_PLL_SHIFT,
111aff8795cSHeiko Stübner PD_BUS_SEL_CPLL = 0,
112aff8795cSHeiko Stübner PD_BUS_SEL_GPLL,
113aff8795cSHeiko Stübner
114aff8795cSHeiko Stübner /* pd bus pclk div: pclk = pd_bus_aclk /(div + 1) */
115aff8795cSHeiko Stübner PD_BUS_PCLK_DIV_SHIFT = 12,
116b223c1aeSSimon Glass PD_BUS_PCLK_DIV_MASK = 7 << PD_BUS_PCLK_DIV_SHIFT,
117aff8795cSHeiko Stübner
118aff8795cSHeiko Stübner /* pd bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */
119aff8795cSHeiko Stübner PD_BUS_HCLK_DIV_SHIFT = 8,
120b223c1aeSSimon Glass PD_BUS_HCLK_DIV_MASK = 3 << PD_BUS_HCLK_DIV_SHIFT,
121aff8795cSHeiko Stübner
122aff8795cSHeiko Stübner /* pd bus aclk div: pd_bus_aclk = pd_bus_src_clk /(div0 * div1) */
123aff8795cSHeiko Stübner PD_BUS_ACLK_DIV0_SHIFT = 3,
124b223c1aeSSimon Glass PD_BUS_ACLK_DIV0_MASK = 0x1f << PD_BUS_ACLK_DIV0_SHIFT,
125aff8795cSHeiko Stübner PD_BUS_ACLK_DIV1_SHIFT = 0,
126b223c1aeSSimon Glass PD_BUS_ACLK_DIV1_MASK = 0x7 << PD_BUS_ACLK_DIV1_SHIFT,
127aff8795cSHeiko Stübner
128cb3c37fcSElaine Zhang /* CLKSEL2: tsadc */
129cb3c37fcSElaine Zhang CLK_TSADC_DIV_CON_SHIFT = 0,
130cb3c37fcSElaine Zhang CLK_TSADC_DIV_CON_MASK = GENMASK(5, 0),
131cb3c37fcSElaine Zhang CLK_TSADC_DIV_CON_WIDTH = 6,
132cb3c37fcSElaine Zhang
133aff8795cSHeiko Stübner /*
134aff8795cSHeiko Stübner * CLKSEL10
135aff8795cSHeiko Stübner * peripheral bus pclk div:
136aff8795cSHeiko Stübner * aclk_bus: pclk_bus = 1:1 or 2:1 or 4:1 or 8:1
137aff8795cSHeiko Stübner */
138aff8795cSHeiko Stübner PERI_SEL_PLL_SHIFT = 15,
139b223c1aeSSimon Glass PERI_SEL_PLL_MASK = 1 << PERI_SEL_PLL_SHIFT,
140aff8795cSHeiko Stübner PERI_SEL_CPLL = 0,
141aff8795cSHeiko Stübner PERI_SEL_GPLL,
142aff8795cSHeiko Stübner
143aff8795cSHeiko Stübner PERI_PCLK_DIV_SHIFT = 12,
144b223c1aeSSimon Glass PERI_PCLK_DIV_MASK = 3 << PERI_PCLK_DIV_SHIFT,
145aff8795cSHeiko Stübner
146aff8795cSHeiko Stübner /* peripheral bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */
147aff8795cSHeiko Stübner PERI_HCLK_DIV_SHIFT = 8,
148b223c1aeSSimon Glass PERI_HCLK_DIV_MASK = 3 << PERI_HCLK_DIV_SHIFT,
149aff8795cSHeiko Stübner
150aff8795cSHeiko Stübner /*
151aff8795cSHeiko Stübner * peripheral bus aclk div:
152aff8795cSHeiko Stübner * aclk_periph = periph_clk_src / (peri_aclk_div_con + 1)
153aff8795cSHeiko Stübner */
154aff8795cSHeiko Stübner PERI_ACLK_DIV_SHIFT = 0,
155b223c1aeSSimon Glass PERI_ACLK_DIV_MASK = 0x1f << PERI_ACLK_DIV_SHIFT,
156aff8795cSHeiko Stübner
157b6e52402SDavid Wu /*
158b6e52402SDavid Wu * CLKSEL24
159b6e52402SDavid Wu * saradc_div_con:
160b6e52402SDavid Wu * clk_saradc=24MHz/(saradc_div_con+1)
161b6e52402SDavid Wu */
162b6e52402SDavid Wu CLK_SARADC_DIV_CON_SHIFT = 8,
163b6e52402SDavid Wu CLK_SARADC_DIV_CON_MASK = GENMASK(15, 8),
164b6e52402SDavid Wu CLK_SARADC_DIV_CON_WIDTH = 8,
165b6e52402SDavid Wu
1662e8ea5b0SElaine Zhang /* CLKSEL26 */
1672e8ea5b0SElaine Zhang CLK_CRYPTO_DIV_CON_SHIFT = 6,
1682e8ea5b0SElaine Zhang CLK_CRYPTO_DIV_CON_MASK = GENMASK(7, 6),
1692e8ea5b0SElaine Zhang
170ced960d2SElaine Zhang /* CLKSEL33 */
171ced960d2SElaine Zhang PCLK_ALIVE_DIV_CON_SHIFT = 8,
172ced960d2SElaine Zhang PCLK_ALIVE_DIV_CON_MASK = 0x1f << PCLK_ALIVE_DIV_CON_SHIFT,
173ced960d2SElaine Zhang
174ddf875abSElaine Zhang /* CLKSEL39 */
175ddf875abSElaine Zhang ACLK_HEVC_SEL_PLL_SHIFT = 14,
176ddf875abSElaine Zhang ACLK_HEVC_SEL_PLL_MASK = 0x3 << ACLK_HEVC_SEL_PLL_SHIFT,
177ddf875abSElaine Zhang ACLK_HEVC_SEL_CPLL = 0,
178ddf875abSElaine Zhang ACLK_HEVC_SEL_GPLL,
179ddf875abSElaine Zhang ACLK_HEVC_DIV_CON_SHIFT = 8,
180ddf875abSElaine Zhang ACLK_HEVC_DIV_CON_MASK = 0x1f << ACLK_HEVC_DIV_CON_SHIFT,
181ddf875abSElaine Zhang
182ddf875abSElaine Zhang /* CLKSEL42 */
183ddf875abSElaine Zhang CLK_HEVC_CORE_SEL_PLL_SHIFT = 14,
184ddf875abSElaine Zhang CLK_HEVC_CORE_SEL_PLL_MASK = 0x3 << CLK_HEVC_CORE_SEL_PLL_SHIFT,
185ddf875abSElaine Zhang CLK_HEVC_CORE_SEL_CPLL = 0,
186ddf875abSElaine Zhang CLK_HEVC_CORE_SEL_GPLL,
187ddf875abSElaine Zhang CLK_HEVC_CORE_DIV_CON_SHIFT = 8,
188ddf875abSElaine Zhang CLK_HEVC_CORE_DIV_CON_MASK = 0x1f << CLK_HEVC_CORE_DIV_CON_SHIFT,
189ddf875abSElaine Zhang CLK_HEVC_CABAC_SEL_PLL_SHIFT = 6,
190ddf875abSElaine Zhang CLK_HEVC_CABAC_SEL_PLL_MASK = 0x3 << CLK_HEVC_CABAC_SEL_PLL_SHIFT,
191ddf875abSElaine Zhang CLK_HEVC_CABAC_SEL_CPLL = 0,
192ddf875abSElaine Zhang CLK_HEVC_CABAC_SEL_GPLL,
193ddf875abSElaine Zhang CLK_HEVC_CABAC_DIV_CON_SHIFT = 0,
194ddf875abSElaine Zhang CLK_HEVC_CABAC_DIV_CON_MASK = 0x1f << CLK_HEVC_CABAC_DIV_CON_SHIFT,
195ddf875abSElaine Zhang
1961ca40e22SElaine Zhang /* MISC */
1971ca40e22SElaine Zhang CLK_TEST_SRC_SEL_SHIFT = 8,
1981ca40e22SElaine Zhang CLK_TEST_SRC_SEL_MASK = 0xf << CLK_TEST_SRC_SEL_SHIFT,
1991ca40e22SElaine Zhang CLK_TEST_SRC_SEL_24M = 8,
2001ca40e22SElaine Zhang CLK_TEST_SRC_SEL_27M,
2011ca40e22SElaine Zhang CLK_TEST_SRC_SEL_32k,
2021ca40e22SElaine Zhang
203aff8795cSHeiko Stübner SOCSTS_DPLL_LOCK = 1 << 5,
204aff8795cSHeiko Stübner SOCSTS_APLL_LOCK = 1 << 6,
205aff8795cSHeiko Stübner SOCSTS_CPLL_LOCK = 1 << 7,
206aff8795cSHeiko Stübner SOCSTS_GPLL_LOCK = 1 << 8,
207aff8795cSHeiko Stübner SOCSTS_NPLL_LOCK = 1 << 9,
208aff8795cSHeiko Stübner };
209aff8795cSHeiko Stübner
210aff8795cSHeiko Stübner #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
211aff8795cSHeiko Stübner
212aff8795cSHeiko Stübner #define PLL_DIVISORS(hz, _nr, _no) {\
213aff8795cSHeiko Stübner .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\
214aff8795cSHeiko Stübner _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
215aff8795cSHeiko Stübner (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\
216aff8795cSHeiko Stübner "divisors on line " __stringify(__LINE__));
217aff8795cSHeiko Stübner
218aff8795cSHeiko Stübner /* Keep divisors as low as possible to reduce jitter and power usage */
219aff8795cSHeiko Stübner static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 1);
2207c7fff39SElaine Zhang static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 4);
221aff8795cSHeiko Stübner static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
222aff8795cSHeiko Stübner
rkclk_get_pll_config(ulong freq_hz)2237c7fff39SElaine Zhang struct pll_div *rkclk_get_pll_config(ulong freq_hz)
2247c7fff39SElaine Zhang {
2257c7fff39SElaine Zhang unsigned int rate_count = ARRAY_SIZE(rk3288_pll_rates);
2267c7fff39SElaine Zhang int i;
2277c7fff39SElaine Zhang
2287c7fff39SElaine Zhang for (i = 0; i < rate_count; i++) {
2297c7fff39SElaine Zhang if (freq_hz == rk3288_pll_rates[i].rate)
2307c7fff39SElaine Zhang return &rk3288_pll_rates[i];
2317c7fff39SElaine Zhang }
2327c7fff39SElaine Zhang return NULL;
2337c7fff39SElaine Zhang }
2347c7fff39SElaine Zhang
rkclk_set_pll(struct rk3288_cru * cru,enum rk_clk_id clk_id,const struct pll_div * div)235aff8795cSHeiko Stübner static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id,
236aff8795cSHeiko Stübner const struct pll_div *div)
237aff8795cSHeiko Stübner {
238aff8795cSHeiko Stübner int pll_id = rk_pll_id(clk_id);
239aff8795cSHeiko Stübner struct rk3288_pll *pll = &cru->pll[pll_id];
240aff8795cSHeiko Stübner /* All PLLs have same VCO and output frequency range restrictions. */
241aff8795cSHeiko Stübner uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
242aff8795cSHeiko Stübner uint output_hz = vco_hz / div->no;
243aff8795cSHeiko Stübner
244aff8795cSHeiko Stübner debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
245aff8795cSHeiko Stübner (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz);
246aff8795cSHeiko Stübner
247aff8795cSHeiko Stübner /* enter reset */
248aff8795cSHeiko Stübner rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
249aff8795cSHeiko Stübner
250b223c1aeSSimon Glass rk_clrsetreg(&pll->con0, CLKR_MASK | PLL_OD_MASK,
251aff8795cSHeiko Stübner ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1));
252aff8795cSHeiko Stübner rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1);
2530fd8dec7SNickey Yang
2547c7fff39SElaine Zhang /* adjust pll bw for better clock jitter */
2557c7fff39SElaine Zhang if (div->nb)
2567c7fff39SElaine Zhang rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, div->nb - 1);
2570fd8dec7SNickey Yang else
258aff8795cSHeiko Stübner rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
259aff8795cSHeiko Stübner
260aff8795cSHeiko Stübner udelay(10);
261aff8795cSHeiko Stübner
262aff8795cSHeiko Stübner /* return from reset */
263aff8795cSHeiko Stübner rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
264aff8795cSHeiko Stübner
265aff8795cSHeiko Stübner return 0;
266aff8795cSHeiko Stübner }
267aff8795cSHeiko Stübner
268b328c914SElaine Zhang /* Get pll rate by id */
rkclk_pll_get_rate(struct rk3288_cru * cru,enum rk_clk_id clk_id)269b328c914SElaine Zhang static u32 rkclk_pll_get_rate(struct rk3288_cru *cru,
270b328c914SElaine Zhang enum rk_clk_id clk_id)
271b328c914SElaine Zhang {
272b328c914SElaine Zhang u32 nr, no, nf;
273b328c914SElaine Zhang u32 con;
274b328c914SElaine Zhang int pll_id = rk_pll_id(clk_id);
275b328c914SElaine Zhang struct rk3288_pll *pll = &cru->pll[pll_id];
276b328c914SElaine Zhang static u8 clk_shift[CLK_COUNT] = {
277b328c914SElaine Zhang 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
278b328c914SElaine Zhang GPLL_MODE_SHIFT, NPLL_MODE_SHIFT
279b328c914SElaine Zhang };
280b328c914SElaine Zhang uint shift;
281b328c914SElaine Zhang
282b328c914SElaine Zhang con = readl(&cru->cru_mode_con);
283b328c914SElaine Zhang shift = clk_shift[clk_id];
284b328c914SElaine Zhang switch ((con >> shift) & CRU_MODE_MASK) {
285b328c914SElaine Zhang case APLL_MODE_SLOW:
286b328c914SElaine Zhang return OSC_HZ;
287b328c914SElaine Zhang case APLL_MODE_NORMAL:
288b328c914SElaine Zhang /* normal mode */
289b328c914SElaine Zhang con = readl(&pll->con0);
290b328c914SElaine Zhang no = ((con & CLKOD_MASK) >> CLKOD_SHIFT) + 1;
291b328c914SElaine Zhang nr = ((con & CLKR_MASK) >> CLKR_SHIFT) + 1;
292b328c914SElaine Zhang con = readl(&pll->con1);
293b328c914SElaine Zhang nf = ((con & CLKF_MASK) >> CLKF_SHIFT) + 1;
294b328c914SElaine Zhang
295b328c914SElaine Zhang return (24 * nf / (nr * no)) * 1000000;
296b328c914SElaine Zhang case APLL_MODE_DEEP:
297b328c914SElaine Zhang default:
298b328c914SElaine Zhang return 32768;
299b328c914SElaine Zhang }
300b328c914SElaine Zhang }
301b328c914SElaine Zhang
rkclk_configure_ddr(struct rk3288_cru * cru,struct rk3288_grf * grf,unsigned int hz)302aff8795cSHeiko Stübner static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf,
303aff8795cSHeiko Stübner unsigned int hz)
304aff8795cSHeiko Stübner {
305aff8795cSHeiko Stübner static const struct pll_div dpll_cfg[] = {
306aff8795cSHeiko Stübner {.nf = 25, .nr = 2, .no = 1},
307aff8795cSHeiko Stübner {.nf = 400, .nr = 9, .no = 2},
308aff8795cSHeiko Stübner {.nf = 500, .nr = 9, .no = 2},
309aff8795cSHeiko Stübner {.nf = 100, .nr = 3, .no = 1},
310aff8795cSHeiko Stübner };
311aff8795cSHeiko Stübner int cfg;
312aff8795cSHeiko Stübner
313aff8795cSHeiko Stübner switch (hz) {
314aff8795cSHeiko Stübner case 300000000:
315aff8795cSHeiko Stübner cfg = 0;
316aff8795cSHeiko Stübner break;
317aff8795cSHeiko Stübner case 533000000: /* actually 533.3P MHz */
318aff8795cSHeiko Stübner cfg = 1;
319aff8795cSHeiko Stübner break;
320aff8795cSHeiko Stübner case 666000000: /* actually 666.6P MHz */
321aff8795cSHeiko Stübner cfg = 2;
322aff8795cSHeiko Stübner break;
323aff8795cSHeiko Stübner case 800000000:
324aff8795cSHeiko Stübner cfg = 3;
325aff8795cSHeiko Stübner break;
326aff8795cSHeiko Stübner default:
327aff8795cSHeiko Stübner debug("Unsupported SDRAM frequency");
328aff8795cSHeiko Stübner return -EINVAL;
329aff8795cSHeiko Stübner }
330aff8795cSHeiko Stübner
331aff8795cSHeiko Stübner /* pll enter slow-mode */
332b223c1aeSSimon Glass rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK,
333aff8795cSHeiko Stübner DPLL_MODE_SLOW << DPLL_MODE_SHIFT);
334aff8795cSHeiko Stübner
335aff8795cSHeiko Stübner rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg]);
336aff8795cSHeiko Stübner
337aff8795cSHeiko Stübner /* wait for pll lock */
338aff8795cSHeiko Stübner while (!(readl(&grf->soc_status[1]) & SOCSTS_DPLL_LOCK))
339aff8795cSHeiko Stübner udelay(1);
340aff8795cSHeiko Stübner
341aff8795cSHeiko Stübner /* PLL enter normal-mode */
342b223c1aeSSimon Glass rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK,
343aff8795cSHeiko Stübner DPLL_MODE_NORMAL << DPLL_MODE_SHIFT);
344aff8795cSHeiko Stübner
345aff8795cSHeiko Stübner return 0;
346aff8795cSHeiko Stübner }
347aff8795cSHeiko Stübner
348aff8795cSHeiko Stübner #ifndef CONFIG_SPL_BUILD
349aff8795cSHeiko Stübner #define VCO_MAX_KHZ 2200000
350aff8795cSHeiko Stübner #define VCO_MIN_KHZ 440000
351aff8795cSHeiko Stübner #define FREF_MAX_KHZ 2200000
352aff8795cSHeiko Stübner #define FREF_MIN_KHZ 269
3537c7fff39SElaine Zhang #define PLL_LIMIT_FREQ 594000000
354aff8795cSHeiko Stübner
pll_para_config(ulong freq_hz,struct pll_div * div,uint * ext_div)355aff8795cSHeiko Stübner static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div)
356aff8795cSHeiko Stübner {
3577c7fff39SElaine Zhang struct pll_div *best_div = NULL;
358aff8795cSHeiko Stübner uint ref_khz = OSC_HZ / 1000, nr, nf = 0;
359aff8795cSHeiko Stübner uint fref_khz;
360aff8795cSHeiko Stübner uint diff_khz, best_diff_khz;
361aff8795cSHeiko Stübner const uint max_nr = 1 << 6, max_nf = 1 << 12, max_no = 1 << 4;
362aff8795cSHeiko Stübner uint vco_khz;
363aff8795cSHeiko Stübner uint no = 1;
364aff8795cSHeiko Stübner uint freq_khz = freq_hz / 1000;
365aff8795cSHeiko Stübner
366aff8795cSHeiko Stübner if (!freq_hz) {
367aff8795cSHeiko Stübner printf("%s: the frequency can not be 0 Hz\n", __func__);
368aff8795cSHeiko Stübner return -EINVAL;
369aff8795cSHeiko Stübner }
370aff8795cSHeiko Stübner
371aff8795cSHeiko Stübner no = DIV_ROUND_UP(VCO_MIN_KHZ, freq_khz);
372aff8795cSHeiko Stübner if (ext_div) {
3733afe5397SElaine Zhang *ext_div = DIV_ROUND_UP(PLL_LIMIT_FREQ, freq_hz);
374aff8795cSHeiko Stübner no = DIV_ROUND_UP(no, *ext_div);
375aff8795cSHeiko Stübner }
376aff8795cSHeiko Stübner
3777c7fff39SElaine Zhang best_div = rkclk_get_pll_config(freq_hz * (*ext_div));
3787c7fff39SElaine Zhang if (best_div) {
3797c7fff39SElaine Zhang div->nr = best_div->nr;
3807c7fff39SElaine Zhang div->nf = best_div->nf;
3817c7fff39SElaine Zhang div->no = best_div->no;
3827c7fff39SElaine Zhang div->nb = best_div->nb;
3837c7fff39SElaine Zhang return 0;
3847c7fff39SElaine Zhang }
3857c7fff39SElaine Zhang
386aff8795cSHeiko Stübner /* only even divisors (and 1) are supported */
387aff8795cSHeiko Stübner if (no > 1)
388aff8795cSHeiko Stübner no = DIV_ROUND_UP(no, 2) * 2;
389aff8795cSHeiko Stübner
390aff8795cSHeiko Stübner vco_khz = freq_khz * no;
391aff8795cSHeiko Stübner if (ext_div)
392aff8795cSHeiko Stübner vco_khz *= *ext_div;
393aff8795cSHeiko Stübner
394aff8795cSHeiko Stübner if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ || no > max_no) {
395aff8795cSHeiko Stübner printf("%s: Cannot find out a supported VCO for Frequency (%luHz).\n",
396aff8795cSHeiko Stübner __func__, freq_hz);
397aff8795cSHeiko Stübner return -1;
398aff8795cSHeiko Stübner }
399aff8795cSHeiko Stübner
400aff8795cSHeiko Stübner div->no = no;
401aff8795cSHeiko Stübner
402aff8795cSHeiko Stübner best_diff_khz = vco_khz;
403aff8795cSHeiko Stübner for (nr = 1; nr < max_nr && best_diff_khz; nr++) {
404aff8795cSHeiko Stübner fref_khz = ref_khz / nr;
405aff8795cSHeiko Stübner if (fref_khz < FREF_MIN_KHZ)
406aff8795cSHeiko Stübner break;
407aff8795cSHeiko Stübner if (fref_khz > FREF_MAX_KHZ)
408aff8795cSHeiko Stübner continue;
409aff8795cSHeiko Stübner
410aff8795cSHeiko Stübner nf = vco_khz / fref_khz;
411aff8795cSHeiko Stübner if (nf >= max_nf)
412aff8795cSHeiko Stübner continue;
413aff8795cSHeiko Stübner diff_khz = vco_khz - nf * fref_khz;
414aff8795cSHeiko Stübner if (nf + 1 < max_nf && diff_khz > fref_khz / 2) {
415aff8795cSHeiko Stübner nf++;
416aff8795cSHeiko Stübner diff_khz = fref_khz - diff_khz;
417aff8795cSHeiko Stübner }
418aff8795cSHeiko Stübner
419aff8795cSHeiko Stübner if (diff_khz >= best_diff_khz)
420aff8795cSHeiko Stübner continue;
421aff8795cSHeiko Stübner
422aff8795cSHeiko Stübner best_diff_khz = diff_khz;
423aff8795cSHeiko Stübner div->nr = nr;
424aff8795cSHeiko Stübner div->nf = nf;
425aff8795cSHeiko Stübner }
426aff8795cSHeiko Stübner
427aff8795cSHeiko Stübner if (best_diff_khz > 4 * 1000) {
428aff8795cSHeiko Stübner printf("%s: Failed to match output frequency %lu, difference is %u Hz, exceed 4MHZ\n",
429aff8795cSHeiko Stübner __func__, freq_hz, best_diff_khz * 1000);
430aff8795cSHeiko Stübner return -EINVAL;
431aff8795cSHeiko Stübner }
432aff8795cSHeiko Stübner
433aff8795cSHeiko Stübner return 0;
434aff8795cSHeiko Stübner }
435aff8795cSHeiko Stübner
rockchip_mac_set_clk(struct rk3288_cru * cru,uint freq)436b0b68708SDavid Wu static int rockchip_mac_set_clk(struct rk3288_cru *cru, uint freq)
437aff8795cSHeiko Stübner {
438b0b68708SDavid Wu ulong ret;
439aff8795cSHeiko Stübner
440b0b68708SDavid Wu /*
441b0b68708SDavid Wu * The gmac clock can be derived either from an external clock
442b0b68708SDavid Wu * or can be generated from internally by a divider from SCLK_MAC.
443b0b68708SDavid Wu */
444b0b68708SDavid Wu if (readl(&cru->cru_clksel_con[21]) & RMII_EXTCLK_MASK) {
445b0b68708SDavid Wu /* An external clock will always generate the right rate... */
446b0b68708SDavid Wu ret = freq;
447b0b68708SDavid Wu } else {
448b0b68708SDavid Wu u32 con = readl(&cru->cru_clksel_con[21]);
449b0b68708SDavid Wu ulong pll_rate;
450b0b68708SDavid Wu u8 div;
451b0b68708SDavid Wu
452b0b68708SDavid Wu if (((con >> EMAC_PLL_SHIFT) & EMAC_PLL_MASK) ==
453b0b68708SDavid Wu EMAC_PLL_SELECT_GENERAL)
454b0b68708SDavid Wu pll_rate = GPLL_HZ;
455b0b68708SDavid Wu else if (((con >> EMAC_PLL_SHIFT) & EMAC_PLL_MASK) ==
456b0b68708SDavid Wu EMAC_PLL_SELECT_CODEC)
457b0b68708SDavid Wu pll_rate = CPLL_HZ;
458b0b68708SDavid Wu else
459b0b68708SDavid Wu pll_rate = NPLL_HZ;
460b0b68708SDavid Wu
461b0b68708SDavid Wu div = DIV_ROUND_UP(pll_rate, freq) - 1;
462b0b68708SDavid Wu if (div <= 0x1f)
463b0b68708SDavid Wu rk_clrsetreg(&cru->cru_clksel_con[21], MAC_DIV_CON_MASK,
464b0b68708SDavid Wu div << MAC_DIV_CON_SHIFT);
465b0b68708SDavid Wu else
466b0b68708SDavid Wu debug("Unsupported div for gmac:%d\n", div);
467b0b68708SDavid Wu
468b0b68708SDavid Wu return DIV_TO_RATE(pll_rate, div);
469b0b68708SDavid Wu }
470b0b68708SDavid Wu
471b0b68708SDavid Wu return ret;
472aff8795cSHeiko Stübner }
473aff8795cSHeiko Stübner
rockchip_vop_set_clk(struct rk3288_cru * cru,struct rk3288_grf * grf,int periph,unsigned int rate_hz)474aff8795cSHeiko Stübner static int rockchip_vop_set_clk(struct rk3288_cru *cru, struct rk3288_grf *grf,
475aff8795cSHeiko Stübner int periph, unsigned int rate_hz)
476aff8795cSHeiko Stübner {
477b328c914SElaine Zhang struct pll_div cpll_config = {0};
478b328c914SElaine Zhang u32 lcdc_div, parent;
479aff8795cSHeiko Stübner int ret;
48055611901SElaine Zhang unsigned int gpll_rate, npll_rate;
481aff8795cSHeiko Stübner
482b328c914SElaine Zhang gpll_rate = rkclk_pll_get_rate(cru, CLK_GENERAL);
483b328c914SElaine Zhang npll_rate = rkclk_pll_get_rate(cru, CLK_NEW);
484b328c914SElaine Zhang
485b328c914SElaine Zhang /* vop dclk source clk: cpll,dclk_div: 1 */
486b328c914SElaine Zhang switch (periph) {
487b328c914SElaine Zhang case DCLK_VOP0:
4885a616fcfSElaine Zhang ret = (readl(&cru->cru_clksel_con[27]) & DCLK_VOP0_PLL_MASK) >>
489b328c914SElaine Zhang DCLK_VOP0_PLL_SHIFT;
490b328c914SElaine Zhang if (ret == DCLK_VOP0_SELECT_CPLL) {
491b328c914SElaine Zhang ret = pll_para_config(rate_hz, &cpll_config, &lcdc_div);
492aff8795cSHeiko Stübner if (ret)
493aff8795cSHeiko Stübner return ret;
494aff8795cSHeiko Stübner
495b328c914SElaine Zhang rk_clrsetreg(&cru->cru_mode_con, CPLL_MODE_MASK,
496b328c914SElaine Zhang CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
497ed2a4091SElaine Zhang rkclk_set_pll(cru, CLK_CODEC, &cpll_config);
498aff8795cSHeiko Stübner
499aff8795cSHeiko Stübner /* waiting for pll lock */
500aff8795cSHeiko Stübner while (1) {
501b328c914SElaine Zhang if (readl(&grf->soc_status[1]) &
502b328c914SElaine Zhang SOCSTS_CPLL_LOCK)
503aff8795cSHeiko Stübner break;
504aff8795cSHeiko Stübner udelay(1);
505aff8795cSHeiko Stübner }
506aff8795cSHeiko Stübner
507b328c914SElaine Zhang rk_clrsetreg(&cru->cru_mode_con, CPLL_MODE_MASK,
508b328c914SElaine Zhang CPLL_MODE_NORMAL << CPLL_MODE_SHIFT);
509b328c914SElaine Zhang parent = DCLK_VOP0_SELECT_CPLL;
510b328c914SElaine Zhang } else if (ret == DCLK_VOP0_SELECT_GPLL) {
511b328c914SElaine Zhang parent = DCLK_VOP0_SELECT_GPLL;
512b328c914SElaine Zhang lcdc_div = DIV_ROUND_UP(gpll_rate,
513b328c914SElaine Zhang rate_hz);
514b328c914SElaine Zhang } else {
515b328c914SElaine Zhang parent = DCLK_VOP0_SELECT_NPLL;
516b328c914SElaine Zhang lcdc_div = DIV_ROUND_UP(npll_rate,
517b328c914SElaine Zhang rate_hz);
518b328c914SElaine Zhang }
519b328c914SElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[27],
520b328c914SElaine Zhang DCLK_VOP0_DIV_MASK | DCLK_VOP0_PLL_MASK,
521b328c914SElaine Zhang ((lcdc_div - 1) << DCLK_VOP0_DIV_SHIFT) |
522b328c914SElaine Zhang (parent << DCLK_VOP0_PLL_SHIFT));
523aff8795cSHeiko Stübner break;
524aff8795cSHeiko Stübner case DCLK_VOP1:
5255a616fcfSElaine Zhang ret = (readl(&cru->cru_clksel_con[29]) & DCLK_VOP1_PLL_MASK) >>
526b328c914SElaine Zhang DCLK_VOP1_PLL_SHIFT;
527b328c914SElaine Zhang if (ret == DCLK_VOP1_SELECT_CPLL) {
528b328c914SElaine Zhang ret = pll_para_config(rate_hz, &cpll_config, &lcdc_div);
529b328c914SElaine Zhang if (ret)
530b328c914SElaine Zhang return ret;
531b328c914SElaine Zhang
532b328c914SElaine Zhang rk_clrsetreg(&cru->cru_mode_con, CPLL_MODE_MASK,
533b328c914SElaine Zhang CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
534ed2a4091SElaine Zhang rkclk_set_pll(cru, CLK_CODEC, &cpll_config);
535b328c914SElaine Zhang
536b328c914SElaine Zhang /* waiting for pll lock */
537b328c914SElaine Zhang while (1) {
538b328c914SElaine Zhang if (readl(&grf->soc_status[1]) &
539b328c914SElaine Zhang SOCSTS_CPLL_LOCK)
540b328c914SElaine Zhang break;
541b328c914SElaine Zhang udelay(1);
542b328c914SElaine Zhang }
543b328c914SElaine Zhang
544b328c914SElaine Zhang rk_clrsetreg(&cru->cru_mode_con, CPLL_MODE_MASK,
545b328c914SElaine Zhang CPLL_MODE_NORMAL << CPLL_MODE_SHIFT);
546b328c914SElaine Zhang
547b328c914SElaine Zhang parent = DCLK_VOP1_SELECT_CPLL;
548b328c914SElaine Zhang } else if (ret == DCLK_VOP1_SELECT_GPLL) {
549b328c914SElaine Zhang parent = DCLK_VOP1_SELECT_GPLL;
550b328c914SElaine Zhang lcdc_div = DIV_ROUND_UP(gpll_rate,
551b328c914SElaine Zhang rate_hz);
552b328c914SElaine Zhang } else {
553b328c914SElaine Zhang parent = DCLK_VOP1_SELECT_NPLL;
554b328c914SElaine Zhang lcdc_div = DIV_ROUND_UP(npll_rate,
555b328c914SElaine Zhang rate_hz);
556b328c914SElaine Zhang }
557b328c914SElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[29],
558b328c914SElaine Zhang DCLK_VOP1_DIV_MASK | DCLK_VOP1_PLL_MASK,
559b328c914SElaine Zhang ((lcdc_div - 1) << DCLK_VOP1_DIV_SHIFT) |
560b328c914SElaine Zhang (parent << DCLK_VOP1_PLL_SHIFT));
561aff8795cSHeiko Stübner break;
56255611901SElaine Zhang case ACLK_VIO0:
56355611901SElaine Zhang lcdc_div = DIV_ROUND_UP(gpll_rate, rate_hz);
5645a616fcfSElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[31],
56555611901SElaine Zhang ACLK_VIO0_PLL_MASK | ACLK_VIO0_DIV_MASK,
56655611901SElaine Zhang ACLK_VIO_SELECT_GPLL << ACLK_VIO0_PLL_SHIFT |
56755611901SElaine Zhang (lcdc_div - 1) << ACLK_VIO0_DIV_SHIFT);
5685a616fcfSElaine Zhang break;
56955611901SElaine Zhang case ACLK_VIO1:
57055611901SElaine Zhang lcdc_div = DIV_ROUND_UP(gpll_rate, rate_hz);
5715a616fcfSElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[31],
57255611901SElaine Zhang ACLK_VIO1_PLL_MASK | ACLK_VIO1_DIV_MASK,
57355611901SElaine Zhang ACLK_VIO_SELECT_GPLL << ACLK_VIO1_PLL_SHIFT |
57455611901SElaine Zhang (lcdc_div - 1) << ACLK_VIO1_DIV_SHIFT);
57555611901SElaine Zhang
57655611901SElaine Zhang lcdc_div = DIV_ROUND_UP(rate_hz, HCLK_VIO_HZ);
57755611901SElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[28],
57855611901SElaine Zhang HCLK_VIO_DIV_MASK,
57955611901SElaine Zhang (lcdc_div - 1) << HCLK_VIO_DIV_SHIFT);
5805a616fcfSElaine Zhang break;
581aff8795cSHeiko Stübner }
582aff8795cSHeiko Stübner
583aff8795cSHeiko Stübner return 0;
584aff8795cSHeiko Stübner }
585d3cb46aaSSimon Glass #endif /* CONFIG_SPL_BUILD */
586aff8795cSHeiko Stübner
rkclk_init(struct rk3288_cru * cru,struct rk3288_grf * grf)587aff8795cSHeiko Stübner static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf)
588aff8795cSHeiko Stübner {
589aff8795cSHeiko Stübner u32 aclk_div;
590aff8795cSHeiko Stübner u32 hclk_div;
591aff8795cSHeiko Stübner u32 pclk_div;
592aff8795cSHeiko Stübner
593aff8795cSHeiko Stübner /* pll enter slow-mode */
594aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_mode_con,
595b223c1aeSSimon Glass GPLL_MODE_MASK | CPLL_MODE_MASK,
596aff8795cSHeiko Stübner GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
597aff8795cSHeiko Stübner CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
598aff8795cSHeiko Stübner
599aff8795cSHeiko Stübner /* init pll */
600aff8795cSHeiko Stübner rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg);
601aff8795cSHeiko Stübner rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg);
602aff8795cSHeiko Stübner
603aff8795cSHeiko Stübner /* waiting for pll lock */
604aff8795cSHeiko Stübner while ((readl(&grf->soc_status[1]) &
605aff8795cSHeiko Stübner (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) !=
606aff8795cSHeiko Stübner (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK))
607aff8795cSHeiko Stübner udelay(1);
608aff8795cSHeiko Stübner
609aff8795cSHeiko Stübner /*
610aff8795cSHeiko Stübner * pd_bus clock pll source selection and
611aff8795cSHeiko Stübner * set up dependent divisors for PCLK/HCLK and ACLK clocks.
612aff8795cSHeiko Stübner */
613aff8795cSHeiko Stübner aclk_div = GPLL_HZ / PD_BUS_ACLK_HZ - 1;
614a0af2ba7SElaine Zhang assert((aclk_div + 1) * PD_BUS_ACLK_HZ <= GPLL_HZ && aclk_div <= 0x1f);
615aff8795cSHeiko Stübner hclk_div = PD_BUS_ACLK_HZ / PD_BUS_HCLK_HZ - 1;
616a0af2ba7SElaine Zhang assert((hclk_div + 1) * PD_BUS_HCLK_HZ <=
6175f42424bSLin Huang PD_BUS_ACLK_HZ && (hclk_div <= 0x3) && (hclk_div != 0x2));
618aff8795cSHeiko Stübner
619aff8795cSHeiko Stübner pclk_div = PD_BUS_ACLK_HZ / PD_BUS_PCLK_HZ - 1;
620a0af2ba7SElaine Zhang assert((pclk_div + 1) * PD_BUS_PCLK_HZ <=
6215f42424bSLin Huang PD_BUS_ACLK_HZ && pclk_div <= 0x7);
622aff8795cSHeiko Stübner
623aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[1],
624b223c1aeSSimon Glass PD_BUS_PCLK_DIV_MASK | PD_BUS_HCLK_DIV_MASK |
625b223c1aeSSimon Glass PD_BUS_ACLK_DIV0_MASK | PD_BUS_ACLK_DIV1_MASK,
626aff8795cSHeiko Stübner pclk_div << PD_BUS_PCLK_DIV_SHIFT |
627aff8795cSHeiko Stübner hclk_div << PD_BUS_HCLK_DIV_SHIFT |
628aff8795cSHeiko Stübner aclk_div << PD_BUS_ACLK_DIV0_SHIFT |
629aff8795cSHeiko Stübner 0 << 0);
630aff8795cSHeiko Stübner
631aff8795cSHeiko Stübner /*
632aff8795cSHeiko Stübner * peri clock pll source selection and
633aff8795cSHeiko Stübner * set up dependent divisors for PCLK/HCLK and ACLK clocks.
634aff8795cSHeiko Stübner */
635aff8795cSHeiko Stübner aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
636a0af2ba7SElaine Zhang assert((aclk_div + 1) * PERI_ACLK_HZ <= GPLL_HZ && aclk_div <= 0x1f);
637aff8795cSHeiko Stübner
638abd0128eSHeiko Stübner hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ);
639a0af2ba7SElaine Zhang assert((1 << hclk_div) * PERI_HCLK_HZ <=
6405f42424bSLin Huang PERI_ACLK_HZ && (hclk_div <= 0x2));
641aff8795cSHeiko Stübner
642abd0128eSHeiko Stübner pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ);
643a0af2ba7SElaine Zhang assert((1 << pclk_div) * PERI_PCLK_HZ <=
6445f42424bSLin Huang PERI_ACLK_HZ && (pclk_div <= 0x3));
645aff8795cSHeiko Stübner
646aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[10],
647b223c1aeSSimon Glass PERI_PCLK_DIV_MASK | PERI_HCLK_DIV_MASK |
648b223c1aeSSimon Glass PERI_ACLK_DIV_MASK,
649aff8795cSHeiko Stübner PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT |
650aff8795cSHeiko Stübner pclk_div << PERI_PCLK_DIV_SHIFT |
651aff8795cSHeiko Stübner hclk_div << PERI_HCLK_DIV_SHIFT |
652aff8795cSHeiko Stübner aclk_div << PERI_ACLK_DIV_SHIFT);
653aff8795cSHeiko Stübner
654ddf875abSElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[39],
655ddf875abSElaine Zhang ACLK_HEVC_SEL_PLL_MASK | ACLK_HEVC_DIV_CON_MASK,
656ddf875abSElaine Zhang ACLK_HEVC_SEL_CPLL << ACLK_HEVC_SEL_PLL_SHIFT |
657ddf875abSElaine Zhang 4 << ACLK_HEVC_DIV_CON_SHIFT);
658ddf875abSElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[42],
659ddf875abSElaine Zhang CLK_HEVC_CORE_SEL_PLL_MASK | CLK_HEVC_CORE_DIV_CON_MASK |
660ddf875abSElaine Zhang CLK_HEVC_CORE_SEL_PLL_MASK | CLK_HEVC_CORE_DIV_CON_MASK,
661ddf875abSElaine Zhang CLK_HEVC_CORE_SEL_CPLL << CLK_HEVC_CORE_SEL_PLL_SHIFT |
662ddf875abSElaine Zhang CLK_HEVC_CABAC_SEL_CPLL << CLK_HEVC_CABAC_DIV_CON_SHIFT |
663ddf875abSElaine Zhang 4 << CLK_HEVC_CORE_DIV_CON_SHIFT |
664ddf875abSElaine Zhang 4 << CLK_HEVC_CABAC_DIV_CON_SHIFT);
665ddf875abSElaine Zhang
666aff8795cSHeiko Stübner /* PLL enter normal-mode */
667aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_mode_con,
668b223c1aeSSimon Glass GPLL_MODE_MASK | CPLL_MODE_MASK,
669aff8795cSHeiko Stübner GPLL_MODE_NORMAL << GPLL_MODE_SHIFT |
670aff8795cSHeiko Stübner CPLL_MODE_NORMAL << CPLL_MODE_SHIFT);
671aff8795cSHeiko Stübner }
672aff8795cSHeiko Stübner
rk3288_clk_configure_cpu(struct rk3288_cru * cru,struct rk3288_grf * grf)673aff8795cSHeiko Stübner void rk3288_clk_configure_cpu(struct rk3288_cru *cru, struct rk3288_grf *grf)
674aff8795cSHeiko Stübner {
675aff8795cSHeiko Stübner /* pll enter slow-mode */
676b223c1aeSSimon Glass rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK,
677aff8795cSHeiko Stübner APLL_MODE_SLOW << APLL_MODE_SHIFT);
678aff8795cSHeiko Stübner
679aff8795cSHeiko Stübner rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg);
680aff8795cSHeiko Stübner
681aff8795cSHeiko Stübner /* waiting for pll lock */
682aff8795cSHeiko Stübner while (!(readl(&grf->soc_status[1]) & SOCSTS_APLL_LOCK))
683aff8795cSHeiko Stübner udelay(1);
684aff8795cSHeiko Stübner
685aff8795cSHeiko Stübner /*
686aff8795cSHeiko Stübner * core clock pll source selection and
687aff8795cSHeiko Stübner * set up dependent divisors for MPAXI/M0AXI and ARM clocks.
688aff8795cSHeiko Stübner * core clock select apll, apll clk = 1800MHz
689aff8795cSHeiko Stübner * arm clk = 1800MHz, mpclk = 450MHz, m0clk = 900MHz
690aff8795cSHeiko Stübner */
691aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[0],
692b223c1aeSSimon Glass CORE_SEL_PLL_MASK | A17_DIV_MASK | MP_DIV_MASK |
693b223c1aeSSimon Glass M0_DIV_MASK,
694aff8795cSHeiko Stübner 0 << A17_DIV_SHIFT |
695aff8795cSHeiko Stübner 3 << MP_DIV_SHIFT |
696aff8795cSHeiko Stübner 1 << M0_DIV_SHIFT);
697aff8795cSHeiko Stübner
698aff8795cSHeiko Stübner /*
699aff8795cSHeiko Stübner * set up dependent divisors for L2RAM/ATCLK and PCLK clocks.
700aff8795cSHeiko Stübner * l2ramclk = 900MHz, atclk = 450MHz, pclk_dbg = 450MHz
701aff8795cSHeiko Stübner */
702aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[37],
703b223c1aeSSimon Glass CLK_L2RAM_DIV_MASK | ATCLK_CORE_DIV_CON_MASK |
704b223c1aeSSimon Glass PCLK_CORE_DBG_DIV_MASK,
705aff8795cSHeiko Stübner 1 << CLK_L2RAM_DIV_SHIFT |
706aff8795cSHeiko Stübner 3 << ATCLK_CORE_DIV_CON_SHIFT |
707aff8795cSHeiko Stübner 3 << PCLK_CORE_DBG_DIV_SHIFT);
708aff8795cSHeiko Stübner
709aff8795cSHeiko Stübner /* PLL enter normal-mode */
710b223c1aeSSimon Glass rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK,
711aff8795cSHeiko Stübner APLL_MODE_NORMAL << APLL_MODE_SHIFT);
712aff8795cSHeiko Stübner }
713aff8795cSHeiko Stübner
rockchip_mmc_get_clk(struct rk3288_cru * cru,uint gclk_rate,int periph)714aff8795cSHeiko Stübner static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint gclk_rate,
715aff8795cSHeiko Stübner int periph)
716aff8795cSHeiko Stübner {
717aff8795cSHeiko Stübner uint src_rate;
718aff8795cSHeiko Stübner uint div, mux;
719aff8795cSHeiko Stübner u32 con;
720aff8795cSHeiko Stübner
721aff8795cSHeiko Stübner switch (periph) {
722aff8795cSHeiko Stübner case HCLK_EMMC:
72345112271SXu Ziyuan case SCLK_EMMC:
72411fbeef5SZiyuan Xu case SCLK_EMMC_SAMPLE:
725aff8795cSHeiko Stübner con = readl(&cru->cru_clksel_con[12]);
726b223c1aeSSimon Glass mux = (con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT;
727b223c1aeSSimon Glass div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT;
728aff8795cSHeiko Stübner break;
729aff8795cSHeiko Stübner case HCLK_SDMMC:
73045112271SXu Ziyuan case SCLK_SDMMC:
731aff8795cSHeiko Stübner con = readl(&cru->cru_clksel_con[11]);
732b223c1aeSSimon Glass mux = (con & MMC0_PLL_MASK) >> MMC0_PLL_SHIFT;
733b223c1aeSSimon Glass div = (con & MMC0_DIV_MASK) >> MMC0_DIV_SHIFT;
734aff8795cSHeiko Stübner break;
735aff8795cSHeiko Stübner case HCLK_SDIO0:
73645112271SXu Ziyuan case SCLK_SDIO0:
737aff8795cSHeiko Stübner con = readl(&cru->cru_clksel_con[12]);
738b223c1aeSSimon Glass mux = (con & SDIO0_PLL_MASK) >> SDIO0_PLL_SHIFT;
739b223c1aeSSimon Glass div = (con & SDIO0_DIV_MASK) >> SDIO0_DIV_SHIFT;
740aff8795cSHeiko Stübner break;
741aff8795cSHeiko Stübner default:
742aff8795cSHeiko Stübner return -EINVAL;
743aff8795cSHeiko Stübner }
744aff8795cSHeiko Stübner
745aff8795cSHeiko Stübner src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : gclk_rate;
746b2259b20SZiyuan Xu return DIV_TO_RATE(src_rate, div) / 2;
747aff8795cSHeiko Stübner }
748aff8795cSHeiko Stübner
rockchip_mmc_set_clk(struct rk3288_cru * cru,uint gclk_rate,int periph,uint freq)749aff8795cSHeiko Stübner static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint gclk_rate,
750aff8795cSHeiko Stübner int periph, uint freq)
751aff8795cSHeiko Stübner {
752aff8795cSHeiko Stübner int src_clk_div;
753aff8795cSHeiko Stübner int mux;
754aff8795cSHeiko Stübner
755aff8795cSHeiko Stübner debug("%s: gclk_rate=%u\n", __func__, gclk_rate);
7563a94d75dSKever Yang /* mmc clock default div 2 internal, need provide double in cru */
7573a94d75dSKever Yang src_clk_div = DIV_ROUND_UP(gclk_rate / 2, freq);
758aff8795cSHeiko Stübner
759aff8795cSHeiko Stübner if (src_clk_div > 0x3f) {
7603a94d75dSKever Yang src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, freq);
761217273cdSKever Yang assert(src_clk_div < 0x40);
762aff8795cSHeiko Stübner mux = EMMC_PLL_SELECT_24MHZ;
763aff8795cSHeiko Stübner assert((int)EMMC_PLL_SELECT_24MHZ ==
764aff8795cSHeiko Stübner (int)MMC0_PLL_SELECT_24MHZ);
765aff8795cSHeiko Stübner } else {
766aff8795cSHeiko Stübner mux = EMMC_PLL_SELECT_GENERAL;
767aff8795cSHeiko Stübner assert((int)EMMC_PLL_SELECT_GENERAL ==
768aff8795cSHeiko Stübner (int)MMC0_PLL_SELECT_GENERAL);
769aff8795cSHeiko Stübner }
770aff8795cSHeiko Stübner switch (periph) {
771aff8795cSHeiko Stübner case HCLK_EMMC:
77245112271SXu Ziyuan case SCLK_EMMC:
773aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[12],
774b223c1aeSSimon Glass EMMC_PLL_MASK | EMMC_DIV_MASK,
775aff8795cSHeiko Stübner mux << EMMC_PLL_SHIFT |
776aff8795cSHeiko Stübner (src_clk_div - 1) << EMMC_DIV_SHIFT);
777aff8795cSHeiko Stübner break;
778aff8795cSHeiko Stübner case HCLK_SDMMC:
77945112271SXu Ziyuan case SCLK_SDMMC:
780aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[11],
781b223c1aeSSimon Glass MMC0_PLL_MASK | MMC0_DIV_MASK,
782aff8795cSHeiko Stübner mux << MMC0_PLL_SHIFT |
783aff8795cSHeiko Stübner (src_clk_div - 1) << MMC0_DIV_SHIFT);
784aff8795cSHeiko Stübner break;
785aff8795cSHeiko Stübner case HCLK_SDIO0:
78645112271SXu Ziyuan case SCLK_SDIO0:
787aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[12],
788b223c1aeSSimon Glass SDIO0_PLL_MASK | SDIO0_DIV_MASK,
789aff8795cSHeiko Stübner mux << SDIO0_PLL_SHIFT |
790aff8795cSHeiko Stübner (src_clk_div - 1) << SDIO0_DIV_SHIFT);
791aff8795cSHeiko Stübner break;
792aff8795cSHeiko Stübner default:
793aff8795cSHeiko Stübner return -EINVAL;
794aff8795cSHeiko Stübner }
795aff8795cSHeiko Stübner
796aff8795cSHeiko Stübner return rockchip_mmc_get_clk(cru, gclk_rate, periph);
797aff8795cSHeiko Stübner }
798aff8795cSHeiko Stübner
rockchip_spi_get_clk(struct rk3288_cru * cru,uint gclk_rate,int periph)799aff8795cSHeiko Stübner static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint gclk_rate,
800aff8795cSHeiko Stübner int periph)
801aff8795cSHeiko Stübner {
802aff8795cSHeiko Stübner uint div, mux;
803aff8795cSHeiko Stübner u32 con;
804aff8795cSHeiko Stübner
805aff8795cSHeiko Stübner switch (periph) {
806aff8795cSHeiko Stübner case SCLK_SPI0:
807aff8795cSHeiko Stübner con = readl(&cru->cru_clksel_con[25]);
808b223c1aeSSimon Glass mux = (con & SPI0_PLL_MASK) >> SPI0_PLL_SHIFT;
809b223c1aeSSimon Glass div = (con & SPI0_DIV_MASK) >> SPI0_DIV_SHIFT;
810aff8795cSHeiko Stübner break;
811aff8795cSHeiko Stübner case SCLK_SPI1:
812aff8795cSHeiko Stübner con = readl(&cru->cru_clksel_con[25]);
813b223c1aeSSimon Glass mux = (con & SPI1_PLL_MASK) >> SPI1_PLL_SHIFT;
814b223c1aeSSimon Glass div = (con & SPI1_DIV_MASK) >> SPI1_DIV_SHIFT;
815aff8795cSHeiko Stübner break;
816aff8795cSHeiko Stübner case SCLK_SPI2:
817aff8795cSHeiko Stübner con = readl(&cru->cru_clksel_con[39]);
818b223c1aeSSimon Glass mux = (con & SPI2_PLL_MASK) >> SPI2_PLL_SHIFT;
819b223c1aeSSimon Glass div = (con & SPI2_DIV_MASK) >> SPI2_DIV_SHIFT;
820aff8795cSHeiko Stübner break;
821aff8795cSHeiko Stübner default:
822aff8795cSHeiko Stübner return -EINVAL;
823aff8795cSHeiko Stübner }
824aff8795cSHeiko Stübner assert(mux == SPI0_PLL_SELECT_GENERAL);
825aff8795cSHeiko Stübner
826aff8795cSHeiko Stübner return DIV_TO_RATE(gclk_rate, div);
827aff8795cSHeiko Stübner }
828aff8795cSHeiko Stübner
rockchip_spi_set_clk(struct rk3288_cru * cru,uint gclk_rate,int periph,uint freq)829aff8795cSHeiko Stübner static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint gclk_rate,
830aff8795cSHeiko Stübner int periph, uint freq)
831aff8795cSHeiko Stübner {
832aff8795cSHeiko Stübner int src_clk_div;
833aff8795cSHeiko Stübner
834aff8795cSHeiko Stübner debug("%s: clk_general_rate=%u\n", __func__, gclk_rate);
835217273cdSKever Yang src_clk_div = DIV_ROUND_UP(gclk_rate, freq) - 1;
836217273cdSKever Yang assert(src_clk_div < 128);
837aff8795cSHeiko Stübner switch (periph) {
838aff8795cSHeiko Stübner case SCLK_SPI0:
839aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[25],
840b223c1aeSSimon Glass SPI0_PLL_MASK | SPI0_DIV_MASK,
841aff8795cSHeiko Stübner SPI0_PLL_SELECT_GENERAL << SPI0_PLL_SHIFT |
842aff8795cSHeiko Stübner src_clk_div << SPI0_DIV_SHIFT);
843aff8795cSHeiko Stübner break;
844aff8795cSHeiko Stübner case SCLK_SPI1:
845aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[25],
846b223c1aeSSimon Glass SPI1_PLL_MASK | SPI1_DIV_MASK,
847aff8795cSHeiko Stübner SPI1_PLL_SELECT_GENERAL << SPI1_PLL_SHIFT |
848aff8795cSHeiko Stübner src_clk_div << SPI1_DIV_SHIFT);
849aff8795cSHeiko Stübner break;
850aff8795cSHeiko Stübner case SCLK_SPI2:
851aff8795cSHeiko Stübner rk_clrsetreg(&cru->cru_clksel_con[39],
852b223c1aeSSimon Glass SPI2_PLL_MASK | SPI2_DIV_MASK,
853aff8795cSHeiko Stübner SPI2_PLL_SELECT_GENERAL << SPI2_PLL_SHIFT |
854aff8795cSHeiko Stübner src_clk_div << SPI2_DIV_SHIFT);
855aff8795cSHeiko Stübner break;
856aff8795cSHeiko Stübner default:
857aff8795cSHeiko Stübner return -EINVAL;
858aff8795cSHeiko Stübner }
859aff8795cSHeiko Stübner
860aff8795cSHeiko Stübner return rockchip_spi_get_clk(cru, gclk_rate, periph);
861aff8795cSHeiko Stübner }
862aff8795cSHeiko Stübner
rockchip_aclk_peri_get_clk(struct rk3288_cru * cru)8638d52d662SWyon Bi static ulong rockchip_aclk_peri_get_clk(struct rk3288_cru *cru)
8648d52d662SWyon Bi {
8658d52d662SWyon Bi uint div, mux;
8668d52d662SWyon Bi u32 con;
8678d52d662SWyon Bi ulong rate, parent_rate;
8688d52d662SWyon Bi
8698d52d662SWyon Bi con = readl(&cru->cru_clksel_con[10]);
8708d52d662SWyon Bi mux = (con & PERI_SEL_PLL_MASK) >> PERI_SEL_PLL_SHIFT;
8718d52d662SWyon Bi div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT;
8728d52d662SWyon Bi if (mux)
8738d52d662SWyon Bi parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL);
8748d52d662SWyon Bi else
8758d52d662SWyon Bi parent_rate = rkclk_pll_get_rate(cru, CLK_CODEC);
8768d52d662SWyon Bi rate = DIV_TO_RATE(parent_rate, div);
8778d52d662SWyon Bi
8788d52d662SWyon Bi return rate;
8798d52d662SWyon Bi }
8808d52d662SWyon Bi
rockchip_aclk_cpu_get_clk(struct rk3288_cru * cru)8818d52d662SWyon Bi static ulong rockchip_aclk_cpu_get_clk(struct rk3288_cru *cru)
8828d52d662SWyon Bi {
8838d52d662SWyon Bi uint div, mux;
8848d52d662SWyon Bi u32 con;
8858d52d662SWyon Bi ulong rate, parent_rate;
8868d52d662SWyon Bi
8878d52d662SWyon Bi con = readl(&cru->cru_clksel_con[1]);
8888d52d662SWyon Bi mux = (con & PD_BUS_SEL_PLL_MASK) >> PD_BUS_SEL_PLL_SHIFT;
8898d52d662SWyon Bi div = (con & PD_BUS_ACLK_DIV0_MASK) >> PD_BUS_ACLK_DIV0_SHIFT;
8908d52d662SWyon Bi if (mux)
8918d52d662SWyon Bi parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL);
8928d52d662SWyon Bi else
8938d52d662SWyon Bi parent_rate = rkclk_pll_get_rate(cru, CLK_CODEC);
8948d52d662SWyon Bi parent_rate = DIV_TO_RATE(parent_rate, div);
8958d52d662SWyon Bi
8968d52d662SWyon Bi div = (con & PD_BUS_ACLK_DIV1_MASK) >> PD_BUS_ACLK_DIV1_SHIFT;
8978d52d662SWyon Bi rate = DIV_TO_RATE(parent_rate, div);
8988d52d662SWyon Bi
8998d52d662SWyon Bi return rate;
9008d52d662SWyon Bi }
9018d52d662SWyon Bi
rockchip_pclk_peri_get_clk(struct rk3288_cru * cru)9028d52d662SWyon Bi static ulong rockchip_pclk_peri_get_clk(struct rk3288_cru *cru)
9038d52d662SWyon Bi {
9048d52d662SWyon Bi uint div;
9058d52d662SWyon Bi u32 con;
9068d52d662SWyon Bi ulong rate, parent_rate;
9078d52d662SWyon Bi
9088d52d662SWyon Bi parent_rate = rockchip_aclk_peri_get_clk(cru);
9098d52d662SWyon Bi con = readl(&cru->cru_clksel_con[10]);
9108d52d662SWyon Bi div = (con & PERI_PCLK_DIV_MASK) >> PERI_PCLK_DIV_SHIFT;
9118d52d662SWyon Bi rate = parent_rate / (1 << div);
9128d52d662SWyon Bi
9138d52d662SWyon Bi return rate;
9148d52d662SWyon Bi }
9158d52d662SWyon Bi
rockchip_pclk_cpu_get_clk(struct rk3288_cru * cru)9168d52d662SWyon Bi static ulong rockchip_pclk_cpu_get_clk(struct rk3288_cru *cru)
9178d52d662SWyon Bi {
9188d52d662SWyon Bi uint div;
9198d52d662SWyon Bi u32 con;
9208d52d662SWyon Bi ulong rate, parent_rate;
9218d52d662SWyon Bi
9228d52d662SWyon Bi parent_rate = rockchip_aclk_cpu_get_clk(cru);
9238d52d662SWyon Bi con = readl(&cru->cru_clksel_con[1]);
9248d52d662SWyon Bi div = (con & PD_BUS_PCLK_DIV_MASK) >> PD_BUS_PCLK_DIV_SHIFT;
9258d52d662SWyon Bi rate = DIV_TO_RATE(parent_rate, div);
9268d52d662SWyon Bi
9278d52d662SWyon Bi return rate;
9288d52d662SWyon Bi }
9298d52d662SWyon Bi
rockchip_i2c_get_clk(struct rk3288_cru * cru,int periph)9308d52d662SWyon Bi static ulong rockchip_i2c_get_clk(struct rk3288_cru *cru, int periph)
9318d52d662SWyon Bi {
9328d52d662SWyon Bi switch (periph) {
9338d52d662SWyon Bi case PCLK_I2C0:
9348d52d662SWyon Bi case PCLK_I2C2:
9358d52d662SWyon Bi return rockchip_pclk_cpu_get_clk(cru);
9368d52d662SWyon Bi case PCLK_I2C1:
9378d52d662SWyon Bi case PCLK_I2C3:
9388d52d662SWyon Bi case PCLK_I2C4:
9398d52d662SWyon Bi case PCLK_I2C5:
9408d52d662SWyon Bi return rockchip_pclk_peri_get_clk(cru);
9418d52d662SWyon Bi default:
9428d52d662SWyon Bi return -EINVAL;
9438d52d662SWyon Bi }
9448d52d662SWyon Bi }
9458d52d662SWyon Bi
rockchip_saradc_get_clk(struct rk3288_cru * cru)946b6e52402SDavid Wu static ulong rockchip_saradc_get_clk(struct rk3288_cru *cru)
947b6e52402SDavid Wu {
948b6e52402SDavid Wu u32 div, val;
949b6e52402SDavid Wu
950b6e52402SDavid Wu val = readl(&cru->cru_clksel_con[24]);
951b6e52402SDavid Wu div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT,
952b6e52402SDavid Wu CLK_SARADC_DIV_CON_WIDTH);
953b6e52402SDavid Wu
954b6e52402SDavid Wu return DIV_TO_RATE(OSC_HZ, div);
955b6e52402SDavid Wu }
956b6e52402SDavid Wu
rockchip_saradc_set_clk(struct rk3288_cru * cru,uint hz)957b6e52402SDavid Wu static ulong rockchip_saradc_set_clk(struct rk3288_cru *cru, uint hz)
958b6e52402SDavid Wu {
959b6e52402SDavid Wu int src_clk_div;
960b6e52402SDavid Wu
961b6e52402SDavid Wu src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
962b6e52402SDavid Wu assert(src_clk_div < 128);
963b6e52402SDavid Wu
964b6e52402SDavid Wu rk_clrsetreg(&cru->cru_clksel_con[24],
965b6e52402SDavid Wu CLK_SARADC_DIV_CON_MASK,
966b6e52402SDavid Wu src_clk_div << CLK_SARADC_DIV_CON_SHIFT);
967b6e52402SDavid Wu
968b6e52402SDavid Wu return rockchip_saradc_get_clk(cru);
969b6e52402SDavid Wu }
970b6e52402SDavid Wu
rockchip_tsadc_get_clk(struct rk3288_cru * cru)971cb3c37fcSElaine Zhang static ulong rockchip_tsadc_get_clk(struct rk3288_cru *cru)
972cb3c37fcSElaine Zhang {
973cb3c37fcSElaine Zhang u32 div, val;
974cb3c37fcSElaine Zhang
975cb3c37fcSElaine Zhang val = readl(&cru->cru_clksel_con[2]);
976cb3c37fcSElaine Zhang div = bitfield_extract(val, CLK_TSADC_DIV_CON_SHIFT,
977cb3c37fcSElaine Zhang CLK_TSADC_DIV_CON_WIDTH);
978cb3c37fcSElaine Zhang
979cb3c37fcSElaine Zhang return DIV_TO_RATE(32768, div);
980cb3c37fcSElaine Zhang }
981cb3c37fcSElaine Zhang
rockchip_tsadc_set_clk(struct rk3288_cru * cru,uint hz)982cb3c37fcSElaine Zhang static ulong rockchip_tsadc_set_clk(struct rk3288_cru *cru, uint hz)
983cb3c37fcSElaine Zhang {
984cb3c37fcSElaine Zhang int src_clk_div;
985cb3c37fcSElaine Zhang
986cb3c37fcSElaine Zhang src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
987cb3c37fcSElaine Zhang assert(src_clk_div < 128);
988cb3c37fcSElaine Zhang
989cb3c37fcSElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[2],
990cb3c37fcSElaine Zhang CLK_TSADC_DIV_CON_MASK,
991cb3c37fcSElaine Zhang src_clk_div << CLK_TSADC_DIV_CON_SHIFT);
992cb3c37fcSElaine Zhang
993cb3c37fcSElaine Zhang return rockchip_tsadc_get_clk(cru);
994cb3c37fcSElaine Zhang }
995cb3c37fcSElaine Zhang
9962e8ea5b0SElaine Zhang #ifndef CONFIG_SPL_BUILD
9972e8ea5b0SElaine Zhang
rockchip_crypto_get_clk(struct rk3288_cru * cru)9988d52d662SWyon Bi static ulong rockchip_crypto_get_clk(struct rk3288_cru *cru)
9992e8ea5b0SElaine Zhang {
10002e8ea5b0SElaine Zhang u32 div, val;
10012e8ea5b0SElaine Zhang
10022e8ea5b0SElaine Zhang val = readl(&cru->cru_clksel_con[26]);
10032e8ea5b0SElaine Zhang div = (val & CLK_CRYPTO_DIV_CON_MASK) >> CLK_CRYPTO_DIV_CON_SHIFT;
10042e8ea5b0SElaine Zhang
10058d52d662SWyon Bi return DIV_TO_RATE(rockchip_aclk_cpu_get_clk(cru), div);
10062e8ea5b0SElaine Zhang }
10072e8ea5b0SElaine Zhang
rockchip_crypto_set_clk(struct rk3288_cru * cru,uint hz)10088d52d662SWyon Bi static ulong rockchip_crypto_set_clk(struct rk3288_cru *cru, uint hz)
10092e8ea5b0SElaine Zhang {
10102e8ea5b0SElaine Zhang int src_clk_div;
10112e8ea5b0SElaine Zhang uint p_rate;
10122e8ea5b0SElaine Zhang
10138d52d662SWyon Bi p_rate = rockchip_aclk_cpu_get_clk(cru);
10142e8ea5b0SElaine Zhang src_clk_div = DIV_ROUND_UP(p_rate, hz) - 1;
10152e8ea5b0SElaine Zhang assert(src_clk_div < 3);
10162e8ea5b0SElaine Zhang
10172e8ea5b0SElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[26],
10182e8ea5b0SElaine Zhang CLK_CRYPTO_DIV_CON_MASK,
10192e8ea5b0SElaine Zhang src_clk_div << CLK_CRYPTO_DIV_CON_SHIFT);
10202e8ea5b0SElaine Zhang
10218d52d662SWyon Bi return rockchip_crypto_get_clk(cru);
10222e8ea5b0SElaine Zhang }
1023ced960d2SElaine Zhang
rk3288_alive_get_clk(struct rk3288_cru * cru,uint gclk_rate)1024ced960d2SElaine Zhang static ulong rk3288_alive_get_clk(struct rk3288_cru *cru, uint gclk_rate)
1025ced960d2SElaine Zhang {
1026ced960d2SElaine Zhang u32 div, con, parent;
1027ced960d2SElaine Zhang
1028ced960d2SElaine Zhang con = readl(&cru->cru_clksel_con[33]);
1029ced960d2SElaine Zhang div = (con & PCLK_ALIVE_DIV_CON_MASK) >>
1030ced960d2SElaine Zhang PCLK_ALIVE_DIV_CON_SHIFT;
1031ced960d2SElaine Zhang parent = gclk_rate;
1032ced960d2SElaine Zhang return DIV_TO_RATE(parent, div);
1033ced960d2SElaine Zhang }
10341ca40e22SElaine Zhang
rockchip_test_get_clk(struct rk3288_cru * cru,int id)10351ca40e22SElaine Zhang static ulong rockchip_test_get_clk(struct rk3288_cru *cru, int id)
10361ca40e22SElaine Zhang {
10371ca40e22SElaine Zhang u32 src, val;
10381ca40e22SElaine Zhang
10391ca40e22SElaine Zhang val = readl(&cru->cru_misc_con);
10401ca40e22SElaine Zhang src = (val & CLK_TEST_SRC_SEL_MASK) >> CLK_TEST_SRC_SEL_SHIFT;
10411ca40e22SElaine Zhang switch (src) {
10421ca40e22SElaine Zhang case CLK_TEST_SRC_SEL_24M:
10431ca40e22SElaine Zhang return 24000000;
10441ca40e22SElaine Zhang case CLK_TEST_SRC_SEL_27M:
10451ca40e22SElaine Zhang return 27000000;
10461ca40e22SElaine Zhang case CLK_TEST_SRC_SEL_32k:
10471ca40e22SElaine Zhang return 32768;
10481ca40e22SElaine Zhang default:
10491ca40e22SElaine Zhang return -ENOENT;
10501ca40e22SElaine Zhang }
10511ca40e22SElaine Zhang }
10521ca40e22SElaine Zhang
rockchip_test_set_clk(struct rk3288_cru * cru,int id,uint hz)10531ca40e22SElaine Zhang static ulong rockchip_test_set_clk(struct rk3288_cru *cru, int id, uint hz)
10541ca40e22SElaine Zhang {
10551ca40e22SElaine Zhang int src = 0;
10561ca40e22SElaine Zhang
10571ca40e22SElaine Zhang switch (hz) {
10581ca40e22SElaine Zhang case 24000000:
10591ca40e22SElaine Zhang src = 8;
10601ca40e22SElaine Zhang break;
10611ca40e22SElaine Zhang case 27000000:
10621ca40e22SElaine Zhang src = 9;
10631ca40e22SElaine Zhang break;
10641ca40e22SElaine Zhang case 32768:
10651ca40e22SElaine Zhang src = 10;
10661ca40e22SElaine Zhang break;
10671ca40e22SElaine Zhang default:
10681ca40e22SElaine Zhang return -EINVAL;
10691ca40e22SElaine Zhang }
10701ca40e22SElaine Zhang rk_clrsetreg(&cru->cru_misc_con,
10711ca40e22SElaine Zhang CLK_TEST_SRC_SEL_MASK,
10721ca40e22SElaine Zhang src << CLK_TEST_SRC_SEL_SHIFT);
10731ca40e22SElaine Zhang
10741ca40e22SElaine Zhang return rockchip_test_get_clk(cru, id);
10751ca40e22SElaine Zhang }
10762e8ea5b0SElaine Zhang #endif
10772e8ea5b0SElaine Zhang
rk3288_clk_get_rate(struct clk * clk)1078aff8795cSHeiko Stübner static ulong rk3288_clk_get_rate(struct clk *clk)
1079aff8795cSHeiko Stübner {
1080aff8795cSHeiko Stübner struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
1081aff8795cSHeiko Stübner ulong new_rate, gclk_rate;
1082aff8795cSHeiko Stübner
1083aff8795cSHeiko Stübner gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
1084aff8795cSHeiko Stübner switch (clk->id) {
1085aff8795cSHeiko Stübner case 0 ... 63:
1086aff8795cSHeiko Stübner new_rate = rkclk_pll_get_rate(priv->cru, clk->id);
1087aff8795cSHeiko Stübner break;
1088aff8795cSHeiko Stübner case HCLK_EMMC:
1089aff8795cSHeiko Stübner case HCLK_SDMMC:
1090aff8795cSHeiko Stübner case HCLK_SDIO0:
109145112271SXu Ziyuan case SCLK_EMMC:
109211fbeef5SZiyuan Xu case SCLK_EMMC_SAMPLE:
109345112271SXu Ziyuan case SCLK_SDMMC:
109411fbeef5SZiyuan Xu case SCLK_SDMMC_SAMPLE:
109545112271SXu Ziyuan case SCLK_SDIO0:
1096aff8795cSHeiko Stübner new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, clk->id);
1097aff8795cSHeiko Stübner break;
1098aff8795cSHeiko Stübner case SCLK_SPI0:
1099aff8795cSHeiko Stübner case SCLK_SPI1:
1100aff8795cSHeiko Stübner case SCLK_SPI2:
1101aff8795cSHeiko Stübner new_rate = rockchip_spi_get_clk(priv->cru, gclk_rate, clk->id);
1102aff8795cSHeiko Stübner break;
1103aff8795cSHeiko Stübner case PCLK_I2C0:
1104aff8795cSHeiko Stübner case PCLK_I2C1:
1105aff8795cSHeiko Stübner case PCLK_I2C2:
1106aff8795cSHeiko Stübner case PCLK_I2C3:
1107aff8795cSHeiko Stübner case PCLK_I2C4:
1108aff8795cSHeiko Stübner case PCLK_I2C5:
11098d52d662SWyon Bi new_rate = rockchip_i2c_get_clk(priv->cru, clk->id);
11108d52d662SWyon Bi break;
11114f0b8efaSKever Yang case PCLK_PWM:
1112*dfaf8216SElaine Zhang case PCLK_RKPWM:
11134f0b8efaSKever Yang return PD_BUS_PCLK_HZ;
1114b6e52402SDavid Wu case SCLK_SARADC:
1115b6e52402SDavid Wu new_rate = rockchip_saradc_get_clk(priv->cru);
1116b6e52402SDavid Wu break;
1117cb3c37fcSElaine Zhang case SCLK_TSADC:
1118cb3c37fcSElaine Zhang new_rate = rockchip_tsadc_get_clk(priv->cru);
1119cb3c37fcSElaine Zhang break;
11202e8ea5b0SElaine Zhang case ACLK_CPU:
11218d52d662SWyon Bi new_rate = rockchip_aclk_cpu_get_clk(priv->cru);
11228d52d662SWyon Bi break;
11238d52d662SWyon Bi case ACLK_PERI:
11248d52d662SWyon Bi new_rate = rockchip_aclk_peri_get_clk(priv->cru);
11258d52d662SWyon Bi break;
11268d52d662SWyon Bi case PCLK_CPU:
11278d52d662SWyon Bi new_rate = rockchip_pclk_cpu_get_clk(priv->cru);
11288d52d662SWyon Bi break;
11298d52d662SWyon Bi case PCLK_PERI:
11308d52d662SWyon Bi new_rate = rockchip_pclk_peri_get_clk(priv->cru);
11312e8ea5b0SElaine Zhang break;
11322e8ea5b0SElaine Zhang #ifndef CONFIG_SPL_BUILD
11332e8ea5b0SElaine Zhang case SCLK_CRYPTO:
11348d52d662SWyon Bi new_rate = rockchip_crypto_get_clk(priv->cru);
11352e8ea5b0SElaine Zhang break;
1136ced960d2SElaine Zhang case PCLK_WDT:
1137ced960d2SElaine Zhang new_rate = rk3288_alive_get_clk(priv->cru, gclk_rate);
1138ced960d2SElaine Zhang break;
11391ca40e22SElaine Zhang case SCLK_TESTOUT_SRC:
11401ca40e22SElaine Zhang case SCLK_TESTOUT:
11411ca40e22SElaine Zhang new_rate = rockchip_test_get_clk(priv->cru, clk->id);
11421ca40e22SElaine Zhang break;
11432e8ea5b0SElaine Zhang #endif
1144aff8795cSHeiko Stübner default:
1145aff8795cSHeiko Stübner return -ENOENT;
1146aff8795cSHeiko Stübner }
1147aff8795cSHeiko Stübner
1148aff8795cSHeiko Stübner return new_rate;
1149aff8795cSHeiko Stübner }
1150aff8795cSHeiko Stübner
rk3288_clk_set_rate(struct clk * clk,ulong rate)1151aff8795cSHeiko Stübner static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate)
1152aff8795cSHeiko Stübner {
1153aff8795cSHeiko Stübner struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
1154aff8795cSHeiko Stübner struct rk3288_cru *cru = priv->cru;
1155aff8795cSHeiko Stübner ulong new_rate, gclk_rate;
1156aff8795cSHeiko Stübner
1157aff8795cSHeiko Stübner gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
1158aff8795cSHeiko Stübner switch (clk->id) {
11593a8a42d9SSimon Glass case PLL_APLL:
11603a8a42d9SSimon Glass /* We only support a fixed rate here */
11613a8a42d9SSimon Glass if (rate != 1800000000)
11623a8a42d9SSimon Glass return -EINVAL;
11633a8a42d9SSimon Glass rk3288_clk_configure_cpu(priv->cru, priv->grf);
11643a8a42d9SSimon Glass new_rate = rate;
11653a8a42d9SSimon Glass break;
1166aff8795cSHeiko Stübner case CLK_DDR:
1167aff8795cSHeiko Stübner new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate);
1168aff8795cSHeiko Stübner break;
1169aff8795cSHeiko Stübner case HCLK_EMMC:
1170aff8795cSHeiko Stübner case HCLK_SDMMC:
1171aff8795cSHeiko Stübner case HCLK_SDIO0:
117245112271SXu Ziyuan case SCLK_EMMC:
117345112271SXu Ziyuan case SCLK_SDMMC:
117445112271SXu Ziyuan case SCLK_SDIO0:
1175aff8795cSHeiko Stübner new_rate = rockchip_mmc_set_clk(cru, gclk_rate, clk->id, rate);
1176aff8795cSHeiko Stübner break;
1177aff8795cSHeiko Stübner case SCLK_SPI0:
1178aff8795cSHeiko Stübner case SCLK_SPI1:
1179aff8795cSHeiko Stübner case SCLK_SPI2:
1180aff8795cSHeiko Stübner new_rate = rockchip_spi_set_clk(cru, gclk_rate, clk->id, rate);
1181aff8795cSHeiko Stübner break;
1182aff8795cSHeiko Stübner #ifndef CONFIG_SPL_BUILD
1183aff8795cSHeiko Stübner case SCLK_MAC:
1184b0b68708SDavid Wu new_rate = rockchip_mac_set_clk(priv->cru, rate);
1185aff8795cSHeiko Stübner break;
1186aff8795cSHeiko Stübner case DCLK_VOP0:
1187aff8795cSHeiko Stübner case DCLK_VOP1:
118855611901SElaine Zhang case ACLK_VIO0:
118955611901SElaine Zhang case ACLK_VIO1:
1190aff8795cSHeiko Stübner new_rate = rockchip_vop_set_clk(cru, priv->grf, clk->id, rate);
1191aff8795cSHeiko Stübner break;
1192aff8795cSHeiko Stübner case SCLK_EDP_24M:
1193aff8795cSHeiko Stübner /* clk_edp_24M source: 24M */
1194aff8795cSHeiko Stübner rk_setreg(&cru->cru_clksel_con[28], 1 << 15);
1195aff8795cSHeiko Stübner
1196aff8795cSHeiko Stübner /* rst edp */
1197aff8795cSHeiko Stübner rk_setreg(&cru->cru_clksel_con[6], 1 << 15);
1198aff8795cSHeiko Stübner udelay(1);
1199aff8795cSHeiko Stübner rk_clrreg(&cru->cru_clksel_con[6], 1 << 15);
1200aff8795cSHeiko Stübner new_rate = rate;
1201aff8795cSHeiko Stübner break;
1202aff8795cSHeiko Stübner case PCLK_HDMI_CTRL:
1203aff8795cSHeiko Stübner /* enable pclk hdmi ctrl */
1204aff8795cSHeiko Stübner rk_clrreg(&cru->cru_clkgate_con[16], 1 << 9);
1205aff8795cSHeiko Stübner
1206aff8795cSHeiko Stübner /* software reset hdmi */
1207aff8795cSHeiko Stübner rk_setreg(&cru->cru_clkgate_con[7], 1 << 9);
1208aff8795cSHeiko Stübner udelay(1);
1209aff8795cSHeiko Stübner rk_clrreg(&cru->cru_clkgate_con[7], 1 << 9);
1210aff8795cSHeiko Stübner new_rate = rate;
1211aff8795cSHeiko Stübner break;
12122e8ea5b0SElaine Zhang case SCLK_CRYPTO:
12138d52d662SWyon Bi new_rate = rockchip_crypto_set_clk(priv->cru, rate);
12142e8ea5b0SElaine Zhang break;
12151ca40e22SElaine Zhang case SCLK_TESTOUT_SRC:
12161ca40e22SElaine Zhang case SCLK_TESTOUT:
12171ca40e22SElaine Zhang new_rate = rockchip_test_set_clk(priv->cru, clk->id, rate);
12181ca40e22SElaine Zhang break;
1219aff8795cSHeiko Stübner #endif
1220b6e52402SDavid Wu case SCLK_SARADC:
1221b6e52402SDavid Wu new_rate = rockchip_saradc_set_clk(priv->cru, rate);
1222b6e52402SDavid Wu break;
1223cb3c37fcSElaine Zhang case SCLK_TSADC:
1224cb3c37fcSElaine Zhang new_rate = rockchip_tsadc_set_clk(priv->cru, rate);
1225cb3c37fcSElaine Zhang break;
1226b0b68708SDavid Wu case PLL_GPLL:
1227b0b68708SDavid Wu case PLL_CPLL:
1228b0b68708SDavid Wu case PLL_NPLL:
1229b0b68708SDavid Wu case ACLK_CPU:
1230b0b68708SDavid Wu case HCLK_CPU:
1231b0b68708SDavid Wu case PCLK_CPU:
1232b0b68708SDavid Wu case ACLK_PERI:
1233b0b68708SDavid Wu case HCLK_PERI:
1234b0b68708SDavid Wu case PCLK_PERI:
1235b0b68708SDavid Wu case SCLK_UART0:
1236b0b68708SDavid Wu return 0;
1237aff8795cSHeiko Stübner default:
1238aff8795cSHeiko Stübner return -ENOENT;
1239aff8795cSHeiko Stübner }
1240aff8795cSHeiko Stübner
1241aff8795cSHeiko Stübner return new_rate;
1242aff8795cSHeiko Stübner }
1243aff8795cSHeiko Stübner
124411fbeef5SZiyuan Xu #define ROCKCHIP_MMC_DELAY_SEL BIT(10)
124511fbeef5SZiyuan Xu #define ROCKCHIP_MMC_DEGREE_MASK 0x3
124611fbeef5SZiyuan Xu #define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
124711fbeef5SZiyuan Xu #define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
124811fbeef5SZiyuan Xu
124911fbeef5SZiyuan Xu #define PSECS_PER_SEC 1000000000000LL
125011fbeef5SZiyuan Xu /*
125111fbeef5SZiyuan Xu * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
125211fbeef5SZiyuan Xu * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
125311fbeef5SZiyuan Xu */
125411fbeef5SZiyuan Xu #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
125511fbeef5SZiyuan Xu
rockchip_mmc_get_phase(struct clk * clk)125611fbeef5SZiyuan Xu int rockchip_mmc_get_phase(struct clk *clk)
125711fbeef5SZiyuan Xu {
125811fbeef5SZiyuan Xu struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
125911fbeef5SZiyuan Xu struct rk3288_cru *cru = priv->cru;
126011fbeef5SZiyuan Xu u32 raw_value, delay_num;
126111fbeef5SZiyuan Xu u16 degrees = 0;
126211fbeef5SZiyuan Xu ulong rate;
126311fbeef5SZiyuan Xu
126411fbeef5SZiyuan Xu rate = rk3288_clk_get_rate(clk);
126511fbeef5SZiyuan Xu
126611fbeef5SZiyuan Xu if (rate < 0)
126711fbeef5SZiyuan Xu return rate;
126811fbeef5SZiyuan Xu
126911fbeef5SZiyuan Xu if (clk->id == SCLK_EMMC_SAMPLE)
127011fbeef5SZiyuan Xu raw_value = readl(&cru->cru_emmc_con[1]);
127111fbeef5SZiyuan Xu else
127211fbeef5SZiyuan Xu raw_value = readl(&cru->cru_sdmmc_con[1]);
127311fbeef5SZiyuan Xu
127411fbeef5SZiyuan Xu degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
127511fbeef5SZiyuan Xu
127611fbeef5SZiyuan Xu if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
127711fbeef5SZiyuan Xu /* degrees/delaynum * 10000 */
127811fbeef5SZiyuan Xu unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
127911fbeef5SZiyuan Xu 36 * (rate / 1000000);
128011fbeef5SZiyuan Xu
128111fbeef5SZiyuan Xu delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
128211fbeef5SZiyuan Xu delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
128311fbeef5SZiyuan Xu degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
128411fbeef5SZiyuan Xu }
128511fbeef5SZiyuan Xu
128611fbeef5SZiyuan Xu return degrees % 360;
128711fbeef5SZiyuan Xu }
128811fbeef5SZiyuan Xu
rockchip_mmc_set_phase(struct clk * clk,u32 degrees)128911fbeef5SZiyuan Xu int rockchip_mmc_set_phase(struct clk *clk, u32 degrees)
129011fbeef5SZiyuan Xu {
129111fbeef5SZiyuan Xu struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
129211fbeef5SZiyuan Xu struct rk3288_cru *cru = priv->cru;
129311fbeef5SZiyuan Xu u8 nineties, remainder, delay_num;
129411fbeef5SZiyuan Xu u32 raw_value, delay;
129511fbeef5SZiyuan Xu ulong rate;
129611fbeef5SZiyuan Xu
129711fbeef5SZiyuan Xu rate = rk3288_clk_get_rate(clk);
129811fbeef5SZiyuan Xu
129911fbeef5SZiyuan Xu if (rate < 0)
130011fbeef5SZiyuan Xu return rate;
130111fbeef5SZiyuan Xu
130211fbeef5SZiyuan Xu nineties = degrees / 90;
130311fbeef5SZiyuan Xu remainder = (degrees % 90);
130411fbeef5SZiyuan Xu
130511fbeef5SZiyuan Xu /*
130611fbeef5SZiyuan Xu * Convert to delay; do a little extra work to make sure we
130711fbeef5SZiyuan Xu * don't overflow 32-bit / 64-bit numbers.
130811fbeef5SZiyuan Xu */
130911fbeef5SZiyuan Xu delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
131011fbeef5SZiyuan Xu delay *= remainder;
131111fbeef5SZiyuan Xu delay = DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 *
131211fbeef5SZiyuan Xu (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
131311fbeef5SZiyuan Xu
131411fbeef5SZiyuan Xu delay_num = (u8)min_t(u32, delay, 255);
131511fbeef5SZiyuan Xu
131611fbeef5SZiyuan Xu raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
131711fbeef5SZiyuan Xu raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
131811fbeef5SZiyuan Xu raw_value |= nineties;
131911fbeef5SZiyuan Xu
132011fbeef5SZiyuan Xu if (clk->id == SCLK_EMMC_SAMPLE)
132111fbeef5SZiyuan Xu writel(raw_value | 0xffff0000, &cru->cru_emmc_con[1]);
132211fbeef5SZiyuan Xu else
132311fbeef5SZiyuan Xu writel(raw_value | 0xffff0000, &cru->cru_sdmmc_con[1]);
132411fbeef5SZiyuan Xu
132511fbeef5SZiyuan Xu debug("mmc set_phase(%d) delay_nums=%u reg=%#x actual_degrees=%d\n",
132611fbeef5SZiyuan Xu degrees, delay_num, raw_value, rockchip_mmc_get_phase(clk));
132711fbeef5SZiyuan Xu
132811fbeef5SZiyuan Xu return 0;
132911fbeef5SZiyuan Xu }
133011fbeef5SZiyuan Xu
rk3288_clk_get_phase(struct clk * clk)133111fbeef5SZiyuan Xu static int rk3288_clk_get_phase(struct clk *clk)
133211fbeef5SZiyuan Xu {
133311fbeef5SZiyuan Xu int ret;
133411fbeef5SZiyuan Xu
133511fbeef5SZiyuan Xu switch (clk->id) {
133611fbeef5SZiyuan Xu case SCLK_EMMC_SAMPLE:
133711fbeef5SZiyuan Xu case SCLK_SDMMC_SAMPLE:
133811fbeef5SZiyuan Xu ret = rockchip_mmc_get_phase(clk);
133911fbeef5SZiyuan Xu break;
134011fbeef5SZiyuan Xu default:
134111fbeef5SZiyuan Xu return -ENOENT;
134211fbeef5SZiyuan Xu }
134311fbeef5SZiyuan Xu
134411fbeef5SZiyuan Xu return ret;
134511fbeef5SZiyuan Xu }
134611fbeef5SZiyuan Xu
rk3288_clk_set_phase(struct clk * clk,int degrees)134711fbeef5SZiyuan Xu static int rk3288_clk_set_phase(struct clk *clk, int degrees)
134811fbeef5SZiyuan Xu {
134911fbeef5SZiyuan Xu int ret;
135011fbeef5SZiyuan Xu
135111fbeef5SZiyuan Xu switch (clk->id) {
135211fbeef5SZiyuan Xu case SCLK_EMMC_SAMPLE:
135311fbeef5SZiyuan Xu case SCLK_SDMMC_SAMPLE:
135411fbeef5SZiyuan Xu ret = rockchip_mmc_set_phase(clk, degrees);
135511fbeef5SZiyuan Xu break;
135611fbeef5SZiyuan Xu default:
135711fbeef5SZiyuan Xu return -ENOENT;
135811fbeef5SZiyuan Xu }
135911fbeef5SZiyuan Xu
136011fbeef5SZiyuan Xu return ret;
136111fbeef5SZiyuan Xu }
136211fbeef5SZiyuan Xu
rk3288_gmac_set_parent(struct clk * clk,struct clk * parent)1363d2866b32SPhilipp Tomsich static int __maybe_unused rk3288_gmac_set_parent(struct clk *clk, struct clk *parent)
1364b0b68708SDavid Wu {
1365b0b68708SDavid Wu struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
1366b0b68708SDavid Wu struct rk3288_cru *cru = priv->cru;
1367b0b68708SDavid Wu const char *clock_output_name;
1368b0b68708SDavid Wu int ret;
1369b0b68708SDavid Wu
1370b0b68708SDavid Wu /*
1371b0b68708SDavid Wu * If the requested parent is in the same clock-controller and
1372b0b68708SDavid Wu * the id is SCLK_MAC_PLL ("mac_pll_src"), switch to the internal
1373b0b68708SDavid Wu * clock.
1374b0b68708SDavid Wu */
1375b0b68708SDavid Wu if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC_PLL)) {
1376b0b68708SDavid Wu debug("%s: switching GAMC to SCLK_MAC_PLL\n", __func__);
1377b0b68708SDavid Wu rk_clrsetreg(&cru->cru_clksel_con[21], RMII_EXTCLK_MASK, 0);
1378b0b68708SDavid Wu return 0;
1379b0b68708SDavid Wu }
1380b0b68708SDavid Wu
1381b0b68708SDavid Wu /*
1382b0b68708SDavid Wu * Otherwise, we need to check the clock-output-names of the
1383b0b68708SDavid Wu * requested parent to see if the requested id is "ext_gmac".
1384b0b68708SDavid Wu */
1385b0b68708SDavid Wu ret = dev_read_string_index(parent->dev, "clock-output-names",
1386b0b68708SDavid Wu parent->id, &clock_output_name);
1387b0b68708SDavid Wu if (ret < 0)
1388b0b68708SDavid Wu return -ENODATA;
1389b0b68708SDavid Wu
1390b0b68708SDavid Wu /* If this is "ext_gmac", switch to the external clock input */
1391b0b68708SDavid Wu if (!strcmp(clock_output_name, "ext_gmac")) {
1392b0b68708SDavid Wu debug("%s: switching GMAC to external clock\n", __func__);
1393b0b68708SDavid Wu rk_clrsetreg(&cru->cru_clksel_con[21], RMII_EXTCLK_MASK,
1394b0b68708SDavid Wu RMII_EXTCLK_SELECT_EXT_CLK << RMII_EXTCLK_SHIFT);
1395b0b68708SDavid Wu return 0;
1396b0b68708SDavid Wu }
1397b0b68708SDavid Wu
1398b0b68708SDavid Wu return -EINVAL;
1399b0b68708SDavid Wu }
1400b0b68708SDavid Wu
rk3288_vop_set_parent(struct clk * clk,struct clk * parent)1401b328c914SElaine Zhang static int __maybe_unused rk3288_vop_set_parent(struct clk *clk,
1402b328c914SElaine Zhang struct clk *parent)
1403b328c914SElaine Zhang {
1404b328c914SElaine Zhang struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
1405b328c914SElaine Zhang struct rk3288_cru *cru = priv->cru;
1406b328c914SElaine Zhang int parent_sel;
1407b328c914SElaine Zhang
1408b328c914SElaine Zhang switch (parent->id) {
1409b328c914SElaine Zhang case PLL_CPLL:
1410b328c914SElaine Zhang parent_sel = 0;
1411b328c914SElaine Zhang break;
1412b328c914SElaine Zhang case PLL_GPLL:
1413b328c914SElaine Zhang parent_sel = 1;
1414b328c914SElaine Zhang break;
1415b328c914SElaine Zhang case PLL_NPLL:
1416b328c914SElaine Zhang parent_sel = 2;
1417b328c914SElaine Zhang break;
1418b328c914SElaine Zhang default:
1419b328c914SElaine Zhang parent_sel = 0;
1420b328c914SElaine Zhang break;
1421b328c914SElaine Zhang }
1422b328c914SElaine Zhang
1423b328c914SElaine Zhang switch (clk->id) {
1424b328c914SElaine Zhang case DCLK_VOP0:
1425b328c914SElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[27],
1426b328c914SElaine Zhang DCLK_VOP0_PLL_MASK, parent_sel << 0);
1427b328c914SElaine Zhang break;
1428b328c914SElaine Zhang case DCLK_VOP1:
1429b328c914SElaine Zhang rk_clrsetreg(&cru->cru_clksel_con[29],
1430b328c914SElaine Zhang DCLK_VOP1_PLL_MASK, parent_sel << 6);
1431b328c914SElaine Zhang break;
1432b328c914SElaine Zhang default:
1433b328c914SElaine Zhang return -EINVAL;
1434b328c914SElaine Zhang }
1435b328c914SElaine Zhang
1436b328c914SElaine Zhang return 0;
1437b328c914SElaine Zhang }
1438b328c914SElaine Zhang
rk3288_clk_set_parent(struct clk * clk,struct clk * parent)1439d2866b32SPhilipp Tomsich static int __maybe_unused rk3288_clk_set_parent(struct clk *clk, struct clk *parent)
1440b0b68708SDavid Wu {
1441b0b68708SDavid Wu switch (clk->id) {
1442b0b68708SDavid Wu case SCLK_MAC:
1443b0b68708SDavid Wu return rk3288_gmac_set_parent(clk, parent);
1444b328c914SElaine Zhang case DCLK_VOP0:
1445b328c914SElaine Zhang case DCLK_VOP1:
1446b328c914SElaine Zhang return rk3288_vop_set_parent(clk, parent);
1447b0b68708SDavid Wu case SCLK_USBPHY480M_SRC:
1448b0b68708SDavid Wu return 0;
1449b0b68708SDavid Wu }
1450b0b68708SDavid Wu
1451b0b68708SDavid Wu debug("%s: unsupported clk %ld\n", __func__, clk->id);
1452b0b68708SDavid Wu return -ENOENT;
1453b0b68708SDavid Wu }
1454b0b68708SDavid Wu
1455aff8795cSHeiko Stübner static struct clk_ops rk3288_clk_ops = {
1456aff8795cSHeiko Stübner .get_rate = rk3288_clk_get_rate,
1457aff8795cSHeiko Stübner .set_rate = rk3288_clk_set_rate,
145811fbeef5SZiyuan Xu .get_phase = rk3288_clk_get_phase,
145911fbeef5SZiyuan Xu .set_phase = rk3288_clk_set_phase,
1460d2866b32SPhilipp Tomsich #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
1461b0b68708SDavid Wu .set_parent = rk3288_clk_set_parent,
1462d2866b32SPhilipp Tomsich #endif
1463aff8795cSHeiko Stübner };
1464aff8795cSHeiko Stübner
rk3288_clk_ofdata_to_platdata(struct udevice * dev)1465aff8795cSHeiko Stübner static int rk3288_clk_ofdata_to_platdata(struct udevice *dev)
1466aff8795cSHeiko Stübner {
1467aff8795cSHeiko Stübner #if !CONFIG_IS_ENABLED(OF_PLATDATA)
1468aff8795cSHeiko Stübner struct rk3288_clk_priv *priv = dev_get_priv(dev);
1469aff8795cSHeiko Stübner
14707d9cf22dSKever Yang priv->cru = dev_read_addr_ptr(dev);
1471aff8795cSHeiko Stübner #endif
1472aff8795cSHeiko Stübner
1473aff8795cSHeiko Stübner return 0;
1474aff8795cSHeiko Stübner }
1475aff8795cSHeiko Stübner
rk3288_clk_probe(struct udevice * dev)1476aff8795cSHeiko Stübner static int rk3288_clk_probe(struct udevice *dev)
1477aff8795cSHeiko Stübner {
1478aff8795cSHeiko Stübner struct rk3288_clk_priv *priv = dev_get_priv(dev);
1479d3cb46aaSSimon Glass bool init_clocks = false;
148055611901SElaine Zhang int ret;
1481aff8795cSHeiko Stübner
1482aff8795cSHeiko Stübner priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
1483aff8795cSHeiko Stübner if (IS_ERR(priv->grf))
1484aff8795cSHeiko Stübner return PTR_ERR(priv->grf);
1485aff8795cSHeiko Stübner #ifdef CONFIG_SPL_BUILD
1486aff8795cSHeiko Stübner #if CONFIG_IS_ENABLED(OF_PLATDATA)
1487aff8795cSHeiko Stübner struct rk3288_clk_plat *plat = dev_get_platdata(dev);
1488aff8795cSHeiko Stübner
1489aff8795cSHeiko Stübner priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
1490aff8795cSHeiko Stübner #endif
1491d3cb46aaSSimon Glass init_clocks = true;
1492aff8795cSHeiko Stübner #endif
1493d3cb46aaSSimon Glass if (!(gd->flags & GD_FLG_RELOC)) {
1494d3cb46aaSSimon Glass u32 reg;
1495d3cb46aaSSimon Glass
1496d3cb46aaSSimon Glass /*
1497d3cb46aaSSimon Glass * Init clocks in U-Boot proper if the NPLL is runnning. This
1498d3cb46aaSSimon Glass * indicates that a previous boot loader set up the clocks, so
1499d3cb46aaSSimon Glass * we need to redo it. U-Boot's SPL does not set this clock.
150087e0d84fSKever Yang * Or if the CPLL is not init, we need to redo the clk_init.
1501d3cb46aaSSimon Glass */
1502d3cb46aaSSimon Glass reg = readl(&priv->cru->cru_mode_con);
150387e0d84fSKever Yang if ((((reg & NPLL_MODE_MASK) >> NPLL_MODE_SHIFT) ==
150487e0d84fSKever Yang NPLL_MODE_NORMAL) ||
150587e0d84fSKever Yang !(reg & CPLL_MODE_MASK))
1506d3cb46aaSSimon Glass init_clocks = true;
1507d3cb46aaSSimon Glass }
1508d3cb46aaSSimon Glass
1509ec0307efSElaine Zhang priv->sync_kernel = false;
1510ec0307efSElaine Zhang if (!priv->armclk_enter_hz)
1511ec0307efSElaine Zhang priv->armclk_enter_hz = rkclk_pll_get_rate(priv->cru,
1512ec0307efSElaine Zhang CLK_ARM);
1513ec0307efSElaine Zhang
1514ec0307efSElaine Zhang if (init_clocks) {
1515d3cb46aaSSimon Glass rkclk_init(priv->cru, priv->grf);
1516ec0307efSElaine Zhang if (!priv->armclk_init_hz)
1517ec0307efSElaine Zhang priv->armclk_init_hz = rkclk_pll_get_rate(priv->cru,
1518ec0307efSElaine Zhang CLK_ARM);
1519ec0307efSElaine Zhang } else {
1520ec0307efSElaine Zhang if (!priv->armclk_init_hz)
1521ec0307efSElaine Zhang priv->armclk_init_hz = priv->armclk_enter_hz;
1522ec0307efSElaine Zhang }
1523aff8795cSHeiko Stübner
152455611901SElaine Zhang ret = clk_set_defaults(dev);
152555611901SElaine Zhang if (ret)
152655611901SElaine Zhang debug("%s clk_set_defaults failed %d\n", __func__, ret);
152755611901SElaine Zhang else
152855611901SElaine Zhang priv->sync_kernel = true;
152955611901SElaine Zhang
1530aff8795cSHeiko Stübner return 0;
1531aff8795cSHeiko Stübner }
1532aff8795cSHeiko Stübner
rk3288_clk_bind(struct udevice * dev)1533aff8795cSHeiko Stübner static int rk3288_clk_bind(struct udevice *dev)
1534aff8795cSHeiko Stübner {
1535aff8795cSHeiko Stübner int ret;
15363d555d75SElaine Zhang struct udevice *sys_child, *sf_child;
1537fbdd1558SKever Yang struct sysreset_reg *priv;
15383d555d75SElaine Zhang struct softreset_reg *sf_priv;
1539aff8795cSHeiko Stübner
1540aff8795cSHeiko Stübner /* The reset driver does not have a device node, so bind it here */
1541fbdd1558SKever Yang ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
1542fbdd1558SKever Yang &sys_child);
1543fbdd1558SKever Yang if (ret) {
1544fbdd1558SKever Yang debug("Warning: No sysreset driver: ret=%d\n", ret);
1545fbdd1558SKever Yang } else {
1546fbdd1558SKever Yang priv = malloc(sizeof(struct sysreset_reg));
1547fbdd1558SKever Yang priv->glb_srst_fst_value = offsetof(struct rk3288_cru,
1548fbdd1558SKever Yang cru_glb_srst_fst_value);
1549fbdd1558SKever Yang priv->glb_srst_snd_value = offsetof(struct rk3288_cru,
1550fbdd1558SKever Yang cru_glb_srst_snd_value);
1551fbdd1558SKever Yang sys_child->priv = priv;
1552fbdd1558SKever Yang }
1553aff8795cSHeiko Stübner
15543d555d75SElaine Zhang ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset",
15553d555d75SElaine Zhang dev_ofnode(dev), &sf_child);
15563d555d75SElaine Zhang if (ret) {
15573d555d75SElaine Zhang debug("Warning: No rockchip reset driver: ret=%d\n", ret);
15583d555d75SElaine Zhang } else {
15593d555d75SElaine Zhang sf_priv = malloc(sizeof(struct softreset_reg));
15603d555d75SElaine Zhang sf_priv->sf_reset_offset = offsetof(struct rk3288_cru,
15613d555d75SElaine Zhang cru_softrst_con[0]);
15623d555d75SElaine Zhang sf_priv->sf_reset_num = 12;
15633d555d75SElaine Zhang sf_child->priv = sf_priv;
15643d555d75SElaine Zhang }
15653d555d75SElaine Zhang
1566aff8795cSHeiko Stübner return 0;
1567aff8795cSHeiko Stübner }
1568aff8795cSHeiko Stübner
1569aff8795cSHeiko Stübner static const struct udevice_id rk3288_clk_ids[] = {
1570aff8795cSHeiko Stübner { .compatible = "rockchip,rk3288-cru" },
1571aff8795cSHeiko Stübner { }
1572aff8795cSHeiko Stübner };
1573aff8795cSHeiko Stübner
1574aff8795cSHeiko Stübner U_BOOT_DRIVER(rockchip_rk3288_cru) = {
1575aff8795cSHeiko Stübner .name = "rockchip_rk3288_cru",
1576aff8795cSHeiko Stübner .id = UCLASS_CLK,
1577aff8795cSHeiko Stübner .of_match = rk3288_clk_ids,
1578aff8795cSHeiko Stübner .priv_auto_alloc_size = sizeof(struct rk3288_clk_priv),
1579aff8795cSHeiko Stübner .platdata_auto_alloc_size = sizeof(struct rk3288_clk_plat),
1580aff8795cSHeiko Stübner .ops = &rk3288_clk_ops,
1581aff8795cSHeiko Stübner .bind = rk3288_clk_bind,
1582aff8795cSHeiko Stübner .ofdata_to_platdata = rk3288_clk_ofdata_to_platdata,
1583aff8795cSHeiko Stübner .probe = rk3288_clk_probe,
1584aff8795cSHeiko Stübner };
1585ec0307efSElaine Zhang
1586ec0307efSElaine Zhang #ifndef CONFIG_SPL_BUILD
1587ec0307efSElaine Zhang /**
1588ec0307efSElaine Zhang * soc_clk_dump() - Print clock frequencies
1589ec0307efSElaine Zhang * Returns zero on success
1590ec0307efSElaine Zhang *
1591ec0307efSElaine Zhang * Implementation for the clk dump command.
1592ec0307efSElaine Zhang */
soc_clk_dump(void)1593ec0307efSElaine Zhang int soc_clk_dump(void)
1594ec0307efSElaine Zhang {
1595ec0307efSElaine Zhang struct udevice *cru_dev;
1596ec0307efSElaine Zhang struct rk3288_clk_priv *priv;
1597ec0307efSElaine Zhang const struct rk3288_clk_info *clk_dump;
1598ec0307efSElaine Zhang struct clk clk;
1599ec0307efSElaine Zhang unsigned long clk_count = ARRAY_SIZE(clks_dump);
1600ec0307efSElaine Zhang unsigned long rate;
1601ec0307efSElaine Zhang int i, ret;
1602ec0307efSElaine Zhang
1603ec0307efSElaine Zhang ret = uclass_get_device_by_driver(UCLASS_CLK,
1604ec0307efSElaine Zhang DM_GET_DRIVER(rockchip_rk3288_cru),
1605ec0307efSElaine Zhang &cru_dev);
1606ec0307efSElaine Zhang if (ret) {
1607ec0307efSElaine Zhang printf("%s failed to get cru device\n", __func__);
1608ec0307efSElaine Zhang return ret;
1609ec0307efSElaine Zhang }
1610ec0307efSElaine Zhang
1611ec0307efSElaine Zhang priv = dev_get_priv(cru_dev);
1612ec0307efSElaine Zhang printf("CLK: (%s. arm: enter %lu KHz, init %lu KHz, kernel %lu%s)\n",
1613ec0307efSElaine Zhang priv->sync_kernel ? "sync kernel" : "uboot",
1614ec0307efSElaine Zhang priv->armclk_enter_hz / 1000,
1615ec0307efSElaine Zhang priv->armclk_init_hz / 1000,
1616ec0307efSElaine Zhang priv->set_armclk_rate ? priv->armclk_hz / 1000 : 0,
1617ec0307efSElaine Zhang priv->set_armclk_rate ? " KHz" : "N/A");
1618ec0307efSElaine Zhang for (i = 0; i < clk_count; i++) {
1619ec0307efSElaine Zhang clk_dump = &clks_dump[i];
1620ec0307efSElaine Zhang if (clk_dump->name) {
1621ec0307efSElaine Zhang clk.id = clk_dump->id;
1622ec0307efSElaine Zhang if (clk_dump->is_cru)
1623ec0307efSElaine Zhang ret = clk_request(cru_dev, &clk);
1624ec0307efSElaine Zhang if (ret < 0)
1625ec0307efSElaine Zhang return ret;
1626ec0307efSElaine Zhang
1627ec0307efSElaine Zhang rate = clk_get_rate(&clk);
1628ec0307efSElaine Zhang clk_free(&clk);
1629ec0307efSElaine Zhang if (i == 0) {
1630ec0307efSElaine Zhang if (rate < 0)
1631ec0307efSElaine Zhang printf(" %s %s\n", clk_dump->name,
1632ec0307efSElaine Zhang "unknown");
1633ec0307efSElaine Zhang else
1634ec0307efSElaine Zhang printf(" %s %lu KHz\n", clk_dump->name,
1635ec0307efSElaine Zhang rate / 1000);
1636ec0307efSElaine Zhang } else {
1637ec0307efSElaine Zhang if (rate < 0)
1638ec0307efSElaine Zhang printf(" %s %s\n", clk_dump->name,
1639ec0307efSElaine Zhang "unknown");
1640ec0307efSElaine Zhang else
1641ec0307efSElaine Zhang printf(" %s %lu KHz\n", clk_dump->name,
1642ec0307efSElaine Zhang rate / 1000);
1643ec0307efSElaine Zhang }
1644ec0307efSElaine Zhang }
1645ec0307efSElaine Zhang }
1646ec0307efSElaine Zhang
1647ec0307efSElaine Zhang return 0;
1648ec0307efSElaine Zhang }
1649ec0307efSElaine Zhang #endif
1650ec0307efSElaine Zhang
1651