]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
bae2f282 AY |
2 | /* |
3 | * (C) Copyright 2016 Rockchip Electronics Co., Ltd | |
4 | * Author: Andy Yan <[email protected]> | |
bae2f282 AY |
5 | */ |
6 | ||
7 | #include <common.h> | |
2e4ce50d | 8 | #include <bitfield.h> |
bae2f282 AY |
9 | #include <clk-uclass.h> |
10 | #include <dm.h> | |
11 | #include <errno.h> | |
f7ae49fc | 12 | #include <log.h> |
336d4615 | 13 | #include <malloc.h> |
bae2f282 | 14 | #include <syscon.h> |
401d1c4f | 15 | #include <asm/global_data.h> |
bae2f282 | 16 | #include <asm/io.h> |
15f09a1a KY |
17 | #include <asm/arch-rockchip/clock.h> |
18 | #include <asm/arch-rockchip/cru_rv1108.h> | |
19 | #include <asm/arch-rockchip/hardware.h> | |
0fd3d911 | 20 | #include <dm/device-internal.h> |
bae2f282 AY |
21 | #include <dm/lists.h> |
22 | #include <dt-bindings/clock/rv1108-cru.h> | |
c05ed00a | 23 | #include <linux/delay.h> |
1af3c7f4 | 24 | #include <linux/stringify.h> |
bae2f282 | 25 | |
5d2cb15c OS |
26 | DECLARE_GLOBAL_DATA_PTR; |
27 | ||
bae2f282 AY |
28 | enum { |
29 | VCO_MAX_HZ = 2400U * 1000000, | |
30 | VCO_MIN_HZ = 600 * 1000000, | |
31 | OUTPUT_MAX_HZ = 2400U * 1000000, | |
32 | OUTPUT_MIN_HZ = 24 * 1000000, | |
33 | }; | |
34 | ||
bae2f282 AY |
35 | #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) |
36 | ||
37 | #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\ | |
38 | .refdiv = _refdiv,\ | |
39 | .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\ | |
40 | .postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\ | |
41 | _Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\ | |
42 | OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\ | |
43 | #hz "Hz cannot be hit with PLL "\ | |
44 | "divisors on line " __stringify(__LINE__)); | |
45 | ||
5d2cb15c OS |
46 | static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1); |
47 | static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1); | |
48 | ||
2e4ce50d | 49 | /* use integer mode */ |
bae2f282 AY |
50 | static inline int rv1108_pll_id(enum rk_clk_id clk_id) |
51 | { | |
52 | int id = 0; | |
53 | ||
54 | switch (clk_id) { | |
55 | case CLK_ARM: | |
56 | case CLK_DDR: | |
57 | id = clk_id - 1; | |
58 | break; | |
59 | case CLK_GENERAL: | |
60 | id = 2; | |
61 | break; | |
62 | default: | |
63 | printf("invalid pll id:%d\n", clk_id); | |
64 | id = -1; | |
65 | break; | |
66 | } | |
67 | ||
68 | return id; | |
69 | } | |
70 | ||
5d2cb15c OS |
71 | static int rkclk_set_pll(struct rv1108_cru *cru, enum rk_clk_id clk_id, |
72 | const struct pll_div *div) | |
73 | { | |
74 | int pll_id = rv1108_pll_id(clk_id); | |
75 | struct rv1108_pll *pll = &cru->pll[pll_id]; | |
76 | ||
77 | /* All PLLs have same VCO and output frequency range restrictions. */ | |
78 | uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000; | |
79 | uint output_hz = vco_hz / div->postdiv1 / div->postdiv2; | |
80 | ||
81 | debug("PLL at %p: fb=%d, ref=%d, pst1=%d, pst2=%d, vco=%u Hz, output=%u Hz\n", | |
82 | pll, div->fbdiv, div->refdiv, div->postdiv1, | |
83 | div->postdiv2, vco_hz, output_hz); | |
84 | assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && | |
85 | output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ); | |
86 | ||
87 | /* | |
88 | * When power on or changing PLL setting, | |
89 | * we must force PLL into slow mode to ensure output stable clock. | |
90 | */ | |
91 | rk_clrsetreg(&pll->con3, WORK_MODE_MASK, | |
92 | WORK_MODE_SLOW << WORK_MODE_SHIFT); | |
93 | ||
94 | /* use integer mode */ | |
95 | rk_setreg(&pll->con3, 1 << DSMPD_SHIFT); | |
96 | /* Power down */ | |
97 | rk_setreg(&pll->con3, 1 << GLOBAL_POWER_DOWN_SHIFT); | |
98 | ||
99 | rk_clrsetreg(&pll->con0, FBDIV_MASK, div->fbdiv << FBDIV_SHIFT); | |
100 | rk_clrsetreg(&pll->con1, POSTDIV1_MASK | POSTDIV2_MASK | REFDIV_MASK, | |
101 | (div->postdiv1 << POSTDIV1_SHIFT | | |
102 | div->postdiv2 << POSTDIV2_SHIFT | | |
103 | div->refdiv << REFDIV_SHIFT)); | |
104 | rk_clrsetreg(&pll->con2, FRACDIV_MASK, | |
105 | (div->refdiv << REFDIV_SHIFT)); | |
106 | ||
107 | /* Power Up */ | |
108 | rk_clrreg(&pll->con3, 1 << GLOBAL_POWER_DOWN_SHIFT); | |
109 | ||
110 | /* waiting for pll lock */ | |
111 | while (readl(&pll->con2) & (1 << LOCK_STA_SHIFT)) | |
112 | udelay(1); | |
113 | ||
114 | /* | |
115 | * set PLL into normal mode. | |
116 | */ | |
117 | rk_clrsetreg(&pll->con3, WORK_MODE_MASK, | |
118 | WORK_MODE_NORMAL << WORK_MODE_SHIFT); | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
bae2f282 AY |
123 | static uint32_t rkclk_pll_get_rate(struct rv1108_cru *cru, |
124 | enum rk_clk_id clk_id) | |
125 | { | |
126 | uint32_t refdiv, fbdiv, postdiv1, postdiv2; | |
127 | uint32_t con0, con1, con3; | |
128 | int pll_id = rv1108_pll_id(clk_id); | |
129 | struct rv1108_pll *pll = &cru->pll[pll_id]; | |
130 | uint32_t freq; | |
131 | ||
132 | con3 = readl(&pll->con3); | |
133 | ||
134 | if (con3 & WORK_MODE_MASK) { | |
135 | con0 = readl(&pll->con0); | |
136 | con1 = readl(&pll->con1); | |
137 | fbdiv = (con0 >> FBDIV_SHIFT) & FBDIV_MASK; | |
138 | postdiv1 = (con1 & POSTDIV1_MASK) >> POSTDIV1_SHIFT; | |
139 | postdiv2 = (con1 & POSTDIV2_MASK) >> POSTDIV2_SHIFT; | |
5d2cb15c | 140 | refdiv = (con1 >> REFDIV_SHIFT) & REFDIV_MASK; |
bae2f282 AY |
141 | freq = (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000; |
142 | } else { | |
143 | freq = OSC_HZ; | |
144 | } | |
145 | ||
146 | return freq; | |
147 | } | |
148 | ||
149 | static int rv1108_mac_set_clk(struct rv1108_cru *cru, ulong rate) | |
150 | { | |
151 | uint32_t con = readl(&cru->clksel_con[24]); | |
152 | ulong pll_rate; | |
153 | uint8_t div; | |
154 | ||
155 | if ((con >> MAC_PLL_SEL_SHIFT) & MAC_PLL_SEL_GPLL) | |
156 | pll_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
157 | else | |
158 | pll_rate = rkclk_pll_get_rate(cru, CLK_ARM); | |
159 | ||
160 | /*default set 50MHZ for gmac*/ | |
161 | if (!rate) | |
162 | rate = 50000000; | |
163 | ||
164 | div = DIV_ROUND_UP(pll_rate, rate) - 1; | |
165 | if (div <= 0x1f) | |
166 | rk_clrsetreg(&cru->clksel_con[24], MAC_CLK_DIV_MASK, | |
167 | div << MAC_CLK_DIV_SHIFT); | |
168 | else | |
169 | debug("Unsupported div for gmac:%d\n", div); | |
170 | ||
171 | return DIV_TO_RATE(pll_rate, div); | |
172 | } | |
173 | ||
174 | static int rv1108_sfc_set_clk(struct rv1108_cru *cru, uint rate) | |
175 | { | |
176 | u32 con = readl(&cru->clksel_con[27]); | |
177 | u32 pll_rate; | |
178 | u32 div; | |
179 | ||
180 | if ((con >> SFC_PLL_SEL_SHIFT) && SFC_PLL_SEL_GPLL) | |
181 | pll_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
182 | else | |
183 | pll_rate = rkclk_pll_get_rate(cru, CLK_DDR); | |
184 | ||
185 | div = DIV_ROUND_UP(pll_rate, rate) - 1; | |
186 | if (div <= 0x3f) | |
187 | rk_clrsetreg(&cru->clksel_con[27], SFC_CLK_DIV_MASK, | |
188 | div << SFC_CLK_DIV_SHIFT); | |
189 | else | |
190 | debug("Unsupported sfc clk rate:%d\n", rate); | |
191 | ||
192 | return DIV_TO_RATE(pll_rate, div); | |
193 | } | |
194 | ||
2e4ce50d DW |
195 | static ulong rv1108_saradc_get_clk(struct rv1108_cru *cru) |
196 | { | |
197 | u32 div, val; | |
198 | ||
199 | val = readl(&cru->clksel_con[22]); | |
200 | div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT, | |
201 | CLK_SARADC_DIV_CON_WIDTH); | |
202 | ||
203 | return DIV_TO_RATE(OSC_HZ, div); | |
204 | } | |
205 | ||
206 | static ulong rv1108_saradc_set_clk(struct rv1108_cru *cru, uint hz) | |
207 | { | |
208 | int src_clk_div; | |
209 | ||
210 | src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1; | |
211 | assert(src_clk_div < 128); | |
212 | ||
213 | rk_clrsetreg(&cru->clksel_con[22], | |
214 | CLK_SARADC_DIV_CON_MASK, | |
215 | src_clk_div << CLK_SARADC_DIV_CON_SHIFT); | |
216 | ||
217 | return rv1108_saradc_get_clk(cru); | |
218 | } | |
219 | ||
5d2cb15c OS |
220 | static ulong rv1108_aclk_vio1_get_clk(struct rv1108_cru *cru) |
221 | { | |
222 | u32 div, val; | |
223 | ||
224 | val = readl(&cru->clksel_con[28]); | |
225 | div = bitfield_extract(val, ACLK_VIO1_CLK_DIV_SHIFT, | |
226 | CLK_VIO_DIV_CON_WIDTH); | |
227 | ||
228 | return DIV_TO_RATE(GPLL_HZ, div); | |
229 | } | |
230 | ||
231 | static ulong rv1108_aclk_vio1_set_clk(struct rv1108_cru *cru, uint hz) | |
232 | { | |
233 | int src_clk_div; | |
234 | ||
235 | src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz) - 1; | |
236 | assert(src_clk_div < 32); | |
237 | ||
238 | rk_clrsetreg(&cru->clksel_con[28], | |
239 | ACLK_VIO1_CLK_DIV_MASK | ACLK_VIO1_PLL_SEL_MASK, | |
240 | (src_clk_div << ACLK_VIO1_CLK_DIV_SHIFT) | | |
241 | (VIO_PLL_SEL_GPLL << ACLK_VIO1_PLL_SEL_SHIFT)); | |
242 | ||
243 | return rv1108_aclk_vio1_get_clk(cru); | |
244 | } | |
245 | ||
246 | static ulong rv1108_aclk_vio0_get_clk(struct rv1108_cru *cru) | |
247 | { | |
248 | u32 div, val; | |
249 | ||
250 | val = readl(&cru->clksel_con[28]); | |
251 | div = bitfield_extract(val, ACLK_VIO0_CLK_DIV_SHIFT, | |
252 | CLK_VIO_DIV_CON_WIDTH); | |
253 | ||
254 | return DIV_TO_RATE(GPLL_HZ, div); | |
255 | } | |
256 | ||
257 | static ulong rv1108_aclk_vio0_set_clk(struct rv1108_cru *cru, uint hz) | |
258 | { | |
259 | int src_clk_div; | |
260 | ||
261 | src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz) - 1; | |
262 | assert(src_clk_div < 32); | |
263 | ||
264 | rk_clrsetreg(&cru->clksel_con[28], | |
265 | ACLK_VIO0_CLK_DIV_MASK | ACLK_VIO0_PLL_SEL_MASK, | |
266 | (src_clk_div << ACLK_VIO0_CLK_DIV_SHIFT) | | |
267 | (VIO_PLL_SEL_GPLL << ACLK_VIO0_PLL_SEL_SHIFT)); | |
268 | ||
269 | /*HCLK_VIO default div = 4*/ | |
270 | rk_clrsetreg(&cru->clksel_con[29], | |
271 | HCLK_VIO_CLK_DIV_MASK, | |
272 | 3 << HCLK_VIO_CLK_DIV_SHIFT); | |
273 | /*PCLK_VIO default div = 4*/ | |
274 | rk_clrsetreg(&cru->clksel_con[29], | |
275 | PCLK_VIO_CLK_DIV_MASK, | |
276 | 3 << PCLK_VIO_CLK_DIV_SHIFT); | |
277 | ||
278 | return rv1108_aclk_vio0_get_clk(cru); | |
279 | } | |
280 | ||
281 | static ulong rv1108_dclk_vop_get_clk(struct rv1108_cru *cru) | |
282 | { | |
283 | u32 div, val; | |
284 | ||
285 | val = readl(&cru->clksel_con[32]); | |
286 | div = bitfield_extract(val, DCLK_VOP_CLK_DIV_SHIFT, | |
287 | DCLK_VOP_DIV_CON_WIDTH); | |
288 | ||
289 | return DIV_TO_RATE(GPLL_HZ, div); | |
290 | } | |
291 | ||
292 | static ulong rv1108_dclk_vop_set_clk(struct rv1108_cru *cru, uint hz) | |
293 | { | |
294 | int src_clk_div; | |
295 | ||
296 | src_clk_div = DIV_ROUND_UP(GPLL_HZ, hz) - 1; | |
297 | assert(src_clk_div < 64); | |
298 | ||
299 | rk_clrsetreg(&cru->clksel_con[32], | |
300 | DCLK_VOP_CLK_DIV_MASK | DCLK_VOP_PLL_SEL_MASK | | |
301 | DCLK_VOP_SEL_SHIFT, | |
302 | (src_clk_div << DCLK_VOP_CLK_DIV_SHIFT) | | |
303 | (DCLK_VOP_PLL_SEL_GPLL << DCLK_VOP_PLL_SEL_SHIFT) | | |
304 | (DCLK_VOP_SEL_PLL << DCLK_VOP_SEL_SHIFT)); | |
305 | ||
306 | return rv1108_dclk_vop_get_clk(cru); | |
307 | } | |
308 | ||
309 | static ulong rv1108_aclk_bus_get_clk(struct rv1108_cru *cru) | |
310 | { | |
311 | u32 div, val; | |
312 | ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
313 | ||
314 | val = readl(&cru->clksel_con[2]); | |
315 | div = bitfield_extract(val, ACLK_BUS_DIV_CON_SHIFT, | |
316 | ACLK_BUS_DIV_CON_WIDTH); | |
317 | ||
318 | return DIV_TO_RATE(parent_rate, div); | |
319 | } | |
320 | ||
321 | static ulong rv1108_aclk_bus_set_clk(struct rv1108_cru *cru, uint hz) | |
322 | { | |
323 | int src_clk_div; | |
324 | ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
325 | ||
326 | src_clk_div = DIV_ROUND_UP(parent_rate, hz) - 1; | |
327 | assert(src_clk_div < 32); | |
328 | ||
329 | rk_clrsetreg(&cru->clksel_con[2], | |
330 | ACLK_BUS_DIV_CON_MASK | ACLK_BUS_PLL_SEL_MASK, | |
331 | (src_clk_div << ACLK_BUS_DIV_CON_SHIFT) | | |
332 | (ACLK_BUS_PLL_SEL_GPLL << ACLK_BUS_PLL_SEL_SHIFT)); | |
333 | ||
334 | return rv1108_aclk_bus_get_clk(cru); | |
335 | } | |
336 | ||
337 | static ulong rv1108_aclk_peri_get_clk(struct rv1108_cru *cru) | |
338 | { | |
339 | u32 div, val; | |
340 | ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
341 | ||
342 | val = readl(&cru->clksel_con[23]); | |
343 | div = bitfield_extract(val, ACLK_PERI_DIV_CON_SHIFT, | |
344 | PERI_DIV_CON_WIDTH); | |
345 | ||
346 | return DIV_TO_RATE(parent_rate, div); | |
347 | } | |
348 | ||
349 | static ulong rv1108_hclk_peri_get_clk(struct rv1108_cru *cru) | |
350 | { | |
351 | u32 div, val; | |
352 | ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
353 | ||
354 | val = readl(&cru->clksel_con[23]); | |
355 | div = bitfield_extract(val, HCLK_PERI_DIV_CON_SHIFT, | |
356 | PERI_DIV_CON_WIDTH); | |
357 | ||
358 | return DIV_TO_RATE(parent_rate, div); | |
359 | } | |
360 | ||
361 | static ulong rv1108_pclk_peri_get_clk(struct rv1108_cru *cru) | |
362 | { | |
363 | u32 div, val; | |
364 | ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
365 | ||
366 | val = readl(&cru->clksel_con[23]); | |
367 | div = bitfield_extract(val, PCLK_PERI_DIV_CON_SHIFT, | |
368 | PERI_DIV_CON_WIDTH); | |
369 | ||
370 | return DIV_TO_RATE(parent_rate, div); | |
371 | } | |
372 | ||
373 | static ulong rv1108_aclk_peri_set_clk(struct rv1108_cru *cru, uint hz) | |
374 | { | |
375 | int src_clk_div; | |
376 | ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
377 | ||
378 | src_clk_div = DIV_ROUND_UP(parent_rate, hz) - 1; | |
379 | assert(src_clk_div < 32); | |
380 | ||
381 | rk_clrsetreg(&cru->clksel_con[23], | |
382 | ACLK_PERI_DIV_CON_MASK | ACLK_PERI_PLL_SEL_MASK, | |
383 | (src_clk_div << ACLK_PERI_DIV_CON_SHIFT) | | |
384 | (ACLK_PERI_PLL_SEL_GPLL << ACLK_PERI_PLL_SEL_SHIFT)); | |
385 | ||
386 | return rv1108_aclk_peri_get_clk(cru); | |
387 | } | |
388 | ||
389 | static ulong rv1108_hclk_peri_set_clk(struct rv1108_cru *cru, uint hz) | |
390 | { | |
391 | int src_clk_div; | |
392 | ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
393 | ||
394 | src_clk_div = DIV_ROUND_UP(parent_rate, hz) - 1; | |
395 | assert(src_clk_div < 32); | |
396 | ||
397 | rk_clrsetreg(&cru->clksel_con[23], | |
398 | HCLK_PERI_DIV_CON_MASK, | |
399 | (src_clk_div << HCLK_PERI_DIV_CON_SHIFT)); | |
400 | ||
401 | return rv1108_hclk_peri_get_clk(cru); | |
402 | } | |
403 | ||
404 | static ulong rv1108_pclk_peri_set_clk(struct rv1108_cru *cru, uint hz) | |
405 | { | |
406 | int src_clk_div; | |
407 | ulong parent_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
408 | ||
409 | src_clk_div = DIV_ROUND_UP(parent_rate, hz) - 1; | |
410 | assert(src_clk_div < 32); | |
411 | ||
412 | rk_clrsetreg(&cru->clksel_con[23], | |
413 | PCLK_PERI_DIV_CON_MASK, | |
414 | (src_clk_div << PCLK_PERI_DIV_CON_SHIFT)); | |
415 | ||
416 | return rv1108_pclk_peri_get_clk(cru); | |
417 | } | |
418 | ||
419 | static ulong rv1108_i2c_get_clk(struct rv1108_cru *cru, ulong clk_id) | |
420 | { | |
421 | u32 div, con; | |
422 | ||
423 | switch (clk_id) { | |
424 | case SCLK_I2C0_PMU: | |
425 | con = readl(&cru->clksel_con[19]); | |
426 | div = bitfield_extract(con, CLK_I2C0_DIV_CON_SHIFT, | |
427 | I2C_DIV_CON_WIDTH); | |
428 | break; | |
429 | case SCLK_I2C1: | |
430 | con = readl(&cru->clksel_con[19]); | |
431 | div = bitfield_extract(con, CLK_I2C1_DIV_CON_SHIFT, | |
432 | I2C_DIV_CON_WIDTH); | |
433 | break; | |
434 | case SCLK_I2C2: | |
435 | con = readl(&cru->clksel_con[20]); | |
436 | div = bitfield_extract(con, CLK_I2C2_DIV_CON_SHIFT, | |
437 | I2C_DIV_CON_WIDTH); | |
438 | break; | |
439 | case SCLK_I2C3: | |
440 | con = readl(&cru->clksel_con[20]); | |
441 | div = bitfield_extract(con, CLK_I2C3_DIV_CON_SHIFT, | |
442 | I2C_DIV_CON_WIDTH); | |
443 | break; | |
444 | default: | |
445 | printf("do not support this i2c bus\n"); | |
446 | return -EINVAL; | |
447 | } | |
448 | ||
449 | return DIV_TO_RATE(GPLL_HZ, div); | |
450 | } | |
451 | ||
452 | static ulong rv1108_i2c_set_clk(struct rv1108_cru *cru, ulong clk_id, uint hz) | |
453 | { | |
454 | int src_clk_div; | |
455 | ||
456 | /* i2c0,4,8 src clock from ppll, i2c1,2,3,5,6,7 src clock from gpll*/ | |
457 | src_clk_div = GPLL_HZ / hz; | |
458 | assert(src_clk_div - 1 <= 127); | |
459 | ||
460 | switch (clk_id) { | |
461 | case SCLK_I2C0_PMU: | |
462 | rk_clrsetreg(&cru->clksel_con[19], | |
463 | CLK_I2C0_DIV_CON_MASK | CLK_I2C1_PLL_SEL_MASK, | |
464 | (src_clk_div << CLK_I2C0_DIV_CON_SHIFT) | | |
465 | (CLK_I2C1_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT)); | |
466 | break; | |
467 | case SCLK_I2C1: | |
468 | rk_clrsetreg(&cru->clksel_con[19], | |
469 | CLK_I2C1_DIV_CON_MASK | CLK_I2C1_PLL_SEL_MASK, | |
470 | (src_clk_div << CLK_I2C1_DIV_CON_SHIFT) | | |
471 | (CLK_I2C1_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT)); | |
472 | break; | |
473 | case SCLK_I2C2: | |
474 | rk_clrsetreg(&cru->clksel_con[20], | |
475 | CLK_I2C2_DIV_CON_MASK | CLK_I2C3_PLL_SEL_MASK, | |
476 | (src_clk_div << CLK_I2C2_DIV_CON_SHIFT) | | |
477 | (CLK_I2C3_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT)); | |
478 | break; | |
479 | case SCLK_I2C3: | |
480 | rk_clrsetreg(&cru->clksel_con[20], | |
481 | CLK_I2C3_DIV_CON_MASK | CLK_I2C3_PLL_SEL_MASK, | |
482 | (src_clk_div << CLK_I2C3_DIV_CON_SHIFT) | | |
483 | (CLK_I2C3_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT)); | |
484 | break; | |
485 | default: | |
486 | printf("do not support this i2c bus\n"); | |
487 | return -EINVAL; | |
488 | } | |
489 | ||
490 | return rv1108_i2c_get_clk(cru, clk_id); | |
491 | } | |
492 | ||
493 | static ulong rv1108_mmc_get_clk(struct rv1108_cru *cru) | |
494 | { | |
495 | u32 div, con; | |
496 | ulong mmc_clk; | |
497 | ||
498 | con = readl(&cru->clksel_con[26]); | |
499 | div = bitfield_extract(con, EMMC_CLK_DIV_SHIFT, 8); | |
500 | ||
501 | con = readl(&cru->clksel_con[25]); | |
502 | ||
503 | if ((con & EMMC_PLL_SEL_MASK) >> EMMC_PLL_SEL_SHIFT == EMMC_PLL_SEL_OSC) | |
504 | mmc_clk = DIV_TO_RATE(OSC_HZ, div) / 2; | |
505 | else | |
506 | mmc_clk = DIV_TO_RATE(GPLL_HZ, div) / 2; | |
507 | ||
508 | debug("%s div %d get_clk %ld\n", __func__, div, mmc_clk); | |
509 | return mmc_clk; | |
510 | } | |
511 | ||
512 | static ulong rv1108_mmc_set_clk(struct rv1108_cru *cru, ulong rate) | |
513 | { | |
514 | int div; | |
515 | u32 pll_rate; | |
516 | ||
517 | div = DIV_ROUND_UP(rkclk_pll_get_rate(cru, CLK_GENERAL), rate); | |
518 | ||
519 | if (div < 127) { | |
520 | debug("%s source gpll\n", __func__); | |
521 | rk_clrsetreg(&cru->clksel_con[25], EMMC_PLL_SEL_MASK, | |
522 | (EMMC_PLL_SEL_GPLL << EMMC_PLL_SEL_SHIFT)); | |
523 | pll_rate = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
524 | } else { | |
525 | debug("%s source 24m\n", __func__); | |
526 | rk_clrsetreg(&cru->clksel_con[25], EMMC_PLL_SEL_MASK, | |
527 | (EMMC_PLL_SEL_OSC << EMMC_PLL_SEL_SHIFT)); | |
528 | pll_rate = OSC_HZ; | |
529 | } | |
530 | ||
531 | div = DIV_ROUND_UP(pll_rate / 2, rate); | |
532 | rk_clrsetreg(&cru->clksel_con[26], EMMC_CLK_DIV_MASK, | |
533 | ((div - 1) << EMMC_CLK_DIV_SHIFT)); | |
534 | ||
535 | debug("%s set_rate %ld div %d\n", __func__, rate, div); | |
536 | ||
537 | return DIV_TO_RATE(pll_rate, div); | |
538 | } | |
539 | ||
bae2f282 AY |
540 | static ulong rv1108_clk_get_rate(struct clk *clk) |
541 | { | |
542 | struct rv1108_clk_priv *priv = dev_get_priv(clk->dev); | |
543 | ||
544 | switch (clk->id) { | |
545 | case 0 ... 63: | |
546 | return rkclk_pll_get_rate(priv->cru, clk->id); | |
2e4ce50d DW |
547 | case SCLK_SARADC: |
548 | return rv1108_saradc_get_clk(priv->cru); | |
5d2cb15c OS |
549 | case ACLK_VIO0: |
550 | return rv1108_aclk_vio0_get_clk(priv->cru); | |
551 | case ACLK_VIO1: | |
552 | return rv1108_aclk_vio1_get_clk(priv->cru); | |
553 | case DCLK_VOP: | |
554 | return rv1108_dclk_vop_get_clk(priv->cru); | |
555 | case ACLK_PRE: | |
556 | return rv1108_aclk_bus_get_clk(priv->cru); | |
557 | case ACLK_PERI: | |
558 | return rv1108_aclk_peri_get_clk(priv->cru); | |
559 | case HCLK_PERI: | |
560 | return rv1108_hclk_peri_get_clk(priv->cru); | |
561 | case PCLK_PERI: | |
562 | return rv1108_pclk_peri_get_clk(priv->cru); | |
563 | case SCLK_I2C0_PMU: | |
564 | case SCLK_I2C1: | |
565 | case SCLK_I2C2: | |
566 | case SCLK_I2C3: | |
567 | return rv1108_i2c_get_clk(priv->cru, clk->id); | |
568 | case HCLK_EMMC: | |
569 | case SCLK_EMMC: | |
570 | case SCLK_EMMC_SAMPLE: | |
571 | return rv1108_mmc_get_clk(priv->cru); | |
bae2f282 AY |
572 | default: |
573 | return -ENOENT; | |
574 | } | |
575 | } | |
576 | ||
577 | static ulong rv1108_clk_set_rate(struct clk *clk, ulong rate) | |
578 | { | |
579 | struct rv1108_clk_priv *priv = dev_get_priv(clk->dev); | |
580 | ulong new_rate; | |
581 | ||
582 | switch (clk->id) { | |
583 | case SCLK_MAC: | |
584 | new_rate = rv1108_mac_set_clk(priv->cru, rate); | |
585 | break; | |
586 | case SCLK_SFC: | |
587 | new_rate = rv1108_sfc_set_clk(priv->cru, rate); | |
588 | break; | |
2e4ce50d DW |
589 | case SCLK_SARADC: |
590 | new_rate = rv1108_saradc_set_clk(priv->cru, rate); | |
591 | break; | |
5d2cb15c OS |
592 | case ACLK_VIO0: |
593 | new_rate = rv1108_aclk_vio0_set_clk(priv->cru, rate); | |
594 | break; | |
595 | case ACLK_VIO1: | |
596 | new_rate = rv1108_aclk_vio1_set_clk(priv->cru, rate); | |
597 | break; | |
598 | case DCLK_VOP: | |
599 | new_rate = rv1108_dclk_vop_set_clk(priv->cru, rate); | |
600 | break; | |
601 | case ACLK_PRE: | |
602 | new_rate = rv1108_aclk_bus_set_clk(priv->cru, rate); | |
603 | break; | |
604 | case ACLK_PERI: | |
605 | new_rate = rv1108_aclk_peri_set_clk(priv->cru, rate); | |
606 | break; | |
607 | case HCLK_PERI: | |
608 | new_rate = rv1108_hclk_peri_set_clk(priv->cru, rate); | |
609 | break; | |
610 | case PCLK_PERI: | |
611 | new_rate = rv1108_pclk_peri_set_clk(priv->cru, rate); | |
612 | break; | |
613 | case SCLK_I2C0_PMU: | |
614 | case SCLK_I2C1: | |
615 | case SCLK_I2C2: | |
616 | case SCLK_I2C3: | |
617 | new_rate = rv1108_i2c_set_clk(priv->cru, clk->id, rate); | |
618 | break; | |
619 | case HCLK_EMMC: | |
620 | case SCLK_EMMC: | |
621 | new_rate = rv1108_mmc_set_clk(priv->cru, rate); | |
622 | break; | |
bae2f282 AY |
623 | default: |
624 | return -ENOENT; | |
625 | } | |
626 | ||
627 | return new_rate; | |
628 | } | |
629 | ||
630 | static const struct clk_ops rv1108_clk_ops = { | |
631 | .get_rate = rv1108_clk_get_rate, | |
632 | .set_rate = rv1108_clk_set_rate, | |
633 | }; | |
634 | ||
635 | static void rkclk_init(struct rv1108_cru *cru) | |
636 | { | |
5d2cb15c OS |
637 | unsigned int apll, dpll, gpll; |
638 | unsigned int aclk_bus, aclk_peri, hclk_peri, pclk_peri; | |
639 | ||
640 | aclk_bus = rv1108_aclk_bus_set_clk(cru, ACLK_BUS_HZ / 2); | |
641 | aclk_peri = rv1108_aclk_peri_set_clk(cru, ACLK_PERI_HZ / 2); | |
642 | hclk_peri = rv1108_hclk_peri_set_clk(cru, HCLK_PERI_HZ / 2); | |
643 | pclk_peri = rv1108_pclk_peri_set_clk(cru, PCLK_PERI_HZ / 2); | |
644 | rv1108_aclk_vio0_set_clk(cru, 297000000); | |
645 | rv1108_aclk_vio1_set_clk(cru, 297000000); | |
646 | ||
647 | /* configure apll */ | |
648 | rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg); | |
649 | rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); | |
650 | aclk_bus = rv1108_aclk_bus_set_clk(cru, ACLK_BUS_HZ); | |
651 | aclk_peri = rv1108_aclk_peri_set_clk(cru, ACLK_PERI_HZ); | |
652 | hclk_peri = rv1108_hclk_peri_set_clk(cru, HCLK_PERI_HZ); | |
653 | pclk_peri = rv1108_pclk_peri_set_clk(cru, PCLK_PERI_HZ); | |
654 | ||
655 | apll = rkclk_pll_get_rate(cru, CLK_ARM); | |
656 | dpll = rkclk_pll_get_rate(cru, CLK_DDR); | |
657 | gpll = rkclk_pll_get_rate(cru, CLK_GENERAL); | |
bae2f282 AY |
658 | |
659 | rk_clrsetreg(&cru->clksel_con[0], CORE_CLK_DIV_MASK, | |
660 | 0 << MAC_CLK_DIV_SHIFT); | |
661 | ||
662 | printf("APLL: %d DPLL:%d GPLL:%d\n", apll, dpll, gpll); | |
5d2cb15c OS |
663 | printf("ACLK_BUS: %d ACLK_PERI:%d HCLK_PERI:%d PCLK_PERI:%d\n", |
664 | aclk_bus, aclk_peri, hclk_peri, pclk_peri); | |
bae2f282 AY |
665 | } |
666 | ||
d1998a9f | 667 | static int rv1108_clk_of_to_plat(struct udevice *dev) |
bae2f282 AY |
668 | { |
669 | struct rv1108_clk_priv *priv = dev_get_priv(dev); | |
670 | ||
bbfef40f | 671 | priv->cru = dev_read_addr_ptr(dev); |
bae2f282 | 672 | |
c877ef3a KY |
673 | return 0; |
674 | } | |
675 | ||
676 | static int rv1108_clk_probe(struct udevice *dev) | |
677 | { | |
678 | struct rv1108_clk_priv *priv = dev_get_priv(dev); | |
679 | ||
bae2f282 AY |
680 | rkclk_init(priv->cru); |
681 | ||
682 | return 0; | |
683 | } | |
684 | ||
685 | static int rv1108_clk_bind(struct udevice *dev) | |
686 | { | |
687 | int ret; | |
a04f6fd1 | 688 | struct udevice *sys_child; |
f24e36da | 689 | struct sysreset_reg *priv; |
bae2f282 AY |
690 | |
691 | /* The reset driver does not have a device node, so bind it here */ | |
f24e36da KY |
692 | ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", |
693 | &sys_child); | |
694 | if (ret) { | |
695 | debug("Warning: No sysreset driver: ret=%d\n", ret); | |
696 | } else { | |
697 | priv = malloc(sizeof(struct sysreset_reg)); | |
698 | priv->glb_srst_fst_value = offsetof(struct rv1108_cru, | |
699 | glb_srst_fst_val); | |
700 | priv->glb_srst_snd_value = offsetof(struct rv1108_cru, | |
701 | glb_srst_snd_val); | |
0fd3d911 | 702 | dev_set_priv(sys_child, priv); |
f24e36da | 703 | } |
bae2f282 | 704 | |
a5ada25e | 705 | #if CONFIG_IS_ENABLED(RESET_ROCKCHIP) |
a04f6fd1 | 706 | ret = offsetof(struct rv1108_cru, softrst_con[0]); |
538f67c3 EZ |
707 | ret = rockchip_reset_bind(dev, ret, 13); |
708 | if (ret) | |
709 | debug("Warning: software reset driver bind faile\n"); | |
710 | #endif | |
711 | ||
bae2f282 AY |
712 | return 0; |
713 | } | |
714 | ||
715 | static const struct udevice_id rv1108_clk_ids[] = { | |
716 | { .compatible = "rockchip,rv1108-cru" }, | |
717 | { } | |
718 | }; | |
719 | ||
720 | U_BOOT_DRIVER(clk_rv1108) = { | |
721 | .name = "clk_rv1108", | |
722 | .id = UCLASS_CLK, | |
723 | .of_match = rv1108_clk_ids, | |
41575d8e | 724 | .priv_auto = sizeof(struct rv1108_clk_priv), |
bae2f282 AY |
725 | .ops = &rv1108_clk_ops, |
726 | .bind = rv1108_clk_bind, | |
d1998a9f | 727 | .of_to_plat = rv1108_clk_of_to_plat, |
bae2f282 AY |
728 | .probe = rv1108_clk_probe, |
729 | }; |