1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * (C) Copyright 2015 Google, Inc
3*4882a593Smuzhiyun * (C) Copyright 2016 Heiko Stuebner <heiko@sntech.de>
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <common.h>
9*4882a593Smuzhiyun #include <clk-uclass.h>
10*4882a593Smuzhiyun #include <dm.h>
11*4882a593Smuzhiyun #include <dt-structs.h>
12*4882a593Smuzhiyun #include <errno.h>
13*4882a593Smuzhiyun #include <mapmem.h>
14*4882a593Smuzhiyun #include <syscon.h>
15*4882a593Smuzhiyun #include <asm/io.h>
16*4882a593Smuzhiyun #include <asm/arch/clock.h>
17*4882a593Smuzhiyun #include <asm/arch/cru_rk3066.h>
18*4882a593Smuzhiyun #include <asm/arch/grf_rk3066.h>
19*4882a593Smuzhiyun #include <asm/arch/hardware.h>
20*4882a593Smuzhiyun #include <dt-bindings/clock/rk3066a-cru.h>
21*4882a593Smuzhiyun #include <dm/device-internal.h>
22*4882a593Smuzhiyun #include <dm/lists.h>
23*4882a593Smuzhiyun #include <dm/uclass-internal.h>
24*4882a593Smuzhiyun #include <linux/log2.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun enum rk3066_clk_type {
29*4882a593Smuzhiyun RK3066_CRU,
30*4882a593Smuzhiyun RK3066A_CRU,
31*4882a593Smuzhiyun };
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun struct rk3066_clk_plat {
34*4882a593Smuzhiyun #if CONFIG_IS_ENABLED(OF_PLATDATA)
35*4882a593Smuzhiyun struct dtd_rockchip_rk3066a_cru dtd;
36*4882a593Smuzhiyun #endif
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #ifndef CONFIG_SPL_BUILD
40*4882a593Smuzhiyun #define RK3066_CLK_DUMP(_id, _name, _iscru) \
41*4882a593Smuzhiyun { \
42*4882a593Smuzhiyun .id = _id, \
43*4882a593Smuzhiyun .name = _name, \
44*4882a593Smuzhiyun .is_cru = _iscru, \
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun static const struct rk3066_clk_info clks_dump[] = {
48*4882a593Smuzhiyun RK3066_CLK_DUMP(PLL_APLL, "apll", true),
49*4882a593Smuzhiyun RK3066_CLK_DUMP(PLL_DPLL, "dpll", true),
50*4882a593Smuzhiyun RK3066_CLK_DUMP(PLL_GPLL, "gpll", true),
51*4882a593Smuzhiyun RK3066_CLK_DUMP(PLL_CPLL, "cpll", true),
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun #endif
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun struct pll_div {
56*4882a593Smuzhiyun u32 nr;
57*4882a593Smuzhiyun u32 nf;
58*4882a593Smuzhiyun u32 no;
59*4882a593Smuzhiyun };
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun enum {
62*4882a593Smuzhiyun VCO_MAX_HZ = 1416U * 1000000,
63*4882a593Smuzhiyun VCO_MIN_HZ = 300 * 1000000,
64*4882a593Smuzhiyun OUTPUT_MAX_HZ = 1416U * 1000000,
65*4882a593Smuzhiyun OUTPUT_MIN_HZ = 30 * 1000000,
66*4882a593Smuzhiyun FREF_MAX_HZ = 1416U * 1000000,
67*4882a593Smuzhiyun FREF_MIN_HZ = 30 * 1000,
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun enum {
71*4882a593Smuzhiyun /* PLL CON0 */
72*4882a593Smuzhiyun PLL_OD_MASK = 0x0f,
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun /* PLL CON1 */
75*4882a593Smuzhiyun PLL_NF_MASK = 0x1fff,
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun /* PLL CON2 */
78*4882a593Smuzhiyun PLL_BWADJ_MASK = 0x0fff,
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun /* PLL CON3 */
81*4882a593Smuzhiyun PLL_RESET_SHIFT = 5,
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /* GRF_SOC_STATUS0 */
84*4882a593Smuzhiyun SOCSTS_DPLL_LOCK = 1 << 4,
85*4882a593Smuzhiyun SOCSTS_APLL_LOCK = 1 << 5,
86*4882a593Smuzhiyun SOCSTS_CPLL_LOCK = 1 << 6,
87*4882a593Smuzhiyun SOCSTS_GPLL_LOCK = 1 << 7,
88*4882a593Smuzhiyun };
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun #define RATE_TO_DIV(input_rate, output_rate) \
91*4882a593Smuzhiyun ((input_rate) / (output_rate) - 1);
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun #define PLL_DIVISORS(hz, _nr, _no) {\
96*4882a593Smuzhiyun .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\
97*4882a593Smuzhiyun _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
98*4882a593Smuzhiyun (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\
99*4882a593Smuzhiyun "divisors on line " __stringify(__LINE__));
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun /* Keep divisors as low as possible to reduce jitter and power usage */
102*4882a593Smuzhiyun #ifdef CONFIG_TPL_BUILD
103*4882a593Smuzhiyun static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
104*4882a593Smuzhiyun static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
105*4882a593Smuzhiyun #endif
106*4882a593Smuzhiyun
rkclk_set_pll(struct rk3066_cru * cru,enum rk_clk_id clk_id,const struct pll_div * div,bool has_bwadj)107*4882a593Smuzhiyun static int rkclk_set_pll(struct rk3066_cru *cru, enum rk_clk_id clk_id,
108*4882a593Smuzhiyun const struct pll_div *div, bool has_bwadj)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun int pll_id = rk_pll_id(clk_id);
111*4882a593Smuzhiyun struct rk3066_pll *pll = &cru->pll[pll_id];
112*4882a593Smuzhiyun /* All PLLs have same VCO and output frequency range restrictions. */
113*4882a593Smuzhiyun uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
114*4882a593Smuzhiyun uint output_hz = vco_hz / div->no;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun debug("PLL at %x: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
117*4882a593Smuzhiyun (uint)pll, div->nf, div->nr, div->no, vco_hz, output_hz);
118*4882a593Smuzhiyun assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
119*4882a593Smuzhiyun output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ &&
120*4882a593Smuzhiyun (div->no == 1 || !(div->no % 2)));
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /* enter reset */
123*4882a593Smuzhiyun rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun rk_clrsetreg(&pll->con0,
126*4882a593Smuzhiyun CLKR_MASK | PLL_OD_MASK,
127*4882a593Smuzhiyun ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1));
128*4882a593Smuzhiyun rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1);
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun if (has_bwadj)
131*4882a593Smuzhiyun rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun udelay(10);
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun /* return from reset */
136*4882a593Smuzhiyun rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
rkclk_configure_ddr(struct rk3066_cru * cru,struct rk3066_grf * grf,unsigned int hz,bool has_bwadj)141*4882a593Smuzhiyun static int rkclk_configure_ddr(struct rk3066_cru *cru, struct rk3066_grf *grf,
142*4882a593Smuzhiyun unsigned int hz, bool has_bwadj)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun static const struct pll_div dpll_cfg[] = {
145*4882a593Smuzhiyun {.nf = 25, .nr = 2, .no = 1},
146*4882a593Smuzhiyun {.nf = 400, .nr = 9, .no = 2},
147*4882a593Smuzhiyun {.nf = 500, .nr = 9, .no = 2},
148*4882a593Smuzhiyun {.nf = 100, .nr = 3, .no = 1},
149*4882a593Smuzhiyun };
150*4882a593Smuzhiyun int cfg;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun switch (hz) {
153*4882a593Smuzhiyun case 300000000:
154*4882a593Smuzhiyun cfg = 0;
155*4882a593Smuzhiyun break;
156*4882a593Smuzhiyun case 533000000: /* actually 533.3P MHz */
157*4882a593Smuzhiyun cfg = 1;
158*4882a593Smuzhiyun break;
159*4882a593Smuzhiyun case 666000000: /* actually 666.6P MHz */
160*4882a593Smuzhiyun cfg = 2;
161*4882a593Smuzhiyun break;
162*4882a593Smuzhiyun case 800000000:
163*4882a593Smuzhiyun cfg = 3;
164*4882a593Smuzhiyun break;
165*4882a593Smuzhiyun default:
166*4882a593Smuzhiyun debug("Unsupported SDRAM frequency");
167*4882a593Smuzhiyun return -EINVAL;
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun /* pll enter slow-mode */
171*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK,
172*4882a593Smuzhiyun DPLL_MODE_SLOW << DPLL_MODE_SHIFT);
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg], has_bwadj);
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun /* wait for pll lock */
177*4882a593Smuzhiyun while (!(readl(&grf->soc_status0) & SOCSTS_DPLL_LOCK))
178*4882a593Smuzhiyun udelay(1);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun /* PLL enter normal-mode */
181*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK,
182*4882a593Smuzhiyun DPLL_MODE_NORMAL << DPLL_MODE_SHIFT);
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun return 0;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
rkclk_configure_cpu(struct rk3066_cru * cru,struct rk3066_grf * grf,unsigned int hz,bool has_bwadj)187*4882a593Smuzhiyun static int rkclk_configure_cpu(struct rk3066_cru *cru, struct rk3066_grf *grf,
188*4882a593Smuzhiyun unsigned int hz, bool has_bwadj)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun static const struct pll_div apll_cfg[] = {
191*4882a593Smuzhiyun {.nf = 50, .nr = 1, .no = 2},
192*4882a593Smuzhiyun {.nf = 59, .nr = 1, .no = 1},
193*4882a593Smuzhiyun };
194*4882a593Smuzhiyun int div_core_peri, div_aclk_core, cfg;
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun /*
197*4882a593Smuzhiyun * We support two possible frequencies, the safe 600MHz
198*4882a593Smuzhiyun * which will work with default pmic settings and will
199*4882a593Smuzhiyun * be set to get away from the 24MHz default and
200*4882a593Smuzhiyun * the maximum of 1.416Ghz, which boards can set if they
201*4882a593Smuzhiyun * were able to get pmic support for it.
202*4882a593Smuzhiyun */
203*4882a593Smuzhiyun switch (hz) {
204*4882a593Smuzhiyun case APLL_SAFE_HZ:
205*4882a593Smuzhiyun cfg = 0;
206*4882a593Smuzhiyun div_core_peri = 1;
207*4882a593Smuzhiyun div_aclk_core = 3;
208*4882a593Smuzhiyun break;
209*4882a593Smuzhiyun case APLL_HZ:
210*4882a593Smuzhiyun cfg = 1;
211*4882a593Smuzhiyun div_core_peri = 2;
212*4882a593Smuzhiyun div_aclk_core = 3;
213*4882a593Smuzhiyun break;
214*4882a593Smuzhiyun default:
215*4882a593Smuzhiyun debug("Unsupported ARMCLK frequency");
216*4882a593Smuzhiyun return -EINVAL;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun /* pll enter slow-mode */
220*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK,
221*4882a593Smuzhiyun APLL_MODE_SLOW << APLL_MODE_SHIFT);
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun rkclk_set_pll(cru, CLK_ARM, &apll_cfg[cfg], has_bwadj);
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* waiting for pll lock */
226*4882a593Smuzhiyun while (!(readl(&grf->soc_status0) & SOCSTS_APLL_LOCK))
227*4882a593Smuzhiyun udelay(1);
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun /* Set divider for peripherals attached to the cpu core. */
230*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[0],
231*4882a593Smuzhiyun CORE_PERI_DIV_MASK,
232*4882a593Smuzhiyun div_core_peri << CORE_PERI_DIV_SHIFT);
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /* set up dependent divisor for aclk_core */
235*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[1],
236*4882a593Smuzhiyun CORE_ACLK_DIV_MASK,
237*4882a593Smuzhiyun div_aclk_core << CORE_ACLK_DIV_SHIFT);
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun /* PLL enter normal-mode */
240*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_mode_con, APLL_MODE_MASK,
241*4882a593Smuzhiyun APLL_MODE_NORMAL << APLL_MODE_SHIFT);
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun return hz;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun /* Get pll rate by id */
rkclk_pll_get_rate(struct rk3066_cru * cru,enum rk_clk_id clk_id)247*4882a593Smuzhiyun static uint32_t rkclk_pll_get_rate(struct rk3066_cru *cru,
248*4882a593Smuzhiyun enum rk_clk_id clk_id)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun uint32_t nr, no, nf;
251*4882a593Smuzhiyun uint32_t con;
252*4882a593Smuzhiyun int pll_id = rk_pll_id(clk_id);
253*4882a593Smuzhiyun struct rk3066_pll *pll = &cru->pll[pll_id];
254*4882a593Smuzhiyun static u8 clk_shift[CLK_COUNT] = {
255*4882a593Smuzhiyun 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT,
256*4882a593Smuzhiyun GPLL_MODE_SHIFT
257*4882a593Smuzhiyun };
258*4882a593Smuzhiyun uint shift;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun con = readl(&cru->cru_mode_con);
261*4882a593Smuzhiyun shift = clk_shift[clk_id];
262*4882a593Smuzhiyun switch ((con >> shift) & APLL_MODE_MASK >> APLL_MODE_SHIFT) {
263*4882a593Smuzhiyun case APLL_MODE_SLOW:
264*4882a593Smuzhiyun return OSC_HZ;
265*4882a593Smuzhiyun case APLL_MODE_NORMAL:
266*4882a593Smuzhiyun /* normal mode */
267*4882a593Smuzhiyun con = readl(&pll->con0);
268*4882a593Smuzhiyun no = ((con >> CLKOD_SHIFT) & (CLKOD_MASK >> CLKOD_SHIFT)) + 1;
269*4882a593Smuzhiyun nr = ((con >> CLKR_SHIFT) & (CLKR_MASK >> CLKR_SHIFT)) + 1;
270*4882a593Smuzhiyun con = readl(&pll->con1);
271*4882a593Smuzhiyun nf = ((con >> CLKF_SHIFT) & (CLKF_MASK >> CLKF_SHIFT)) + 1;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun return (24 * nf / (nr * no)) * 1000000;
274*4882a593Smuzhiyun case APLL_MODE_DEEP:
275*4882a593Smuzhiyun default:
276*4882a593Smuzhiyun return 32768;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
rockchip_mmc_get_clk(struct rk3066_cru * cru,uint gclk_rate,int periph)280*4882a593Smuzhiyun static ulong rockchip_mmc_get_clk(struct rk3066_cru *cru, uint gclk_rate,
281*4882a593Smuzhiyun int periph)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun uint div;
284*4882a593Smuzhiyun u32 con;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun switch (periph) {
287*4882a593Smuzhiyun case HCLK_EMMC:
288*4882a593Smuzhiyun con = readl(&cru->cru_clksel_con[12]);
289*4882a593Smuzhiyun div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK >> EMMC_DIV_SHIFT;
290*4882a593Smuzhiyun break;
291*4882a593Smuzhiyun case HCLK_SDMMC:
292*4882a593Smuzhiyun con = readl(&cru->cru_clksel_con[11]);
293*4882a593Smuzhiyun div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK >> MMC0_DIV_SHIFT;
294*4882a593Smuzhiyun break;
295*4882a593Smuzhiyun case HCLK_SDIO:
296*4882a593Smuzhiyun con = readl(&cru->cru_clksel_con[12]);
297*4882a593Smuzhiyun div = (con >> SDIO_DIV_SHIFT) & SDIO_DIV_MASK >> SDIO_DIV_SHIFT;
298*4882a593Smuzhiyun break;
299*4882a593Smuzhiyun default:
300*4882a593Smuzhiyun return -EINVAL;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun return DIV_TO_RATE(gclk_rate, div);
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
rockchip_mmc_set_clk(struct rk3066_cru * cru,uint gclk_rate,int periph,uint freq)306*4882a593Smuzhiyun static ulong rockchip_mmc_set_clk(struct rk3066_cru *cru, uint gclk_rate,
307*4882a593Smuzhiyun int periph, uint freq)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun int src_clk_div;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun debug("%s: gclk_rate=%u\n", __func__, gclk_rate);
312*4882a593Smuzhiyun src_clk_div = RATE_TO_DIV(gclk_rate, freq);
313*4882a593Smuzhiyun if (src_clk_div > 0x3f)
314*4882a593Smuzhiyun src_clk_div = 0x3f;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun switch (periph) {
317*4882a593Smuzhiyun case HCLK_EMMC:
318*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[12],
319*4882a593Smuzhiyun EMMC_DIV_MASK,
320*4882a593Smuzhiyun src_clk_div << EMMC_DIV_SHIFT);
321*4882a593Smuzhiyun break;
322*4882a593Smuzhiyun case HCLK_SDMMC:
323*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[11],
324*4882a593Smuzhiyun MMC0_DIV_MASK,
325*4882a593Smuzhiyun src_clk_div << MMC0_DIV_SHIFT);
326*4882a593Smuzhiyun break;
327*4882a593Smuzhiyun case HCLK_SDIO:
328*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[12],
329*4882a593Smuzhiyun SDIO_DIV_MASK,
330*4882a593Smuzhiyun src_clk_div << SDIO_DIV_SHIFT);
331*4882a593Smuzhiyun break;
332*4882a593Smuzhiyun default:
333*4882a593Smuzhiyun return -EINVAL;
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun return rockchip_mmc_get_clk(cru, gclk_rate, periph);
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun
rockchip_spi_get_clk(struct rk3066_cru * cru,uint gclk_rate,int periph)339*4882a593Smuzhiyun static ulong rockchip_spi_get_clk(struct rk3066_cru *cru, uint gclk_rate,
340*4882a593Smuzhiyun int periph)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun uint div;
343*4882a593Smuzhiyun u32 con;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun switch (periph) {
346*4882a593Smuzhiyun case SCLK_SPI0:
347*4882a593Smuzhiyun con = readl(&cru->cru_clksel_con[25]);
348*4882a593Smuzhiyun div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK >> SPI0_DIV_SHIFT;
349*4882a593Smuzhiyun break;
350*4882a593Smuzhiyun case SCLK_SPI1:
351*4882a593Smuzhiyun con = readl(&cru->cru_clksel_con[25]);
352*4882a593Smuzhiyun div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK >> SPI1_DIV_SHIFT;
353*4882a593Smuzhiyun break;
354*4882a593Smuzhiyun default:
355*4882a593Smuzhiyun return -EINVAL;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun return DIV_TO_RATE(gclk_rate, div);
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
rockchip_spi_set_clk(struct rk3066_cru * cru,uint gclk_rate,int periph,uint freq)361*4882a593Smuzhiyun static ulong rockchip_spi_set_clk(struct rk3066_cru *cru, uint gclk_rate,
362*4882a593Smuzhiyun int periph, uint freq)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun int src_clk_div = RATE_TO_DIV(gclk_rate, freq);
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun switch (periph) {
367*4882a593Smuzhiyun case SCLK_SPI0:
368*4882a593Smuzhiyun assert(src_clk_div <= SPI0_DIV_MASK >> SPI0_DIV_SHIFT);
369*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[25],
370*4882a593Smuzhiyun SPI0_DIV_MASK,
371*4882a593Smuzhiyun src_clk_div << SPI0_DIV_SHIFT);
372*4882a593Smuzhiyun break;
373*4882a593Smuzhiyun case SCLK_SPI1:
374*4882a593Smuzhiyun assert(src_clk_div <= SPI1_DIV_MASK >> SPI1_DIV_SHIFT);
375*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[25],
376*4882a593Smuzhiyun SPI1_DIV_MASK,
377*4882a593Smuzhiyun src_clk_div << SPI1_DIV_SHIFT);
378*4882a593Smuzhiyun break;
379*4882a593Smuzhiyun default:
380*4882a593Smuzhiyun return -EINVAL;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun return rockchip_spi_get_clk(cru, gclk_rate, periph);
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun #ifdef CONFIG_TPL_BUILD
rkclk_init(struct rk3066_cru * cru,struct rk3066_grf * grf,bool has_bwadj)386*4882a593Smuzhiyun static void rkclk_init(struct rk3066_cru *cru, struct rk3066_grf *grf,
387*4882a593Smuzhiyun bool has_bwadj)
388*4882a593Smuzhiyun {
389*4882a593Smuzhiyun u32 aclk_div, hclk_div, pclk_div, h2p_div;
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun /* pll enter slow-mode */
392*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_mode_con,
393*4882a593Smuzhiyun GPLL_MODE_MASK |
394*4882a593Smuzhiyun CPLL_MODE_MASK,
395*4882a593Smuzhiyun GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
396*4882a593Smuzhiyun CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun /* init pll */
399*4882a593Smuzhiyun rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg, has_bwadj);
400*4882a593Smuzhiyun rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg, has_bwadj);
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun /* waiting for pll lock */
403*4882a593Smuzhiyun while ((readl(&grf->soc_status0) &
404*4882a593Smuzhiyun (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) !=
405*4882a593Smuzhiyun (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK))
406*4882a593Smuzhiyun udelay(1);
407*4882a593Smuzhiyun
408*4882a593Smuzhiyun /*
409*4882a593Smuzhiyun * cpu clock pll source selection and
410*4882a593Smuzhiyun * reparent aclk_cpu_pre from apll to gpll
411*4882a593Smuzhiyun * set up dependent divisors for PCLK/HCLK and ACLK clocks.
412*4882a593Smuzhiyun */
413*4882a593Smuzhiyun aclk_div = RATE_TO_DIV(GPLL_HZ, CPU_ACLK_HZ);
414*4882a593Smuzhiyun assert((aclk_div + 1) * CPU_ACLK_HZ <= GPLL_HZ && aclk_div < 0x1f);
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[0],
417*4882a593Smuzhiyun CPU_ACLK_PLL_MASK |
418*4882a593Smuzhiyun A9_CPU_DIV_MASK,
419*4882a593Smuzhiyun CPU_ACLK_PLL_SELECT_GPLL << CPU_ACLK_PLL_SHIFT |
420*4882a593Smuzhiyun aclk_div << A9_CPU_DIV_SHIFT);
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun hclk_div = ilog2(CPU_ACLK_HZ / CPU_HCLK_HZ);
423*4882a593Smuzhiyun assert((1 << hclk_div) * CPU_HCLK_HZ <= CPU_ACLK_HZ && hclk_div < 0x3);
424*4882a593Smuzhiyun pclk_div = ilog2(CPU_ACLK_HZ / CPU_PCLK_HZ);
425*4882a593Smuzhiyun assert((1 << pclk_div) * CPU_PCLK_HZ <= CPU_ACLK_HZ && pclk_div < 0x4);
426*4882a593Smuzhiyun h2p_div = ilog2(CPU_HCLK_HZ / CPU_H2P_HZ);
427*4882a593Smuzhiyun assert((1 << h2p_div) * CPU_H2P_HZ <= CPU_HCLK_HZ && pclk_div < 0x3);
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[1],
430*4882a593Smuzhiyun AHB2APB_DIV_MASK |
431*4882a593Smuzhiyun CPU_PCLK_DIV_MASK |
432*4882a593Smuzhiyun CPU_HCLK_DIV_MASK,
433*4882a593Smuzhiyun h2p_div << AHB2APB_DIV_SHIFT |
434*4882a593Smuzhiyun pclk_div << CPU_PCLK_DIV_SHIFT |
435*4882a593Smuzhiyun hclk_div << CPU_HCLK_DIV_SHIFT);
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun /*
438*4882a593Smuzhiyun * peri clock pll source selection and
439*4882a593Smuzhiyun * set up dependent divisors for PCLK/HCLK and ACLK clocks.
440*4882a593Smuzhiyun */
441*4882a593Smuzhiyun aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
442*4882a593Smuzhiyun assert((aclk_div + 1) * PERI_ACLK_HZ <= GPLL_HZ && aclk_div < 0x1f);
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ);
445*4882a593Smuzhiyun assert((1 << hclk_div) * PERI_HCLK_HZ <=
446*4882a593Smuzhiyun PERI_ACLK_HZ && (hclk_div < 0x4));
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ);
449*4882a593Smuzhiyun assert((1 << pclk_div) * PERI_PCLK_HZ <=
450*4882a593Smuzhiyun PERI_ACLK_HZ && (pclk_div < 0x4));
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_clksel_con[10],
453*4882a593Smuzhiyun PERI_PCLK_DIV_MASK |
454*4882a593Smuzhiyun PERI_HCLK_DIV_MASK |
455*4882a593Smuzhiyun PERI_ACLK_DIV_MASK,
456*4882a593Smuzhiyun PERI_SEL_GPLL << PERI_SEL_PLL_SHIFT |
457*4882a593Smuzhiyun pclk_div << PERI_PCLK_DIV_SHIFT |
458*4882a593Smuzhiyun hclk_div << PERI_HCLK_DIV_SHIFT |
459*4882a593Smuzhiyun aclk_div << PERI_ACLK_DIV_SHIFT);
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun /* PLL enter normal-mode */
462*4882a593Smuzhiyun rk_clrsetreg(&cru->cru_mode_con,
463*4882a593Smuzhiyun GPLL_MODE_MASK |
464*4882a593Smuzhiyun CPLL_MODE_MASK,
465*4882a593Smuzhiyun GPLL_MODE_NORMAL << GPLL_MODE_SHIFT |
466*4882a593Smuzhiyun CPLL_MODE_NORMAL << CPLL_MODE_SHIFT);
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun rockchip_mmc_set_clk(cru, PERI_HCLK_HZ, HCLK_SDMMC, 16000000);
469*4882a593Smuzhiyun }
470*4882a593Smuzhiyun #endif
471*4882a593Smuzhiyun
rk3066_clk_get_rate(struct clk * clk)472*4882a593Smuzhiyun static ulong rk3066_clk_get_rate(struct clk *clk)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun struct rk3066_clk_priv *priv = dev_get_priv(clk->dev);
475*4882a593Smuzhiyun ulong new_rate, gclk_rate;
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
478*4882a593Smuzhiyun switch (clk->id) {
479*4882a593Smuzhiyun case 1 ... 4:
480*4882a593Smuzhiyun new_rate = rkclk_pll_get_rate(priv->cru, clk->id);
481*4882a593Smuzhiyun break;
482*4882a593Smuzhiyun case HCLK_EMMC:
483*4882a593Smuzhiyun case HCLK_SDMMC:
484*4882a593Smuzhiyun case HCLK_SDIO:
485*4882a593Smuzhiyun new_rate = rockchip_mmc_get_clk(priv->cru, PERI_HCLK_HZ,
486*4882a593Smuzhiyun clk->id);
487*4882a593Smuzhiyun break;
488*4882a593Smuzhiyun case SCLK_SPI0:
489*4882a593Smuzhiyun case SCLK_SPI1:
490*4882a593Smuzhiyun new_rate = rockchip_spi_get_clk(priv->cru, PERI_PCLK_HZ,
491*4882a593Smuzhiyun clk->id);
492*4882a593Smuzhiyun break;
493*4882a593Smuzhiyun case PCLK_I2C0:
494*4882a593Smuzhiyun case PCLK_I2C1:
495*4882a593Smuzhiyun case PCLK_I2C2:
496*4882a593Smuzhiyun case PCLK_I2C3:
497*4882a593Smuzhiyun case PCLK_I2C4:
498*4882a593Smuzhiyun return gclk_rate;
499*4882a593Smuzhiyun default:
500*4882a593Smuzhiyun return -ENOENT;
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun return new_rate;
504*4882a593Smuzhiyun }
505*4882a593Smuzhiyun
rk3066_clk_set_rate(struct clk * clk,ulong rate)506*4882a593Smuzhiyun static ulong rk3066_clk_set_rate(struct clk *clk, ulong rate)
507*4882a593Smuzhiyun {
508*4882a593Smuzhiyun struct rk3066_clk_priv *priv = dev_get_priv(clk->dev);
509*4882a593Smuzhiyun struct rk3066_cru *cru = priv->cru;
510*4882a593Smuzhiyun ulong new_rate;
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun switch (clk->id) {
513*4882a593Smuzhiyun case PLL_APLL:
514*4882a593Smuzhiyun new_rate = rkclk_configure_cpu(priv->cru, priv->grf, rate,
515*4882a593Smuzhiyun priv->has_bwadj);
516*4882a593Smuzhiyun break;
517*4882a593Smuzhiyun case CLK_DDR:
518*4882a593Smuzhiyun new_rate = rkclk_configure_ddr(priv->cru, priv->grf, rate,
519*4882a593Smuzhiyun priv->has_bwadj);
520*4882a593Smuzhiyun break;
521*4882a593Smuzhiyun case HCLK_EMMC:
522*4882a593Smuzhiyun case HCLK_SDMMC:
523*4882a593Smuzhiyun case HCLK_SDIO:
524*4882a593Smuzhiyun new_rate = rockchip_mmc_set_clk(cru, PERI_HCLK_HZ,
525*4882a593Smuzhiyun clk->id, rate);
526*4882a593Smuzhiyun break;
527*4882a593Smuzhiyun case SCLK_SPI0:
528*4882a593Smuzhiyun case SCLK_SPI1:
529*4882a593Smuzhiyun new_rate = rockchip_spi_set_clk(cru, PERI_PCLK_HZ,
530*4882a593Smuzhiyun clk->id, rate);
531*4882a593Smuzhiyun break;
532*4882a593Smuzhiyun default:
533*4882a593Smuzhiyun return -ENOENT;
534*4882a593Smuzhiyun }
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun return new_rate;
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun static struct clk_ops rk3066_clk_ops = {
540*4882a593Smuzhiyun .get_rate = rk3066_clk_get_rate,
541*4882a593Smuzhiyun .set_rate = rk3066_clk_set_rate,
542*4882a593Smuzhiyun };
543*4882a593Smuzhiyun
rk3066_clk_ofdata_to_platdata(struct udevice * dev)544*4882a593Smuzhiyun static int rk3066_clk_ofdata_to_platdata(struct udevice *dev)
545*4882a593Smuzhiyun {
546*4882a593Smuzhiyun #if !CONFIG_IS_ENABLED(OF_PLATDATA)
547*4882a593Smuzhiyun struct rk3066_clk_priv *priv = dev_get_priv(dev);
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun priv->cru = dev_read_addr_ptr(dev);
550*4882a593Smuzhiyun #endif
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun return 0;
553*4882a593Smuzhiyun }
554*4882a593Smuzhiyun
rk3066_clk_probe(struct udevice * dev)555*4882a593Smuzhiyun static int rk3066_clk_probe(struct udevice *dev)
556*4882a593Smuzhiyun {
557*4882a593Smuzhiyun struct rk3066_clk_priv *priv = dev_get_priv(dev);
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
560*4882a593Smuzhiyun if (IS_ERR(priv->grf))
561*4882a593Smuzhiyun return PTR_ERR(priv->grf);
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun #ifdef CONFIG_TPL_BUILD
564*4882a593Smuzhiyun #if CONFIG_IS_ENABLED(OF_PLATDATA)
565*4882a593Smuzhiyun struct rk3066_clk_plat *plat = dev_get_platdata(dev);
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun priv->cru = map_sysmem(plat->dtd.reg[0], plat->dtd.reg[1]);
568*4882a593Smuzhiyun #endif
569*4882a593Smuzhiyun priv->sync_kernel = false;
570*4882a593Smuzhiyun if (!priv->armclk_enter_hz)
571*4882a593Smuzhiyun priv->armclk_enter_hz = rkclk_pll_get_rate(priv->cru,
572*4882a593Smuzhiyun CLK_ARM);
573*4882a593Smuzhiyun rkclk_init(priv->cru, priv->grf, 1);
574*4882a593Smuzhiyun if (!priv->armclk_init_hz)
575*4882a593Smuzhiyun priv->armclk_init_hz = rkclk_pll_get_rate(priv->cru,
576*4882a593Smuzhiyun CLK_ARM);
577*4882a593Smuzhiyun #endif
578*4882a593Smuzhiyun return 0;
579*4882a593Smuzhiyun }
580*4882a593Smuzhiyun
rk3066_clk_bind(struct udevice * dev)581*4882a593Smuzhiyun static int rk3066_clk_bind(struct udevice *dev)
582*4882a593Smuzhiyun {
583*4882a593Smuzhiyun int ret;
584*4882a593Smuzhiyun struct udevice *sys_child, *sf_child;
585*4882a593Smuzhiyun struct sysreset_reg *priv;
586*4882a593Smuzhiyun struct softreset_reg *sf_priv;
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun /* The reset driver does not have a device node, so bind it here */
589*4882a593Smuzhiyun ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
590*4882a593Smuzhiyun &sys_child);
591*4882a593Smuzhiyun if (ret) {
592*4882a593Smuzhiyun debug("Warning: No sysreset driver: ret=%d\n", ret);
593*4882a593Smuzhiyun } else {
594*4882a593Smuzhiyun priv = malloc(sizeof(struct sysreset_reg));
595*4882a593Smuzhiyun priv->glb_srst_fst_value = offsetof(struct rk3066_cru,
596*4882a593Smuzhiyun cru_glb_srst_fst_value);
597*4882a593Smuzhiyun priv->glb_srst_snd_value = offsetof(struct rk3066_cru,
598*4882a593Smuzhiyun cru_glb_srst_snd_value);
599*4882a593Smuzhiyun sys_child->priv = priv;
600*4882a593Smuzhiyun }
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun ret = device_bind_driver_to_node(dev, "rockchip_reset", "reset",
603*4882a593Smuzhiyun dev_ofnode(dev), &sf_child);
604*4882a593Smuzhiyun if (ret) {
605*4882a593Smuzhiyun debug("Warning: No rockchip reset driver: ret=%d\n", ret);
606*4882a593Smuzhiyun } else {
607*4882a593Smuzhiyun sf_priv = malloc(sizeof(struct softreset_reg));
608*4882a593Smuzhiyun sf_priv->sf_reset_offset = offsetof(struct rk3066_cru,
609*4882a593Smuzhiyun cru_softrst_con[0]);
610*4882a593Smuzhiyun sf_priv->sf_reset_num = 9;
611*4882a593Smuzhiyun sf_child->priv = sf_priv;
612*4882a593Smuzhiyun }
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun return 0;
615*4882a593Smuzhiyun }
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun static const struct udevice_id rk3066_clk_ids[] = {
618*4882a593Smuzhiyun { .compatible = "rockchip,rk3066a-cru" },
619*4882a593Smuzhiyun { }
620*4882a593Smuzhiyun };
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun U_BOOT_DRIVER(rockchip_rk3066a_cru) = {
623*4882a593Smuzhiyun .name = "rockchip_rk3066a_cru",
624*4882a593Smuzhiyun .id = UCLASS_CLK,
625*4882a593Smuzhiyun .of_match = rk3066_clk_ids,
626*4882a593Smuzhiyun .priv_auto_alloc_size = sizeof(struct rk3066_clk_priv),
627*4882a593Smuzhiyun .platdata_auto_alloc_size = sizeof(struct rk3066_clk_plat),
628*4882a593Smuzhiyun .ops = &rk3066_clk_ops,
629*4882a593Smuzhiyun .bind = rk3066_clk_bind,
630*4882a593Smuzhiyun .ofdata_to_platdata = rk3066_clk_ofdata_to_platdata,
631*4882a593Smuzhiyun .probe = rk3066_clk_probe,
632*4882a593Smuzhiyun };
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun #ifndef CONFIG_SPL_BUILD
635*4882a593Smuzhiyun /**
636*4882a593Smuzhiyun * soc_clk_dump() - Print clock frequencies
637*4882a593Smuzhiyun * Returns zero on success
638*4882a593Smuzhiyun *
639*4882a593Smuzhiyun * Implementation for the clk dump command.
640*4882a593Smuzhiyun */
soc_clk_dump(void)641*4882a593Smuzhiyun int soc_clk_dump(void)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun struct udevice *cru_dev;
644*4882a593Smuzhiyun struct rk3066_clk_priv *priv;
645*4882a593Smuzhiyun const struct rk3066_clk_info *clk_dump;
646*4882a593Smuzhiyun struct clk clk;
647*4882a593Smuzhiyun unsigned long clk_count = ARRAY_SIZE(clks_dump);
648*4882a593Smuzhiyun unsigned long rate;
649*4882a593Smuzhiyun int i, ret;
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun ret = uclass_get_device_by_driver(UCLASS_CLK,
652*4882a593Smuzhiyun DM_GET_DRIVER(rockchip_rk3066a_cru),
653*4882a593Smuzhiyun &cru_dev);
654*4882a593Smuzhiyun if (ret) {
655*4882a593Smuzhiyun printf("%s failed to get cru device\n", __func__);
656*4882a593Smuzhiyun return ret;
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun priv = dev_get_priv(cru_dev);
660*4882a593Smuzhiyun printf("CLK: (%s. arm: enter %lu KHz, init %lu KHz, kernel %lu%s)\n",
661*4882a593Smuzhiyun priv->sync_kernel ? "sync kernel" : "uboot",
662*4882a593Smuzhiyun priv->armclk_enter_hz / 1000,
663*4882a593Smuzhiyun priv->armclk_init_hz / 1000,
664*4882a593Smuzhiyun priv->set_armclk_rate ? priv->armclk_hz / 1000 : 0,
665*4882a593Smuzhiyun priv->set_armclk_rate ? " KHz" : "N/A");
666*4882a593Smuzhiyun for (i = 0; i < clk_count; i++) {
667*4882a593Smuzhiyun clk_dump = &clks_dump[i];
668*4882a593Smuzhiyun if (clk_dump->name) {
669*4882a593Smuzhiyun clk.id = clk_dump->id;
670*4882a593Smuzhiyun if (clk_dump->is_cru)
671*4882a593Smuzhiyun ret = clk_request(cru_dev, &clk);
672*4882a593Smuzhiyun if (ret < 0)
673*4882a593Smuzhiyun return ret;
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun rate = clk_get_rate(&clk);
676*4882a593Smuzhiyun clk_free(&clk);
677*4882a593Smuzhiyun if (i == 0) {
678*4882a593Smuzhiyun if (rate < 0)
679*4882a593Smuzhiyun printf(" %s %s\n", clk_dump->name,
680*4882a593Smuzhiyun "unknown");
681*4882a593Smuzhiyun else
682*4882a593Smuzhiyun printf(" %s %lu KHz\n", clk_dump->name,
683*4882a593Smuzhiyun rate / 1000);
684*4882a593Smuzhiyun } else {
685*4882a593Smuzhiyun if (rate < 0)
686*4882a593Smuzhiyun printf(" %s %s\n", clk_dump->name,
687*4882a593Smuzhiyun "unknown");
688*4882a593Smuzhiyun else
689*4882a593Smuzhiyun printf(" %s %lu KHz\n", clk_dump->name,
690*4882a593Smuzhiyun rate / 1000);
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun }
693*4882a593Smuzhiyun }
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun return 0;
696*4882a593Smuzhiyun }
697*4882a593Smuzhiyun #endif
698*4882a593Smuzhiyun
699