]>
Commit | Line | Data |
---|---|---|
d49a5267 KY |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * (C) Copyright 2017 Rockchip Electronics Co., Ltd | |
4 | */ | |
5 | ||
6 | #include <common.h> | |
7 | #include <bitfield.h> | |
8 | #include <clk-uclass.h> | |
9 | #include <dm.h> | |
10 | #include <errno.h> | |
f7ae49fc | 11 | #include <log.h> |
336d4615 | 12 | #include <malloc.h> |
d49a5267 KY |
13 | #include <syscon.h> |
14 | #include <asm/arch-rockchip/clock.h> | |
15 | #include <asm/arch-rockchip/cru_px30.h> | |
16 | #include <asm/arch-rockchip/hardware.h> | |
17 | #include <asm/io.h> | |
18 | #include <dm/lists.h> | |
19 | #include <dt-bindings/clock/px30-cru.h> | |
cd93d625 | 20 | #include <linux/bitops.h> |
c05ed00a | 21 | #include <linux/delay.h> |
d49a5267 KY |
22 | |
23 | DECLARE_GLOBAL_DATA_PTR; | |
24 | ||
25 | enum { | |
26 | VCO_MAX_HZ = 3200U * 1000000, | |
27 | VCO_MIN_HZ = 800 * 1000000, | |
28 | OUTPUT_MAX_HZ = 3200U * 1000000, | |
29 | OUTPUT_MIN_HZ = 24 * 1000000, | |
30 | }; | |
31 | ||
32 | #define PX30_VOP_PLL_LIMIT 600000000 | |
33 | ||
34 | #define PX30_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, \ | |
35 | _postdiv2, _dsmpd, _frac) \ | |
36 | { \ | |
37 | .rate = _rate##U, \ | |
38 | .fbdiv = _fbdiv, \ | |
39 | .postdiv1 = _postdiv1, \ | |
40 | .refdiv = _refdiv, \ | |
41 | .postdiv2 = _postdiv2, \ | |
42 | .dsmpd = _dsmpd, \ | |
43 | .frac = _frac, \ | |
44 | } | |
45 | ||
46 | #define PX30_CPUCLK_RATE(_rate, _aclk_div, _pclk_div) \ | |
47 | { \ | |
48 | .rate = _rate##U, \ | |
49 | .aclk_div = _aclk_div, \ | |
50 | .pclk_div = _pclk_div, \ | |
51 | } | |
52 | ||
53 | #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) | |
54 | ||
55 | #define PX30_CLK_DUMP(_id, _name, _iscru) \ | |
56 | { \ | |
57 | .id = _id, \ | |
58 | .name = _name, \ | |
59 | .is_cru = _iscru, \ | |
60 | } | |
61 | ||
62 | static struct pll_rate_table px30_pll_rates[] = { | |
63 | /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */ | |
64 | PX30_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0), | |
65 | PX30_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0), | |
66 | PX30_PLL_RATE(1100000000, 12, 550, 1, 1, 1, 0), | |
67 | PX30_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0), | |
68 | PX30_PLL_RATE(1000000000, 6, 500, 2, 1, 1, 0), | |
69 | PX30_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0), | |
70 | PX30_PLL_RATE(600000000, 1, 75, 3, 1, 1, 0), | |
71 | }; | |
72 | ||
73 | static struct cpu_rate_table px30_cpu_rates[] = { | |
74 | PX30_CPUCLK_RATE(1200000000, 1, 5), | |
75 | PX30_CPUCLK_RATE(1008000000, 1, 5), | |
76 | PX30_CPUCLK_RATE(816000000, 1, 3), | |
77 | PX30_CPUCLK_RATE(600000000, 1, 3), | |
78 | PX30_CPUCLK_RATE(408000000, 1, 1), | |
79 | }; | |
80 | ||
81 | static u8 pll_mode_shift[PLL_COUNT] = { | |
82 | APLL_MODE_SHIFT, DPLL_MODE_SHIFT, CPLL_MODE_SHIFT, | |
83 | NPLL_MODE_SHIFT, GPLL_MODE_SHIFT | |
84 | }; | |
85 | ||
86 | static u32 pll_mode_mask[PLL_COUNT] = { | |
87 | APLL_MODE_MASK, DPLL_MODE_MASK, CPLL_MODE_MASK, | |
88 | NPLL_MODE_MASK, GPLL_MODE_MASK | |
89 | }; | |
90 | ||
91 | static struct pll_rate_table auto_table; | |
92 | ||
93 | static ulong px30_clk_get_pll_rate(struct px30_clk_priv *priv, | |
94 | enum px30_pll_id pll_id); | |
95 | ||
96 | static struct pll_rate_table *pll_clk_set_by_auto(u32 drate) | |
97 | { | |
98 | struct pll_rate_table *rate = &auto_table; | |
99 | u32 ref_khz = OSC_HZ / KHz, refdiv, fbdiv = 0; | |
100 | u32 postdiv1, postdiv2 = 1; | |
101 | u32 fref_khz; | |
102 | u32 diff_khz, best_diff_khz; | |
103 | const u32 max_refdiv = 63, max_fbdiv = 3200, min_fbdiv = 16; | |
104 | const u32 max_postdiv1 = 7, max_postdiv2 = 7; | |
105 | u32 vco_khz; | |
106 | u32 rate_khz = drate / KHz; | |
107 | ||
108 | if (!drate) { | |
109 | printf("%s: the frequency can't be 0 Hz\n", __func__); | |
110 | return NULL; | |
111 | } | |
112 | ||
113 | postdiv1 = DIV_ROUND_UP(VCO_MIN_HZ / 1000, rate_khz); | |
114 | if (postdiv1 > max_postdiv1) { | |
115 | postdiv2 = DIV_ROUND_UP(postdiv1, max_postdiv1); | |
116 | postdiv1 = DIV_ROUND_UP(postdiv1, postdiv2); | |
117 | } | |
118 | ||
119 | vco_khz = rate_khz * postdiv1 * postdiv2; | |
120 | ||
121 | if (vco_khz < (VCO_MIN_HZ / KHz) || vco_khz > (VCO_MAX_HZ / KHz) || | |
122 | postdiv2 > max_postdiv2) { | |
123 | printf("%s: Cannot find out a supported VCO for Freq (%uHz)\n", | |
124 | __func__, rate_khz); | |
125 | return NULL; | |
126 | } | |
127 | ||
128 | rate->postdiv1 = postdiv1; | |
129 | rate->postdiv2 = postdiv2; | |
130 | ||
131 | best_diff_khz = vco_khz; | |
132 | for (refdiv = 1; refdiv < max_refdiv && best_diff_khz; refdiv++) { | |
133 | fref_khz = ref_khz / refdiv; | |
134 | ||
135 | fbdiv = vco_khz / fref_khz; | |
136 | if (fbdiv >= max_fbdiv || fbdiv <= min_fbdiv) | |
137 | continue; | |
138 | ||
139 | diff_khz = vco_khz - fbdiv * fref_khz; | |
140 | if (fbdiv + 1 < max_fbdiv && diff_khz > fref_khz / 2) { | |
141 | fbdiv++; | |
142 | diff_khz = fref_khz - diff_khz; | |
143 | } | |
144 | ||
145 | if (diff_khz >= best_diff_khz) | |
146 | continue; | |
147 | ||
148 | best_diff_khz = diff_khz; | |
149 | rate->refdiv = refdiv; | |
150 | rate->fbdiv = fbdiv; | |
151 | } | |
152 | ||
153 | if (best_diff_khz > 4 * (MHz / KHz)) { | |
154 | printf("%s: Failed to match output frequency %u bestis %u Hz\n", | |
155 | __func__, rate_khz, | |
156 | best_diff_khz * KHz); | |
157 | return NULL; | |
158 | } | |
159 | ||
160 | return rate; | |
161 | } | |
162 | ||
163 | static const struct pll_rate_table *get_pll_settings(unsigned long rate) | |
164 | { | |
165 | unsigned int rate_count = ARRAY_SIZE(px30_pll_rates); | |
166 | int i; | |
167 | ||
168 | for (i = 0; i < rate_count; i++) { | |
169 | if (rate == px30_pll_rates[i].rate) | |
170 | return &px30_pll_rates[i]; | |
171 | } | |
172 | ||
173 | return pll_clk_set_by_auto(rate); | |
174 | } | |
175 | ||
176 | static const struct cpu_rate_table *get_cpu_settings(unsigned long rate) | |
177 | { | |
178 | unsigned int rate_count = ARRAY_SIZE(px30_cpu_rates); | |
179 | int i; | |
180 | ||
181 | for (i = 0; i < rate_count; i++) { | |
182 | if (rate == px30_cpu_rates[i].rate) | |
183 | return &px30_cpu_rates[i]; | |
184 | } | |
185 | ||
186 | return NULL; | |
187 | } | |
188 | ||
189 | /* | |
190 | * How to calculate the PLL(from TRM V0.3 Part 1 Page 63): | |
191 | * Formulas also embedded within the Fractional PLL Verilog model: | |
192 | * If DSMPD = 1 (DSM is disabled, "integer mode") | |
193 | * FOUTVCO = FREF / REFDIV * FBDIV | |
194 | * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2 | |
195 | * Where: | |
196 | * FOUTVCO = Fractional PLL non-divided output frequency | |
197 | * FOUTPOSTDIV = Fractional PLL divided output frequency | |
198 | * (output of second post divider) | |
199 | * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input) | |
200 | * REFDIV = Fractional PLL input reference clock divider | |
201 | * FBDIV = Integer value programmed into feedback divide | |
202 | * | |
203 | */ | |
204 | static int rkclk_set_pll(struct px30_pll *pll, unsigned int *mode, | |
205 | enum px30_pll_id pll_id, | |
206 | unsigned long drate) | |
207 | { | |
208 | const struct pll_rate_table *rate; | |
209 | uint vco_hz, output_hz; | |
210 | ||
211 | rate = get_pll_settings(drate); | |
212 | if (!rate) { | |
213 | printf("%s unsupport rate\n", __func__); | |
214 | return -EINVAL; | |
215 | } | |
216 | ||
217 | /* All PLLs have same VCO and output frequency range restrictions. */ | |
218 | vco_hz = OSC_HZ / 1000 * rate->fbdiv / rate->refdiv * 1000; | |
219 | output_hz = vco_hz / rate->postdiv1 / rate->postdiv2; | |
220 | ||
221 | debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n", | |
222 | pll, rate->fbdiv, rate->refdiv, rate->postdiv1, | |
223 | rate->postdiv2, vco_hz, output_hz); | |
224 | assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && | |
225 | output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ); | |
226 | ||
227 | /* | |
228 | * When power on or changing PLL setting, | |
229 | * we must force PLL into slow mode to ensure output stable clock. | |
230 | */ | |
231 | rk_clrsetreg(mode, pll_mode_mask[pll_id], | |
232 | PLLMUX_FROM_XIN24M << pll_mode_shift[pll_id]); | |
233 | ||
234 | /* use integer mode */ | |
235 | rk_setreg(&pll->con1, 1 << PLL_DSMPD_SHIFT); | |
236 | /* Power down */ | |
237 | rk_setreg(&pll->con1, 1 << PLL_PD_SHIFT); | |
238 | ||
239 | rk_clrsetreg(&pll->con0, | |
240 | PLL_POSTDIV1_MASK | PLL_FBDIV_MASK, | |
241 | (rate->postdiv1 << PLL_POSTDIV1_SHIFT) | rate->fbdiv); | |
242 | rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK | PLL_REFDIV_MASK, | |
243 | (rate->postdiv2 << PLL_POSTDIV2_SHIFT | | |
244 | rate->refdiv << PLL_REFDIV_SHIFT)); | |
245 | ||
246 | /* Power Up */ | |
247 | rk_clrreg(&pll->con1, 1 << PLL_PD_SHIFT); | |
248 | ||
249 | /* waiting for pll lock */ | |
250 | while (!(readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT))) | |
251 | udelay(1); | |
252 | ||
253 | rk_clrsetreg(mode, pll_mode_mask[pll_id], | |
254 | PLLMUX_FROM_PLL << pll_mode_shift[pll_id]); | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
259 | static uint32_t rkclk_pll_get_rate(struct px30_pll *pll, unsigned int *mode, | |
260 | enum px30_pll_id pll_id) | |
261 | { | |
262 | u32 refdiv, fbdiv, postdiv1, postdiv2; | |
263 | u32 con, shift, mask; | |
264 | ||
265 | con = readl(mode); | |
266 | shift = pll_mode_shift[pll_id]; | |
267 | mask = pll_mode_mask[pll_id]; | |
268 | ||
269 | switch ((con & mask) >> shift) { | |
270 | case PLLMUX_FROM_XIN24M: | |
271 | return OSC_HZ; | |
272 | case PLLMUX_FROM_PLL: | |
273 | /* normal mode */ | |
274 | con = readl(&pll->con0); | |
275 | postdiv1 = (con & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT; | |
276 | fbdiv = (con & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT; | |
277 | con = readl(&pll->con1); | |
278 | postdiv2 = (con & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT; | |
279 | refdiv = (con & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT; | |
280 | return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; | |
281 | case PLLMUX_FROM_RTC32K: | |
282 | default: | |
283 | return 32768; | |
284 | } | |
285 | } | |
286 | ||
287 | static ulong px30_i2c_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
288 | { | |
289 | struct px30_cru *cru = priv->cru; | |
290 | u32 div, con; | |
291 | ||
292 | switch (clk_id) { | |
293 | case SCLK_I2C0: | |
294 | con = readl(&cru->clksel_con[49]); | |
295 | div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
296 | break; | |
297 | case SCLK_I2C1: | |
298 | con = readl(&cru->clksel_con[49]); | |
299 | div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
300 | break; | |
301 | case SCLK_I2C2: | |
302 | con = readl(&cru->clksel_con[50]); | |
303 | div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
304 | break; | |
305 | case SCLK_I2C3: | |
306 | con = readl(&cru->clksel_con[50]); | |
307 | div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
308 | break; | |
309 | default: | |
310 | printf("do not support this i2c bus\n"); | |
311 | return -EINVAL; | |
312 | } | |
313 | ||
314 | return DIV_TO_RATE(priv->gpll_hz, div); | |
315 | } | |
316 | ||
317 | static ulong px30_i2c_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) | |
318 | { | |
319 | struct px30_cru *cru = priv->cru; | |
320 | int src_clk_div; | |
321 | ||
322 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
323 | assert(src_clk_div - 1 <= 127); | |
324 | ||
325 | switch (clk_id) { | |
326 | case SCLK_I2C0: | |
327 | rk_clrsetreg(&cru->clksel_con[49], | |
328 | CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT | | |
329 | CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT, | |
330 | (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT | | |
331 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT); | |
332 | break; | |
333 | case SCLK_I2C1: | |
334 | rk_clrsetreg(&cru->clksel_con[49], | |
335 | CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT | | |
336 | CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT, | |
337 | (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT | | |
338 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT); | |
339 | break; | |
340 | case SCLK_I2C2: | |
341 | rk_clrsetreg(&cru->clksel_con[50], | |
342 | CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT | | |
343 | CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT, | |
344 | (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT | | |
345 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT); | |
346 | break; | |
347 | case SCLK_I2C3: | |
348 | rk_clrsetreg(&cru->clksel_con[50], | |
349 | CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT | | |
350 | CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT, | |
351 | (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT | | |
352 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT); | |
353 | break; | |
354 | default: | |
355 | printf("do not support this i2c bus\n"); | |
356 | return -EINVAL; | |
357 | } | |
358 | ||
359 | return px30_i2c_get_clk(priv, clk_id); | |
360 | } | |
361 | ||
362 | /* | |
363 | * calculate best rational approximation for a given fraction | |
364 | * taking into account restricted register size, e.g. to find | |
365 | * appropriate values for a pll with 5 bit denominator and | |
366 | * 8 bit numerator register fields, trying to set up with a | |
367 | * frequency ratio of 3.1415, one would say: | |
368 | * | |
369 | * rational_best_approximation(31415, 10000, | |
370 | * (1 << 8) - 1, (1 << 5) - 1, &n, &d); | |
371 | * | |
372 | * you may look at given_numerator as a fixed point number, | |
373 | * with the fractional part size described in given_denominator. | |
374 | * | |
375 | * for theoretical background, see: | |
376 | * http://en.wikipedia.org/wiki/Continued_fraction | |
377 | */ | |
378 | static void rational_best_approximation(unsigned long given_numerator, | |
379 | unsigned long given_denominator, | |
380 | unsigned long max_numerator, | |
381 | unsigned long max_denominator, | |
382 | unsigned long *best_numerator, | |
383 | unsigned long *best_denominator) | |
384 | { | |
385 | unsigned long n, d, n0, d0, n1, d1; | |
386 | ||
387 | n = given_numerator; | |
388 | d = given_denominator; | |
389 | n0 = 0; | |
390 | d1 = 0; | |
391 | n1 = 1; | |
392 | d0 = 1; | |
393 | for (;;) { | |
394 | unsigned long t, a; | |
395 | ||
396 | if (n1 > max_numerator || d1 > max_denominator) { | |
397 | n1 = n0; | |
398 | d1 = d0; | |
399 | break; | |
400 | } | |
401 | if (d == 0) | |
402 | break; | |
403 | t = d; | |
404 | a = n / d; | |
405 | d = n % d; | |
406 | n = t; | |
407 | t = n0 + a * n1; | |
408 | n0 = n1; | |
409 | n1 = t; | |
410 | t = d0 + a * d1; | |
411 | d0 = d1; | |
412 | d1 = t; | |
413 | } | |
414 | *best_numerator = n1; | |
415 | *best_denominator = d1; | |
416 | } | |
417 | ||
418 | static ulong px30_i2s_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
419 | { | |
420 | u32 con, fracdiv, gate; | |
421 | u32 clk_src = priv->gpll_hz / 2; | |
422 | unsigned long m, n; | |
423 | struct px30_cru *cru = priv->cru; | |
424 | ||
425 | switch (clk_id) { | |
426 | case SCLK_I2S1: | |
427 | con = readl(&cru->clksel_con[30]); | |
428 | fracdiv = readl(&cru->clksel_con[31]); | |
429 | gate = readl(&cru->clkgate_con[10]); | |
430 | m = fracdiv & CLK_I2S1_FRAC_NUMERATOR_MASK; | |
431 | m >>= CLK_I2S1_FRAC_NUMERATOR_SHIFT; | |
432 | n = fracdiv & CLK_I2S1_FRAC_DENOMINATOR_MASK; | |
433 | n >>= CLK_I2S1_FRAC_DENOMINATOR_SHIFT; | |
434 | debug("con30: 0x%x, gate: 0x%x, frac: 0x%x\n", | |
435 | con, gate, fracdiv); | |
436 | break; | |
437 | default: | |
438 | printf("do not support this i2s bus\n"); | |
439 | return -EINVAL; | |
440 | } | |
441 | ||
442 | return clk_src * n / m; | |
443 | } | |
444 | ||
445 | static ulong px30_i2s_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) | |
446 | { | |
447 | u32 clk_src; | |
448 | unsigned long m, n, val; | |
449 | struct px30_cru *cru = priv->cru; | |
450 | ||
451 | clk_src = priv->gpll_hz / 2; | |
452 | rational_best_approximation(hz, clk_src, | |
453 | GENMASK(16 - 1, 0), | |
454 | GENMASK(16 - 1, 0), | |
455 | &m, &n); | |
456 | switch (clk_id) { | |
457 | case SCLK_I2S1: | |
458 | rk_clrsetreg(&cru->clksel_con[30], | |
459 | CLK_I2S1_PLL_SEL_MASK, CLK_I2S1_PLL_SEL_GPLL); | |
460 | rk_clrsetreg(&cru->clksel_con[30], | |
461 | CLK_I2S1_DIV_CON_MASK, 0x1); | |
462 | rk_clrsetreg(&cru->clksel_con[30], | |
463 | CLK_I2S1_SEL_MASK, CLK_I2S1_SEL_FRAC); | |
464 | val = m << CLK_I2S1_FRAC_NUMERATOR_SHIFT | n; | |
465 | writel(val, &cru->clksel_con[31]); | |
466 | rk_clrsetreg(&cru->clkgate_con[10], | |
467 | CLK_I2S1_OUT_MCLK_PAD_MASK, | |
468 | CLK_I2S1_OUT_MCLK_PAD_ENABLE); | |
469 | break; | |
470 | default: | |
471 | printf("do not support this i2s bus\n"); | |
472 | return -EINVAL; | |
473 | } | |
474 | ||
475 | return px30_i2s_get_clk(priv, clk_id); | |
476 | } | |
477 | ||
478 | static ulong px30_nandc_get_clk(struct px30_clk_priv *priv) | |
479 | { | |
480 | struct px30_cru *cru = priv->cru; | |
481 | u32 div, con; | |
482 | ||
483 | con = readl(&cru->clksel_con[15]); | |
484 | div = (con & NANDC_DIV_MASK) >> NANDC_DIV_SHIFT; | |
485 | ||
486 | return DIV_TO_RATE(priv->gpll_hz, div); | |
487 | } | |
488 | ||
489 | static ulong px30_nandc_set_clk(struct px30_clk_priv *priv, | |
490 | ulong set_rate) | |
491 | { | |
492 | struct px30_cru *cru = priv->cru; | |
493 | int src_clk_div; | |
494 | ||
495 | /* Select nandc source from GPLL by default */ | |
496 | /* nandc clock defaulg div 2 internal, need provide double in cru */ | |
497 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, set_rate); | |
498 | assert(src_clk_div - 1 <= 31); | |
499 | ||
500 | rk_clrsetreg(&cru->clksel_con[15], | |
501 | NANDC_CLK_SEL_MASK | NANDC_PLL_MASK | | |
502 | NANDC_DIV_MASK, | |
503 | NANDC_CLK_SEL_NANDC << NANDC_CLK_SEL_SHIFT | | |
504 | NANDC_SEL_GPLL << NANDC_PLL_SHIFT | | |
505 | (src_clk_div - 1) << NANDC_DIV_SHIFT); | |
506 | ||
507 | return px30_nandc_get_clk(priv); | |
508 | } | |
509 | ||
510 | static ulong px30_mmc_get_clk(struct px30_clk_priv *priv, uint clk_id) | |
511 | { | |
512 | struct px30_cru *cru = priv->cru; | |
513 | u32 div, con, con_id; | |
514 | ||
515 | switch (clk_id) { | |
516 | case HCLK_SDMMC: | |
517 | case SCLK_SDMMC: | |
518 | con_id = 16; | |
519 | break; | |
520 | case HCLK_EMMC: | |
521 | case SCLK_EMMC: | |
522 | case SCLK_EMMC_SAMPLE: | |
523 | con_id = 20; | |
524 | break; | |
525 | default: | |
526 | return -EINVAL; | |
527 | } | |
528 | ||
529 | con = readl(&cru->clksel_con[con_id]); | |
530 | div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT; | |
531 | ||
532 | if ((con & EMMC_PLL_MASK) >> EMMC_PLL_SHIFT | |
533 | == EMMC_SEL_24M) | |
534 | return DIV_TO_RATE(OSC_HZ, div) / 2; | |
535 | else | |
536 | return DIV_TO_RATE(priv->gpll_hz, div) / 2; | |
537 | } | |
538 | ||
539 | static ulong px30_mmc_set_clk(struct px30_clk_priv *priv, | |
540 | ulong clk_id, ulong set_rate) | |
541 | { | |
542 | struct px30_cru *cru = priv->cru; | |
543 | int src_clk_div; | |
544 | u32 con_id; | |
545 | ||
546 | switch (clk_id) { | |
547 | case HCLK_SDMMC: | |
548 | case SCLK_SDMMC: | |
549 | con_id = 16; | |
550 | break; | |
551 | case HCLK_EMMC: | |
552 | case SCLK_EMMC: | |
553 | con_id = 20; | |
554 | break; | |
555 | default: | |
556 | return -EINVAL; | |
557 | } | |
558 | ||
559 | /* Select clk_sdmmc/emmc source from GPLL by default */ | |
560 | /* mmc clock defaulg div 2 internal, need provide double in cru */ | |
561 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz / 2, set_rate); | |
562 | ||
563 | if (src_clk_div > 127) { | |
564 | /* use 24MHz source for 400KHz clock */ | |
565 | src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate); | |
566 | rk_clrsetreg(&cru->clksel_con[con_id], | |
567 | EMMC_PLL_MASK | EMMC_DIV_MASK, | |
568 | EMMC_SEL_24M << EMMC_PLL_SHIFT | | |
569 | (src_clk_div - 1) << EMMC_DIV_SHIFT); | |
570 | } else { | |
571 | rk_clrsetreg(&cru->clksel_con[con_id], | |
572 | EMMC_PLL_MASK | EMMC_DIV_MASK, | |
573 | EMMC_SEL_GPLL << EMMC_PLL_SHIFT | | |
574 | (src_clk_div - 1) << EMMC_DIV_SHIFT); | |
575 | } | |
576 | rk_clrsetreg(&cru->clksel_con[con_id + 1], EMMC_CLK_SEL_MASK, | |
577 | EMMC_CLK_SEL_EMMC); | |
578 | ||
579 | return px30_mmc_get_clk(priv, clk_id); | |
580 | } | |
581 | ||
582 | static ulong px30_pwm_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
583 | { | |
584 | struct px30_cru *cru = priv->cru; | |
585 | u32 div, con; | |
586 | ||
587 | switch (clk_id) { | |
588 | case SCLK_PWM0: | |
589 | con = readl(&cru->clksel_con[52]); | |
590 | div = con >> CLK_PWM0_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK; | |
591 | break; | |
592 | case SCLK_PWM1: | |
593 | con = readl(&cru->clksel_con[52]); | |
594 | div = con >> CLK_PWM1_DIV_CON_SHIFT & CLK_PWM_DIV_CON_MASK; | |
595 | break; | |
596 | default: | |
597 | printf("do not support this pwm bus\n"); | |
598 | return -EINVAL; | |
599 | } | |
600 | ||
601 | return DIV_TO_RATE(priv->gpll_hz, div); | |
602 | } | |
603 | ||
604 | static ulong px30_pwm_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) | |
605 | { | |
606 | struct px30_cru *cru = priv->cru; | |
607 | int src_clk_div; | |
608 | ||
609 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
610 | assert(src_clk_div - 1 <= 127); | |
611 | ||
612 | switch (clk_id) { | |
613 | case SCLK_PWM0: | |
614 | rk_clrsetreg(&cru->clksel_con[52], | |
615 | CLK_PWM_DIV_CON_MASK << CLK_PWM0_DIV_CON_SHIFT | | |
616 | CLK_PWM_PLL_SEL_MASK << CLK_PWM0_PLL_SEL_SHIFT, | |
617 | (src_clk_div - 1) << CLK_PWM0_DIV_CON_SHIFT | | |
618 | CLK_PWM_PLL_SEL_GPLL << CLK_PWM0_PLL_SEL_SHIFT); | |
619 | break; | |
620 | case SCLK_PWM1: | |
621 | rk_clrsetreg(&cru->clksel_con[52], | |
622 | CLK_PWM_DIV_CON_MASK << CLK_PWM1_DIV_CON_SHIFT | | |
623 | CLK_PWM_PLL_SEL_MASK << CLK_PWM1_PLL_SEL_SHIFT, | |
624 | (src_clk_div - 1) << CLK_PWM1_DIV_CON_SHIFT | | |
625 | CLK_PWM_PLL_SEL_GPLL << CLK_PWM1_PLL_SEL_SHIFT); | |
626 | break; | |
627 | default: | |
628 | printf("do not support this pwm bus\n"); | |
629 | return -EINVAL; | |
630 | } | |
631 | ||
632 | return px30_pwm_get_clk(priv, clk_id); | |
633 | } | |
634 | ||
635 | static ulong px30_saradc_get_clk(struct px30_clk_priv *priv) | |
636 | { | |
637 | struct px30_cru *cru = priv->cru; | |
638 | u32 div, con; | |
639 | ||
640 | con = readl(&cru->clksel_con[55]); | |
641 | div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; | |
642 | ||
643 | return DIV_TO_RATE(OSC_HZ, div); | |
644 | } | |
645 | ||
646 | static ulong px30_saradc_set_clk(struct px30_clk_priv *priv, uint hz) | |
647 | { | |
648 | struct px30_cru *cru = priv->cru; | |
649 | int src_clk_div; | |
650 | ||
651 | src_clk_div = DIV_ROUND_UP(OSC_HZ, hz); | |
652 | assert(src_clk_div - 1 <= 2047); | |
653 | ||
654 | rk_clrsetreg(&cru->clksel_con[55], | |
655 | CLK_SARADC_DIV_CON_MASK, | |
656 | (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT); | |
657 | ||
658 | return px30_saradc_get_clk(priv); | |
659 | } | |
660 | ||
661 | static ulong px30_tsadc_get_clk(struct px30_clk_priv *priv) | |
662 | { | |
663 | struct px30_cru *cru = priv->cru; | |
664 | u32 div, con; | |
665 | ||
666 | con = readl(&cru->clksel_con[54]); | |
667 | div = con >> CLK_SARADC_DIV_CON_SHIFT & CLK_SARADC_DIV_CON_MASK; | |
668 | ||
669 | return DIV_TO_RATE(OSC_HZ, div); | |
670 | } | |
671 | ||
672 | static ulong px30_tsadc_set_clk(struct px30_clk_priv *priv, uint hz) | |
673 | { | |
674 | struct px30_cru *cru = priv->cru; | |
675 | int src_clk_div; | |
676 | ||
677 | src_clk_div = DIV_ROUND_UP(OSC_HZ, hz); | |
678 | assert(src_clk_div - 1 <= 2047); | |
679 | ||
680 | rk_clrsetreg(&cru->clksel_con[54], | |
681 | CLK_SARADC_DIV_CON_MASK, | |
682 | (src_clk_div - 1) << CLK_SARADC_DIV_CON_SHIFT); | |
683 | ||
684 | return px30_tsadc_get_clk(priv); | |
685 | } | |
686 | ||
687 | static ulong px30_spi_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
688 | { | |
689 | struct px30_cru *cru = priv->cru; | |
690 | u32 div, con; | |
691 | ||
692 | switch (clk_id) { | |
693 | case SCLK_SPI0: | |
694 | con = readl(&cru->clksel_con[53]); | |
695 | div = con >> CLK_SPI0_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK; | |
696 | break; | |
697 | case SCLK_SPI1: | |
698 | con = readl(&cru->clksel_con[53]); | |
699 | div = con >> CLK_SPI1_DIV_CON_SHIFT & CLK_SPI_DIV_CON_MASK; | |
700 | break; | |
701 | default: | |
702 | printf("do not support this pwm bus\n"); | |
703 | return -EINVAL; | |
704 | } | |
705 | ||
706 | return DIV_TO_RATE(priv->gpll_hz, div); | |
707 | } | |
708 | ||
709 | static ulong px30_spi_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) | |
710 | { | |
711 | struct px30_cru *cru = priv->cru; | |
712 | int src_clk_div; | |
713 | ||
714 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
715 | assert(src_clk_div - 1 <= 127); | |
716 | ||
717 | switch (clk_id) { | |
718 | case SCLK_SPI0: | |
719 | rk_clrsetreg(&cru->clksel_con[53], | |
720 | CLK_SPI_DIV_CON_MASK << CLK_SPI0_DIV_CON_SHIFT | | |
721 | CLK_SPI_PLL_SEL_MASK << CLK_SPI0_PLL_SEL_SHIFT, | |
722 | (src_clk_div - 1) << CLK_SPI0_DIV_CON_SHIFT | | |
723 | CLK_SPI_PLL_SEL_GPLL << CLK_SPI0_PLL_SEL_SHIFT); | |
724 | break; | |
725 | case SCLK_SPI1: | |
726 | rk_clrsetreg(&cru->clksel_con[53], | |
727 | CLK_SPI_DIV_CON_MASK << CLK_SPI1_DIV_CON_SHIFT | | |
728 | CLK_SPI_PLL_SEL_MASK << CLK_SPI1_PLL_SEL_SHIFT, | |
729 | (src_clk_div - 1) << CLK_SPI1_DIV_CON_SHIFT | | |
730 | CLK_SPI_PLL_SEL_GPLL << CLK_SPI1_PLL_SEL_SHIFT); | |
731 | break; | |
732 | default: | |
733 | printf("do not support this pwm bus\n"); | |
734 | return -EINVAL; | |
735 | } | |
736 | ||
737 | return px30_spi_get_clk(priv, clk_id); | |
738 | } | |
739 | ||
740 | static ulong px30_vop_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
741 | { | |
742 | struct px30_cru *cru = priv->cru; | |
743 | u32 div, con, parent; | |
744 | ||
745 | switch (clk_id) { | |
746 | case ACLK_VOPB: | |
747 | case ACLK_VOPL: | |
748 | con = readl(&cru->clksel_con[3]); | |
749 | div = con & ACLK_VO_DIV_MASK; | |
750 | parent = priv->gpll_hz; | |
751 | break; | |
752 | case DCLK_VOPB: | |
753 | con = readl(&cru->clksel_con[5]); | |
754 | div = con & DCLK_VOPB_DIV_MASK; | |
755 | parent = rkclk_pll_get_rate(&cru->pll[CPLL], &cru->mode, CPLL); | |
756 | break; | |
757 | case DCLK_VOPL: | |
758 | con = readl(&cru->clksel_con[8]); | |
759 | div = con & DCLK_VOPL_DIV_MASK; | |
760 | parent = rkclk_pll_get_rate(&cru->pll[NPLL], &cru->mode, NPLL); | |
761 | break; | |
762 | default: | |
763 | return -ENOENT; | |
764 | } | |
765 | ||
766 | return DIV_TO_RATE(parent, div); | |
767 | } | |
768 | ||
769 | static ulong px30_vop_set_clk(struct px30_clk_priv *priv, ulong clk_id, uint hz) | |
770 | { | |
771 | struct px30_cru *cru = priv->cru; | |
772 | ulong npll_hz; | |
773 | int src_clk_div; | |
774 | ||
775 | switch (clk_id) { | |
776 | case ACLK_VOPB: | |
777 | case ACLK_VOPL: | |
778 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
779 | assert(src_clk_div - 1 <= 31); | |
780 | rk_clrsetreg(&cru->clksel_con[3], | |
781 | ACLK_VO_PLL_MASK | ACLK_VO_DIV_MASK, | |
782 | ACLK_VO_SEL_GPLL << ACLK_VO_PLL_SHIFT | | |
783 | (src_clk_div - 1) << ACLK_VO_DIV_SHIFT); | |
784 | break; | |
785 | case DCLK_VOPB: | |
786 | if (hz < PX30_VOP_PLL_LIMIT) { | |
787 | src_clk_div = DIV_ROUND_UP(PX30_VOP_PLL_LIMIT, hz); | |
788 | if (src_clk_div % 2) | |
789 | src_clk_div = src_clk_div - 1; | |
790 | } else { | |
791 | src_clk_div = 1; | |
792 | } | |
793 | assert(src_clk_div - 1 <= 255); | |
794 | rkclk_set_pll(&cru->pll[CPLL], &cru->mode, | |
795 | CPLL, hz * src_clk_div); | |
796 | rk_clrsetreg(&cru->clksel_con[5], | |
797 | DCLK_VOPB_SEL_MASK | DCLK_VOPB_PLL_SEL_MASK | | |
798 | DCLK_VOPB_DIV_MASK, | |
799 | DCLK_VOPB_SEL_DIVOUT << DCLK_VOPB_SEL_SHIFT | | |
800 | DCLK_VOPB_PLL_SEL_CPLL << DCLK_VOPB_PLL_SEL_SHIFT | | |
801 | (src_clk_div - 1) << DCLK_VOPB_DIV_SHIFT); | |
802 | break; | |
803 | case DCLK_VOPL: | |
804 | npll_hz = px30_clk_get_pll_rate(priv, NPLL); | |
805 | if (npll_hz >= PX30_VOP_PLL_LIMIT && npll_hz >= hz && | |
806 | npll_hz % hz == 0) { | |
807 | src_clk_div = npll_hz / hz; | |
808 | assert(src_clk_div - 1 <= 255); | |
809 | } else { | |
810 | if (hz < PX30_VOP_PLL_LIMIT) { | |
811 | src_clk_div = DIV_ROUND_UP(PX30_VOP_PLL_LIMIT, | |
812 | hz); | |
813 | if (src_clk_div % 2) | |
814 | src_clk_div = src_clk_div - 1; | |
815 | } else { | |
816 | src_clk_div = 1; | |
817 | } | |
818 | assert(src_clk_div - 1 <= 255); | |
819 | rkclk_set_pll(&cru->pll[NPLL], &cru->mode, NPLL, | |
820 | hz * src_clk_div); | |
821 | } | |
822 | rk_clrsetreg(&cru->clksel_con[8], | |
823 | DCLK_VOPL_SEL_MASK | DCLK_VOPL_PLL_SEL_MASK | | |
824 | DCLK_VOPL_DIV_MASK, | |
825 | DCLK_VOPL_SEL_DIVOUT << DCLK_VOPL_SEL_SHIFT | | |
826 | DCLK_VOPL_PLL_SEL_NPLL << DCLK_VOPL_PLL_SEL_SHIFT | | |
827 | (src_clk_div - 1) << DCLK_VOPL_DIV_SHIFT); | |
828 | break; | |
829 | default: | |
830 | printf("do not support this vop freq\n"); | |
831 | return -EINVAL; | |
832 | } | |
833 | ||
834 | return px30_vop_get_clk(priv, clk_id); | |
835 | } | |
836 | ||
837 | static ulong px30_bus_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
838 | { | |
839 | struct px30_cru *cru = priv->cru; | |
840 | u32 div, con, parent; | |
841 | ||
842 | switch (clk_id) { | |
843 | case ACLK_BUS_PRE: | |
844 | con = readl(&cru->clksel_con[23]); | |
845 | div = (con & BUS_ACLK_DIV_MASK) >> BUS_ACLK_DIV_SHIFT; | |
846 | parent = priv->gpll_hz; | |
847 | break; | |
848 | case HCLK_BUS_PRE: | |
849 | con = readl(&cru->clksel_con[24]); | |
850 | div = (con & BUS_HCLK_DIV_MASK) >> BUS_HCLK_DIV_SHIFT; | |
851 | parent = priv->gpll_hz; | |
852 | break; | |
853 | case PCLK_BUS_PRE: | |
854 | case PCLK_WDT_NS: | |
855 | parent = px30_bus_get_clk(priv, ACLK_BUS_PRE); | |
856 | con = readl(&cru->clksel_con[24]); | |
857 | div = (con & BUS_PCLK_DIV_MASK) >> BUS_PCLK_DIV_SHIFT; | |
858 | break; | |
859 | default: | |
860 | return -ENOENT; | |
861 | } | |
862 | ||
863 | return DIV_TO_RATE(parent, div); | |
864 | } | |
865 | ||
866 | static ulong px30_bus_set_clk(struct px30_clk_priv *priv, ulong clk_id, | |
867 | ulong hz) | |
868 | { | |
869 | struct px30_cru *cru = priv->cru; | |
870 | int src_clk_div; | |
871 | ||
872 | /* | |
873 | * select gpll as pd_bus bus clock source and | |
874 | * set up dependent divisors for PCLK/HCLK and ACLK clocks. | |
875 | */ | |
876 | switch (clk_id) { | |
877 | case ACLK_BUS_PRE: | |
878 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
879 | assert(src_clk_div - 1 <= 31); | |
880 | rk_clrsetreg(&cru->clksel_con[23], | |
881 | BUS_PLL_SEL_MASK | BUS_ACLK_DIV_MASK, | |
882 | BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT | | |
883 | (src_clk_div - 1) << BUS_ACLK_DIV_SHIFT); | |
884 | break; | |
885 | case HCLK_BUS_PRE: | |
886 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
887 | assert(src_clk_div - 1 <= 31); | |
888 | rk_clrsetreg(&cru->clksel_con[24], | |
889 | BUS_PLL_SEL_MASK | BUS_HCLK_DIV_MASK, | |
890 | BUS_PLL_SEL_GPLL << BUS_PLL_SEL_SHIFT | | |
891 | (src_clk_div - 1) << BUS_HCLK_DIV_SHIFT); | |
892 | break; | |
893 | case PCLK_BUS_PRE: | |
894 | src_clk_div = | |
895 | DIV_ROUND_UP(px30_bus_get_clk(priv, ACLK_BUS_PRE), hz); | |
896 | assert(src_clk_div - 1 <= 3); | |
897 | rk_clrsetreg(&cru->clksel_con[24], | |
898 | BUS_PCLK_DIV_MASK, | |
899 | (src_clk_div - 1) << BUS_PCLK_DIV_SHIFT); | |
900 | break; | |
901 | default: | |
902 | printf("do not support this bus freq\n"); | |
903 | return -EINVAL; | |
904 | } | |
905 | ||
906 | return px30_bus_get_clk(priv, clk_id); | |
907 | } | |
908 | ||
909 | static ulong px30_peri_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
910 | { | |
911 | struct px30_cru *cru = priv->cru; | |
912 | u32 div, con, parent; | |
913 | ||
914 | switch (clk_id) { | |
915 | case ACLK_PERI_PRE: | |
916 | con = readl(&cru->clksel_con[14]); | |
917 | div = (con & PERI_ACLK_DIV_MASK) >> PERI_ACLK_DIV_SHIFT; | |
918 | parent = priv->gpll_hz; | |
919 | break; | |
920 | case HCLK_PERI_PRE: | |
921 | con = readl(&cru->clksel_con[14]); | |
922 | div = (con & PERI_HCLK_DIV_MASK) >> PERI_HCLK_DIV_SHIFT; | |
923 | parent = priv->gpll_hz; | |
924 | break; | |
925 | default: | |
926 | return -ENOENT; | |
927 | } | |
928 | ||
929 | return DIV_TO_RATE(parent, div); | |
930 | } | |
931 | ||
932 | static ulong px30_peri_set_clk(struct px30_clk_priv *priv, ulong clk_id, | |
933 | ulong hz) | |
934 | { | |
935 | struct px30_cru *cru = priv->cru; | |
936 | int src_clk_div; | |
937 | ||
938 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
939 | assert(src_clk_div - 1 <= 31); | |
940 | ||
941 | /* | |
942 | * select gpll as pd_peri bus clock source and | |
943 | * set up dependent divisors for HCLK and ACLK clocks. | |
944 | */ | |
945 | switch (clk_id) { | |
946 | case ACLK_PERI_PRE: | |
947 | rk_clrsetreg(&cru->clksel_con[14], | |
948 | PERI_PLL_SEL_MASK | PERI_ACLK_DIV_MASK, | |
949 | PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT | | |
950 | (src_clk_div - 1) << PERI_ACLK_DIV_SHIFT); | |
951 | break; | |
952 | case HCLK_PERI_PRE: | |
953 | rk_clrsetreg(&cru->clksel_con[14], | |
954 | PERI_PLL_SEL_MASK | PERI_HCLK_DIV_MASK, | |
955 | PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT | | |
956 | (src_clk_div - 1) << PERI_HCLK_DIV_SHIFT); | |
957 | break; | |
958 | default: | |
959 | printf("do not support this peri freq\n"); | |
960 | return -EINVAL; | |
961 | } | |
962 | ||
963 | return px30_peri_get_clk(priv, clk_id); | |
964 | } | |
965 | ||
966 | #ifndef CONFIG_SPL_BUILD | |
967 | static ulong px30_crypto_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
968 | { | |
969 | struct px30_cru *cru = priv->cru; | |
970 | u32 div, con, parent; | |
971 | ||
972 | switch (clk_id) { | |
973 | case SCLK_CRYPTO: | |
974 | con = readl(&cru->clksel_con[25]); | |
975 | div = (con & CRYPTO_DIV_MASK) >> CRYPTO_DIV_SHIFT; | |
976 | parent = priv->gpll_hz; | |
977 | break; | |
978 | case SCLK_CRYPTO_APK: | |
979 | con = readl(&cru->clksel_con[25]); | |
980 | div = (con & CRYPTO_APK_DIV_MASK) >> CRYPTO_APK_DIV_SHIFT; | |
981 | parent = priv->gpll_hz; | |
982 | break; | |
983 | default: | |
984 | return -ENOENT; | |
985 | } | |
986 | ||
987 | return DIV_TO_RATE(parent, div); | |
988 | } | |
989 | ||
990 | static ulong px30_crypto_set_clk(struct px30_clk_priv *priv, ulong clk_id, | |
991 | ulong hz) | |
992 | { | |
993 | struct px30_cru *cru = priv->cru; | |
994 | int src_clk_div; | |
995 | ||
996 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
997 | assert(src_clk_div - 1 <= 31); | |
998 | ||
999 | /* | |
1000 | * select gpll as crypto clock source and | |
1001 | * set up dependent divisors for crypto clocks. | |
1002 | */ | |
1003 | switch (clk_id) { | |
1004 | case SCLK_CRYPTO: | |
1005 | rk_clrsetreg(&cru->clksel_con[25], | |
1006 | CRYPTO_PLL_SEL_MASK | CRYPTO_DIV_MASK, | |
1007 | CRYPTO_PLL_SEL_GPLL << CRYPTO_PLL_SEL_SHIFT | | |
1008 | (src_clk_div - 1) << CRYPTO_DIV_SHIFT); | |
1009 | break; | |
1010 | case SCLK_CRYPTO_APK: | |
1011 | rk_clrsetreg(&cru->clksel_con[25], | |
1012 | CRYPTO_APK_PLL_SEL_MASK | CRYPTO_APK_DIV_MASK, | |
1013 | CRYPTO_PLL_SEL_GPLL << CRYPTO_APK_SEL_SHIFT | | |
1014 | (src_clk_div - 1) << CRYPTO_APK_DIV_SHIFT); | |
1015 | break; | |
1016 | default: | |
1017 | printf("do not support this peri freq\n"); | |
1018 | return -EINVAL; | |
1019 | } | |
1020 | ||
1021 | return px30_crypto_get_clk(priv, clk_id); | |
1022 | } | |
1023 | ||
1024 | static ulong px30_i2s1_mclk_get_clk(struct px30_clk_priv *priv, ulong clk_id) | |
1025 | { | |
1026 | struct px30_cru *cru = priv->cru; | |
1027 | u32 con; | |
1028 | ||
1029 | con = readl(&cru->clksel_con[30]); | |
1030 | ||
1031 | if (!(con & CLK_I2S1_OUT_SEL_MASK)) | |
1032 | return -ENOENT; | |
1033 | ||
1034 | return 12000000; | |
1035 | } | |
1036 | ||
1037 | static ulong px30_i2s1_mclk_set_clk(struct px30_clk_priv *priv, ulong clk_id, | |
1038 | ulong hz) | |
1039 | { | |
1040 | struct px30_cru *cru = priv->cru; | |
1041 | ||
1042 | if (hz != 12000000) { | |
1043 | printf("do not support this i2s1_mclk freq\n"); | |
1044 | return -EINVAL; | |
1045 | } | |
1046 | ||
1047 | rk_clrsetreg(&cru->clksel_con[30], CLK_I2S1_OUT_SEL_MASK, | |
1048 | CLK_I2S1_OUT_SEL_OSC); | |
1049 | rk_clrsetreg(&cru->clkgate_con[10], CLK_I2S1_OUT_MCLK_PAD_MASK, | |
1050 | CLK_I2S1_OUT_MCLK_PAD_ENABLE); | |
1051 | ||
1052 | return px30_i2s1_mclk_get_clk(priv, clk_id); | |
1053 | } | |
1054 | ||
1055 | static ulong px30_mac_set_clk(struct px30_clk_priv *priv, uint hz) | |
1056 | { | |
1057 | struct px30_cru *cru = priv->cru; | |
1058 | u32 con = readl(&cru->clksel_con[22]); | |
1059 | ulong pll_rate; | |
1060 | u8 div; | |
1061 | ||
1062 | if ((con >> GMAC_PLL_SEL_SHIFT) & GMAC_PLL_SEL_CPLL) | |
1063 | pll_rate = px30_clk_get_pll_rate(priv, CPLL); | |
1064 | else if ((con >> GMAC_PLL_SEL_SHIFT) & GMAC_PLL_SEL_NPLL) | |
1065 | pll_rate = px30_clk_get_pll_rate(priv, NPLL); | |
1066 | else | |
1067 | pll_rate = priv->gpll_hz; | |
1068 | ||
1069 | /*default set 50MHZ for gmac*/ | |
1070 | if (!hz) | |
1071 | hz = 50000000; | |
1072 | ||
1073 | div = DIV_ROUND_UP(pll_rate, hz) - 1; | |
1074 | assert(div < 32); | |
1075 | rk_clrsetreg(&cru->clksel_con[22], CLK_GMAC_DIV_MASK, | |
1076 | div << CLK_GMAC_DIV_SHIFT); | |
1077 | ||
1078 | return DIV_TO_RATE(pll_rate, div); | |
1079 | } | |
1080 | ||
1081 | static int px30_mac_set_speed_clk(struct px30_clk_priv *priv, uint hz) | |
1082 | { | |
1083 | struct px30_cru *cru = priv->cru; | |
1084 | ||
1085 | if (hz != 2500000 && hz != 25000000) { | |
1086 | debug("Unsupported mac speed:%d\n", hz); | |
1087 | return -EINVAL; | |
1088 | } | |
1089 | ||
1090 | rk_clrsetreg(&cru->clksel_con[23], RMII_CLK_SEL_MASK, | |
1091 | ((hz == 2500000) ? 0 : 1) << RMII_CLK_SEL_SHIFT); | |
1092 | ||
1093 | return 0; | |
1094 | } | |
1095 | ||
1096 | #endif | |
1097 | ||
1098 | static ulong px30_clk_get_pll_rate(struct px30_clk_priv *priv, | |
1099 | enum px30_pll_id pll_id) | |
1100 | { | |
1101 | struct px30_cru *cru = priv->cru; | |
1102 | ||
1103 | return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id); | |
1104 | } | |
1105 | ||
1106 | static ulong px30_clk_set_pll_rate(struct px30_clk_priv *priv, | |
1107 | enum px30_pll_id pll_id, ulong hz) | |
1108 | { | |
1109 | struct px30_cru *cru = priv->cru; | |
1110 | ||
1111 | if (rkclk_set_pll(&cru->pll[pll_id], &cru->mode, pll_id, hz)) | |
1112 | return -EINVAL; | |
1113 | return rkclk_pll_get_rate(&cru->pll[pll_id], &cru->mode, pll_id); | |
1114 | } | |
1115 | ||
1116 | static ulong px30_armclk_set_clk(struct px30_clk_priv *priv, ulong hz) | |
1117 | { | |
1118 | struct px30_cru *cru = priv->cru; | |
1119 | const struct cpu_rate_table *rate; | |
1120 | ulong old_rate; | |
1121 | ||
1122 | rate = get_cpu_settings(hz); | |
1123 | if (!rate) { | |
1124 | printf("%s unsupport rate\n", __func__); | |
1125 | return -EINVAL; | |
1126 | } | |
1127 | ||
1128 | /* | |
1129 | * select apll as cpu/core clock pll source and | |
1130 | * set up dependent divisors for PERI and ACLK clocks. | |
1131 | * core hz : apll = 1:1 | |
1132 | */ | |
1133 | old_rate = px30_clk_get_pll_rate(priv, APLL); | |
1134 | if (old_rate > hz) { | |
1135 | if (rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, hz)) | |
1136 | return -EINVAL; | |
1137 | rk_clrsetreg(&cru->clksel_con[0], | |
1138 | CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | | |
1139 | CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, | |
1140 | rate->aclk_div << CORE_ACLK_DIV_SHIFT | | |
1141 | rate->pclk_div << CORE_DBG_DIV_SHIFT | | |
1142 | CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | | |
1143 | 0 << CORE_DIV_CON_SHIFT); | |
1144 | } else if (old_rate < hz) { | |
1145 | rk_clrsetreg(&cru->clksel_con[0], | |
1146 | CORE_CLK_PLL_SEL_MASK | CORE_DIV_CON_MASK | | |
1147 | CORE_ACLK_DIV_MASK | CORE_DBG_DIV_MASK, | |
1148 | rate->aclk_div << CORE_ACLK_DIV_SHIFT | | |
1149 | rate->pclk_div << CORE_DBG_DIV_SHIFT | | |
1150 | CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT | | |
1151 | 0 << CORE_DIV_CON_SHIFT); | |
1152 | if (rkclk_set_pll(&cru->pll[APLL], &cru->mode, APLL, hz)) | |
1153 | return -EINVAL; | |
1154 | } | |
1155 | ||
1156 | return px30_clk_get_pll_rate(priv, APLL); | |
1157 | } | |
1158 | ||
1159 | static ulong px30_clk_get_rate(struct clk *clk) | |
1160 | { | |
1161 | struct px30_clk_priv *priv = dev_get_priv(clk->dev); | |
1162 | ulong rate = 0; | |
1163 | ||
1164 | if (!priv->gpll_hz && clk->id > ARMCLK) { | |
1165 | printf("%s gpll=%lu\n", __func__, priv->gpll_hz); | |
1166 | return -ENOENT; | |
1167 | } | |
1168 | ||
1169 | debug("%s %ld\n", __func__, clk->id); | |
1170 | switch (clk->id) { | |
1171 | case PLL_APLL: | |
1172 | rate = px30_clk_get_pll_rate(priv, APLL); | |
1173 | break; | |
1174 | case PLL_DPLL: | |
1175 | rate = px30_clk_get_pll_rate(priv, DPLL); | |
1176 | break; | |
1177 | case PLL_CPLL: | |
1178 | rate = px30_clk_get_pll_rate(priv, CPLL); | |
1179 | break; | |
1180 | case PLL_NPLL: | |
1181 | rate = px30_clk_get_pll_rate(priv, NPLL); | |
1182 | break; | |
1183 | case ARMCLK: | |
1184 | rate = px30_clk_get_pll_rate(priv, APLL); | |
1185 | break; | |
1186 | case HCLK_SDMMC: | |
1187 | case HCLK_EMMC: | |
1188 | case SCLK_SDMMC: | |
1189 | case SCLK_EMMC: | |
1190 | case SCLK_EMMC_SAMPLE: | |
1191 | rate = px30_mmc_get_clk(priv, clk->id); | |
1192 | break; | |
1193 | case SCLK_I2C0: | |
1194 | case SCLK_I2C1: | |
1195 | case SCLK_I2C2: | |
1196 | case SCLK_I2C3: | |
1197 | rate = px30_i2c_get_clk(priv, clk->id); | |
1198 | break; | |
1199 | case SCLK_I2S1: | |
1200 | rate = px30_i2s_get_clk(priv, clk->id); | |
1201 | break; | |
1202 | case SCLK_NANDC: | |
1203 | rate = px30_nandc_get_clk(priv); | |
1204 | break; | |
1205 | case SCLK_PWM0: | |
1206 | case SCLK_PWM1: | |
1207 | rate = px30_pwm_get_clk(priv, clk->id); | |
1208 | break; | |
1209 | case SCLK_SARADC: | |
1210 | rate = px30_saradc_get_clk(priv); | |
1211 | break; | |
1212 | case SCLK_TSADC: | |
1213 | rate = px30_tsadc_get_clk(priv); | |
1214 | break; | |
1215 | case SCLK_SPI0: | |
1216 | case SCLK_SPI1: | |
1217 | rate = px30_spi_get_clk(priv, clk->id); | |
1218 | break; | |
1219 | case ACLK_VOPB: | |
1220 | case ACLK_VOPL: | |
1221 | case DCLK_VOPB: | |
1222 | case DCLK_VOPL: | |
1223 | rate = px30_vop_get_clk(priv, clk->id); | |
1224 | break; | |
1225 | case ACLK_BUS_PRE: | |
1226 | case HCLK_BUS_PRE: | |
1227 | case PCLK_BUS_PRE: | |
1228 | case PCLK_WDT_NS: | |
1229 | rate = px30_bus_get_clk(priv, clk->id); | |
1230 | break; | |
1231 | case ACLK_PERI_PRE: | |
1232 | case HCLK_PERI_PRE: | |
1233 | rate = px30_peri_get_clk(priv, clk->id); | |
1234 | break; | |
1235 | #ifndef CONFIG_SPL_BUILD | |
1236 | case SCLK_CRYPTO: | |
1237 | case SCLK_CRYPTO_APK: | |
1238 | rate = px30_crypto_get_clk(priv, clk->id); | |
1239 | break; | |
1240 | #endif | |
1241 | default: | |
1242 | return -ENOENT; | |
1243 | } | |
1244 | ||
1245 | return rate; | |
1246 | } | |
1247 | ||
1248 | static ulong px30_clk_set_rate(struct clk *clk, ulong rate) | |
1249 | { | |
1250 | struct px30_clk_priv *priv = dev_get_priv(clk->dev); | |
1251 | ulong ret = 0; | |
1252 | ||
1253 | if (!priv->gpll_hz && clk->id > ARMCLK) { | |
1254 | printf("%s gpll=%lu\n", __func__, priv->gpll_hz); | |
1255 | return -ENOENT; | |
1256 | } | |
1257 | ||
1258 | debug("%s %ld %ld\n", __func__, clk->id, rate); | |
1259 | switch (clk->id) { | |
1260 | case PLL_NPLL: | |
1261 | ret = px30_clk_set_pll_rate(priv, NPLL, rate); | |
1262 | break; | |
1263 | case ARMCLK: | |
1264 | ret = px30_armclk_set_clk(priv, rate); | |
1265 | break; | |
1266 | case HCLK_SDMMC: | |
1267 | case HCLK_EMMC: | |
1268 | case SCLK_SDMMC: | |
1269 | case SCLK_EMMC: | |
1270 | ret = px30_mmc_set_clk(priv, clk->id, rate); | |
1271 | break; | |
1272 | case SCLK_I2C0: | |
1273 | case SCLK_I2C1: | |
1274 | case SCLK_I2C2: | |
1275 | case SCLK_I2C3: | |
1276 | ret = px30_i2c_set_clk(priv, clk->id, rate); | |
1277 | break; | |
1278 | case SCLK_I2S1: | |
1279 | ret = px30_i2s_set_clk(priv, clk->id, rate); | |
1280 | break; | |
1281 | case SCLK_NANDC: | |
1282 | ret = px30_nandc_set_clk(priv, rate); | |
1283 | break; | |
1284 | case SCLK_PWM0: | |
1285 | case SCLK_PWM1: | |
1286 | ret = px30_pwm_set_clk(priv, clk->id, rate); | |
1287 | break; | |
1288 | case SCLK_SARADC: | |
1289 | ret = px30_saradc_set_clk(priv, rate); | |
1290 | break; | |
1291 | case SCLK_TSADC: | |
1292 | ret = px30_tsadc_set_clk(priv, rate); | |
1293 | break; | |
1294 | case SCLK_SPI0: | |
1295 | case SCLK_SPI1: | |
1296 | ret = px30_spi_set_clk(priv, clk->id, rate); | |
1297 | break; | |
1298 | case ACLK_VOPB: | |
1299 | case ACLK_VOPL: | |
1300 | case DCLK_VOPB: | |
1301 | case DCLK_VOPL: | |
1302 | ret = px30_vop_set_clk(priv, clk->id, rate); | |
1303 | break; | |
1304 | case ACLK_BUS_PRE: | |
1305 | case HCLK_BUS_PRE: | |
1306 | case PCLK_BUS_PRE: | |
1307 | ret = px30_bus_set_clk(priv, clk->id, rate); | |
1308 | break; | |
1309 | case ACLK_PERI_PRE: | |
1310 | case HCLK_PERI_PRE: | |
1311 | ret = px30_peri_set_clk(priv, clk->id, rate); | |
1312 | break; | |
1313 | #ifndef CONFIG_SPL_BUILD | |
1314 | case SCLK_CRYPTO: | |
1315 | case SCLK_CRYPTO_APK: | |
1316 | ret = px30_crypto_set_clk(priv, clk->id, rate); | |
1317 | break; | |
1318 | case SCLK_I2S1_OUT: | |
1319 | ret = px30_i2s1_mclk_set_clk(priv, clk->id, rate); | |
1320 | break; | |
1321 | case SCLK_GMAC: | |
1322 | case SCLK_GMAC_SRC: | |
1323 | ret = px30_mac_set_clk(priv, rate); | |
1324 | break; | |
1325 | case SCLK_GMAC_RMII: | |
1326 | ret = px30_mac_set_speed_clk(priv, rate); | |
1327 | break; | |
1328 | #endif | |
1329 | default: | |
1330 | return -ENOENT; | |
1331 | } | |
1332 | ||
1333 | return ret; | |
1334 | } | |
1335 | ||
1336 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) | |
1337 | static int px30_gmac_set_parent(struct clk *clk, struct clk *parent) | |
1338 | { | |
1339 | struct px30_clk_priv *priv = dev_get_priv(clk->dev); | |
1340 | struct px30_cru *cru = priv->cru; | |
1341 | ||
1342 | if (parent->id == SCLK_GMAC_SRC) { | |
1343 | debug("%s: switching GAMC to SCLK_GMAC_SRC\n", __func__); | |
1344 | rk_clrsetreg(&cru->clksel_con[23], RMII_EXTCLK_SEL_MASK, | |
1345 | RMII_EXTCLK_SEL_INT << RMII_EXTCLK_SEL_SHIFT); | |
1346 | } else { | |
1347 | debug("%s: switching GMAC to external clock\n", __func__); | |
1348 | rk_clrsetreg(&cru->clksel_con[23], RMII_EXTCLK_SEL_MASK, | |
1349 | RMII_EXTCLK_SEL_EXT << RMII_EXTCLK_SEL_SHIFT); | |
1350 | } | |
1351 | return 0; | |
1352 | } | |
1353 | ||
1354 | static int px30_clk_set_parent(struct clk *clk, struct clk *parent) | |
1355 | { | |
1356 | switch (clk->id) { | |
1357 | case SCLK_GMAC: | |
1358 | return px30_gmac_set_parent(clk, parent); | |
1359 | default: | |
1360 | return -ENOENT; | |
1361 | } | |
1362 | } | |
1363 | #endif | |
1364 | ||
1365 | static int px30_clk_enable(struct clk *clk) | |
1366 | { | |
1367 | switch (clk->id) { | |
1368 | case HCLK_HOST: | |
1369 | case SCLK_GMAC: | |
1370 | case SCLK_GMAC_RX_TX: | |
1371 | case SCLK_MAC_REF: | |
1372 | case SCLK_MAC_REFOUT: | |
1373 | case ACLK_GMAC: | |
1374 | case PCLK_GMAC: | |
1375 | case SCLK_GMAC_RMII: | |
1376 | /* Required to successfully probe the Designware GMAC driver */ | |
1377 | return 0; | |
1378 | } | |
1379 | ||
1380 | debug("%s: unsupported clk %ld\n", __func__, clk->id); | |
1381 | return -ENOENT; | |
1382 | } | |
1383 | ||
1384 | static struct clk_ops px30_clk_ops = { | |
1385 | .get_rate = px30_clk_get_rate, | |
1386 | .set_rate = px30_clk_set_rate, | |
1387 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) | |
1388 | .set_parent = px30_clk_set_parent, | |
1389 | #endif | |
1390 | .enable = px30_clk_enable, | |
1391 | }; | |
1392 | ||
1393 | static void px30_clk_init(struct px30_clk_priv *priv) | |
1394 | { | |
1395 | ulong npll_hz; | |
1396 | int ret; | |
1397 | ||
1398 | npll_hz = px30_clk_get_pll_rate(priv, NPLL); | |
1399 | if (npll_hz != NPLL_HZ) { | |
1400 | ret = px30_clk_set_pll_rate(priv, NPLL, NPLL_HZ); | |
1401 | if (ret < 0) | |
1402 | printf("%s failed to set npll rate\n", __func__); | |
1403 | } | |
1404 | ||
1405 | px30_bus_set_clk(priv, ACLK_BUS_PRE, ACLK_BUS_HZ); | |
1406 | px30_bus_set_clk(priv, HCLK_BUS_PRE, HCLK_BUS_HZ); | |
1407 | px30_bus_set_clk(priv, PCLK_BUS_PRE, PCLK_BUS_HZ); | |
1408 | px30_peri_set_clk(priv, ACLK_PERI_PRE, ACLK_PERI_HZ); | |
1409 | px30_peri_set_clk(priv, HCLK_PERI_PRE, HCLK_PERI_HZ); | |
1410 | } | |
1411 | ||
1412 | static int px30_clk_probe(struct udevice *dev) | |
1413 | { | |
1414 | struct px30_clk_priv *priv = dev_get_priv(dev); | |
1415 | struct clk clk_gpll; | |
1416 | int ret; | |
1417 | ||
1418 | if (px30_clk_get_pll_rate(priv, APLL) != APLL_HZ) | |
1419 | px30_armclk_set_clk(priv, APLL_HZ); | |
1420 | ||
1421 | /* get the GPLL rate from the pmucru */ | |
1422 | ret = clk_get_by_name(dev, "gpll", &clk_gpll); | |
1423 | if (ret) { | |
1424 | printf("%s: failed to get gpll clk from pmucru\n", __func__); | |
1425 | return ret; | |
1426 | } | |
1427 | ||
1428 | priv->gpll_hz = clk_get_rate(&clk_gpll); | |
1429 | ||
1430 | px30_clk_init(priv); | |
1431 | ||
1432 | return 0; | |
1433 | } | |
1434 | ||
1435 | static int px30_clk_ofdata_to_platdata(struct udevice *dev) | |
1436 | { | |
1437 | struct px30_clk_priv *priv = dev_get_priv(dev); | |
1438 | ||
1439 | priv->cru = dev_read_addr_ptr(dev); | |
1440 | ||
1441 | return 0; | |
1442 | } | |
1443 | ||
1444 | static int px30_clk_bind(struct udevice *dev) | |
1445 | { | |
1446 | int ret; | |
1447 | struct udevice *sys_child; | |
1448 | struct sysreset_reg *priv; | |
1449 | ||
1450 | /* The reset driver does not have a device node, so bind it here */ | |
1451 | ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", | |
1452 | &sys_child); | |
1453 | if (ret) { | |
1454 | debug("Warning: No sysreset driver: ret=%d\n", ret); | |
1455 | } else { | |
1456 | priv = malloc(sizeof(struct sysreset_reg)); | |
1457 | priv->glb_srst_fst_value = offsetof(struct px30_cru, | |
1458 | glb_srst_fst); | |
1459 | priv->glb_srst_snd_value = offsetof(struct px30_cru, | |
1460 | glb_srst_snd); | |
1461 | sys_child->priv = priv; | |
1462 | } | |
1463 | ||
1464 | #if CONFIG_IS_ENABLED(RESET_ROCKCHIP) | |
1465 | ret = offsetof(struct px30_cru, softrst_con[0]); | |
1466 | ret = rockchip_reset_bind(dev, ret, 12); | |
1467 | if (ret) | |
1468 | debug("Warning: software reset driver bind faile\n"); | |
1469 | #endif | |
1470 | ||
1471 | return 0; | |
1472 | } | |
1473 | ||
1474 | static const struct udevice_id px30_clk_ids[] = { | |
1475 | { .compatible = "rockchip,px30-cru" }, | |
1476 | { } | |
1477 | }; | |
1478 | ||
1479 | U_BOOT_DRIVER(rockchip_px30_cru) = { | |
1480 | .name = "rockchip_px30_cru", | |
1481 | .id = UCLASS_CLK, | |
1482 | .of_match = px30_clk_ids, | |
41575d8e | 1483 | .priv_auto = sizeof(struct px30_clk_priv), |
d49a5267 KY |
1484 | .ofdata_to_platdata = px30_clk_ofdata_to_platdata, |
1485 | .ops = &px30_clk_ops, | |
1486 | .bind = px30_clk_bind, | |
1487 | .probe = px30_clk_probe, | |
1488 | }; | |
1489 | ||
1490 | static ulong px30_pclk_pmu_get_pmuclk(struct px30_pmuclk_priv *priv) | |
1491 | { | |
1492 | struct px30_pmucru *pmucru = priv->pmucru; | |
1493 | u32 div, con; | |
1494 | ||
1495 | con = readl(&pmucru->pmu_clksel_con[0]); | |
1496 | div = (con & CLK_PMU_PCLK_DIV_MASK) >> CLK_PMU_PCLK_DIV_SHIFT; | |
1497 | ||
1498 | return DIV_TO_RATE(priv->gpll_hz, div); | |
1499 | } | |
1500 | ||
1501 | static ulong px30_pclk_pmu_set_pmuclk(struct px30_pmuclk_priv *priv, ulong hz) | |
1502 | { | |
1503 | struct px30_pmucru *pmucru = priv->pmucru; | |
1504 | int src_clk_div; | |
1505 | ||
1506 | src_clk_div = DIV_ROUND_UP(priv->gpll_hz, hz); | |
1507 | assert(src_clk_div - 1 <= 31); | |
1508 | ||
1509 | rk_clrsetreg(&pmucru->pmu_clksel_con[0], | |
1510 | CLK_PMU_PCLK_DIV_MASK, | |
1511 | (src_clk_div - 1) << CLK_PMU_PCLK_DIV_SHIFT); | |
1512 | ||
1513 | return px30_pclk_pmu_get_pmuclk(priv); | |
1514 | } | |
1515 | ||
1516 | static ulong px30_pmuclk_get_gpll_rate(struct px30_pmuclk_priv *priv) | |
1517 | { | |
1518 | struct px30_pmucru *pmucru = priv->pmucru; | |
1519 | ||
1520 | return rkclk_pll_get_rate(&pmucru->pll, &pmucru->pmu_mode, GPLL); | |
1521 | } | |
1522 | ||
1523 | static ulong px30_pmuclk_set_gpll_rate(struct px30_pmuclk_priv *priv, ulong hz) | |
1524 | { | |
1525 | struct px30_pmucru *pmucru = priv->pmucru; | |
1526 | ulong pclk_pmu_rate; | |
1527 | u32 div; | |
1528 | ||
1529 | if (priv->gpll_hz == hz) | |
1530 | return priv->gpll_hz; | |
1531 | ||
1532 | div = DIV_ROUND_UP(hz, priv->gpll_hz); | |
1533 | ||
1534 | /* save clock rate */ | |
1535 | pclk_pmu_rate = px30_pclk_pmu_get_pmuclk(priv); | |
1536 | ||
1537 | /* avoid rate too large, reduce rate first */ | |
1538 | px30_pclk_pmu_set_pmuclk(priv, pclk_pmu_rate / div); | |
1539 | ||
1540 | /* change gpll rate */ | |
1541 | rkclk_set_pll(&pmucru->pll, &pmucru->pmu_mode, GPLL, hz); | |
1542 | priv->gpll_hz = px30_pmuclk_get_gpll_rate(priv); | |
1543 | ||
1544 | /* restore clock rate */ | |
1545 | px30_pclk_pmu_set_pmuclk(priv, pclk_pmu_rate); | |
1546 | ||
1547 | return priv->gpll_hz; | |
1548 | } | |
1549 | ||
1550 | static ulong px30_pmuclk_get_rate(struct clk *clk) | |
1551 | { | |
1552 | struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev); | |
1553 | ulong rate = 0; | |
1554 | ||
1555 | debug("%s %ld\n", __func__, clk->id); | |
1556 | switch (clk->id) { | |
1557 | case PLL_GPLL: | |
1558 | rate = px30_pmuclk_get_gpll_rate(priv); | |
1559 | break; | |
1560 | case PCLK_PMU_PRE: | |
1561 | rate = px30_pclk_pmu_get_pmuclk(priv); | |
1562 | break; | |
1563 | default: | |
1564 | return -ENOENT; | |
1565 | } | |
1566 | ||
1567 | return rate; | |
1568 | } | |
1569 | ||
1570 | static ulong px30_pmuclk_set_rate(struct clk *clk, ulong rate) | |
1571 | { | |
1572 | struct px30_pmuclk_priv *priv = dev_get_priv(clk->dev); | |
1573 | ulong ret = 0; | |
1574 | ||
1575 | debug("%s %ld %ld\n", __func__, clk->id, rate); | |
1576 | switch (clk->id) { | |
1577 | case PLL_GPLL: | |
1578 | ret = px30_pmuclk_set_gpll_rate(priv, rate); | |
1579 | break; | |
1580 | case PCLK_PMU_PRE: | |
1581 | ret = px30_pclk_pmu_set_pmuclk(priv, rate); | |
1582 | break; | |
1583 | default: | |
1584 | return -ENOENT; | |
1585 | } | |
1586 | ||
1587 | return ret; | |
1588 | } | |
1589 | ||
1590 | static struct clk_ops px30_pmuclk_ops = { | |
1591 | .get_rate = px30_pmuclk_get_rate, | |
1592 | .set_rate = px30_pmuclk_set_rate, | |
1593 | }; | |
1594 | ||
1595 | static void px30_pmuclk_init(struct px30_pmuclk_priv *priv) | |
1596 | { | |
1597 | priv->gpll_hz = px30_pmuclk_get_gpll_rate(priv); | |
1598 | px30_pmuclk_set_gpll_rate(priv, GPLL_HZ); | |
1599 | ||
1600 | px30_pclk_pmu_set_pmuclk(priv, PCLK_PMU_HZ); | |
1601 | } | |
1602 | ||
1603 | static int px30_pmuclk_probe(struct udevice *dev) | |
1604 | { | |
1605 | struct px30_pmuclk_priv *priv = dev_get_priv(dev); | |
1606 | ||
1607 | px30_pmuclk_init(priv); | |
1608 | ||
1609 | return 0; | |
1610 | } | |
1611 | ||
1612 | static int px30_pmuclk_ofdata_to_platdata(struct udevice *dev) | |
1613 | { | |
1614 | struct px30_pmuclk_priv *priv = dev_get_priv(dev); | |
1615 | ||
1616 | priv->pmucru = dev_read_addr_ptr(dev); | |
1617 | ||
1618 | return 0; | |
1619 | } | |
1620 | ||
1621 | static const struct udevice_id px30_pmuclk_ids[] = { | |
1622 | { .compatible = "rockchip,px30-pmucru" }, | |
1623 | { } | |
1624 | }; | |
1625 | ||
1626 | U_BOOT_DRIVER(rockchip_px30_pmucru) = { | |
1627 | .name = "rockchip_px30_pmucru", | |
1628 | .id = UCLASS_CLK, | |
1629 | .of_match = px30_pmuclk_ids, | |
41575d8e | 1630 | .priv_auto = sizeof(struct px30_pmuclk_priv), |
d49a5267 KY |
1631 | .ofdata_to_platdata = px30_pmuclk_ofdata_to_platdata, |
1632 | .ops = &px30_pmuclk_ops, | |
1633 | .probe = px30_pmuclk_probe, | |
1634 | }; |