1aff8795cSHeiko Stübner /*
2aff8795cSHeiko Stübner * (C) Copyright 2015 Google, Inc
38fa6979bSPhilipp Tomsich * (C) 2017 Theobroma Systems Design und Consulting GmbH
4aff8795cSHeiko Stübner *
5aff8795cSHeiko Stübner * SPDX-License-Identifier: GPL-2.0
6aff8795cSHeiko Stübner */
7aff8795cSHeiko Stübner
8aff8795cSHeiko Stübner #include <common.h>
9aff8795cSHeiko Stübner #include <clk-uclass.h>
10aff8795cSHeiko Stübner #include <dm.h>
115ae2fd97SKever Yang #include <dt-structs.h>
12aff8795cSHeiko Stübner #include <errno.h>
135ae2fd97SKever Yang #include <mapmem.h>
14aff8795cSHeiko Stübner #include <syscon.h>
15e78991bfSDavid Wu #include <bitfield.h>
16aff8795cSHeiko Stübner #include <asm/io.h>
17aff8795cSHeiko Stübner #include <asm/arch/clock.h>
18aff8795cSHeiko Stübner #include <asm/arch/cru_rk3399.h>
19aff8795cSHeiko Stübner #include <asm/arch/hardware.h>
20aff8795cSHeiko Stübner #include <dm/lists.h>
21aff8795cSHeiko Stübner #include <dt-bindings/clock/rk3399-cru.h>
22aff8795cSHeiko Stübner
23aff8795cSHeiko Stübner DECLARE_GLOBAL_DATA_PTR;
24aff8795cSHeiko Stübner
255ae2fd97SKever Yang #if CONFIG_IS_ENABLED(OF_PLATDATA)
265ae2fd97SKever Yang struct rk3399_clk_plat {
275ae2fd97SKever Yang struct dtd_rockchip_rk3399_cru dtd;
285e79f443SKever Yang };
295e79f443SKever Yang
305ae2fd97SKever Yang struct rk3399_pmuclk_plat {
315ae2fd97SKever Yang struct dtd_rockchip_rk3399_pmucru dtd;
325ae2fd97SKever Yang };
335ae2fd97SKever Yang #endif
345ae2fd97SKever Yang
35aff8795cSHeiko Stübner struct pll_div {
36aff8795cSHeiko Stübner u32 refdiv;
37aff8795cSHeiko Stübner u32 fbdiv;
38aff8795cSHeiko Stübner u32 postdiv1;
39aff8795cSHeiko Stübner u32 postdiv2;
40aff8795cSHeiko Stübner u32 frac;
41ccced9e1SLin Huang u32 freq;
42aff8795cSHeiko Stübner };
43aff8795cSHeiko Stübner
44aff8795cSHeiko Stübner #define RATE_TO_DIV(input_rate, output_rate) \
45aff8795cSHeiko Stübner ((input_rate) / (output_rate) - 1);
46aff8795cSHeiko Stübner #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
47aff8795cSHeiko Stübner
48aff8795cSHeiko Stübner #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
49aff8795cSHeiko Stübner .refdiv = _refdiv,\
50aff8795cSHeiko Stübner .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
51ccced9e1SLin Huang .postdiv1 = _postdiv1, .postdiv2 = _postdiv2, .freq = hz};
52aff8795cSHeiko Stübner
531d2570d3SCaesar Wang #if !defined(CONFIG_SPL_BUILD)
54aff8795cSHeiko Stübner static const struct pll_div ppll_init_cfg = PLL_DIVISORS(PPLL_HZ, 2, 2, 1);
5561dff33bSPhilipp Tomsich #endif
5647b08574SKever Yang static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 3, 1);
574897499eSElaine Zhang static const struct pll_div npll_init_cfg = PLL_DIVISORS(NPLL_HZ, 1, 3, 1);
58ccced9e1SLin Huang static const struct pll_div apll_1600_cfg = PLL_DIVISORS(1600*MHz, 3, 1, 1);
598b75ff34SElaine Zhang static const struct pll_div apll_816_cfg = PLL_DIVISORS(816 * MHz, 1, 2, 1);
60ccced9e1SLin Huang static const struct pll_div apll_600_cfg = PLL_DIVISORS(600*MHz, 1, 2, 1);
61aff8795cSHeiko Stübner
62ccced9e1SLin Huang static const struct pll_div *apll_cfgs[] = {
63ccced9e1SLin Huang [APLL_1600_MHZ] = &apll_1600_cfg,
648b75ff34SElaine Zhang [APLL_816_MHZ] = &apll_816_cfg,
65ccced9e1SLin Huang [APLL_600_MHZ] = &apll_600_cfg,
66aff8795cSHeiko Stübner };
67aff8795cSHeiko Stübner
688b75ff34SElaine Zhang #ifndef CONFIG_SPL_BUILD
698b75ff34SElaine Zhang #define RK3399_CLK_DUMP(_id, _name, _iscru) \
708b75ff34SElaine Zhang { \
718b75ff34SElaine Zhang .id = _id, \
728b75ff34SElaine Zhang .name = _name, \
738b75ff34SElaine Zhang .is_cru = _iscru, \
748b75ff34SElaine Zhang }
758b75ff34SElaine Zhang
768b75ff34SElaine Zhang static const struct rk3399_clk_info clks_dump[] = {
778b75ff34SElaine Zhang RK3399_CLK_DUMP(PLL_APLLL, "aplll", true),
788b75ff34SElaine Zhang RK3399_CLK_DUMP(PLL_APLLB, "apllb", true),
798b75ff34SElaine Zhang RK3399_CLK_DUMP(PLL_DPLL, "dpll", true),
808b75ff34SElaine Zhang RK3399_CLK_DUMP(PLL_CPLL, "cpll", true),
818b75ff34SElaine Zhang RK3399_CLK_DUMP(PLL_GPLL, "gpll", true),
828b75ff34SElaine Zhang RK3399_CLK_DUMP(PLL_NPLL, "npll", true),
838b75ff34SElaine Zhang RK3399_CLK_DUMP(PLL_VPLL, "vpll", true),
848b75ff34SElaine Zhang RK3399_CLK_DUMP(ACLK_PERIHP, "aclk_perihp", true),
858b75ff34SElaine Zhang RK3399_CLK_DUMP(HCLK_PERIHP, "hclk_perihp", true),
868b75ff34SElaine Zhang RK3399_CLK_DUMP(PCLK_PERIHP, "pclk_perihp", true),
878b75ff34SElaine Zhang RK3399_CLK_DUMP(ACLK_PERILP0, "aclk_perilp0", true),
888b75ff34SElaine Zhang RK3399_CLK_DUMP(HCLK_PERILP0, "hclk_perilp0", true),
898b75ff34SElaine Zhang RK3399_CLK_DUMP(PCLK_PERILP0, "pclk_perilp0", true),
908b75ff34SElaine Zhang RK3399_CLK_DUMP(HCLK_PERILP1, "hclk_perilp1", true),
918b75ff34SElaine Zhang RK3399_CLK_DUMP(PCLK_PERILP1, "pclk_perilp1", true),
928b75ff34SElaine Zhang };
938b75ff34SElaine Zhang #endif
948b75ff34SElaine Zhang
95aff8795cSHeiko Stübner enum {
96aff8795cSHeiko Stübner /* PLL_CON0 */
97aff8795cSHeiko Stübner PLL_FBDIV_MASK = 0xfff,
98aff8795cSHeiko Stübner PLL_FBDIV_SHIFT = 0,
99aff8795cSHeiko Stübner
100aff8795cSHeiko Stübner /* PLL_CON1 */
101aff8795cSHeiko Stübner PLL_POSTDIV2_SHIFT = 12,
102aff8795cSHeiko Stübner PLL_POSTDIV2_MASK = 0x7 << PLL_POSTDIV2_SHIFT,
103aff8795cSHeiko Stübner PLL_POSTDIV1_SHIFT = 8,
104aff8795cSHeiko Stübner PLL_POSTDIV1_MASK = 0x7 << PLL_POSTDIV1_SHIFT,
105aff8795cSHeiko Stübner PLL_REFDIV_MASK = 0x3f,
106aff8795cSHeiko Stübner PLL_REFDIV_SHIFT = 0,
107aff8795cSHeiko Stübner
108aff8795cSHeiko Stübner /* PLL_CON2 */
109aff8795cSHeiko Stübner PLL_LOCK_STATUS_SHIFT = 31,
110aff8795cSHeiko Stübner PLL_LOCK_STATUS_MASK = 1 << PLL_LOCK_STATUS_SHIFT,
111aff8795cSHeiko Stübner PLL_FRACDIV_MASK = 0xffffff,
112aff8795cSHeiko Stübner PLL_FRACDIV_SHIFT = 0,
113aff8795cSHeiko Stübner
114aff8795cSHeiko Stübner /* PLL_CON3 */
115aff8795cSHeiko Stübner PLL_MODE_SHIFT = 8,
116aff8795cSHeiko Stübner PLL_MODE_MASK = 3 << PLL_MODE_SHIFT,
117aff8795cSHeiko Stübner PLL_MODE_SLOW = 0,
118aff8795cSHeiko Stübner PLL_MODE_NORM,
119aff8795cSHeiko Stübner PLL_MODE_DEEP,
120aff8795cSHeiko Stübner PLL_DSMPD_SHIFT = 3,
121aff8795cSHeiko Stübner PLL_DSMPD_MASK = 1 << PLL_DSMPD_SHIFT,
122aff8795cSHeiko Stübner PLL_INTEGER_MODE = 1,
123aff8795cSHeiko Stübner
124aff8795cSHeiko Stübner /* PMUCRU_CLKSEL_CON0 */
125aff8795cSHeiko Stübner PMU_PCLK_DIV_CON_MASK = 0x1f,
126aff8795cSHeiko Stübner PMU_PCLK_DIV_CON_SHIFT = 0,
127aff8795cSHeiko Stübner
128aff8795cSHeiko Stübner /* PMUCRU_CLKSEL_CON1 */
129aff8795cSHeiko Stübner SPI3_PLL_SEL_SHIFT = 7,
130aff8795cSHeiko Stübner SPI3_PLL_SEL_MASK = 1 << SPI3_PLL_SEL_SHIFT,
131aff8795cSHeiko Stübner SPI3_PLL_SEL_24M = 0,
132aff8795cSHeiko Stübner SPI3_PLL_SEL_PPLL = 1,
133aff8795cSHeiko Stübner SPI3_DIV_CON_SHIFT = 0x0,
134aff8795cSHeiko Stübner SPI3_DIV_CON_MASK = 0x7f,
135aff8795cSHeiko Stübner
136aff8795cSHeiko Stübner /* PMUCRU_CLKSEL_CON2 */
137aff8795cSHeiko Stübner I2C_DIV_CON_MASK = 0x7f,
1385e79f443SKever Yang CLK_I2C8_DIV_CON_SHIFT = 8,
1395e79f443SKever Yang CLK_I2C0_DIV_CON_SHIFT = 0,
140aff8795cSHeiko Stübner
141aff8795cSHeiko Stübner /* PMUCRU_CLKSEL_CON3 */
1425e79f443SKever Yang CLK_I2C4_DIV_CON_SHIFT = 0,
143aff8795cSHeiko Stübner
144ccced9e1SLin Huang /* CLKSEL_CON0 / CLKSEL_CON2 */
145ccced9e1SLin Huang ACLKM_CORE_DIV_CON_MASK = 0x1f,
146ccced9e1SLin Huang ACLKM_CORE_DIV_CON_SHIFT = 8,
147ccced9e1SLin Huang CLK_CORE_PLL_SEL_MASK = 3,
148ccced9e1SLin Huang CLK_CORE_PLL_SEL_SHIFT = 6,
149ccced9e1SLin Huang CLK_CORE_PLL_SEL_ALPLL = 0x0,
150ccced9e1SLin Huang CLK_CORE_PLL_SEL_ABPLL = 0x1,
151ccced9e1SLin Huang CLK_CORE_PLL_SEL_DPLL = 0x10,
152ccced9e1SLin Huang CLK_CORE_PLL_SEL_GPLL = 0x11,
153ccced9e1SLin Huang CLK_CORE_DIV_MASK = 0x1f,
154ccced9e1SLin Huang CLK_CORE_DIV_SHIFT = 0,
155aff8795cSHeiko Stübner
156ccced9e1SLin Huang /* CLKSEL_CON1 / CLKSEL_CON3 */
157ccced9e1SLin Huang PCLK_DBG_DIV_MASK = 0x1f,
158ccced9e1SLin Huang PCLK_DBG_DIV_SHIFT = 0x8,
159ccced9e1SLin Huang ATCLK_CORE_DIV_MASK = 0x1f,
160ccced9e1SLin Huang ATCLK_CORE_DIV_SHIFT = 0,
161aff8795cSHeiko Stübner
162aff8795cSHeiko Stübner /* CLKSEL_CON14 */
163aff8795cSHeiko Stübner PCLK_PERIHP_DIV_CON_SHIFT = 12,
164aff8795cSHeiko Stübner PCLK_PERIHP_DIV_CON_MASK = 0x7 << PCLK_PERIHP_DIV_CON_SHIFT,
165aff8795cSHeiko Stübner HCLK_PERIHP_DIV_CON_SHIFT = 8,
166aff8795cSHeiko Stübner HCLK_PERIHP_DIV_CON_MASK = 3 << HCLK_PERIHP_DIV_CON_SHIFT,
167aff8795cSHeiko Stübner ACLK_PERIHP_PLL_SEL_SHIFT = 7,
168aff8795cSHeiko Stübner ACLK_PERIHP_PLL_SEL_MASK = 1 << ACLK_PERIHP_PLL_SEL_SHIFT,
169aff8795cSHeiko Stübner ACLK_PERIHP_PLL_SEL_CPLL = 0,
170aff8795cSHeiko Stübner ACLK_PERIHP_PLL_SEL_GPLL = 1,
171aff8795cSHeiko Stübner ACLK_PERIHP_DIV_CON_SHIFT = 0,
172aff8795cSHeiko Stübner ACLK_PERIHP_DIV_CON_MASK = 0x1f,
173aff8795cSHeiko Stübner
174aff8795cSHeiko Stübner /* CLKSEL_CON21 */
175aff8795cSHeiko Stübner ACLK_EMMC_PLL_SEL_SHIFT = 7,
176aff8795cSHeiko Stübner ACLK_EMMC_PLL_SEL_MASK = 0x1 << ACLK_EMMC_PLL_SEL_SHIFT,
177aff8795cSHeiko Stübner ACLK_EMMC_PLL_SEL_GPLL = 0x1,
178aff8795cSHeiko Stübner ACLK_EMMC_DIV_CON_SHIFT = 0,
179aff8795cSHeiko Stübner ACLK_EMMC_DIV_CON_MASK = 0x1f,
180aff8795cSHeiko Stübner
181aff8795cSHeiko Stübner /* CLKSEL_CON22 */
182aff8795cSHeiko Stübner CLK_EMMC_PLL_SHIFT = 8,
183aff8795cSHeiko Stübner CLK_EMMC_PLL_MASK = 0x7 << CLK_EMMC_PLL_SHIFT,
184aff8795cSHeiko Stübner CLK_EMMC_PLL_SEL_GPLL = 0x1,
185fd4b2dc0SKever Yang CLK_EMMC_PLL_SEL_24M = 0x5,
186aff8795cSHeiko Stübner CLK_EMMC_DIV_CON_SHIFT = 0,
187aff8795cSHeiko Stübner CLK_EMMC_DIV_CON_MASK = 0x7f << CLK_EMMC_DIV_CON_SHIFT,
188aff8795cSHeiko Stübner
189aff8795cSHeiko Stübner /* CLKSEL_CON23 */
190aff8795cSHeiko Stübner PCLK_PERILP0_DIV_CON_SHIFT = 12,
191aff8795cSHeiko Stübner PCLK_PERILP0_DIV_CON_MASK = 0x7 << PCLK_PERILP0_DIV_CON_SHIFT,
192aff8795cSHeiko Stübner HCLK_PERILP0_DIV_CON_SHIFT = 8,
193aff8795cSHeiko Stübner HCLK_PERILP0_DIV_CON_MASK = 3 << HCLK_PERILP0_DIV_CON_SHIFT,
194aff8795cSHeiko Stübner ACLK_PERILP0_PLL_SEL_SHIFT = 7,
195aff8795cSHeiko Stübner ACLK_PERILP0_PLL_SEL_MASK = 1 << ACLK_PERILP0_PLL_SEL_SHIFT,
196aff8795cSHeiko Stübner ACLK_PERILP0_PLL_SEL_CPLL = 0,
197aff8795cSHeiko Stübner ACLK_PERILP0_PLL_SEL_GPLL = 1,
198aff8795cSHeiko Stübner ACLK_PERILP0_DIV_CON_SHIFT = 0,
199aff8795cSHeiko Stübner ACLK_PERILP0_DIV_CON_MASK = 0x1f,
200aff8795cSHeiko Stübner
201187d951bSElaine Zhang /* CRU_CLK_SEL24_CON */
202187d951bSElaine Zhang CRYPTO0_PLL_SEL_SHIFT = 6,
203187d951bSElaine Zhang CRYPTO0_PLL_SEL_MASK = 3 << CRYPTO0_PLL_SEL_SHIFT,
204187d951bSElaine Zhang CRYPTO_PLL_SEL_CPLL = 0,
205187d951bSElaine Zhang CRYPTO_PLL_SEL_GPLL,
206187d951bSElaine Zhang CRYPTO_PLL_SEL_PPLL = 0,
207187d951bSElaine Zhang CRYPTO0_DIV_SHIFT = 0,
208187d951bSElaine Zhang CRYPTO0_DIV_MASK = 0x1f << CRYPTO0_DIV_SHIFT,
209187d951bSElaine Zhang
210aff8795cSHeiko Stübner /* CLKSEL_CON25 */
211aff8795cSHeiko Stübner PCLK_PERILP1_DIV_CON_SHIFT = 8,
212aff8795cSHeiko Stübner PCLK_PERILP1_DIV_CON_MASK = 0x7 << PCLK_PERILP1_DIV_CON_SHIFT,
213aff8795cSHeiko Stübner HCLK_PERILP1_PLL_SEL_SHIFT = 7,
214aff8795cSHeiko Stübner HCLK_PERILP1_PLL_SEL_MASK = 1 << HCLK_PERILP1_PLL_SEL_SHIFT,
215aff8795cSHeiko Stübner HCLK_PERILP1_PLL_SEL_CPLL = 0,
216aff8795cSHeiko Stübner HCLK_PERILP1_PLL_SEL_GPLL = 1,
217aff8795cSHeiko Stübner HCLK_PERILP1_DIV_CON_SHIFT = 0,
218aff8795cSHeiko Stübner HCLK_PERILP1_DIV_CON_MASK = 0x1f,
219aff8795cSHeiko Stübner
220aff8795cSHeiko Stübner /* CLKSEL_CON26 */
221aff8795cSHeiko Stübner CLK_SARADC_DIV_CON_SHIFT = 8,
222e78991bfSDavid Wu CLK_SARADC_DIV_CON_MASK = GENMASK(15, 8),
223e78991bfSDavid Wu CLK_SARADC_DIV_CON_WIDTH = 8,
224187d951bSElaine Zhang CRYPTO1_PLL_SEL_SHIFT = 6,
225187d951bSElaine Zhang CRYPTO1_PLL_SEL_MASK = 3 << CRYPTO1_PLL_SEL_SHIFT,
226187d951bSElaine Zhang CRYPTO1_DIV_SHIFT = 0,
227187d951bSElaine Zhang CRYPTO1_DIV_MASK = 0x1f << CRYPTO1_DIV_SHIFT,
228aff8795cSHeiko Stübner
229aff8795cSHeiko Stübner /* CLKSEL_CON27 */
230aff8795cSHeiko Stübner CLK_TSADC_SEL_X24M = 0x0,
231aff8795cSHeiko Stübner CLK_TSADC_SEL_SHIFT = 15,
232aff8795cSHeiko Stübner CLK_TSADC_SEL_MASK = 1 << CLK_TSADC_SEL_SHIFT,
233aff8795cSHeiko Stübner CLK_TSADC_DIV_CON_SHIFT = 0,
234aff8795cSHeiko Stübner CLK_TSADC_DIV_CON_MASK = 0x3ff,
235aff8795cSHeiko Stübner
236aff8795cSHeiko Stübner /* CLKSEL_CON47 & CLKSEL_CON48 */
237aff8795cSHeiko Stübner ACLK_VOP_PLL_SEL_SHIFT = 6,
238aff8795cSHeiko Stübner ACLK_VOP_PLL_SEL_MASK = 0x3 << ACLK_VOP_PLL_SEL_SHIFT,
239aff8795cSHeiko Stübner ACLK_VOP_PLL_SEL_CPLL = 0x1,
2406bfdfc4fSElaine Zhang ACLK_VOP_PLL_SEL_GPLL = 0x2,
241aff8795cSHeiko Stübner ACLK_VOP_DIV_CON_SHIFT = 0,
242aff8795cSHeiko Stübner ACLK_VOP_DIV_CON_MASK = 0x1f << ACLK_VOP_DIV_CON_SHIFT,
243aff8795cSHeiko Stübner
244aff8795cSHeiko Stübner /* CLKSEL_CON49 & CLKSEL_CON50 */
245aff8795cSHeiko Stübner DCLK_VOP_DCLK_SEL_SHIFT = 11,
246aff8795cSHeiko Stübner DCLK_VOP_DCLK_SEL_MASK = 1 << DCLK_VOP_DCLK_SEL_SHIFT,
247aff8795cSHeiko Stübner DCLK_VOP_DCLK_SEL_DIVOUT = 0,
248aff8795cSHeiko Stübner DCLK_VOP_PLL_SEL_SHIFT = 8,
249aff8795cSHeiko Stübner DCLK_VOP_PLL_SEL_MASK = 3 << DCLK_VOP_PLL_SEL_SHIFT,
250aff8795cSHeiko Stübner DCLK_VOP_PLL_SEL_VPLL = 0,
2516bfdfc4fSElaine Zhang DCLK_VOP_PLL_SEL_CPLL = 1,
252aff8795cSHeiko Stübner DCLK_VOP_DIV_CON_MASK = 0xff,
253aff8795cSHeiko Stübner DCLK_VOP_DIV_CON_SHIFT = 0,
254aff8795cSHeiko Stübner
255981ee0bdSElaine Zhang /* CLKSEL_CON57 */
256981ee0bdSElaine Zhang PCLK_ALIVE_DIV_CON_SHIFT = 0,
257981ee0bdSElaine Zhang PCLK_ALIVE_DIV_CON_MASK = 0x1f << PCLK_ALIVE_DIV_CON_SHIFT,
258981ee0bdSElaine Zhang
259aff8795cSHeiko Stübner /* CLKSEL_CON58 */
2608fa6979bSPhilipp Tomsich CLK_SPI_PLL_SEL_WIDTH = 1,
2618fa6979bSPhilipp Tomsich CLK_SPI_PLL_SEL_MASK = ((1 < CLK_SPI_PLL_SEL_WIDTH) - 1),
262aff8795cSHeiko Stübner CLK_SPI_PLL_SEL_CPLL = 0,
263aff8795cSHeiko Stübner CLK_SPI_PLL_SEL_GPLL = 1,
2648fa6979bSPhilipp Tomsich CLK_SPI_PLL_DIV_CON_WIDTH = 7,
2658fa6979bSPhilipp Tomsich CLK_SPI_PLL_DIV_CON_MASK = ((1 << CLK_SPI_PLL_DIV_CON_WIDTH) - 1),
2668fa6979bSPhilipp Tomsich
267aff8795cSHeiko Stübner CLK_SPI5_PLL_DIV_CON_SHIFT = 8,
268aff8795cSHeiko Stübner CLK_SPI5_PLL_SEL_SHIFT = 15,
269aff8795cSHeiko Stübner
270aff8795cSHeiko Stübner /* CLKSEL_CON59 */
271aff8795cSHeiko Stübner CLK_SPI1_PLL_SEL_SHIFT = 15,
272aff8795cSHeiko Stübner CLK_SPI1_PLL_DIV_CON_SHIFT = 8,
273aff8795cSHeiko Stübner CLK_SPI0_PLL_SEL_SHIFT = 7,
274aff8795cSHeiko Stübner CLK_SPI0_PLL_DIV_CON_SHIFT = 0,
275aff8795cSHeiko Stübner
276aff8795cSHeiko Stübner /* CLKSEL_CON60 */
277aff8795cSHeiko Stübner CLK_SPI4_PLL_SEL_SHIFT = 15,
278aff8795cSHeiko Stübner CLK_SPI4_PLL_DIV_CON_SHIFT = 8,
279aff8795cSHeiko Stübner CLK_SPI2_PLL_SEL_SHIFT = 7,
280aff8795cSHeiko Stübner CLK_SPI2_PLL_DIV_CON_SHIFT = 0,
281aff8795cSHeiko Stübner
282aff8795cSHeiko Stübner /* CLKSEL_CON61 */
283aff8795cSHeiko Stübner CLK_I2C_PLL_SEL_MASK = 1,
284aff8795cSHeiko Stübner CLK_I2C_PLL_SEL_CPLL = 0,
285aff8795cSHeiko Stübner CLK_I2C_PLL_SEL_GPLL = 1,
286aff8795cSHeiko Stübner CLK_I2C5_PLL_SEL_SHIFT = 15,
287aff8795cSHeiko Stübner CLK_I2C5_DIV_CON_SHIFT = 8,
288aff8795cSHeiko Stübner CLK_I2C1_PLL_SEL_SHIFT = 7,
289aff8795cSHeiko Stübner CLK_I2C1_DIV_CON_SHIFT = 0,
290aff8795cSHeiko Stübner
291aff8795cSHeiko Stübner /* CLKSEL_CON62 */
292aff8795cSHeiko Stübner CLK_I2C6_PLL_SEL_SHIFT = 15,
293aff8795cSHeiko Stübner CLK_I2C6_DIV_CON_SHIFT = 8,
294aff8795cSHeiko Stübner CLK_I2C2_PLL_SEL_SHIFT = 7,
295aff8795cSHeiko Stübner CLK_I2C2_DIV_CON_SHIFT = 0,
296aff8795cSHeiko Stübner
297aff8795cSHeiko Stübner /* CLKSEL_CON63 */
298aff8795cSHeiko Stübner CLK_I2C7_PLL_SEL_SHIFT = 15,
299aff8795cSHeiko Stübner CLK_I2C7_DIV_CON_SHIFT = 8,
300aff8795cSHeiko Stübner CLK_I2C3_PLL_SEL_SHIFT = 7,
301aff8795cSHeiko Stübner CLK_I2C3_DIV_CON_SHIFT = 0,
302aff8795cSHeiko Stübner
303aff8795cSHeiko Stübner /* CRU_SOFTRST_CON4 */
304aff8795cSHeiko Stübner RESETN_DDR0_REQ_SHIFT = 8,
305aff8795cSHeiko Stübner RESETN_DDR0_REQ_MASK = 1 << RESETN_DDR0_REQ_SHIFT,
306aff8795cSHeiko Stübner RESETN_DDRPHY0_REQ_SHIFT = 9,
307aff8795cSHeiko Stübner RESETN_DDRPHY0_REQ_MASK = 1 << RESETN_DDRPHY0_REQ_SHIFT,
308aff8795cSHeiko Stübner RESETN_DDR1_REQ_SHIFT = 12,
309aff8795cSHeiko Stübner RESETN_DDR1_REQ_MASK = 1 << RESETN_DDR1_REQ_SHIFT,
310aff8795cSHeiko Stübner RESETN_DDRPHY1_REQ_SHIFT = 13,
311aff8795cSHeiko Stübner RESETN_DDRPHY1_REQ_MASK = 1 << RESETN_DDRPHY1_REQ_SHIFT,
312aff8795cSHeiko Stübner };
313aff8795cSHeiko Stübner
314aff8795cSHeiko Stübner #define VCO_MAX_KHZ (3200 * (MHz / KHz))
315aff8795cSHeiko Stübner #define VCO_MIN_KHZ (800 * (MHz / KHz))
316aff8795cSHeiko Stübner #define OUTPUT_MAX_KHZ (3200 * (MHz / KHz))
317aff8795cSHeiko Stübner #define OUTPUT_MIN_KHZ (16 * (MHz / KHz))
318aff8795cSHeiko Stübner
319aff8795cSHeiko Stübner /*
320aff8795cSHeiko Stübner * the div restructions of pll in integer mode, these are defined in
321aff8795cSHeiko Stübner * * CRU_*PLL_CON0 or PMUCRU_*PLL_CON0
322aff8795cSHeiko Stübner */
323aff8795cSHeiko Stübner #define PLL_DIV_MIN 16
324aff8795cSHeiko Stübner #define PLL_DIV_MAX 3200
325aff8795cSHeiko Stübner
326aff8795cSHeiko Stübner /*
327aff8795cSHeiko Stübner * How to calculate the PLL(from TRM V0.3 Part 1 Page 63):
328aff8795cSHeiko Stübner * Formulas also embedded within the Fractional PLL Verilog model:
329aff8795cSHeiko Stübner * If DSMPD = 1 (DSM is disabled, "integer mode")
330aff8795cSHeiko Stübner * FOUTVCO = FREF / REFDIV * FBDIV
331aff8795cSHeiko Stübner * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
332aff8795cSHeiko Stübner * Where:
333aff8795cSHeiko Stübner * FOUTVCO = Fractional PLL non-divided output frequency
334aff8795cSHeiko Stübner * FOUTPOSTDIV = Fractional PLL divided output frequency
335aff8795cSHeiko Stübner * (output of second post divider)
336aff8795cSHeiko Stübner * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input)
337aff8795cSHeiko Stübner * REFDIV = Fractional PLL input reference clock divider
338aff8795cSHeiko Stübner * FBDIV = Integer value programmed into feedback divide
339aff8795cSHeiko Stübner *
340aff8795cSHeiko Stübner */
3416bfdfc4fSElaine Zhang
rkclk_pll_get_rate(u32 * pll_con)3426bfdfc4fSElaine Zhang static uint32_t rkclk_pll_get_rate(u32 *pll_con)
3436bfdfc4fSElaine Zhang {
3446bfdfc4fSElaine Zhang u32 refdiv, fbdiv, postdiv1, postdiv2;
3456bfdfc4fSElaine Zhang u32 con;
3466bfdfc4fSElaine Zhang
3476bfdfc4fSElaine Zhang con = readl(&pll_con[3]);
3486bfdfc4fSElaine Zhang switch ((con & PLL_MODE_MASK) >> PLL_MODE_SHIFT) {
3496bfdfc4fSElaine Zhang case PLL_MODE_SLOW:
3506bfdfc4fSElaine Zhang return OSC_HZ;
3516bfdfc4fSElaine Zhang case PLL_MODE_NORM:
3526bfdfc4fSElaine Zhang /* normal mode */
3536bfdfc4fSElaine Zhang con = readl(&pll_con[0]);
3546bfdfc4fSElaine Zhang fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
3556bfdfc4fSElaine Zhang con = readl(&pll_con[1]);
3566bfdfc4fSElaine Zhang postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
3576bfdfc4fSElaine Zhang postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
3586bfdfc4fSElaine Zhang refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
3596bfdfc4fSElaine Zhang return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
3606bfdfc4fSElaine Zhang case PLL_MODE_DEEP:
3616bfdfc4fSElaine Zhang default:
3626bfdfc4fSElaine Zhang return 32768;
3636bfdfc4fSElaine Zhang }
3646bfdfc4fSElaine Zhang }
3656bfdfc4fSElaine Zhang
rkclk_set_pll(u32 * pll_con,const struct pll_div * div)366aff8795cSHeiko Stübner static void rkclk_set_pll(u32 *pll_con, const struct pll_div *div)
367aff8795cSHeiko Stübner {
368aff8795cSHeiko Stübner /* All 8 PLLs have same VCO and output frequency range restrictions. */
369aff8795cSHeiko Stübner u32 vco_khz = OSC_HZ / 1000 * div->fbdiv / div->refdiv;
370aff8795cSHeiko Stübner u32 output_khz = vco_khz / div->postdiv1 / div->postdiv2;
371aff8795cSHeiko Stübner
372aff8795cSHeiko Stübner debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, "
373aff8795cSHeiko Stübner "postdiv2=%d, vco=%u khz, output=%u khz\n",
374aff8795cSHeiko Stübner pll_con, div->fbdiv, div->refdiv, div->postdiv1,
375aff8795cSHeiko Stübner div->postdiv2, vco_khz, output_khz);
376aff8795cSHeiko Stübner assert(vco_khz >= VCO_MIN_KHZ && vco_khz <= VCO_MAX_KHZ &&
377aff8795cSHeiko Stübner output_khz >= OUTPUT_MIN_KHZ && output_khz <= OUTPUT_MAX_KHZ &&
378aff8795cSHeiko Stübner div->fbdiv >= PLL_DIV_MIN && div->fbdiv <= PLL_DIV_MAX);
379aff8795cSHeiko Stübner
380aff8795cSHeiko Stübner /*
381aff8795cSHeiko Stübner * When power on or changing PLL setting,
382aff8795cSHeiko Stübner * we must force PLL into slow mode to ensure output stable clock.
383aff8795cSHeiko Stübner */
384aff8795cSHeiko Stübner rk_clrsetreg(&pll_con[3], PLL_MODE_MASK,
385aff8795cSHeiko Stübner PLL_MODE_SLOW << PLL_MODE_SHIFT);
386aff8795cSHeiko Stübner
387aff8795cSHeiko Stübner /* use integer mode */
388aff8795cSHeiko Stübner rk_clrsetreg(&pll_con[3], PLL_DSMPD_MASK,
389aff8795cSHeiko Stübner PLL_INTEGER_MODE << PLL_DSMPD_SHIFT);
390aff8795cSHeiko Stübner
391aff8795cSHeiko Stübner rk_clrsetreg(&pll_con[0], PLL_FBDIV_MASK,
392aff8795cSHeiko Stübner div->fbdiv << PLL_FBDIV_SHIFT);
393aff8795cSHeiko Stübner rk_clrsetreg(&pll_con[1],
394aff8795cSHeiko Stübner PLL_POSTDIV2_MASK | PLL_POSTDIV1_MASK |
395aff8795cSHeiko Stübner PLL_REFDIV_MASK | PLL_REFDIV_SHIFT,
396aff8795cSHeiko Stübner (div->postdiv2 << PLL_POSTDIV2_SHIFT) |
397aff8795cSHeiko Stübner (div->postdiv1 << PLL_POSTDIV1_SHIFT) |
398aff8795cSHeiko Stübner (div->refdiv << PLL_REFDIV_SHIFT));
399aff8795cSHeiko Stübner
400aff8795cSHeiko Stübner /* waiting for pll lock */
401aff8795cSHeiko Stübner while (!(readl(&pll_con[2]) & (1 << PLL_LOCK_STATUS_SHIFT)))
402aff8795cSHeiko Stübner udelay(1);
403aff8795cSHeiko Stübner
404aff8795cSHeiko Stübner /* pll enter normal mode */
405aff8795cSHeiko Stübner rk_clrsetreg(&pll_con[3], PLL_MODE_MASK,
406aff8795cSHeiko Stübner PLL_MODE_NORM << PLL_MODE_SHIFT);
407aff8795cSHeiko Stübner }
408aff8795cSHeiko Stübner
rk3399_pll_get_rate(struct rk3399_clk_priv * priv,enum rk3399_pll_id pll_id)4096bfdfc4fSElaine Zhang static ulong rk3399_pll_get_rate(struct rk3399_clk_priv *priv,
4106bfdfc4fSElaine Zhang enum rk3399_pll_id pll_id)
4116bfdfc4fSElaine Zhang {
4126bfdfc4fSElaine Zhang struct rk3399_cru *cru = priv->cru;
4136bfdfc4fSElaine Zhang u32 *pll_con;
4146bfdfc4fSElaine Zhang
4156bfdfc4fSElaine Zhang switch (pll_id) {
4166bfdfc4fSElaine Zhang case PLL_APLLL:
4176bfdfc4fSElaine Zhang pll_con = &cru->apll_l_con[0];
4186bfdfc4fSElaine Zhang break;
4196bfdfc4fSElaine Zhang case PLL_APLLB:
4206bfdfc4fSElaine Zhang pll_con = &cru->apll_b_con[0];
4216bfdfc4fSElaine Zhang break;
4226bfdfc4fSElaine Zhang case PLL_DPLL:
4236bfdfc4fSElaine Zhang pll_con = &cru->dpll_con[0];
4246bfdfc4fSElaine Zhang break;
4256bfdfc4fSElaine Zhang case PLL_CPLL:
4266bfdfc4fSElaine Zhang pll_con = &cru->cpll_con[0];
4276bfdfc4fSElaine Zhang break;
4286bfdfc4fSElaine Zhang case PLL_GPLL:
4296bfdfc4fSElaine Zhang pll_con = &cru->gpll_con[0];
4306bfdfc4fSElaine Zhang break;
4316bfdfc4fSElaine Zhang case PLL_NPLL:
4326bfdfc4fSElaine Zhang pll_con = &cru->npll_con[0];
4336bfdfc4fSElaine Zhang break;
4346bfdfc4fSElaine Zhang case PLL_VPLL:
4356bfdfc4fSElaine Zhang pll_con = &cru->vpll_con[0];
4366bfdfc4fSElaine Zhang break;
4376bfdfc4fSElaine Zhang default:
4386bfdfc4fSElaine Zhang pll_con = &cru->vpll_con[0];
4396bfdfc4fSElaine Zhang break;
4406bfdfc4fSElaine Zhang }
4416bfdfc4fSElaine Zhang
4426bfdfc4fSElaine Zhang return rkclk_pll_get_rate(pll_con);
4436bfdfc4fSElaine Zhang }
4446bfdfc4fSElaine Zhang
pll_para_config(u32 freq_hz,struct pll_div * div)445aff8795cSHeiko Stübner static int pll_para_config(u32 freq_hz, struct pll_div *div)
446aff8795cSHeiko Stübner {
447aff8795cSHeiko Stübner u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0;
448aff8795cSHeiko Stübner u32 postdiv1, postdiv2 = 1;
449aff8795cSHeiko Stübner u32 fref_khz;
450aff8795cSHeiko Stübner u32 diff_khz, best_diff_khz;
451aff8795cSHeiko Stübner const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16;
452aff8795cSHeiko Stübner const u32 max_postdiv1 = 7, max_postdiv2 = 7;
453aff8795cSHeiko Stübner u32 vco_khz;
454aff8795cSHeiko Stübner u32 freq_khz = freq_hz / KHz;
455aff8795cSHeiko Stübner
456aff8795cSHeiko Stübner if (!freq_hz) {
457aff8795cSHeiko Stübner printf("%s: the frequency can't be 0 Hz\n", __func__);
458aff8795cSHeiko Stübner return -1;
459aff8795cSHeiko Stübner }
460aff8795cSHeiko Stübner
461aff8795cSHeiko Stübner postdiv1 = DIV_ROUND_UP(VCO_MIN_KHZ, freq_khz);
462aff8795cSHeiko Stübner if (postdiv1 > max_postdiv1) {
463aff8795cSHeiko Stübner postdiv2 = DIV_ROUND_UP(postdiv1, max_postdiv1);
464aff8795cSHeiko Stübner postdiv1 = DIV_ROUND_UP(postdiv1, postdiv2);
465aff8795cSHeiko Stübner }
466aff8795cSHeiko Stübner
467aff8795cSHeiko Stübner vco_khz = freq_khz * postdiv1 * postdiv2;
468aff8795cSHeiko Stübner
469aff8795cSHeiko Stübner if (vco_khz < VCO_MIN_KHZ || vco_khz > VCO_MAX_KHZ ||
470aff8795cSHeiko Stübner postdiv2 > max_postdiv2) {
471aff8795cSHeiko Stübner printf("%s: Cannot find out a supported VCO"
472aff8795cSHeiko Stübner " for Frequency (%uHz).\n", __func__, freq_hz);
473aff8795cSHeiko Stübner return -1;
474aff8795cSHeiko Stübner }
475aff8795cSHeiko Stübner
476aff8795cSHeiko Stübner div->postdiv1 = postdiv1;
477aff8795cSHeiko Stübner div->postdiv2 = postdiv2;
478aff8795cSHeiko Stübner
479aff8795cSHeiko Stübner best_diff_khz = vco_khz;
480aff8795cSHeiko Stübner for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) {
481aff8795cSHeiko Stübner fref_khz = ref_khz / refdiv;
482aff8795cSHeiko Stübner
483aff8795cSHeiko Stübner fbdiv = vco_khz / fref_khz;
484aff8795cSHeiko Stübner if ((fbdiv >= max_fbdiv) || (fbdiv <= min_fbdiv))
485aff8795cSHeiko Stübner continue;
486aff8795cSHeiko Stübner diff_khz = vco_khz - fbdiv * fref_khz;
487aff8795cSHeiko Stübner if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) {
488aff8795cSHeiko Stübner fbdiv++;
489aff8795cSHeiko Stübner diff_khz = fref_khz - diff_khz;
490aff8795cSHeiko Stübner }
491aff8795cSHeiko Stübner
492aff8795cSHeiko Stübner if (diff_khz >= best_diff_khz)
493aff8795cSHeiko Stübner continue;
494aff8795cSHeiko Stübner
495aff8795cSHeiko Stübner best_diff_khz = diff_khz;
496aff8795cSHeiko Stübner div->refdiv = refdiv;
497aff8795cSHeiko Stübner div->fbdiv = fbdiv;
498aff8795cSHeiko Stübner }
499aff8795cSHeiko Stübner
500aff8795cSHeiko Stübner if (best_diff_khz > 4 * (MHz/KHz)) {
501aff8795cSHeiko Stübner printf("%s: Failed to match output frequency %u, "
502aff8795cSHeiko Stübner "difference is %u Hz,exceed 4MHZ\n", __func__, freq_hz,
503aff8795cSHeiko Stübner best_diff_khz * KHz);
504aff8795cSHeiko Stübner return -1;
505aff8795cSHeiko Stübner }
506aff8795cSHeiko Stübner return 0;
507aff8795cSHeiko Stübner }
508aff8795cSHeiko Stübner
rk3399_configure_cpu(struct rk3399_cru * cru,enum apll_frequencies freq,enum cpu_cluster cluster)509aff8795cSHeiko Stübner void rk3399_configure_cpu(struct rk3399_cru *cru,
510ccced9e1SLin Huang enum apll_frequencies freq,
511ccced9e1SLin Huang enum cpu_cluster cluster)
512aff8795cSHeiko Stübner {
513aff8795cSHeiko Stübner u32 aclkm_div;
514aff8795cSHeiko Stübner u32 pclk_dbg_div;
515ccced9e1SLin Huang u32 atclk_div, apll_hz;
516ccced9e1SLin Huang int con_base, parent;
517ccced9e1SLin Huang u32 *pll_con;
518aff8795cSHeiko Stübner
519ccced9e1SLin Huang switch (cluster) {
520ccced9e1SLin Huang case CPU_CLUSTER_LITTLE:
521ccced9e1SLin Huang con_base = 0;
522ccced9e1SLin Huang parent = CLK_CORE_PLL_SEL_ALPLL;
523ccced9e1SLin Huang pll_con = &cru->apll_l_con[0];
524ccced9e1SLin Huang break;
525ccced9e1SLin Huang case CPU_CLUSTER_BIG:
526ccced9e1SLin Huang default:
527ccced9e1SLin Huang con_base = 2;
528ccced9e1SLin Huang parent = CLK_CORE_PLL_SEL_ABPLL;
529ccced9e1SLin Huang pll_con = &cru->apll_b_con[0];
530ccced9e1SLin Huang break;
531ccced9e1SLin Huang }
532aff8795cSHeiko Stübner
533ccced9e1SLin Huang apll_hz = apll_cfgs[freq]->freq;
534ccced9e1SLin Huang rkclk_set_pll(pll_con, apll_cfgs[freq]);
535ccced9e1SLin Huang
536ccced9e1SLin Huang aclkm_div = apll_hz / ACLKM_CORE_HZ - 1;
53788c36f12SElaine Zhang assert((aclkm_div + 1) * ACLKM_CORE_HZ <= apll_hz &&
538aff8795cSHeiko Stübner aclkm_div < 0x1f);
539aff8795cSHeiko Stübner
540ccced9e1SLin Huang pclk_dbg_div = apll_hz / PCLK_DBG_HZ - 1;
54188c36f12SElaine Zhang assert((pclk_dbg_div + 1) * PCLK_DBG_HZ <= apll_hz &&
542aff8795cSHeiko Stübner pclk_dbg_div < 0x1f);
543aff8795cSHeiko Stübner
544ccced9e1SLin Huang atclk_div = apll_hz / ATCLK_CORE_HZ - 1;
54588c36f12SElaine Zhang assert((atclk_div + 1) * ATCLK_CORE_HZ <= apll_hz &&
546aff8795cSHeiko Stübner atclk_div < 0x1f);
547aff8795cSHeiko Stübner
548ccced9e1SLin Huang rk_clrsetreg(&cru->clksel_con[con_base],
549ccced9e1SLin Huang ACLKM_CORE_DIV_CON_MASK | CLK_CORE_PLL_SEL_MASK |
550ccced9e1SLin Huang CLK_CORE_DIV_MASK,
551ccced9e1SLin Huang aclkm_div << ACLKM_CORE_DIV_CON_SHIFT |
552ccced9e1SLin Huang parent << CLK_CORE_PLL_SEL_SHIFT |
553ccced9e1SLin Huang 0 << CLK_CORE_DIV_SHIFT);
554aff8795cSHeiko Stübner
555ccced9e1SLin Huang rk_clrsetreg(&cru->clksel_con[con_base + 1],
556ccced9e1SLin Huang PCLK_DBG_DIV_MASK | ATCLK_CORE_DIV_MASK,
557ccced9e1SLin Huang pclk_dbg_div << PCLK_DBG_DIV_SHIFT |
558ccced9e1SLin Huang atclk_div << ATCLK_CORE_DIV_SHIFT);
559aff8795cSHeiko Stübner }
560aff8795cSHeiko Stübner #define I2C_CLK_REG_MASK(bus) \
561aff8795cSHeiko Stübner (I2C_DIV_CON_MASK << \
562aff8795cSHeiko Stübner CLK_I2C ##bus## _DIV_CON_SHIFT | \
563aff8795cSHeiko Stübner CLK_I2C_PLL_SEL_MASK << \
564aff8795cSHeiko Stübner CLK_I2C ##bus## _PLL_SEL_SHIFT)
565aff8795cSHeiko Stübner
566aff8795cSHeiko Stübner #define I2C_CLK_REG_VALUE(bus, clk_div) \
567aff8795cSHeiko Stübner ((clk_div - 1) << \
568aff8795cSHeiko Stübner CLK_I2C ##bus## _DIV_CON_SHIFT | \
569aff8795cSHeiko Stübner CLK_I2C_PLL_SEL_GPLL << \
570aff8795cSHeiko Stübner CLK_I2C ##bus## _PLL_SEL_SHIFT)
571aff8795cSHeiko Stübner
572aff8795cSHeiko Stübner #define I2C_CLK_DIV_VALUE(con, bus) \
573aff8795cSHeiko Stübner (con >> CLK_I2C ##bus## _DIV_CON_SHIFT) & \
574aff8795cSHeiko Stübner I2C_DIV_CON_MASK;
575aff8795cSHeiko Stübner
5765e79f443SKever Yang #define I2C_PMUCLK_REG_MASK(bus) \
5775e79f443SKever Yang (I2C_DIV_CON_MASK << \
5785e79f443SKever Yang CLK_I2C ##bus## _DIV_CON_SHIFT)
5795e79f443SKever Yang
5805e79f443SKever Yang #define I2C_PMUCLK_REG_VALUE(bus, clk_div) \
5815e79f443SKever Yang ((clk_div - 1) << \
5825e79f443SKever Yang CLK_I2C ##bus## _DIV_CON_SHIFT)
5835e79f443SKever Yang
rk3399_i2c_get_clk(struct rk3399_cru * cru,ulong clk_id)584aff8795cSHeiko Stübner static ulong rk3399_i2c_get_clk(struct rk3399_cru *cru, ulong clk_id)
585aff8795cSHeiko Stübner {
586aff8795cSHeiko Stübner u32 div, con;
587aff8795cSHeiko Stübner
588aff8795cSHeiko Stübner switch (clk_id) {
589aff8795cSHeiko Stübner case SCLK_I2C1:
590aff8795cSHeiko Stübner con = readl(&cru->clksel_con[61]);
591aff8795cSHeiko Stübner div = I2C_CLK_DIV_VALUE(con, 1);
592aff8795cSHeiko Stübner break;
593aff8795cSHeiko Stübner case SCLK_I2C2:
594aff8795cSHeiko Stübner con = readl(&cru->clksel_con[62]);
595aff8795cSHeiko Stübner div = I2C_CLK_DIV_VALUE(con, 2);
596aff8795cSHeiko Stübner break;
597aff8795cSHeiko Stübner case SCLK_I2C3:
598aff8795cSHeiko Stübner con = readl(&cru->clksel_con[63]);
599aff8795cSHeiko Stübner div = I2C_CLK_DIV_VALUE(con, 3);
600aff8795cSHeiko Stübner break;
601aff8795cSHeiko Stübner case SCLK_I2C5:
602aff8795cSHeiko Stübner con = readl(&cru->clksel_con[61]);
603aff8795cSHeiko Stübner div = I2C_CLK_DIV_VALUE(con, 5);
604aff8795cSHeiko Stübner break;
605aff8795cSHeiko Stübner case SCLK_I2C6:
606aff8795cSHeiko Stübner con = readl(&cru->clksel_con[62]);
607aff8795cSHeiko Stübner div = I2C_CLK_DIV_VALUE(con, 6);
608aff8795cSHeiko Stübner break;
609aff8795cSHeiko Stübner case SCLK_I2C7:
610aff8795cSHeiko Stübner con = readl(&cru->clksel_con[63]);
611aff8795cSHeiko Stübner div = I2C_CLK_DIV_VALUE(con, 7);
612aff8795cSHeiko Stübner break;
613aff8795cSHeiko Stübner default:
614aff8795cSHeiko Stübner printf("do not support this i2c bus\n");
615aff8795cSHeiko Stübner return -EINVAL;
616aff8795cSHeiko Stübner }
617aff8795cSHeiko Stübner
618aff8795cSHeiko Stübner return DIV_TO_RATE(GPLL_HZ, div);
619aff8795cSHeiko Stübner }
620aff8795cSHeiko Stübner
rk3399_i2c_set_clk(struct rk3399_cru * cru,ulong clk_id,uint hz)621aff8795cSHeiko Stübner static ulong rk3399_i2c_set_clk(struct rk3399_cru *cru, ulong clk_id, uint hz)
622aff8795cSHeiko Stübner {
623aff8795cSHeiko Stübner int src_clk_div;
624aff8795cSHeiko Stübner
625aff8795cSHeiko Stübner /* i2c0,4,8 src clock from ppll, i2c1,2,3,5,6,7 src clock from gpll*/
626aff8795cSHeiko Stübner src_clk_div = GPLL_HZ / hz;
6275f42424bSLin Huang assert(src_clk_div - 1 <= 127);
628aff8795cSHeiko Stübner
629aff8795cSHeiko Stübner switch (clk_id) {
630aff8795cSHeiko Stübner case SCLK_I2C1:
631aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[61], I2C_CLK_REG_MASK(1),
632aff8795cSHeiko Stübner I2C_CLK_REG_VALUE(1, src_clk_div));
633aff8795cSHeiko Stübner break;
634aff8795cSHeiko Stübner case SCLK_I2C2:
635aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[62], I2C_CLK_REG_MASK(2),
636aff8795cSHeiko Stübner I2C_CLK_REG_VALUE(2, src_clk_div));
637aff8795cSHeiko Stübner break;
638aff8795cSHeiko Stübner case SCLK_I2C3:
639aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[63], I2C_CLK_REG_MASK(3),
640aff8795cSHeiko Stübner I2C_CLK_REG_VALUE(3, src_clk_div));
641aff8795cSHeiko Stübner break;
642aff8795cSHeiko Stübner case SCLK_I2C5:
643aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[61], I2C_CLK_REG_MASK(5),
644aff8795cSHeiko Stübner I2C_CLK_REG_VALUE(5, src_clk_div));
645aff8795cSHeiko Stübner break;
646aff8795cSHeiko Stübner case SCLK_I2C6:
647aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[62], I2C_CLK_REG_MASK(6),
648aff8795cSHeiko Stübner I2C_CLK_REG_VALUE(6, src_clk_div));
649aff8795cSHeiko Stübner break;
650aff8795cSHeiko Stübner case SCLK_I2C7:
651aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[63], I2C_CLK_REG_MASK(7),
652aff8795cSHeiko Stübner I2C_CLK_REG_VALUE(7, src_clk_div));
653aff8795cSHeiko Stübner break;
654aff8795cSHeiko Stübner default:
655aff8795cSHeiko Stübner printf("do not support this i2c bus\n");
656aff8795cSHeiko Stübner return -EINVAL;
657aff8795cSHeiko Stübner }
658aff8795cSHeiko Stübner
659beb90a53SPhilipp Tomsich return rk3399_i2c_get_clk(cru, clk_id);
660aff8795cSHeiko Stübner }
661aff8795cSHeiko Stübner
6628fa6979bSPhilipp Tomsich /*
6638fa6979bSPhilipp Tomsich * RK3399 SPI clocks have a common divider-width (7 bits) and a single bit
6648fa6979bSPhilipp Tomsich * to select either CPLL or GPLL as the clock-parent. The location within
6658fa6979bSPhilipp Tomsich * the enclosing CLKSEL_CON (i.e. div_shift and sel_shift) are variable.
6668fa6979bSPhilipp Tomsich */
6678fa6979bSPhilipp Tomsich
6688fa6979bSPhilipp Tomsich struct spi_clkreg {
6698fa6979bSPhilipp Tomsich uint8_t reg; /* CLKSEL_CON[reg] register in CRU */
6708fa6979bSPhilipp Tomsich uint8_t div_shift;
6718fa6979bSPhilipp Tomsich uint8_t sel_shift;
6728fa6979bSPhilipp Tomsich };
6738fa6979bSPhilipp Tomsich
6748fa6979bSPhilipp Tomsich /*
6758fa6979bSPhilipp Tomsich * The entries are numbered relative to their offset from SCLK_SPI0.
6768fa6979bSPhilipp Tomsich *
6778fa6979bSPhilipp Tomsich * Note that SCLK_SPI3 (which is configured via PMUCRU and requires different
6788fa6979bSPhilipp Tomsich * logic is not supported).
6798fa6979bSPhilipp Tomsich */
6808fa6979bSPhilipp Tomsich static const struct spi_clkreg spi_clkregs[] = {
6818fa6979bSPhilipp Tomsich [0] = { .reg = 59,
6828fa6979bSPhilipp Tomsich .div_shift = CLK_SPI0_PLL_DIV_CON_SHIFT,
6838fa6979bSPhilipp Tomsich .sel_shift = CLK_SPI0_PLL_SEL_SHIFT, },
6848fa6979bSPhilipp Tomsich [1] = { .reg = 59,
6858fa6979bSPhilipp Tomsich .div_shift = CLK_SPI1_PLL_DIV_CON_SHIFT,
6868fa6979bSPhilipp Tomsich .sel_shift = CLK_SPI1_PLL_SEL_SHIFT, },
6878fa6979bSPhilipp Tomsich [2] = { .reg = 60,
6888fa6979bSPhilipp Tomsich .div_shift = CLK_SPI2_PLL_DIV_CON_SHIFT,
6898fa6979bSPhilipp Tomsich .sel_shift = CLK_SPI2_PLL_SEL_SHIFT, },
6908fa6979bSPhilipp Tomsich [3] = { .reg = 60,
6918fa6979bSPhilipp Tomsich .div_shift = CLK_SPI4_PLL_DIV_CON_SHIFT,
6928fa6979bSPhilipp Tomsich .sel_shift = CLK_SPI4_PLL_SEL_SHIFT, },
6938fa6979bSPhilipp Tomsich [4] = { .reg = 58,
6948fa6979bSPhilipp Tomsich .div_shift = CLK_SPI5_PLL_DIV_CON_SHIFT,
6958fa6979bSPhilipp Tomsich .sel_shift = CLK_SPI5_PLL_SEL_SHIFT, },
6968fa6979bSPhilipp Tomsich };
6978fa6979bSPhilipp Tomsich
rk3399_spi_get_clk(struct rk3399_cru * cru,ulong clk_id)6988fa6979bSPhilipp Tomsich static ulong rk3399_spi_get_clk(struct rk3399_cru *cru, ulong clk_id)
6998fa6979bSPhilipp Tomsich {
7008fa6979bSPhilipp Tomsich const struct spi_clkreg *spiclk = NULL;
7018fa6979bSPhilipp Tomsich u32 div, val;
7028fa6979bSPhilipp Tomsich
7038fa6979bSPhilipp Tomsich switch (clk_id) {
7048fa6979bSPhilipp Tomsich case SCLK_SPI0 ... SCLK_SPI5:
7058fa6979bSPhilipp Tomsich spiclk = &spi_clkregs[clk_id - SCLK_SPI0];
7068fa6979bSPhilipp Tomsich break;
7078fa6979bSPhilipp Tomsich
7088fa6979bSPhilipp Tomsich default:
70990aa625cSMasahiro Yamada pr_err("%s: SPI clk-id %ld not supported\n", __func__, clk_id);
7108fa6979bSPhilipp Tomsich return -EINVAL;
7118fa6979bSPhilipp Tomsich }
7128fa6979bSPhilipp Tomsich
7138fa6979bSPhilipp Tomsich val = readl(&cru->clksel_con[spiclk->reg]);
714feab7f33SPhilipp Tomsich div = bitfield_extract(val, spiclk->div_shift,
715feab7f33SPhilipp Tomsich CLK_SPI_PLL_DIV_CON_WIDTH);
7168fa6979bSPhilipp Tomsich
7178fa6979bSPhilipp Tomsich return DIV_TO_RATE(GPLL_HZ, div);
7188fa6979bSPhilipp Tomsich }
7198fa6979bSPhilipp Tomsich
rk3399_spi_set_clk(struct rk3399_cru * cru,ulong clk_id,uint hz)7208fa6979bSPhilipp Tomsich static ulong rk3399_spi_set_clk(struct rk3399_cru *cru, ulong clk_id, uint hz)
7218fa6979bSPhilipp Tomsich {
7228fa6979bSPhilipp Tomsich const struct spi_clkreg *spiclk = NULL;
7238fa6979bSPhilipp Tomsich int src_clk_div;
7248fa6979bSPhilipp Tomsich
725217273cdSKever Yang src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz) - 1;
726217273cdSKever Yang assert(src_clk_div < 128);
7278fa6979bSPhilipp Tomsich
7288fa6979bSPhilipp Tomsich switch (clk_id) {
7298fa6979bSPhilipp Tomsich case SCLK_SPI1 ... SCLK_SPI5:
7308fa6979bSPhilipp Tomsich spiclk = &spi_clkregs[clk_id - SCLK_SPI0];
7318fa6979bSPhilipp Tomsich break;
7328fa6979bSPhilipp Tomsich
7338fa6979bSPhilipp Tomsich default:
73490aa625cSMasahiro Yamada pr_err("%s: SPI clk-id %ld not supported\n", __func__, clk_id);
7358fa6979bSPhilipp Tomsich return -EINVAL;
7368fa6979bSPhilipp Tomsich }
7378fa6979bSPhilipp Tomsich
7388fa6979bSPhilipp Tomsich rk_clrsetreg(&cru->clksel_con[spiclk->reg],
7398fa6979bSPhilipp Tomsich ((CLK_SPI_PLL_DIV_CON_MASK << spiclk->div_shift) |
7408fa6979bSPhilipp Tomsich (CLK_SPI_PLL_SEL_GPLL << spiclk->sel_shift)),
7418fa6979bSPhilipp Tomsich ((src_clk_div << spiclk->div_shift) |
7428fa6979bSPhilipp Tomsich (CLK_SPI_PLL_SEL_GPLL << spiclk->sel_shift)));
7438fa6979bSPhilipp Tomsich
744beb90a53SPhilipp Tomsich return rk3399_spi_get_clk(cru, clk_id);
7458fa6979bSPhilipp Tomsich }
7468fa6979bSPhilipp Tomsich
7476bfdfc4fSElaine Zhang #define RK3399_LIMIT_PLL_ACLK_VOP (400 * 1000000)
7486bfdfc4fSElaine Zhang
rk3399_vop_set_clk(struct rk3399_cru * cru,ulong clk_id,u32 hz)749aff8795cSHeiko Stübner static ulong rk3399_vop_set_clk(struct rk3399_cru *cru, ulong clk_id, u32 hz)
750aff8795cSHeiko Stübner {
7516bfdfc4fSElaine Zhang struct pll_div vpll_config = {0}, cpll_config = {0};
7526bfdfc4fSElaine Zhang int aclk_vop = RK3399_LIMIT_PLL_ACLK_VOP;
753aff8795cSHeiko Stübner void *aclkreg_addr, *dclkreg_addr;
7546bfdfc4fSElaine Zhang u32 div = 1;
755aff8795cSHeiko Stübner
756aff8795cSHeiko Stübner switch (clk_id) {
757aff8795cSHeiko Stübner case DCLK_VOP0:
758aff8795cSHeiko Stübner aclkreg_addr = &cru->clksel_con[47];
759aff8795cSHeiko Stübner dclkreg_addr = &cru->clksel_con[49];
760aff8795cSHeiko Stübner break;
761aff8795cSHeiko Stübner case DCLK_VOP1:
762aff8795cSHeiko Stübner aclkreg_addr = &cru->clksel_con[48];
763aff8795cSHeiko Stübner dclkreg_addr = &cru->clksel_con[50];
764aff8795cSHeiko Stübner break;
765aff8795cSHeiko Stübner default:
766aff8795cSHeiko Stübner return -EINVAL;
767aff8795cSHeiko Stübner }
768aff8795cSHeiko Stübner /* vop aclk source clk: cpll */
7696bfdfc4fSElaine Zhang div = GPLL_HZ / aclk_vop;
7705f42424bSLin Huang assert(div - 1 <= 31);
771aff8795cSHeiko Stübner
772aff8795cSHeiko Stübner rk_clrsetreg(aclkreg_addr,
773aff8795cSHeiko Stübner ACLK_VOP_PLL_SEL_MASK | ACLK_VOP_DIV_CON_MASK,
7746bfdfc4fSElaine Zhang ACLK_VOP_PLL_SEL_GPLL << ACLK_VOP_PLL_SEL_SHIFT |
775aff8795cSHeiko Stübner (div - 1) << ACLK_VOP_DIV_CON_SHIFT);
776*6cf98e2fSElaine Zhang rk_clrsetreg(&cru->clksel_con[42],
777*6cf98e2fSElaine Zhang ACLK_VOP_PLL_SEL_MASK | ACLK_VOP_DIV_CON_MASK,
778*6cf98e2fSElaine Zhang ACLK_VOP_PLL_SEL_GPLL << ACLK_VOP_PLL_SEL_SHIFT |
779*6cf98e2fSElaine Zhang (div - 1) << ACLK_VOP_DIV_CON_SHIFT);
780aff8795cSHeiko Stübner
7816bfdfc4fSElaine Zhang if (readl(dclkreg_addr) & DCLK_VOP_PLL_SEL_MASK) {
782c6c6283cSElaine Zhang if (pll_para_config(hz, &cpll_config))
7836bfdfc4fSElaine Zhang return -1;
7846bfdfc4fSElaine Zhang rkclk_set_pll(&cru->cpll_con[0], &cpll_config);
7856bfdfc4fSElaine Zhang } else {
786c6c6283cSElaine Zhang if (pll_para_config(hz, &vpll_config))
7876bfdfc4fSElaine Zhang return -1;
788aff8795cSHeiko Stübner rkclk_set_pll(&cru->vpll_con[0], &vpll_config);
7896bfdfc4fSElaine Zhang }
790aff8795cSHeiko Stübner
791aff8795cSHeiko Stübner rk_clrsetreg(dclkreg_addr,
7926bfdfc4fSElaine Zhang DCLK_VOP_DCLK_SEL_MASK | DCLK_VOP_DIV_CON_MASK,
793aff8795cSHeiko Stübner DCLK_VOP_DCLK_SEL_DIVOUT << DCLK_VOP_DCLK_SEL_SHIFT |
794c6c6283cSElaine Zhang (1 - 1) << DCLK_VOP_DIV_CON_SHIFT);
795aff8795cSHeiko Stübner
796aff8795cSHeiko Stübner return hz;
797aff8795cSHeiko Stübner }
798aff8795cSHeiko Stübner
rk3399_mmc_get_clk(struct rk3399_cru * cru,uint clk_id)799aff8795cSHeiko Stübner static ulong rk3399_mmc_get_clk(struct rk3399_cru *cru, uint clk_id)
800aff8795cSHeiko Stübner {
801aff8795cSHeiko Stübner u32 div, con;
802aff8795cSHeiko Stübner
803aff8795cSHeiko Stübner switch (clk_id) {
804998c61aeSPhilipp Tomsich case HCLK_SDMMC:
805aff8795cSHeiko Stübner case SCLK_SDMMC:
806aff8795cSHeiko Stübner con = readl(&cru->clksel_con[16]);
8073a94d75dSKever Yang /* dwmmc controller have internal div 2 */
8083a94d75dSKever Yang div = 2;
809aff8795cSHeiko Stübner break;
810aff8795cSHeiko Stübner case SCLK_EMMC:
811b2a78faeSchenfen con = readl(&cru->clksel_con[22]);
8123a94d75dSKever Yang div = 1;
813aff8795cSHeiko Stübner break;
814aff8795cSHeiko Stübner default:
815aff8795cSHeiko Stübner return -EINVAL;
816aff8795cSHeiko Stübner }
817aff8795cSHeiko Stübner
8183a94d75dSKever Yang div *= (con & CLK_EMMC_DIV_CON_MASK) >> CLK_EMMC_DIV_CON_SHIFT;
819fd4b2dc0SKever Yang if ((con & CLK_EMMC_PLL_MASK) >> CLK_EMMC_PLL_SHIFT
820fd4b2dc0SKever Yang == CLK_EMMC_PLL_SEL_24M)
8213a94d75dSKever Yang return DIV_TO_RATE(OSC_HZ, div);
822fd4b2dc0SKever Yang else
823aff8795cSHeiko Stübner return DIV_TO_RATE(GPLL_HZ, div);
824aff8795cSHeiko Stübner }
825aff8795cSHeiko Stübner
rk3399_mmc_set_clk(struct rk3399_cru * cru,ulong clk_id,ulong set_rate)826aff8795cSHeiko Stübner static ulong rk3399_mmc_set_clk(struct rk3399_cru *cru,
827aff8795cSHeiko Stübner ulong clk_id, ulong set_rate)
828aff8795cSHeiko Stübner {
829aff8795cSHeiko Stübner int src_clk_div;
830aff8795cSHeiko Stübner int aclk_emmc = 198*MHz;
831aff8795cSHeiko Stübner
832aff8795cSHeiko Stübner switch (clk_id) {
833998c61aeSPhilipp Tomsich case HCLK_SDMMC:
834aff8795cSHeiko Stübner case SCLK_SDMMC:
835fd4b2dc0SKever Yang /* Select clk_sdmmc source from GPLL by default */
8363a94d75dSKever Yang /* mmc clock defaulg div 2 internal, provide double in cru */
8373a94d75dSKever Yang src_clk_div = DIV_ROUND_UP(GPLL_HZ / 2, set_rate);
838aff8795cSHeiko Stübner
839217273cdSKever Yang if (src_clk_div > 128) {
840fd4b2dc0SKever Yang /* use 24MHz source for 400KHz clock */
8413a94d75dSKever Yang src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate);
842217273cdSKever Yang assert(src_clk_div - 1 < 128);
843fd4b2dc0SKever Yang rk_clrsetreg(&cru->clksel_con[16],
844fd4b2dc0SKever Yang CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK,
845fd4b2dc0SKever Yang CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT |
846fd4b2dc0SKever Yang (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT);
847fd4b2dc0SKever Yang } else {
848aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[16],
849aff8795cSHeiko Stübner CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK,
850aff8795cSHeiko Stübner CLK_EMMC_PLL_SEL_GPLL << CLK_EMMC_PLL_SHIFT |
851aff8795cSHeiko Stübner (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT);
852fd4b2dc0SKever Yang }
853aff8795cSHeiko Stübner break;
854aff8795cSHeiko Stübner case SCLK_EMMC:
855aff8795cSHeiko Stübner /* Select aclk_emmc source from GPLL */
856217273cdSKever Yang src_clk_div = DIV_ROUND_UP(GPLL_HZ , aclk_emmc);
857217273cdSKever Yang assert(src_clk_div - 1 < 32);
858aff8795cSHeiko Stübner
859aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[21],
860aff8795cSHeiko Stübner ACLK_EMMC_PLL_SEL_MASK | ACLK_EMMC_DIV_CON_MASK,
861aff8795cSHeiko Stübner ACLK_EMMC_PLL_SEL_GPLL << ACLK_EMMC_PLL_SEL_SHIFT |
862aff8795cSHeiko Stübner (src_clk_div - 1) << ACLK_EMMC_DIV_CON_SHIFT);
863aff8795cSHeiko Stübner
864aff8795cSHeiko Stübner /* Select clk_emmc source from GPLL too */
865217273cdSKever Yang src_clk_div = DIV_ROUND_UP(GPLL_HZ, set_rate);
866b2a78faeSchenfen if (src_clk_div > 128) {
867b2a78faeSchenfen /* use 24MHz source for 400KHz clock */
868b2a78faeSchenfen src_clk_div = DIV_ROUND_UP(OSC_HZ, set_rate);
869217273cdSKever Yang assert(src_clk_div - 1 < 128);
870b2a78faeSchenfen rk_clrsetreg(&cru->clksel_con[22],
871b2a78faeSchenfen CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK,
872b2a78faeSchenfen CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT |
873b2a78faeSchenfen (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT);
874b2a78faeSchenfen } else {
875aff8795cSHeiko Stübner rk_clrsetreg(&cru->clksel_con[22],
876aff8795cSHeiko Stübner CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK,
877aff8795cSHeiko Stübner CLK_EMMC_PLL_SEL_GPLL << CLK_EMMC_PLL_SHIFT |
878aff8795cSHeiko Stübner (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT);
879b2a78faeSchenfen }
880aff8795cSHeiko Stübner break;
881aff8795cSHeiko Stübner default:
882aff8795cSHeiko Stübner return -EINVAL;
883aff8795cSHeiko Stübner }
884aff8795cSHeiko Stübner return rk3399_mmc_get_clk(cru, clk_id);
885aff8795cSHeiko Stübner }
886aff8795cSHeiko Stübner
rk3399_gmac_set_clk(struct rk3399_cru * cru,ulong rate)8870ee68417SPhilipp Tomsich static ulong rk3399_gmac_set_clk(struct rk3399_cru *cru, ulong rate)
8880ee68417SPhilipp Tomsich {
8890ee68417SPhilipp Tomsich ulong ret;
8900ee68417SPhilipp Tomsich
8910ee68417SPhilipp Tomsich /*
8920ee68417SPhilipp Tomsich * The RGMII CLK can be derived either from an external "clkin"
8930ee68417SPhilipp Tomsich * or can be generated from internally by a divider from SCLK_MAC.
8940ee68417SPhilipp Tomsich */
8950ee68417SPhilipp Tomsich if (readl(&cru->clksel_con[19]) & BIT(4)) {
8960ee68417SPhilipp Tomsich /* An external clock will always generate the right rate... */
8970ee68417SPhilipp Tomsich ret = rate;
8980ee68417SPhilipp Tomsich } else {
8990ee68417SPhilipp Tomsich /*
9000ee68417SPhilipp Tomsich * No platform uses an internal clock to date.
9010ee68417SPhilipp Tomsich * Implement this once it becomes necessary and print an error
9020ee68417SPhilipp Tomsich * if someone tries to use it (while it remains unimplemented).
9030ee68417SPhilipp Tomsich */
9040ee68417SPhilipp Tomsich pr_err("%s: internal clock is UNIMPLEMENTED\n", __func__);
9050ee68417SPhilipp Tomsich ret = 0;
9060ee68417SPhilipp Tomsich }
9070ee68417SPhilipp Tomsich
9080ee68417SPhilipp Tomsich return ret;
9090ee68417SPhilipp Tomsich }
9100ee68417SPhilipp Tomsich
9115ae2fd97SKever Yang #define PMUSGRF_DDR_RGN_CON16 0xff330040
rk3399_ddr_set_clk(struct rk3399_cru * cru,ulong set_rate)9125ae2fd97SKever Yang static ulong rk3399_ddr_set_clk(struct rk3399_cru *cru,
9135ae2fd97SKever Yang ulong set_rate)
9145ae2fd97SKever Yang {
9155ae2fd97SKever Yang struct pll_div dpll_cfg;
9165ae2fd97SKever Yang
9175ae2fd97SKever Yang /* IC ECO bug, need to set this register */
9185ae2fd97SKever Yang writel(0xc000c000, PMUSGRF_DDR_RGN_CON16);
9195ae2fd97SKever Yang
9205ae2fd97SKever Yang /* clk_ddrc == DPLL = 24MHz / refdiv * fbdiv / postdiv1 / postdiv2 */
9215ae2fd97SKever Yang switch (set_rate) {
92251c830f2SYouMin Chen case 50 * MHz:
92351c830f2SYouMin Chen dpll_cfg = (struct pll_div)
92451c830f2SYouMin Chen {.refdiv = 1, .fbdiv = 12, .postdiv1 = 3, .postdiv2 = 2};
92551c830f2SYouMin Chen break;
9265ae2fd97SKever Yang case 200 * MHz:
9275ae2fd97SKever Yang dpll_cfg = (struct pll_div)
9285ae2fd97SKever Yang {.refdiv = 1, .fbdiv = 50, .postdiv1 = 6, .postdiv2 = 1};
9295ae2fd97SKever Yang break;
9305ae2fd97SKever Yang case 300 * MHz:
9315ae2fd97SKever Yang dpll_cfg = (struct pll_div)
9325ae2fd97SKever Yang {.refdiv = 2, .fbdiv = 100, .postdiv1 = 4, .postdiv2 = 1};
9335ae2fd97SKever Yang break;
93451c830f2SYouMin Chen case 400 * MHz:
93551c830f2SYouMin Chen dpll_cfg = (struct pll_div)
93651c830f2SYouMin Chen {.refdiv = 1, .fbdiv = 50, .postdiv1 = 3, .postdiv2 = 1};
93751c830f2SYouMin Chen break;
9385ae2fd97SKever Yang case 666 * MHz:
9395ae2fd97SKever Yang dpll_cfg = (struct pll_div)
9405ae2fd97SKever Yang {.refdiv = 2, .fbdiv = 111, .postdiv1 = 2, .postdiv2 = 1};
9415ae2fd97SKever Yang break;
9425ae2fd97SKever Yang case 800 * MHz:
9435ae2fd97SKever Yang dpll_cfg = (struct pll_div)
9445ae2fd97SKever Yang {.refdiv = 1, .fbdiv = 100, .postdiv1 = 3, .postdiv2 = 1};
9455ae2fd97SKever Yang break;
9465ae2fd97SKever Yang case 933 * MHz:
9475ae2fd97SKever Yang dpll_cfg = (struct pll_div)
9485ae2fd97SKever Yang {.refdiv = 1, .fbdiv = 116, .postdiv1 = 3, .postdiv2 = 1};
9495ae2fd97SKever Yang break;
9505ae2fd97SKever Yang default:
95190aa625cSMasahiro Yamada pr_err("Unsupported SDRAM frequency!,%ld\n", set_rate);
9525ae2fd97SKever Yang }
9535ae2fd97SKever Yang rkclk_set_pll(&cru->dpll_con[0], &dpll_cfg);
9545ae2fd97SKever Yang
9555ae2fd97SKever Yang return set_rate;
9565ae2fd97SKever Yang }
957e78991bfSDavid Wu
rk3399_saradc_get_clk(struct rk3399_cru * cru)958e78991bfSDavid Wu static ulong rk3399_saradc_get_clk(struct rk3399_cru *cru)
959e78991bfSDavid Wu {
960e78991bfSDavid Wu u32 div, val;
961e78991bfSDavid Wu
962e78991bfSDavid Wu val = readl(&cru->clksel_con[26]);
963e78991bfSDavid Wu div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT,
964e78991bfSDavid Wu CLK_SARADC_DIV_CON_WIDTH);
965e78991bfSDavid Wu
966e78991bfSDavid Wu return DIV_TO_RATE(OSC_HZ, div);
967e78991bfSDavid Wu }
968e78991bfSDavid Wu
rk3399_saradc_set_clk(struct rk3399_cru * cru,uint hz)969e78991bfSDavid Wu static ulong rk3399_saradc_set_clk(struct rk3399_cru *cru, uint hz)
970e78991bfSDavid Wu {
971e78991bfSDavid Wu int src_clk_div;
972e78991bfSDavid Wu
973e78991bfSDavid Wu src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
9745f42424bSLin Huang assert(src_clk_div <= 255);
975e78991bfSDavid Wu
976e78991bfSDavid Wu rk_clrsetreg(&cru->clksel_con[26],
977e78991bfSDavid Wu CLK_SARADC_DIV_CON_MASK,
978e78991bfSDavid Wu src_clk_div << CLK_SARADC_DIV_CON_SHIFT);
979e78991bfSDavid Wu
980e78991bfSDavid Wu return rk3399_saradc_get_clk(cru);
981e78991bfSDavid Wu }
982e78991bfSDavid Wu
rk3399_tsadc_get_clk(struct rk3399_cru * cru)983cb3c37fcSElaine Zhang static ulong rk3399_tsadc_get_clk(struct rk3399_cru *cru)
984cb3c37fcSElaine Zhang {
985cb3c37fcSElaine Zhang u32 div, val;
986cb3c37fcSElaine Zhang
987cb3c37fcSElaine Zhang val = readl(&cru->clksel_con[27]);
988cb3c37fcSElaine Zhang div = bitfield_extract(val, CLK_TSADC_SEL_SHIFT,
989cb3c37fcSElaine Zhang 10);
990cb3c37fcSElaine Zhang
991cb3c37fcSElaine Zhang return DIV_TO_RATE(OSC_HZ, div);
992cb3c37fcSElaine Zhang }
993cb3c37fcSElaine Zhang
rk3399_tsadc_set_clk(struct rk3399_cru * cru,uint hz)994cb3c37fcSElaine Zhang static ulong rk3399_tsadc_set_clk(struct rk3399_cru *cru, uint hz)
995cb3c37fcSElaine Zhang {
996cb3c37fcSElaine Zhang int src_clk_div;
997cb3c37fcSElaine Zhang
998cb3c37fcSElaine Zhang src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1;
999cb3c37fcSElaine Zhang assert(src_clk_div <= 255);
1000cb3c37fcSElaine Zhang
1001cb3c37fcSElaine Zhang rk_clrsetreg(&cru->clksel_con[27],
1002cb3c37fcSElaine Zhang CLK_TSADC_DIV_CON_MASK | CLK_TSADC_SEL_MASK,
1003cb3c37fcSElaine Zhang (CLK_TSADC_SEL_X24M << CLK_TSADC_SEL_SHIFT) |
1004cb3c37fcSElaine Zhang (src_clk_div << CLK_TSADC_DIV_CON_SHIFT));
1005cb3c37fcSElaine Zhang
1006cb3c37fcSElaine Zhang return rk3399_tsadc_get_clk(cru);
1007cb3c37fcSElaine Zhang }
1008cb3c37fcSElaine Zhang
rk3399_crypto_get_clk(struct rk3399_clk_priv * priv,ulong clk_id)1009187d951bSElaine Zhang static ulong rk3399_crypto_get_clk(struct rk3399_clk_priv *priv, ulong clk_id)
1010187d951bSElaine Zhang {
1011187d951bSElaine Zhang struct rk3399_cru *cru = priv->cru;
1012187d951bSElaine Zhang u32 div, con, parent;
1013187d951bSElaine Zhang
1014187d951bSElaine Zhang switch (clk_id) {
1015187d951bSElaine Zhang case SCLK_CRYPTO0:
1016187d951bSElaine Zhang con = readl(&cru->clksel_con[24]);
1017187d951bSElaine Zhang div = (con & CRYPTO0_DIV_MASK) >> CRYPTO0_DIV_SHIFT;
1018187d951bSElaine Zhang parent = GPLL_HZ;
1019187d951bSElaine Zhang break;
1020187d951bSElaine Zhang case SCLK_CRYPTO1:
1021187d951bSElaine Zhang con = readl(&cru->clksel_con[26]);
1022187d951bSElaine Zhang div = (con & CRYPTO1_DIV_MASK) >> CRYPTO1_DIV_SHIFT;
1023187d951bSElaine Zhang parent = GPLL_HZ;
1024187d951bSElaine Zhang break;
1025187d951bSElaine Zhang default:
1026187d951bSElaine Zhang return -ENOENT;
1027187d951bSElaine Zhang }
1028187d951bSElaine Zhang
1029187d951bSElaine Zhang return DIV_TO_RATE(parent, div);
1030187d951bSElaine Zhang }
1031187d951bSElaine Zhang
rk3399_crypto_set_clk(struct rk3399_clk_priv * priv,ulong clk_id,ulong hz)1032187d951bSElaine Zhang static ulong rk3399_crypto_set_clk(struct rk3399_clk_priv *priv, ulong clk_id,
1033187d951bSElaine Zhang ulong hz)
1034187d951bSElaine Zhang {
1035187d951bSElaine Zhang struct rk3399_cru *cru = priv->cru;
1036187d951bSElaine Zhang int src_clk_div;
1037187d951bSElaine Zhang
1038187d951bSElaine Zhang src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz);
1039187d951bSElaine Zhang assert(src_clk_div - 1 <= 31);
1040187d951bSElaine Zhang
1041187d951bSElaine Zhang /*
1042187d951bSElaine Zhang * select gpll as crypto clock source and
1043187d951bSElaine Zhang * set up dependent divisors for crypto clocks.
1044187d951bSElaine Zhang */
1045187d951bSElaine Zhang switch (clk_id) {
1046187d951bSElaine Zhang case SCLK_CRYPTO0:
1047187d951bSElaine Zhang rk_clrsetreg(&cru->clksel_con[24],
1048187d951bSElaine Zhang CRYPTO0_PLL_SEL_MASK | CRYPTO0_DIV_MASK,
1049187d951bSElaine Zhang CRYPTO_PLL_SEL_GPLL << CRYPTO0_PLL_SEL_SHIFT |
1050187d951bSElaine Zhang (src_clk_div - 1) << CRYPTO0_DIV_SHIFT);
1051187d951bSElaine Zhang break;
1052187d951bSElaine Zhang case SCLK_CRYPTO1:
1053187d951bSElaine Zhang rk_clrsetreg(&cru->clksel_con[26],
1054187d951bSElaine Zhang CRYPTO1_PLL_SEL_MASK | CRYPTO1_DIV_MASK,
1055187d951bSElaine Zhang CRYPTO_PLL_SEL_GPLL << CRYPTO1_PLL_SEL_SHIFT |
1056187d951bSElaine Zhang (src_clk_div - 1) << CRYPTO1_DIV_SHIFT);
1057187d951bSElaine Zhang break;
1058187d951bSElaine Zhang default:
1059187d951bSElaine Zhang printf("do not support this peri freq\n");
1060187d951bSElaine Zhang return -EINVAL;
1061187d951bSElaine Zhang }
1062187d951bSElaine Zhang
1063187d951bSElaine Zhang return rk3399_crypto_get_clk(priv, clk_id);
1064187d951bSElaine Zhang }
10658b75ff34SElaine Zhang
1066c3723ef3SJoseph Chen #ifndef CONFIG_SPL_BUILD
rk3399_peri_get_clk(struct rk3399_clk_priv * priv,ulong clk_id)10678b75ff34SElaine Zhang static ulong rk3399_peri_get_clk(struct rk3399_clk_priv *priv, ulong clk_id)
10688b75ff34SElaine Zhang {
10698b75ff34SElaine Zhang struct rk3399_cru *cru = priv->cru;
10708b75ff34SElaine Zhang u32 div, con, parent;
10718b75ff34SElaine Zhang
10728b75ff34SElaine Zhang switch (clk_id) {
10738b75ff34SElaine Zhang case ACLK_PERIHP:
10748b75ff34SElaine Zhang con = readl(&cru->clksel_con[14]);
10758b75ff34SElaine Zhang div = (con & ACLK_PERIHP_DIV_CON_MASK) >>
10768b75ff34SElaine Zhang ACLK_PERIHP_DIV_CON_SHIFT;
10778b75ff34SElaine Zhang parent = GPLL_HZ;
10788b75ff34SElaine Zhang break;
10798b75ff34SElaine Zhang case PCLK_PERIHP:
10808b75ff34SElaine Zhang con = readl(&cru->clksel_con[14]);
10818b75ff34SElaine Zhang div = (con & PCLK_PERIHP_DIV_CON_MASK) >>
10828b75ff34SElaine Zhang PCLK_PERIHP_DIV_CON_SHIFT;
10838b75ff34SElaine Zhang parent = rk3399_peri_get_clk(priv, ACLK_PERIHP);
10848b75ff34SElaine Zhang break;
10858b75ff34SElaine Zhang case HCLK_PERIHP:
10868b75ff34SElaine Zhang con = readl(&cru->clksel_con[14]);
10878b75ff34SElaine Zhang div = (con & HCLK_PERIHP_DIV_CON_MASK) >>
10888b75ff34SElaine Zhang HCLK_PERIHP_DIV_CON_SHIFT;
10898b75ff34SElaine Zhang parent = rk3399_peri_get_clk(priv, ACLK_PERIHP);
10908b75ff34SElaine Zhang break;
10918b75ff34SElaine Zhang case ACLK_PERILP0:
10928b75ff34SElaine Zhang con = readl(&cru->clksel_con[23]);
10938b75ff34SElaine Zhang div = (con & ACLK_PERILP0_DIV_CON_MASK) >>
10948b75ff34SElaine Zhang ACLK_PERILP0_DIV_CON_SHIFT;
10958b75ff34SElaine Zhang parent = GPLL_HZ;
10968b75ff34SElaine Zhang break;
10978b75ff34SElaine Zhang case HCLK_PERILP0:
10988b75ff34SElaine Zhang con = readl(&cru->clksel_con[23]);
10998b75ff34SElaine Zhang div = (con & HCLK_PERILP0_DIV_CON_MASK) >>
11008b75ff34SElaine Zhang HCLK_PERILP0_DIV_CON_SHIFT;
11018b75ff34SElaine Zhang parent = rk3399_peri_get_clk(priv, ACLK_PERILP0);
11028b75ff34SElaine Zhang break;
11038b75ff34SElaine Zhang case PCLK_PERILP0:
11048b75ff34SElaine Zhang con = readl(&cru->clksel_con[23]);
11058b75ff34SElaine Zhang div = (con & PCLK_PERILP0_DIV_CON_MASK) >>
11068b75ff34SElaine Zhang PCLK_PERILP0_DIV_CON_SHIFT;
11078b75ff34SElaine Zhang parent = rk3399_peri_get_clk(priv, ACLK_PERILP0);
11088b75ff34SElaine Zhang break;
11098b75ff34SElaine Zhang case HCLK_PERILP1:
11108b75ff34SElaine Zhang con = readl(&cru->clksel_con[25]);
11118b75ff34SElaine Zhang div = (con & HCLK_PERILP1_DIV_CON_MASK) >>
11128b75ff34SElaine Zhang HCLK_PERILP1_DIV_CON_SHIFT;
11138b75ff34SElaine Zhang parent = GPLL_HZ;
11148b75ff34SElaine Zhang break;
11158b75ff34SElaine Zhang case PCLK_PERILP1:
11168b75ff34SElaine Zhang con = readl(&cru->clksel_con[25]);
11178b75ff34SElaine Zhang div = (con & PCLK_PERILP1_DIV_CON_MASK) >>
11188b75ff34SElaine Zhang PCLK_PERILP1_DIV_CON_SHIFT;
11198b75ff34SElaine Zhang parent = rk3399_peri_get_clk(priv, HCLK_PERILP1);
11208b75ff34SElaine Zhang break;
11218b75ff34SElaine Zhang default:
11228b75ff34SElaine Zhang return -ENOENT;
11238b75ff34SElaine Zhang }
11248b75ff34SElaine Zhang
11258b75ff34SElaine Zhang return DIV_TO_RATE(parent, div);
11268b75ff34SElaine Zhang }
11278b75ff34SElaine Zhang
rk3399_alive_get_clk(struct rk3399_clk_priv * priv)1128981ee0bdSElaine Zhang static ulong rk3399_alive_get_clk(struct rk3399_clk_priv *priv)
1129981ee0bdSElaine Zhang {
1130981ee0bdSElaine Zhang struct rk3399_cru *cru = priv->cru;
1131981ee0bdSElaine Zhang u32 div, con, parent;
1132981ee0bdSElaine Zhang
1133981ee0bdSElaine Zhang con = readl(&cru->clksel_con[57]);
1134981ee0bdSElaine Zhang div = (con & PCLK_ALIVE_DIV_CON_MASK) >>
1135981ee0bdSElaine Zhang PCLK_ALIVE_DIV_CON_SHIFT;
1136981ee0bdSElaine Zhang parent = GPLL_HZ;
1137981ee0bdSElaine Zhang return DIV_TO_RATE(parent, div);
1138981ee0bdSElaine Zhang }
1139187d951bSElaine Zhang #endif
1140187d951bSElaine Zhang
rk3399_clk_get_rate(struct clk * clk)1141aff8795cSHeiko Stübner static ulong rk3399_clk_get_rate(struct clk *clk)
1142aff8795cSHeiko Stübner {
1143aff8795cSHeiko Stübner struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
1144aff8795cSHeiko Stübner ulong rate = 0;
1145aff8795cSHeiko Stübner
1146aff8795cSHeiko Stübner switch (clk->id) {
11476bfdfc4fSElaine Zhang case PLL_APLLL:
11486bfdfc4fSElaine Zhang case PLL_APLLB:
11496bfdfc4fSElaine Zhang case PLL_DPLL:
11506bfdfc4fSElaine Zhang case PLL_CPLL:
11516bfdfc4fSElaine Zhang case PLL_GPLL:
11526bfdfc4fSElaine Zhang case PLL_NPLL:
11536bfdfc4fSElaine Zhang case PLL_VPLL:
11548b75ff34SElaine Zhang rate = rk3399_pll_get_rate(priv, clk->id);
11556bfdfc4fSElaine Zhang break;
1156998c61aeSPhilipp Tomsich case HCLK_SDMMC:
1157aff8795cSHeiko Stübner case SCLK_SDMMC:
1158aff8795cSHeiko Stübner case SCLK_EMMC:
1159aff8795cSHeiko Stübner rate = rk3399_mmc_get_clk(priv->cru, clk->id);
1160aff8795cSHeiko Stübner break;
1161aff8795cSHeiko Stübner case SCLK_I2C1:
1162aff8795cSHeiko Stübner case SCLK_I2C2:
1163aff8795cSHeiko Stübner case SCLK_I2C3:
1164aff8795cSHeiko Stübner case SCLK_I2C5:
1165aff8795cSHeiko Stübner case SCLK_I2C6:
1166aff8795cSHeiko Stübner case SCLK_I2C7:
1167aff8795cSHeiko Stübner rate = rk3399_i2c_get_clk(priv->cru, clk->id);
1168aff8795cSHeiko Stübner break;
11698fa6979bSPhilipp Tomsich case SCLK_SPI0...SCLK_SPI5:
11708fa6979bSPhilipp Tomsich rate = rk3399_spi_get_clk(priv->cru, clk->id);
11718fa6979bSPhilipp Tomsich break;
11728fa6979bSPhilipp Tomsich case SCLK_UART0:
1173e959b707SLin Huang case SCLK_UART1:
11748fa6979bSPhilipp Tomsich case SCLK_UART2:
1175e959b707SLin Huang case SCLK_UART3:
11768fa6979bSPhilipp Tomsich return 24000000;
1177ffc1fac5SPhilipp Tomsich break;
1178ffc1fac5SPhilipp Tomsich case PCLK_HDMI_CTRL:
1179ffc1fac5SPhilipp Tomsich break;
1180aff8795cSHeiko Stübner case DCLK_VOP0:
1181aff8795cSHeiko Stübner case DCLK_VOP1:
1182aff8795cSHeiko Stübner break;
1183a70feb46SPhilipp Tomsich case PCLK_EFUSE1024NS:
1184a70feb46SPhilipp Tomsich break;
1185e78991bfSDavid Wu case SCLK_SARADC:
1186e78991bfSDavid Wu rate = rk3399_saradc_get_clk(priv->cru);
1187e78991bfSDavid Wu break;
1188cb3c37fcSElaine Zhang case SCLK_TSADC:
1189cb3c37fcSElaine Zhang rate = rk3399_tsadc_get_clk(priv->cru);
1190cb3c37fcSElaine Zhang break;
1191187d951bSElaine Zhang case SCLK_CRYPTO0:
1192187d951bSElaine Zhang case SCLK_CRYPTO1:
1193187d951bSElaine Zhang rate = rk3399_crypto_get_clk(priv, clk->id);
1194187d951bSElaine Zhang break;
1195c3723ef3SJoseph Chen #ifndef CONFIG_SPL_BUILD
11968b75ff34SElaine Zhang case ACLK_PERIHP:
11978b75ff34SElaine Zhang case HCLK_PERIHP:
11988b75ff34SElaine Zhang case PCLK_PERIHP:
11998b75ff34SElaine Zhang case ACLK_PERILP0:
12008b75ff34SElaine Zhang case HCLK_PERILP0:
12018b75ff34SElaine Zhang case PCLK_PERILP0:
12028b75ff34SElaine Zhang case HCLK_PERILP1:
12038b75ff34SElaine Zhang case PCLK_PERILP1:
12048b75ff34SElaine Zhang rate = rk3399_peri_get_clk(priv, clk->id);
12058b75ff34SElaine Zhang break;
1206981ee0bdSElaine Zhang case PCLK_ALIVE:
1207981ee0bdSElaine Zhang case PCLK_WDT:
1208981ee0bdSElaine Zhang rate = rk3399_alive_get_clk(priv);
1209981ee0bdSElaine Zhang break;
1210187d951bSElaine Zhang #endif
1211aff8795cSHeiko Stübner default:
1212aff8795cSHeiko Stübner return -ENOENT;
1213aff8795cSHeiko Stübner }
1214aff8795cSHeiko Stübner
1215aff8795cSHeiko Stübner return rate;
1216aff8795cSHeiko Stübner }
1217aff8795cSHeiko Stübner
rk3399_clk_set_rate(struct clk * clk,ulong rate)1218aff8795cSHeiko Stübner static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate)
1219aff8795cSHeiko Stübner {
1220aff8795cSHeiko Stübner struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
1221aff8795cSHeiko Stübner ulong ret = 0;
1222aff8795cSHeiko Stübner
1223aff8795cSHeiko Stübner switch (clk->id) {
1224aff8795cSHeiko Stübner case 0 ... 63:
1225aff8795cSHeiko Stübner return 0;
1226a6de9238SPhilipp Tomsich
1227a6de9238SPhilipp Tomsich case ACLK_PERIHP:
1228a6de9238SPhilipp Tomsich case HCLK_PERIHP:
1229a6de9238SPhilipp Tomsich case PCLK_PERIHP:
1230a6de9238SPhilipp Tomsich return 0;
1231a6de9238SPhilipp Tomsich
1232a6de9238SPhilipp Tomsich case ACLK_PERILP0:
1233a6de9238SPhilipp Tomsich case HCLK_PERILP0:
1234a6de9238SPhilipp Tomsich case PCLK_PERILP0:
1235a6de9238SPhilipp Tomsich return 0;
1236a6de9238SPhilipp Tomsich
1237a6de9238SPhilipp Tomsich case ACLK_CCI:
1238a6de9238SPhilipp Tomsich return 0;
1239a6de9238SPhilipp Tomsich
1240a6de9238SPhilipp Tomsich case HCLK_PERILP1:
1241a6de9238SPhilipp Tomsich case PCLK_PERILP1:
1242a6de9238SPhilipp Tomsich return 0;
1243a6de9238SPhilipp Tomsich
1244998c61aeSPhilipp Tomsich case HCLK_SDMMC:
1245aff8795cSHeiko Stübner case SCLK_SDMMC:
1246aff8795cSHeiko Stübner case SCLK_EMMC:
1247aff8795cSHeiko Stübner ret = rk3399_mmc_set_clk(priv->cru, clk->id, rate);
1248aff8795cSHeiko Stübner break;
124965d83303SPhilipp Tomsich case SCLK_MAC:
12500ee68417SPhilipp Tomsich ret = rk3399_gmac_set_clk(priv->cru, rate);
125165d83303SPhilipp Tomsich break;
1252aff8795cSHeiko Stübner case SCLK_I2C1:
1253aff8795cSHeiko Stübner case SCLK_I2C2:
1254aff8795cSHeiko Stübner case SCLK_I2C3:
1255aff8795cSHeiko Stübner case SCLK_I2C5:
1256aff8795cSHeiko Stübner case SCLK_I2C6:
1257aff8795cSHeiko Stübner case SCLK_I2C7:
1258aff8795cSHeiko Stübner ret = rk3399_i2c_set_clk(priv->cru, clk->id, rate);
1259aff8795cSHeiko Stübner break;
12608fa6979bSPhilipp Tomsich case SCLK_SPI0...SCLK_SPI5:
12618fa6979bSPhilipp Tomsich ret = rk3399_spi_set_clk(priv->cru, clk->id, rate);
12628fa6979bSPhilipp Tomsich break;
1263ffc1fac5SPhilipp Tomsich case PCLK_HDMI_CTRL:
1264ffc1fac5SPhilipp Tomsich case PCLK_VIO_GRF:
1265ffc1fac5SPhilipp Tomsich /* the PCLK gates for video are enabled by default */
1266ffc1fac5SPhilipp Tomsich break;
1267aff8795cSHeiko Stübner case DCLK_VOP0:
1268aff8795cSHeiko Stübner case DCLK_VOP1:
12695e79f443SKever Yang ret = rk3399_vop_set_clk(priv->cru, clk->id, rate);
1270aff8795cSHeiko Stübner break;
12715ae2fd97SKever Yang case SCLK_DDRCLK:
12725ae2fd97SKever Yang ret = rk3399_ddr_set_clk(priv->cru, rate);
12735ae2fd97SKever Yang break;
1274a70feb46SPhilipp Tomsich case PCLK_EFUSE1024NS:
1275a70feb46SPhilipp Tomsich break;
1276e78991bfSDavid Wu case SCLK_SARADC:
1277e78991bfSDavid Wu ret = rk3399_saradc_set_clk(priv->cru, rate);
1278e78991bfSDavid Wu break;
1279cb3c37fcSElaine Zhang case SCLK_TSADC:
1280cb3c37fcSElaine Zhang ret = rk3399_tsadc_set_clk(priv->cru, rate);
1281cb3c37fcSElaine Zhang break;
1282187d951bSElaine Zhang case SCLK_CRYPTO0:
1283187d951bSElaine Zhang case SCLK_CRYPTO1:
1284187d951bSElaine Zhang ret = rk3399_crypto_set_clk(priv, clk->id, rate);
1285187d951bSElaine Zhang break;
1286aff8795cSHeiko Stübner default:
1287aff8795cSHeiko Stübner return -ENOENT;
1288aff8795cSHeiko Stübner }
1289aff8795cSHeiko Stübner
1290aff8795cSHeiko Stübner return ret;
1291aff8795cSHeiko Stübner }
1292aff8795cSHeiko Stübner
rk3399_gmac_set_parent(struct clk * clk,struct clk * parent)1293d2866b32SPhilipp Tomsich static int __maybe_unused rk3399_gmac_set_parent(struct clk *clk, struct clk *parent)
12940ee68417SPhilipp Tomsich {
12950ee68417SPhilipp Tomsich struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
12960ee68417SPhilipp Tomsich const char *clock_output_name;
12970ee68417SPhilipp Tomsich int ret;
12980ee68417SPhilipp Tomsich
12990ee68417SPhilipp Tomsich /*
13000ee68417SPhilipp Tomsich * If the requested parent is in the same clock-controller and
13010ee68417SPhilipp Tomsich * the id is SCLK_MAC ("clk_gmac"), switch to the internal clock.
13020ee68417SPhilipp Tomsich */
13030ee68417SPhilipp Tomsich if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC)) {
13040ee68417SPhilipp Tomsich debug("%s: switching RGMII to SCLK_MAC\n", __func__);
13050ee68417SPhilipp Tomsich rk_clrreg(&priv->cru->clksel_con[19], BIT(4));
13060ee68417SPhilipp Tomsich return 0;
13070ee68417SPhilipp Tomsich }
13080ee68417SPhilipp Tomsich
13090ee68417SPhilipp Tomsich /*
13100ee68417SPhilipp Tomsich * Otherwise, we need to check the clock-output-names of the
13110ee68417SPhilipp Tomsich * requested parent to see if the requested id is "clkin_gmac".
13120ee68417SPhilipp Tomsich */
13130ee68417SPhilipp Tomsich ret = dev_read_string_index(parent->dev, "clock-output-names",
13140ee68417SPhilipp Tomsich parent->id, &clock_output_name);
13150ee68417SPhilipp Tomsich if (ret < 0)
13160ee68417SPhilipp Tomsich return -ENODATA;
13170ee68417SPhilipp Tomsich
13180ee68417SPhilipp Tomsich /* If this is "clkin_gmac", switch to the external clock input */
13190ee68417SPhilipp Tomsich if (!strcmp(clock_output_name, "clkin_gmac")) {
13200ee68417SPhilipp Tomsich debug("%s: switching RGMII to CLKIN\n", __func__);
13210ee68417SPhilipp Tomsich rk_setreg(&priv->cru->clksel_con[19], BIT(4));
13220ee68417SPhilipp Tomsich return 0;
13230ee68417SPhilipp Tomsich }
13240ee68417SPhilipp Tomsich
13250ee68417SPhilipp Tomsich return -EINVAL;
13260ee68417SPhilipp Tomsich }
13270ee68417SPhilipp Tomsich
rk3399_dclk_vop_set_parent(struct clk * clk,struct clk * parent)13286bfdfc4fSElaine Zhang static int __maybe_unused rk3399_dclk_vop_set_parent(struct clk *clk,
13296bfdfc4fSElaine Zhang struct clk *parent)
13306bfdfc4fSElaine Zhang {
13316bfdfc4fSElaine Zhang struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
13326bfdfc4fSElaine Zhang void *dclkreg_addr;
13336bfdfc4fSElaine Zhang
13346bfdfc4fSElaine Zhang switch (clk->id) {
13356bfdfc4fSElaine Zhang case DCLK_VOP0_DIV:
13366bfdfc4fSElaine Zhang dclkreg_addr = &priv->cru->clksel_con[49];
13376bfdfc4fSElaine Zhang break;
13386bfdfc4fSElaine Zhang case DCLK_VOP1_DIV:
13396bfdfc4fSElaine Zhang dclkreg_addr = &priv->cru->clksel_con[50];
13406bfdfc4fSElaine Zhang break;
13416bfdfc4fSElaine Zhang default:
13426bfdfc4fSElaine Zhang return -EINVAL;
13436bfdfc4fSElaine Zhang }
13446bfdfc4fSElaine Zhang if (parent->id == PLL_CPLL) {
13456bfdfc4fSElaine Zhang rk_clrsetreg(dclkreg_addr, DCLK_VOP_PLL_SEL_MASK,
13466bfdfc4fSElaine Zhang DCLK_VOP_PLL_SEL_CPLL << DCLK_VOP_PLL_SEL_SHIFT);
13476bfdfc4fSElaine Zhang } else {
13486bfdfc4fSElaine Zhang rk_clrsetreg(dclkreg_addr, DCLK_VOP_PLL_SEL_MASK,
13496bfdfc4fSElaine Zhang DCLK_VOP_PLL_SEL_VPLL << DCLK_VOP_PLL_SEL_SHIFT);
13506bfdfc4fSElaine Zhang }
13516bfdfc4fSElaine Zhang
13526bfdfc4fSElaine Zhang return 0;
13536bfdfc4fSElaine Zhang }
13546bfdfc4fSElaine Zhang
rk3399_clk_set_parent(struct clk * clk,struct clk * parent)1355d2866b32SPhilipp Tomsich static int __maybe_unused rk3399_clk_set_parent(struct clk *clk, struct clk *parent)
13560ee68417SPhilipp Tomsich {
13570ee68417SPhilipp Tomsich switch (clk->id) {
13580ee68417SPhilipp Tomsich case SCLK_RMII_SRC:
13590ee68417SPhilipp Tomsich return rk3399_gmac_set_parent(clk, parent);
13606bfdfc4fSElaine Zhang case DCLK_VOP0_DIV:
13616bfdfc4fSElaine Zhang case DCLK_VOP1_DIV:
13626bfdfc4fSElaine Zhang return rk3399_dclk_vop_set_parent(clk, parent);
13630ee68417SPhilipp Tomsich }
13640ee68417SPhilipp Tomsich
13650ee68417SPhilipp Tomsich debug("%s: unsupported clk %ld\n", __func__, clk->id);
13660ee68417SPhilipp Tomsich return -ENOENT;
13670ee68417SPhilipp Tomsich }
13680ee68417SPhilipp Tomsich
rk3399_clk_enable(struct clk * clk)1369cce8d7cdSPhilipp Tomsich static int rk3399_clk_enable(struct clk *clk)
1370cce8d7cdSPhilipp Tomsich {
1371cce8d7cdSPhilipp Tomsich switch (clk->id) {
1372cce8d7cdSPhilipp Tomsich case HCLK_HOST0:
1373cce8d7cdSPhilipp Tomsich case HCLK_HOST0_ARB:
13740a3a50d6SWilliam Wu case SCLK_USBPHY0_480M_SRC:
1375cce8d7cdSPhilipp Tomsich case HCLK_HOST1:
1376cce8d7cdSPhilipp Tomsich case HCLK_HOST1_ARB:
13770a3a50d6SWilliam Wu case SCLK_USBPHY1_480M_SRC:
137845d1e0c8SFrank Wang case ACLK_USB3OTG1:
137945d1e0c8SFrank Wang case ACLK_USB3_GRF:
138045d1e0c8SFrank Wang case SCLK_USB3OTG1_REF:
138145d1e0c8SFrank Wang case SCLK_USB3OTG1_SUSPEND:
1382cce8d7cdSPhilipp Tomsich return 0;
1383cce8d7cdSPhilipp Tomsich }
1384cce8d7cdSPhilipp Tomsich
1385cce8d7cdSPhilipp Tomsich debug("%s: unsupported clk %ld\n", __func__, clk->id);
1386cce8d7cdSPhilipp Tomsich return -ENOENT;
1387cce8d7cdSPhilipp Tomsich }
1388cce8d7cdSPhilipp Tomsich
1389aff8795cSHeiko Stübner static struct clk_ops rk3399_clk_ops = {
1390aff8795cSHeiko Stübner .get_rate = rk3399_clk_get_rate,
1391aff8795cSHeiko Stübner .set_rate = rk3399_clk_set_rate,
1392d2866b32SPhilipp Tomsich #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
13930ee68417SPhilipp Tomsich .set_parent = rk3399_clk_set_parent,
1394d2866b32SPhilipp Tomsich #endif
1395cce8d7cdSPhilipp Tomsich .enable = rk3399_clk_enable,
1396aff8795cSHeiko Stübner };
1397aff8795cSHeiko Stübner
rkclk_init(struct rk3399_cru * cru)1398cd5a540cSKever Yang static void rkclk_init(struct rk3399_cru *cru)
1399cd5a540cSKever Yang {
1400cd5a540cSKever Yang u32 aclk_div;
1401cd5a540cSKever Yang u32 hclk_div;
1402cd5a540cSKever Yang u32 pclk_div;
1403cd5a540cSKever Yang
14048b75ff34SElaine Zhang rk3399_configure_cpu(cru, APLL_816_MHZ, CPU_CLUSTER_LITTLE);
1405243edbf9SJoseph Chen rk3399_configure_cpu(cru, APLL_816_MHZ, CPU_CLUSTER_BIG);
1406ccced9e1SLin Huang
1407cd5a540cSKever Yang /*
1408cd5a540cSKever Yang * some cru registers changed by bootrom, we'd better reset them to
1409cd5a540cSKever Yang * reset/default values described in TRM to avoid confusion in kernel.
1410cd5a540cSKever Yang * Please consider these three lines as a fix of bootrom bug.
1411cd5a540cSKever Yang */
141209e1ca43SElaine Zhang if (rkclk_pll_get_rate(&cru->npll_con[0]) != NPLL_HZ)
141309e1ca43SElaine Zhang rkclk_set_pll(&cru->npll_con[0], &npll_init_cfg);
141409e1ca43SElaine Zhang
141509e1ca43SElaine Zhang if (rkclk_pll_get_rate(&cru->gpll_con[0]) == GPLL_HZ)
141609e1ca43SElaine Zhang return;
141709e1ca43SElaine Zhang
1418cd5a540cSKever Yang rk_clrsetreg(&cru->clksel_con[12], 0xffff, 0x4101);
1419cd5a540cSKever Yang rk_clrsetreg(&cru->clksel_con[19], 0xffff, 0x033f);
1420cd5a540cSKever Yang rk_clrsetreg(&cru->clksel_con[56], 0x0003, 0x0003);
1421cd5a540cSKever Yang
1422cd5a540cSKever Yang /* configure perihp aclk, hclk, pclk */
14234897499eSElaine Zhang aclk_div = DIV_ROUND_UP(GPLL_HZ, PERIHP_ACLK_HZ) - 1;
1424cd5a540cSKever Yang
1425cd5a540cSKever Yang hclk_div = PERIHP_ACLK_HZ / PERIHP_HCLK_HZ - 1;
142688c36f12SElaine Zhang assert((hclk_div + 1) * PERIHP_HCLK_HZ <=
14275f42424bSLin Huang PERIHP_ACLK_HZ && (hclk_div <= 0x3));
1428cd5a540cSKever Yang
1429cd5a540cSKever Yang pclk_div = PERIHP_ACLK_HZ / PERIHP_PCLK_HZ - 1;
143088c36f12SElaine Zhang assert((pclk_div + 1) * PERIHP_PCLK_HZ <=
14315f42424bSLin Huang PERIHP_ACLK_HZ && (pclk_div <= 0x7));
1432cd5a540cSKever Yang
1433cd5a540cSKever Yang rk_clrsetreg(&cru->clksel_con[14],
1434cd5a540cSKever Yang PCLK_PERIHP_DIV_CON_MASK | HCLK_PERIHP_DIV_CON_MASK |
1435cd5a540cSKever Yang ACLK_PERIHP_PLL_SEL_MASK | ACLK_PERIHP_DIV_CON_MASK,
1436cd5a540cSKever Yang pclk_div << PCLK_PERIHP_DIV_CON_SHIFT |
1437cd5a540cSKever Yang hclk_div << HCLK_PERIHP_DIV_CON_SHIFT |
1438cd5a540cSKever Yang ACLK_PERIHP_PLL_SEL_GPLL << ACLK_PERIHP_PLL_SEL_SHIFT |
1439cd5a540cSKever Yang aclk_div << ACLK_PERIHP_DIV_CON_SHIFT);
1440cd5a540cSKever Yang
1441cd5a540cSKever Yang /* configure perilp0 aclk, hclk, pclk */
14424897499eSElaine Zhang aclk_div = DIV_ROUND_UP(GPLL_HZ, PERILP0_ACLK_HZ) - 1;
1443cd5a540cSKever Yang
1444cd5a540cSKever Yang hclk_div = PERILP0_ACLK_HZ / PERILP0_HCLK_HZ - 1;
144588c36f12SElaine Zhang assert((hclk_div + 1) * PERILP0_HCLK_HZ <=
14465f42424bSLin Huang PERILP0_ACLK_HZ && (hclk_div <= 0x3));
1447cd5a540cSKever Yang
1448cd5a540cSKever Yang pclk_div = PERILP0_ACLK_HZ / PERILP0_PCLK_HZ - 1;
144988c36f12SElaine Zhang assert((pclk_div + 1) * PERILP0_PCLK_HZ <=
14505f42424bSLin Huang PERILP0_ACLK_HZ && (pclk_div <= 0x7));
1451cd5a540cSKever Yang
1452cd5a540cSKever Yang rk_clrsetreg(&cru->clksel_con[23],
1453cd5a540cSKever Yang PCLK_PERILP0_DIV_CON_MASK | HCLK_PERILP0_DIV_CON_MASK |
1454cd5a540cSKever Yang ACLK_PERILP0_PLL_SEL_MASK | ACLK_PERILP0_DIV_CON_MASK,
1455cd5a540cSKever Yang pclk_div << PCLK_PERILP0_DIV_CON_SHIFT |
1456cd5a540cSKever Yang hclk_div << HCLK_PERILP0_DIV_CON_SHIFT |
1457cd5a540cSKever Yang ACLK_PERILP0_PLL_SEL_GPLL << ACLK_PERILP0_PLL_SEL_SHIFT |
1458cd5a540cSKever Yang aclk_div << ACLK_PERILP0_DIV_CON_SHIFT);
1459cd5a540cSKever Yang
1460cd5a540cSKever Yang /* perilp1 hclk select gpll as source */
14614897499eSElaine Zhang hclk_div = DIV_ROUND_UP(GPLL_HZ, PERILP1_HCLK_HZ) - 1;
146288c36f12SElaine Zhang assert((hclk_div + 1) * PERILP1_HCLK_HZ <=
14635f42424bSLin Huang GPLL_HZ && (hclk_div <= 0x1f));
1464cd5a540cSKever Yang
14651702a77fSLin Huang pclk_div = PERILP1_HCLK_HZ / PERILP1_PCLK_HZ - 1;
146688c36f12SElaine Zhang assert((pclk_div + 1) * PERILP1_PCLK_HZ <=
14675f42424bSLin Huang PERILP1_HCLK_HZ && (pclk_div <= 0x7));
1468cd5a540cSKever Yang
1469cd5a540cSKever Yang rk_clrsetreg(&cru->clksel_con[25],
1470cd5a540cSKever Yang PCLK_PERILP1_DIV_CON_MASK | HCLK_PERILP1_DIV_CON_MASK |
1471cd5a540cSKever Yang HCLK_PERILP1_PLL_SEL_MASK,
1472cd5a540cSKever Yang pclk_div << PCLK_PERILP1_DIV_CON_SHIFT |
1473cd5a540cSKever Yang hclk_div << HCLK_PERILP1_DIV_CON_SHIFT |
1474cd5a540cSKever Yang HCLK_PERILP1_PLL_SEL_GPLL << HCLK_PERILP1_PLL_SEL_SHIFT);
14754897499eSElaine Zhang
147609e1ca43SElaine Zhang rk_clrsetreg(&cru->clksel_con[21],
147709e1ca43SElaine Zhang ACLK_EMMC_PLL_SEL_MASK | ACLK_EMMC_DIV_CON_MASK,
147809e1ca43SElaine Zhang ACLK_EMMC_PLL_SEL_GPLL << ACLK_EMMC_PLL_SEL_SHIFT |
147909e1ca43SElaine Zhang (4 - 1) << ACLK_EMMC_DIV_CON_SHIFT);
148009e1ca43SElaine Zhang rk_clrsetreg(&cru->clksel_con[22], 0x3f << 0, 7 << 0);
148109e1ca43SElaine Zhang
1482e57a08e5SElaine Zhang /*
1483e57a08e5SElaine Zhang * I2c MUx is in cpll by default, but cpll is for dclk_vop exclusive.
1484e57a08e5SElaine Zhang * If dclk_vop set rate after i2c init, the CPLL changed,
1485e57a08e5SElaine Zhang * but the i2c not perception, it will resulting the wrong
1486e57a08e5SElaine Zhang * frequency of the i2c.
1487e57a08e5SElaine Zhang * So set the i2c frequency according to the kernel configuration,
1488e57a08e5SElaine Zhang * and Hang I2C on the GPLL.
1489e57a08e5SElaine Zhang */
1490e57a08e5SElaine Zhang rk_clrsetreg(&cru->clksel_con[61], I2C_CLK_REG_MASK(1),
1491e57a08e5SElaine Zhang I2C_CLK_REG_VALUE(1, 4));
1492e57a08e5SElaine Zhang rk_clrsetreg(&cru->clksel_con[62], I2C_CLK_REG_MASK(2),
1493e57a08e5SElaine Zhang I2C_CLK_REG_VALUE(2, 4));
1494e57a08e5SElaine Zhang rk_clrsetreg(&cru->clksel_con[63], I2C_CLK_REG_MASK(3),
1495e57a08e5SElaine Zhang I2C_CLK_REG_VALUE(3, 4));
1496e57a08e5SElaine Zhang rk_clrsetreg(&cru->clksel_con[61], I2C_CLK_REG_MASK(5),
1497e57a08e5SElaine Zhang I2C_CLK_REG_VALUE(5, 4));
1498e57a08e5SElaine Zhang rk_clrsetreg(&cru->clksel_con[62], I2C_CLK_REG_MASK(6),
1499e57a08e5SElaine Zhang I2C_CLK_REG_VALUE(6, 4));
1500e57a08e5SElaine Zhang rk_clrsetreg(&cru->clksel_con[63], I2C_CLK_REG_MASK(7),
1501e57a08e5SElaine Zhang I2C_CLK_REG_VALUE(7, 4));
1502e57a08e5SElaine Zhang
15034897499eSElaine Zhang rkclk_set_pll(&cru->gpll_con[0], &gpll_init_cfg);
1504cd5a540cSKever Yang }
1505cd5a540cSKever Yang
rk3399_clk_probe(struct udevice * dev)1506aff8795cSHeiko Stübner static int rk3399_clk_probe(struct udevice *dev)
1507aff8795cSHeiko Stübner {
1508aff8795cSHeiko Stübner struct rk3399_clk_priv *priv = dev_get_priv(dev);
1509aff8795cSHeiko Stübner
15105ae2fd97SKever Yang #if CONFIG_IS_ENABLED(OF_PLATDATA)
15115ae2fd97SKever Yang struct rk3399_clk_plat *plat = dev_get_platdata(dev);
1512aff8795cSHeiko Stübner
1513a28bfcc3SSimon Glass priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
15145ae2fd97SKever Yang #endif
1515044bc79dSElaine Zhang
1516044bc79dSElaine Zhang priv->sync_kernel = false;
1517044bc79dSElaine Zhang if (!priv->armlclk_enter_hz)
1518044bc79dSElaine Zhang priv->armlclk_enter_hz =
1519044bc79dSElaine Zhang rkclk_pll_get_rate(&priv->cru->apll_l_con[0]);
1520044bc79dSElaine Zhang if (!priv->armbclk_enter_hz)
1521044bc79dSElaine Zhang priv->armbclk_enter_hz =
1522044bc79dSElaine Zhang rkclk_pll_get_rate(&priv->cru->apll_b_con[0]);
15235ae2fd97SKever Yang rkclk_init(priv->cru);
1524044bc79dSElaine Zhang if (!priv->armlclk_init_hz)
1525044bc79dSElaine Zhang priv->armlclk_init_hz =
1526044bc79dSElaine Zhang rkclk_pll_get_rate(&priv->cru->apll_l_con[0]);
1527044bc79dSElaine Zhang if (!priv->armbclk_init_hz)
1528044bc79dSElaine Zhang priv->armbclk_init_hz =
1529044bc79dSElaine Zhang rkclk_pll_get_rate(&priv->cru->apll_b_con[0]);
1530e57a08e5SElaine Zhang
1531aff8795cSHeiko Stübner return 0;
1532aff8795cSHeiko Stübner }
1533aff8795cSHeiko Stübner
rk3399_clk_ofdata_to_platdata(struct udevice * dev)1534aff8795cSHeiko Stübner static int rk3399_clk_ofdata_to_platdata(struct udevice *dev)
1535aff8795cSHeiko Stübner {
15365ae2fd97SKever Yang #if !CONFIG_IS_ENABLED(OF_PLATDATA)
1537aff8795cSHeiko Stübner struct rk3399_clk_priv *priv = dev_get_priv(dev);
1538aff8795cSHeiko Stübner
1539db4c0dc8SPhilipp Tomsich priv->cru = dev_read_addr_ptr(dev);
15405ae2fd97SKever Yang #endif
1541aff8795cSHeiko Stübner return 0;
1542aff8795cSHeiko Stübner }
1543aff8795cSHeiko Stübner
rk3399_clk_bind(struct udevice * dev)1544aff8795cSHeiko Stübner static int rk3399_clk_bind(struct udevice *dev)
1545aff8795cSHeiko Stübner {
1546aff8795cSHeiko Stübner int ret;
15473d555d75SElaine Zhang struct udevice *sys_child, *sf_child;
1548fbdd1558SKever Yang struct sysreset_reg *priv;
15493d555d75SElaine Zhang struct softreset_reg *sf_priv;
1550aff8795cSHeiko Stübner
1551aff8795cSHeiko Stübner /* The reset driver does not have a device node, so bind it here */
1552fbdd1558SKever Yang ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
1553fbdd1558SKever Yang &sys_child);
1554fbdd1558SKever Yang if (ret) {
1555fbdd1558SKever Yang debug("Warning: No sysreset driver: ret=%d\n", ret);
1556fbdd1558SKever Yang } else {
1557fbdd1558SKever Yang priv = malloc(sizeof(struct sysreset_reg));
1558fbdd1558SKever Yang priv->glb_srst_fst_value = offsetof(struct rk3399_cru,
1559fbdd1558SKever Yang glb_srst_fst_value);
1560fbdd1558SKever Yang priv->glb_srst_snd_value = offsetof(struct rk3399_cru,
1561fbdd1558SKever Yang glb_srst_snd_value);
1562fbdd1558SKever Yang sys_child->priv = priv;
1563fbdd1558SKever Yang }
1564aff8795cSHeiko Stübner
15653d555d75SElaine Zhang ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset",
15663d555d75SElaine Zhang dev_ofnode(dev), &sf_child);
15673d555d75SElaine Zhang if (ret) {
15683d555d75SElaine Zhang debug("Warning: No rockchip reset driver: ret=%d\n", ret);
15693d555d75SElaine Zhang } else {
15703d555d75SElaine Zhang sf_priv = malloc(sizeof(struct softreset_reg));
15713d555d75SElaine Zhang sf_priv->sf_reset_offset = offsetof(struct rk3399_cru,
15723d555d75SElaine Zhang softrst_con[0]);
15733d555d75SElaine Zhang sf_priv->sf_reset_num = 21;
15743d555d75SElaine Zhang sf_child->priv = sf_priv;
15753d555d75SElaine Zhang }
15763d555d75SElaine Zhang
1577aff8795cSHeiko Stübner return 0;
1578aff8795cSHeiko Stübner }
1579aff8795cSHeiko Stübner
1580aff8795cSHeiko Stübner static const struct udevice_id rk3399_clk_ids[] = {
1581aff8795cSHeiko Stübner { .compatible = "rockchip,rk3399-cru" },
1582aff8795cSHeiko Stübner { }
1583aff8795cSHeiko Stübner };
1584aff8795cSHeiko Stübner
1585aff8795cSHeiko Stübner U_BOOT_DRIVER(clk_rk3399) = {
15865ae2fd97SKever Yang .name = "rockchip_rk3399_cru",
1587aff8795cSHeiko Stübner .id = UCLASS_CLK,
1588aff8795cSHeiko Stübner .of_match = rk3399_clk_ids,
1589aff8795cSHeiko Stübner .priv_auto_alloc_size = sizeof(struct rk3399_clk_priv),
1590aff8795cSHeiko Stübner .ofdata_to_platdata = rk3399_clk_ofdata_to_platdata,
1591aff8795cSHeiko Stübner .ops = &rk3399_clk_ops,
1592aff8795cSHeiko Stübner .bind = rk3399_clk_bind,
1593aff8795cSHeiko Stübner .probe = rk3399_clk_probe,
15945ae2fd97SKever Yang #if CONFIG_IS_ENABLED(OF_PLATDATA)
15955ae2fd97SKever Yang .platdata_auto_alloc_size = sizeof(struct rk3399_clk_plat),
15965ae2fd97SKever Yang #endif
1597aff8795cSHeiko Stübner };
15985e79f443SKever Yang
rk3399_i2c_get_pmuclk(struct rk3399_pmucru * pmucru,ulong clk_id)15995e79f443SKever Yang static ulong rk3399_i2c_get_pmuclk(struct rk3399_pmucru *pmucru, ulong clk_id)
16005e79f443SKever Yang {
16015e79f443SKever Yang u32 div, con;
16025e79f443SKever Yang
16035e79f443SKever Yang switch (clk_id) {
16045e79f443SKever Yang case SCLK_I2C0_PMU:
16055e79f443SKever Yang con = readl(&pmucru->pmucru_clksel[2]);
16065e79f443SKever Yang div = I2C_CLK_DIV_VALUE(con, 0);
16075e79f443SKever Yang break;
16085e79f443SKever Yang case SCLK_I2C4_PMU:
16095e79f443SKever Yang con = readl(&pmucru->pmucru_clksel[3]);
16105e79f443SKever Yang div = I2C_CLK_DIV_VALUE(con, 4);
16115e79f443SKever Yang break;
16125e79f443SKever Yang case SCLK_I2C8_PMU:
16135e79f443SKever Yang con = readl(&pmucru->pmucru_clksel[2]);
16145e79f443SKever Yang div = I2C_CLK_DIV_VALUE(con, 8);
16155e79f443SKever Yang break;
16165e79f443SKever Yang default:
16175e79f443SKever Yang printf("do not support this i2c bus\n");
16185e79f443SKever Yang return -EINVAL;
16195e79f443SKever Yang }
16205e79f443SKever Yang
16215e79f443SKever Yang return DIV_TO_RATE(PPLL_HZ, div);
16225e79f443SKever Yang }
16235e79f443SKever Yang
rk3399_i2c_set_pmuclk(struct rk3399_pmucru * pmucru,ulong clk_id,uint hz)16245e79f443SKever Yang static ulong rk3399_i2c_set_pmuclk(struct rk3399_pmucru *pmucru, ulong clk_id,
16255e79f443SKever Yang uint hz)
16265e79f443SKever Yang {
16275e79f443SKever Yang int src_clk_div;
16285e79f443SKever Yang
16295e79f443SKever Yang src_clk_div = PPLL_HZ / hz;
16305e79f443SKever Yang assert(src_clk_div - 1 < 127);
16315e79f443SKever Yang
16325e79f443SKever Yang switch (clk_id) {
16335e79f443SKever Yang case SCLK_I2C0_PMU:
16345e79f443SKever Yang rk_clrsetreg(&pmucru->pmucru_clksel[2], I2C_PMUCLK_REG_MASK(0),
16355e79f443SKever Yang I2C_PMUCLK_REG_VALUE(0, src_clk_div));
16365e79f443SKever Yang break;
16375e79f443SKever Yang case SCLK_I2C4_PMU:
16385e79f443SKever Yang rk_clrsetreg(&pmucru->pmucru_clksel[3], I2C_PMUCLK_REG_MASK(4),
16395e79f443SKever Yang I2C_PMUCLK_REG_VALUE(4, src_clk_div));
16405e79f443SKever Yang break;
16415e79f443SKever Yang case SCLK_I2C8_PMU:
16425e79f443SKever Yang rk_clrsetreg(&pmucru->pmucru_clksel[2], I2C_PMUCLK_REG_MASK(8),
16435e79f443SKever Yang I2C_PMUCLK_REG_VALUE(8, src_clk_div));
16445e79f443SKever Yang break;
16455e79f443SKever Yang default:
16465e79f443SKever Yang printf("do not support this i2c bus\n");
16475e79f443SKever Yang return -EINVAL;
16485e79f443SKever Yang }
16495e79f443SKever Yang
16505e79f443SKever Yang return DIV_TO_RATE(PPLL_HZ, src_clk_div);
16515e79f443SKever Yang }
16525e79f443SKever Yang
rk3399_pwm_get_clk(struct rk3399_pmucru * pmucru)16535e79f443SKever Yang static ulong rk3399_pwm_get_clk(struct rk3399_pmucru *pmucru)
16545e79f443SKever Yang {
16555e79f443SKever Yang u32 div, con;
16565e79f443SKever Yang
16575e79f443SKever Yang /* PWM closk rate is same as pclk_pmu */
16585e79f443SKever Yang con = readl(&pmucru->pmucru_clksel[0]);
16595e79f443SKever Yang div = con & PMU_PCLK_DIV_CON_MASK;
16605e79f443SKever Yang
16615e79f443SKever Yang return DIV_TO_RATE(PPLL_HZ, div);
16625e79f443SKever Yang }
16635e79f443SKever Yang
rk3399_pmuclk_get_rate(struct clk * clk)16645e79f443SKever Yang static ulong rk3399_pmuclk_get_rate(struct clk *clk)
16655e79f443SKever Yang {
16665e79f443SKever Yang struct rk3399_pmuclk_priv *priv = dev_get_priv(clk->dev);
16675e79f443SKever Yang ulong rate = 0;
16685e79f443SKever Yang
16695e79f443SKever Yang switch (clk->id) {
16705e79f443SKever Yang case PCLK_RKPWM_PMU:
1671981ee0bdSElaine Zhang case PCLK_WDT_M0_PMU:
16725e79f443SKever Yang rate = rk3399_pwm_get_clk(priv->pmucru);
16735e79f443SKever Yang break;
16745e79f443SKever Yang case SCLK_I2C0_PMU:
16755e79f443SKever Yang case SCLK_I2C4_PMU:
16765e79f443SKever Yang case SCLK_I2C8_PMU:
16775e79f443SKever Yang rate = rk3399_i2c_get_pmuclk(priv->pmucru, clk->id);
16785e79f443SKever Yang break;
1679e959b707SLin Huang case SCLK_UART4_PMU:
1680e959b707SLin Huang rate = 24000000;
1681e959b707SLin Huang break;
16825e79f443SKever Yang default:
16835e79f443SKever Yang return -ENOENT;
16845e79f443SKever Yang }
16855e79f443SKever Yang
16865e79f443SKever Yang return rate;
16875e79f443SKever Yang }
16885e79f443SKever Yang
rk3399_pmuclk_set_rate(struct clk * clk,ulong rate)16895e79f443SKever Yang static ulong rk3399_pmuclk_set_rate(struct clk *clk, ulong rate)
16905e79f443SKever Yang {
16915e79f443SKever Yang struct rk3399_pmuclk_priv *priv = dev_get_priv(clk->dev);
16925e79f443SKever Yang ulong ret = 0;
16935e79f443SKever Yang
16945e79f443SKever Yang switch (clk->id) {
16955e79f443SKever Yang case SCLK_I2C0_PMU:
16965e79f443SKever Yang case SCLK_I2C4_PMU:
16975e79f443SKever Yang case SCLK_I2C8_PMU:
16985e79f443SKever Yang ret = rk3399_i2c_set_pmuclk(priv->pmucru, clk->id, rate);
16995e79f443SKever Yang break;
17005e79f443SKever Yang default:
17015e79f443SKever Yang return -ENOENT;
17025e79f443SKever Yang }
17035e79f443SKever Yang
17045e79f443SKever Yang return ret;
17055e79f443SKever Yang }
17065e79f443SKever Yang
17075e79f443SKever Yang static struct clk_ops rk3399_pmuclk_ops = {
17085e79f443SKever Yang .get_rate = rk3399_pmuclk_get_rate,
17095e79f443SKever Yang .set_rate = rk3399_pmuclk_set_rate,
17105e79f443SKever Yang };
17115e79f443SKever Yang
17125ae2fd97SKever Yang #ifndef CONFIG_SPL_BUILD
pmuclk_init(struct rk3399_pmucru * pmucru)17135e79f443SKever Yang static void pmuclk_init(struct rk3399_pmucru *pmucru)
17145e79f443SKever Yang {
17155e79f443SKever Yang u32 pclk_div;
17165e79f443SKever Yang
17175e79f443SKever Yang /* configure pmu pll(ppll) */
17185e79f443SKever Yang rkclk_set_pll(&pmucru->ppll_con[0], &ppll_init_cfg);
17195e79f443SKever Yang
17205e79f443SKever Yang /* configure pmu pclk */
17215e79f443SKever Yang pclk_div = PPLL_HZ / PMU_PCLK_HZ - 1;
17225e79f443SKever Yang rk_clrsetreg(&pmucru->pmucru_clksel[0],
17235e79f443SKever Yang PMU_PCLK_DIV_CON_MASK,
17245e79f443SKever Yang pclk_div << PMU_PCLK_DIV_CON_SHIFT);
17255e79f443SKever Yang }
17265ae2fd97SKever Yang #endif
17275e79f443SKever Yang
rk3399_pmuclk_probe(struct udevice * dev)17285e79f443SKever Yang static int rk3399_pmuclk_probe(struct udevice *dev)
17295e79f443SKever Yang {
173061dff33bSPhilipp Tomsich #if CONFIG_IS_ENABLED(OF_PLATDATA) || !defined(CONFIG_SPL_BUILD)
17315e79f443SKever Yang struct rk3399_pmuclk_priv *priv = dev_get_priv(dev);
173261dff33bSPhilipp Tomsich #endif
17335e79f443SKever Yang
17345ae2fd97SKever Yang #if CONFIG_IS_ENABLED(OF_PLATDATA)
17355ae2fd97SKever Yang struct rk3399_pmuclk_plat *plat = dev_get_platdata(dev);
17365e79f443SKever Yang
1737a28bfcc3SSimon Glass priv->pmucru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
17385ae2fd97SKever Yang #endif
17395ae2fd97SKever Yang
17405ae2fd97SKever Yang #ifndef CONFIG_SPL_BUILD
17415ae2fd97SKever Yang pmuclk_init(priv->pmucru);
17425ae2fd97SKever Yang #endif
17435e79f443SKever Yang return 0;
17445e79f443SKever Yang }
17455e79f443SKever Yang
rk3399_pmuclk_ofdata_to_platdata(struct udevice * dev)17465e79f443SKever Yang static int rk3399_pmuclk_ofdata_to_platdata(struct udevice *dev)
17475e79f443SKever Yang {
17485ae2fd97SKever Yang #if !CONFIG_IS_ENABLED(OF_PLATDATA)
17495e79f443SKever Yang struct rk3399_pmuclk_priv *priv = dev_get_priv(dev);
17505e79f443SKever Yang
1751db4c0dc8SPhilipp Tomsich priv->pmucru = dev_read_addr_ptr(dev);
17525ae2fd97SKever Yang #endif
17535e79f443SKever Yang return 0;
17545e79f443SKever Yang }
17555e79f443SKever Yang
rk3399_pmuclk_bind(struct udevice * dev)17563d555d75SElaine Zhang static int rk3399_pmuclk_bind(struct udevice *dev)
17573d555d75SElaine Zhang {
17583d555d75SElaine Zhang int ret = 0;
17593d555d75SElaine Zhang struct udevice *sf_child;
1760564ceaddSKever Yang struct softreset_reg *sf_priv;
17613d555d75SElaine Zhang
17623d555d75SElaine Zhang ret = device_bind_driver_to_node(dev, "rockchip_reset",
17633d555d75SElaine Zhang "reset", dev_ofnode(dev),
17643d555d75SElaine Zhang &sf_child);
1765564ceaddSKever Yang if (ret) {
17663d555d75SElaine Zhang debug("Warning: No rockchip reset driver: ret=%d\n", ret);
1767564ceaddSKever Yang } else {
1768564ceaddSKever Yang sf_priv = malloc(sizeof(struct softreset_reg));
17693d555d75SElaine Zhang sf_priv->sf_reset_offset = offsetof(struct rk3399_pmucru,
17703d555d75SElaine Zhang pmucru_softrst_con[0]);
17713d555d75SElaine Zhang sf_priv->sf_reset_num = 2;
17723d555d75SElaine Zhang sf_child->priv = sf_priv;
1773564ceaddSKever Yang }
17743d555d75SElaine Zhang
1775564ceaddSKever Yang return 0;
17763d555d75SElaine Zhang }
17773d555d75SElaine Zhang
17785e79f443SKever Yang static const struct udevice_id rk3399_pmuclk_ids[] = {
17795e79f443SKever Yang { .compatible = "rockchip,rk3399-pmucru" },
17805e79f443SKever Yang { }
17815e79f443SKever Yang };
17825e79f443SKever Yang
1783c8a6bc96SSimon Glass U_BOOT_DRIVER(rockchip_rk3399_pmuclk) = {
17845ae2fd97SKever Yang .name = "rockchip_rk3399_pmucru",
17855e79f443SKever Yang .id = UCLASS_CLK,
17865e79f443SKever Yang .of_match = rk3399_pmuclk_ids,
17875e79f443SKever Yang .priv_auto_alloc_size = sizeof(struct rk3399_pmuclk_priv),
17885e79f443SKever Yang .ofdata_to_platdata = rk3399_pmuclk_ofdata_to_platdata,
17895e79f443SKever Yang .ops = &rk3399_pmuclk_ops,
17905e79f443SKever Yang .probe = rk3399_pmuclk_probe,
17913d555d75SElaine Zhang .bind = rk3399_pmuclk_bind,
17925ae2fd97SKever Yang #if CONFIG_IS_ENABLED(OF_PLATDATA)
17935ae2fd97SKever Yang .platdata_auto_alloc_size = sizeof(struct rk3399_pmuclk_plat),
17945ae2fd97SKever Yang #endif
17955e79f443SKever Yang };
17968b75ff34SElaine Zhang
17978b75ff34SElaine Zhang #ifndef CONFIG_SPL_BUILD
17988b75ff34SElaine Zhang /**
17998b75ff34SElaine Zhang * soc_clk_dump() - Print clock frequencies
18008b75ff34SElaine Zhang * Returns zero on success
18018b75ff34SElaine Zhang *
18028b75ff34SElaine Zhang * Implementation for the clk dump command.
18038b75ff34SElaine Zhang */
soc_clk_dump(void)18048b75ff34SElaine Zhang int soc_clk_dump(void)
18058b75ff34SElaine Zhang {
18068b75ff34SElaine Zhang struct udevice *cru_dev, *pmucru_dev;
1807044bc79dSElaine Zhang struct rk3399_clk_priv *priv;
18088b75ff34SElaine Zhang const struct rk3399_clk_info *clk_dump;
18098b75ff34SElaine Zhang struct clk clk;
18108b75ff34SElaine Zhang unsigned long clk_count = ARRAY_SIZE(clks_dump);
18118b75ff34SElaine Zhang unsigned long rate;
18128b75ff34SElaine Zhang int i, ret;
18138b75ff34SElaine Zhang
18148b75ff34SElaine Zhang ret = uclass_get_device_by_driver(UCLASS_CLK,
18158b75ff34SElaine Zhang DM_GET_DRIVER(clk_rk3399),
18168b75ff34SElaine Zhang &cru_dev);
18178b75ff34SElaine Zhang if (ret) {
18188b75ff34SElaine Zhang printf("%s failed to get cru device\n", __func__);
18198b75ff34SElaine Zhang return ret;
18208b75ff34SElaine Zhang }
18218b75ff34SElaine Zhang
18228b75ff34SElaine Zhang ret = uclass_get_device_by_driver(UCLASS_CLK,
18238b75ff34SElaine Zhang DM_GET_DRIVER(rockchip_rk3399_pmuclk),
18248b75ff34SElaine Zhang &pmucru_dev);
18258b75ff34SElaine Zhang if (ret) {
18268b75ff34SElaine Zhang printf("%s failed to get pmucru device\n", __func__);
18278b75ff34SElaine Zhang return ret;
18288b75ff34SElaine Zhang }
18298b75ff34SElaine Zhang
1830044bc79dSElaine Zhang priv = dev_get_priv(cru_dev);
1831044bc79dSElaine Zhang printf("CLK: (%s. arml: enter %lu KHz, init %lu KHz, kernel %lu%s)\n",
1832044bc79dSElaine Zhang priv->sync_kernel ? "sync kernel" : "uboot",
1833044bc79dSElaine Zhang priv->armlclk_enter_hz / 1000,
1834044bc79dSElaine Zhang priv->armlclk_init_hz / 1000,
1835044bc79dSElaine Zhang priv->set_armclk_rate ? priv->armlclk_hz / 1000 : 0,
1836044bc79dSElaine Zhang priv->set_armclk_rate ? " KHz" : "N/A");
1837044bc79dSElaine Zhang printf("CLK: (%s. armb: enter %lu KHz, init %lu KHz, kernel %lu%s)\n",
1838044bc79dSElaine Zhang priv->sync_kernel ? "sync kernel" : "uboot",
1839044bc79dSElaine Zhang priv->armbclk_enter_hz / 1000,
1840044bc79dSElaine Zhang priv->armbclk_init_hz / 1000,
1841044bc79dSElaine Zhang priv->set_armclk_rate ? priv->armbclk_hz / 1000 : 0,
1842044bc79dSElaine Zhang priv->set_armclk_rate ? " KHz" : "N/A");
18438b75ff34SElaine Zhang for (i = 0; i < clk_count; i++) {
18448b75ff34SElaine Zhang clk_dump = &clks_dump[i];
18458b75ff34SElaine Zhang if (clk_dump->name) {
18468b75ff34SElaine Zhang clk.id = clk_dump->id;
18478b75ff34SElaine Zhang if (clk_dump->is_cru)
18488b75ff34SElaine Zhang ret = clk_request(cru_dev, &clk);
18498b75ff34SElaine Zhang else
18508b75ff34SElaine Zhang ret = clk_request(pmucru_dev, &clk);
18518b75ff34SElaine Zhang if (ret < 0)
18528b75ff34SElaine Zhang return ret;
18538b75ff34SElaine Zhang
18548b75ff34SElaine Zhang rate = clk_get_rate(&clk);
18558b75ff34SElaine Zhang clk_free(&clk);
18568b75ff34SElaine Zhang if (i == 0) {
18578b75ff34SElaine Zhang if (rate < 0)
18588b75ff34SElaine Zhang printf(" %s %s\n", clk_dump->name,
18598b75ff34SElaine Zhang "unknown");
18608b75ff34SElaine Zhang else
18618b75ff34SElaine Zhang printf(" %s %lu KHz\n", clk_dump->name,
18628b75ff34SElaine Zhang rate / 1000);
18638b75ff34SElaine Zhang } else {
18648b75ff34SElaine Zhang if (rate < 0)
18658b75ff34SElaine Zhang printf(" %s %s\n", clk_dump->name,
18668b75ff34SElaine Zhang "unknown");
18678b75ff34SElaine Zhang else
18688b75ff34SElaine Zhang printf(" %s %lu KHz\n", clk_dump->name,
18698b75ff34SElaine Zhang rate / 1000);
18708b75ff34SElaine Zhang }
18718b75ff34SElaine Zhang }
18728b75ff34SElaine Zhang }
18738b75ff34SElaine Zhang
18748b75ff34SElaine Zhang return 0;
18758b75ff34SElaine Zhang }
18768b75ff34SElaine Zhang #endif
1877