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