]>
Commit | Line | Data |
---|---|---|
41793000 KY |
1 | /* |
2 | * (C) Copyright 2017 Rockchip Electronics Co., Ltd | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0 | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
b375d841 | 8 | #include <bitfield.h> |
41793000 KY |
9 | #include <clk-uclass.h> |
10 | #include <dm.h> | |
11 | #include <errno.h> | |
12 | #include <syscon.h> | |
13 | #include <asm/arch/clock.h> | |
14 | #include <asm/arch/cru_rk3328.h> | |
15 | #include <asm/arch/hardware.h> | |
7cd4ebab | 16 | #include <asm/arch/grf_rk3328.h> |
41793000 KY |
17 | #include <asm/io.h> |
18 | #include <dm/lists.h> | |
19 | #include <dt-bindings/clock/rk3328-cru.h> | |
20 | ||
21 | DECLARE_GLOBAL_DATA_PTR; | |
22 | ||
23 | struct pll_div { | |
24 | u32 refdiv; | |
25 | u32 fbdiv; | |
26 | u32 postdiv1; | |
27 | u32 postdiv2; | |
28 | u32 frac; | |
29 | }; | |
30 | ||
31 | #define RATE_TO_DIV(input_rate, output_rate) \ | |
32 | ((input_rate) / (output_rate) - 1); | |
33 | #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) | |
34 | ||
35 | #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\ | |
36 | .refdiv = _refdiv,\ | |
37 | .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\ | |
38 | .postdiv1 = _postdiv1, .postdiv2 = _postdiv2}; | |
39 | ||
40 | static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 4, 1); | |
41 | static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 2, 2, 1); | |
42 | ||
43 | static const struct pll_div apll_816_cfg = PLL_DIVISORS(816 * MHz, 1, 2, 1); | |
44 | static const struct pll_div apll_600_cfg = PLL_DIVISORS(600 * MHz, 1, 3, 1); | |
45 | ||
46 | static const struct pll_div *apll_cfgs[] = { | |
47 | [APLL_816_MHZ] = &apll_816_cfg, | |
48 | [APLL_600_MHZ] = &apll_600_cfg, | |
49 | }; | |
50 | ||
51 | enum { | |
52 | /* PLL_CON0 */ | |
53 | PLL_POSTDIV1_SHIFT = 12, | |
54 | PLL_POSTDIV1_MASK = 0x7 << PLL_POSTDIV1_SHIFT, | |
55 | PLL_FBDIV_SHIFT = 0, | |
56 | PLL_FBDIV_MASK = 0xfff, | |
57 | ||
58 | /* PLL_CON1 */ | |
59 | PLL_DSMPD_SHIFT = 12, | |
60 | PLL_DSMPD_MASK = 1 << PLL_DSMPD_SHIFT, | |
61 | PLL_INTEGER_MODE = 1, | |
62 | PLL_LOCK_STATUS_SHIFT = 10, | |
63 | PLL_LOCK_STATUS_MASK = 1 << PLL_LOCK_STATUS_SHIFT, | |
64 | PLL_POSTDIV2_SHIFT = 6, | |
65 | PLL_POSTDIV2_MASK = 0x7 << PLL_POSTDIV2_SHIFT, | |
66 | PLL_REFDIV_SHIFT = 0, | |
67 | PLL_REFDIV_MASK = 0x3f, | |
68 | ||
69 | /* PLL_CON2 */ | |
70 | PLL_FRACDIV_SHIFT = 0, | |
71 | PLL_FRACDIV_MASK = 0xffffff, | |
72 | ||
73 | /* MODE_CON */ | |
74 | APLL_MODE_SHIFT = 0, | |
75 | NPLL_MODE_SHIFT = 1, | |
76 | DPLL_MODE_SHIFT = 4, | |
77 | CPLL_MODE_SHIFT = 8, | |
78 | GPLL_MODE_SHIFT = 12, | |
79 | PLL_MODE_SLOW = 0, | |
80 | PLL_MODE_NORM, | |
81 | ||
82 | /* CLKSEL_CON0 */ | |
83 | CLK_CORE_PLL_SEL_APLL = 0, | |
84 | CLK_CORE_PLL_SEL_GPLL, | |
85 | CLK_CORE_PLL_SEL_DPLL, | |
86 | CLK_CORE_PLL_SEL_NPLL, | |
87 | CLK_CORE_PLL_SEL_SHIFT = 6, | |
88 | CLK_CORE_PLL_SEL_MASK = 3 << CLK_CORE_PLL_SEL_SHIFT, | |
89 | CLK_CORE_DIV_SHIFT = 0, | |
90 | CLK_CORE_DIV_MASK = 0x1f, | |
91 | ||
92 | /* CLKSEL_CON1 */ | |
93 | ACLKM_CORE_DIV_SHIFT = 4, | |
94 | ACLKM_CORE_DIV_MASK = 0x7 << ACLKM_CORE_DIV_SHIFT, | |
95 | PCLK_DBG_DIV_SHIFT = 0, | |
96 | PCLK_DBG_DIV_MASK = 0xF << PCLK_DBG_DIV_SHIFT, | |
97 | ||
7cd4ebab DW |
98 | /* CLKSEL_CON27 */ |
99 | GMAC2IO_PLL_SEL_SHIFT = 7, | |
100 | GMAC2IO_PLL_SEL_MASK = 1 << GMAC2IO_PLL_SEL_SHIFT, | |
101 | GMAC2IO_PLL_SEL_CPLL = 0, | |
102 | GMAC2IO_PLL_SEL_GPLL = 1, | |
103 | GMAC2IO_CLK_DIV_MASK = 0x1f, | |
104 | GMAC2IO_CLK_DIV_SHIFT = 0, | |
105 | ||
41793000 KY |
106 | /* CLKSEL_CON28 */ |
107 | ACLK_PERIHP_PLL_SEL_CPLL = 0, | |
108 | ACLK_PERIHP_PLL_SEL_GPLL, | |
109 | ACLK_PERIHP_PLL_SEL_HDMIPHY, | |
110 | ACLK_PERIHP_PLL_SEL_SHIFT = 6, | |
111 | ACLK_PERIHP_PLL_SEL_MASK = 3 << ACLK_PERIHP_PLL_SEL_SHIFT, | |
112 | ACLK_PERIHP_DIV_CON_SHIFT = 0, | |
113 | ACLK_PERIHP_DIV_CON_MASK = 0x1f, | |
114 | ||
115 | /* CLKSEL_CON29 */ | |
116 | PCLK_PERIHP_DIV_CON_SHIFT = 4, | |
117 | PCLK_PERIHP_DIV_CON_MASK = 0x7 << PCLK_PERIHP_DIV_CON_SHIFT, | |
118 | HCLK_PERIHP_DIV_CON_SHIFT = 0, | |
119 | HCLK_PERIHP_DIV_CON_MASK = 3 << HCLK_PERIHP_DIV_CON_SHIFT, | |
120 | ||
121 | /* CLKSEL_CON22 */ | |
122 | CLK_TSADC_DIV_CON_SHIFT = 0, | |
123 | CLK_TSADC_DIV_CON_MASK = 0x3ff, | |
124 | ||
125 | /* CLKSEL_CON23 */ | |
126 | CLK_SARADC_DIV_CON_SHIFT = 0, | |
b375d841 DW |
127 | CLK_SARADC_DIV_CON_MASK = GENMASK(9, 0), |
128 | CLK_SARADC_DIV_CON_WIDTH = 10, | |
41793000 KY |
129 | |
130 | /* CLKSEL_CON24 */ | |
131 | CLK_PWM_PLL_SEL_CPLL = 0, | |
132 | CLK_PWM_PLL_SEL_GPLL, | |
133 | CLK_PWM_PLL_SEL_SHIFT = 15, | |
134 | CLK_PWM_PLL_SEL_MASK = 1 << CLK_PWM_PLL_SEL_SHIFT, | |
135 | CLK_PWM_DIV_CON_SHIFT = 8, | |
136 | CLK_PWM_DIV_CON_MASK = 0x7f << CLK_PWM_DIV_CON_SHIFT, | |
137 | ||
138 | CLK_SPI_PLL_SEL_CPLL = 0, | |
139 | CLK_SPI_PLL_SEL_GPLL, | |
140 | CLK_SPI_PLL_SEL_SHIFT = 7, | |
141 | CLK_SPI_PLL_SEL_MASK = 1 << CLK_SPI_PLL_SEL_SHIFT, | |
142 | CLK_SPI_DIV_CON_SHIFT = 0, | |
143 | CLK_SPI_DIV_CON_MASK = 0x7f << CLK_SPI_DIV_CON_SHIFT, | |
144 | ||
145 | /* CLKSEL_CON30 */ | |
146 | CLK_SDMMC_PLL_SEL_CPLL = 0, | |
147 | CLK_SDMMC_PLL_SEL_GPLL, | |
148 | CLK_SDMMC_PLL_SEL_24M, | |
149 | CLK_SDMMC_PLL_SEL_USBPHY, | |
150 | CLK_SDMMC_PLL_SHIFT = 8, | |
151 | CLK_SDMMC_PLL_MASK = 0x3 << CLK_SDMMC_PLL_SHIFT, | |
152 | CLK_SDMMC_DIV_CON_SHIFT = 0, | |
153 | CLK_SDMMC_DIV_CON_MASK = 0xff << CLK_SDMMC_DIV_CON_SHIFT, | |
154 | ||
155 | /* CLKSEL_CON32 */ | |
156 | CLK_EMMC_PLL_SEL_CPLL = 0, | |
157 | CLK_EMMC_PLL_SEL_GPLL, | |
158 | CLK_EMMC_PLL_SEL_24M, | |
159 | CLK_EMMC_PLL_SEL_USBPHY, | |
160 | CLK_EMMC_PLL_SHIFT = 8, | |
161 | CLK_EMMC_PLL_MASK = 0x3 << CLK_EMMC_PLL_SHIFT, | |
162 | CLK_EMMC_DIV_CON_SHIFT = 0, | |
163 | CLK_EMMC_DIV_CON_MASK = 0xff << CLK_EMMC_DIV_CON_SHIFT, | |
164 | ||
165 | /* CLKSEL_CON34 */ | |
166 | CLK_I2C_PLL_SEL_CPLL = 0, | |
167 | CLK_I2C_PLL_SEL_GPLL, | |
168 | CLK_I2C_DIV_CON_MASK = 0x7f, | |
169 | CLK_I2C_PLL_SEL_MASK = 1, | |
170 | CLK_I2C1_PLL_SEL_SHIFT = 15, | |
171 | CLK_I2C1_DIV_CON_SHIFT = 8, | |
172 | CLK_I2C0_PLL_SEL_SHIFT = 7, | |
173 | CLK_I2C0_DIV_CON_SHIFT = 0, | |
174 | ||
175 | /* CLKSEL_CON35 */ | |
176 | CLK_I2C3_PLL_SEL_SHIFT = 15, | |
177 | CLK_I2C3_DIV_CON_SHIFT = 8, | |
178 | CLK_I2C2_PLL_SEL_SHIFT = 7, | |
179 | CLK_I2C2_DIV_CON_SHIFT = 0, | |
180 | }; | |
181 | ||
182 | #define VCO_MAX_KHZ (3200 * (MHz / KHz)) | |
183 | #define VCO_MIN_KHZ (800 * (MHz / KHz)) | |
184 | #define OUTPUT_MAX_KHZ (3200 * (MHz / KHz)) | |
185 | #define OUTPUT_MIN_KHZ (16 * (MHz / KHz)) | |
186 | ||
187 | /* | |
188 | * the div restructions of pll in integer mode, these are defined in | |
189 | * * CRU_*PLL_CON0 or PMUCRU_*PLL_CON0 | |
190 | */ | |
191 | #define PLL_DIV_MIN 16 | |
192 | #define PLL_DIV_MAX 3200 | |
193 | ||
194 | /* | |
195 | * How to calculate the PLL(from TRM V0.3 Part 1 Page 63): | |
196 | * Formulas also embedded within the Fractional PLL Verilog model: | |
197 | * If DSMPD = 1 (DSM is disabled, "integer mode") | |
198 | * FOUTVCO = FREF / REFDIV * FBDIV | |
199 | * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2 | |
200 | * Where: | |
201 | * FOUTVCO = Fractional PLL non-divided output frequency | |
202 | * FOUTPOSTDIV = Fractional PLL divided output frequency | |
203 | * (output of second post divider) | |
204 | * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input) | |
205 | * REFDIV = Fractional PLL input reference clock divider | |
206 | * FBDIV = Integer value programmed into feedback divide | |
207 | * | |
208 | */ | |
209 | static void rkclk_set_pll(struct rk3328_cru *cru, enum rk_clk_id clk_id, | |
210 | const struct pll_div *div) | |
211 | { | |
212 | u32 *pll_con; | |
213 | u32 mode_shift, mode_mask; | |
214 | ||
215 | pll_con = NULL; | |
216 | mode_shift = 0; | |
217 | switch (clk_id) { | |
218 | case CLK_ARM: | |
219 | pll_con = cru->apll_con; | |
220 | mode_shift = APLL_MODE_SHIFT; | |
221 | break; | |
222 | case CLK_DDR: | |
223 | pll_con = cru->dpll_con; | |
224 | mode_shift = DPLL_MODE_SHIFT; | |
225 | break; | |
226 | case CLK_CODEC: | |
227 | pll_con = cru->cpll_con; | |
228 | mode_shift = CPLL_MODE_SHIFT; | |
229 | break; | |
230 | case CLK_GENERAL: | |
231 | pll_con = cru->gpll_con; | |
232 | mode_shift = GPLL_MODE_SHIFT; | |
233 | break; | |
234 | case CLK_NEW: | |
235 | pll_con = cru->npll_con; | |
236 | mode_shift = NPLL_MODE_SHIFT; | |
237 | break; | |
238 | default: | |
239 | break; | |
240 | } | |
241 | mode_mask = 1 << mode_shift; | |
242 | ||
243 | /* All 8 PLLs have same VCO and output frequency range restrictions. */ | |
244 | u32 vco_khz = OSC_HZ / 1000 * div->fbdiv / div->refdiv; | |
245 | u32 output_khz = vco_khz / div->postdiv1 / div->postdiv2; | |
246 | ||
247 | debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, \ | |
248 | postdiv2=%d, vco=%u khz, output=%u khz\n", | |
249 | pll_con, div->fbdiv, div->refdiv, div->postdiv1, | |
250 | div->postdiv2, vco_khz, output_khz); | |
251 | assert(vco_khz >= VCO_MIN_KHZ && vco_khz <= VCO_MAX_KHZ && | |
252 | output_khz >= OUTPUT_MIN_KHZ && output_khz <= OUTPUT_MAX_KHZ && | |
253 | div->fbdiv >= PLL_DIV_MIN && div->fbdiv <= PLL_DIV_MAX); | |
254 | ||
255 | /* | |
256 | * When power on or changing PLL setting, | |
257 | * we must force PLL into slow mode to ensure output stable clock. | |
258 | */ | |
259 | rk_clrsetreg(&cru->mode_con, mode_mask, PLL_MODE_SLOW << mode_shift); | |
260 | ||
261 | /* use integer mode */ | |
262 | rk_clrsetreg(&pll_con[1], PLL_DSMPD_MASK, | |
263 | PLL_INTEGER_MODE << PLL_DSMPD_SHIFT); | |
264 | ||
265 | rk_clrsetreg(&pll_con[0], | |
266 | PLL_FBDIV_MASK | PLL_POSTDIV1_MASK, | |
267 | (div->fbdiv << PLL_FBDIV_SHIFT) | | |
268 | (div->postdiv1 << PLL_POSTDIV1_SHIFT)); | |
269 | rk_clrsetreg(&pll_con[1], | |
270 | PLL_POSTDIV2_MASK | PLL_REFDIV_MASK, | |
271 | (div->postdiv2 << PLL_POSTDIV2_SHIFT) | | |
272 | (div->refdiv << PLL_REFDIV_SHIFT)); | |
273 | ||
274 | /* waiting for pll lock */ | |
275 | while (!(readl(&pll_con[1]) & (1 << PLL_LOCK_STATUS_SHIFT))) | |
276 | udelay(1); | |
277 | ||
278 | /* pll enter normal mode */ | |
279 | rk_clrsetreg(&cru->mode_con, mode_mask, PLL_MODE_NORM << mode_shift); | |
280 | } | |
281 | ||
282 | static void rkclk_init(struct rk3328_cru *cru) | |
283 | { | |
284 | u32 aclk_div; | |
285 | u32 hclk_div; | |
286 | u32 pclk_div; | |
287 | ||
288 | /* configure gpll cpll */ | |
289 | rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); | |
290 | rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg); | |
291 | ||
292 | /* configure perihp aclk, hclk, pclk */ | |
293 | aclk_div = GPLL_HZ / PERIHP_ACLK_HZ - 1; | |
294 | hclk_div = PERIHP_ACLK_HZ / PERIHP_HCLK_HZ - 1; | |
295 | pclk_div = PERIHP_ACLK_HZ / PERIHP_PCLK_HZ - 1; | |
296 | ||
297 | rk_clrsetreg(&cru->clksel_con[28], | |
298 | ACLK_PERIHP_PLL_SEL_MASK | ACLK_PERIHP_DIV_CON_MASK, | |
299 | ACLK_PERIHP_PLL_SEL_GPLL << ACLK_PERIHP_PLL_SEL_SHIFT | | |
300 | aclk_div << ACLK_PERIHP_DIV_CON_SHIFT); | |
301 | rk_clrsetreg(&cru->clksel_con[29], | |
302 | PCLK_PERIHP_DIV_CON_MASK | HCLK_PERIHP_DIV_CON_MASK, | |
303 | pclk_div << PCLK_PERIHP_DIV_CON_SHIFT | | |
304 | hclk_div << HCLK_PERIHP_DIV_CON_SHIFT); | |
305 | } | |
306 | ||
307 | void rk3328_configure_cpu(struct rk3328_cru *cru, | |
308 | enum apll_frequencies apll_freq) | |
309 | { | |
310 | u32 clk_core_div; | |
311 | u32 aclkm_div; | |
312 | u32 pclk_dbg_div; | |
313 | ||
314 | rkclk_set_pll(cru, CLK_ARM, apll_cfgs[apll_freq]); | |
315 | ||
316 | clk_core_div = APLL_HZ / CLK_CORE_HZ - 1; | |
317 | aclkm_div = APLL_HZ / ACLKM_CORE_HZ / (clk_core_div + 1) - 1; | |
318 | pclk_dbg_div = APLL_HZ / PCLK_DBG_HZ / (clk_core_div + 1) - 1; | |
319 | ||
320 | rk_clrsetreg(&cru->clksel_con[0], | |
321 | CLK_CORE_PLL_SEL_MASK | CLK_CORE_DIV_MASK, | |
322 | CLK_CORE_PLL_SEL_APLL << CLK_CORE_PLL_SEL_SHIFT | | |
323 | clk_core_div << CLK_CORE_DIV_SHIFT); | |
324 | ||
325 | rk_clrsetreg(&cru->clksel_con[1], | |
326 | PCLK_DBG_DIV_MASK | ACLKM_CORE_DIV_MASK, | |
327 | pclk_dbg_div << PCLK_DBG_DIV_SHIFT | | |
328 | aclkm_div << ACLKM_CORE_DIV_SHIFT); | |
329 | } | |
330 | ||
331 | ||
332 | static ulong rk3328_i2c_get_clk(struct rk3328_cru *cru, ulong clk_id) | |
333 | { | |
334 | u32 div, con; | |
335 | ||
336 | switch (clk_id) { | |
337 | case SCLK_I2C0: | |
338 | con = readl(&cru->clksel_con[34]); | |
339 | div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
340 | break; | |
341 | case SCLK_I2C1: | |
342 | con = readl(&cru->clksel_con[34]); | |
343 | div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
344 | break; | |
345 | case SCLK_I2C2: | |
346 | con = readl(&cru->clksel_con[35]); | |
347 | div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
348 | break; | |
349 | case SCLK_I2C3: | |
350 | con = readl(&cru->clksel_con[35]); | |
351 | div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
352 | break; | |
353 | default: | |
354 | printf("do not support this i2c bus\n"); | |
355 | return -EINVAL; | |
356 | } | |
357 | ||
358 | return DIV_TO_RATE(GPLL_HZ, div); | |
359 | } | |
360 | ||
361 | static ulong rk3328_i2c_set_clk(struct rk3328_cru *cru, ulong clk_id, uint hz) | |
362 | { | |
363 | int src_clk_div; | |
364 | ||
365 | src_clk_div = GPLL_HZ / hz; | |
366 | assert(src_clk_div - 1 < 127); | |
367 | ||
368 | switch (clk_id) { | |
369 | case SCLK_I2C0: | |
370 | rk_clrsetreg(&cru->clksel_con[34], | |
371 | CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT | | |
372 | CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT, | |
373 | (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT | | |
374 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT); | |
375 | break; | |
376 | case SCLK_I2C1: | |
377 | rk_clrsetreg(&cru->clksel_con[34], | |
378 | CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT | | |
379 | CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT, | |
380 | (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT | | |
381 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT); | |
382 | break; | |
383 | case SCLK_I2C2: | |
384 | rk_clrsetreg(&cru->clksel_con[35], | |
385 | CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT | | |
386 | CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT, | |
387 | (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT | | |
388 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT); | |
389 | break; | |
390 | case SCLK_I2C3: | |
391 | rk_clrsetreg(&cru->clksel_con[35], | |
392 | CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT | | |
393 | CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT, | |
394 | (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT | | |
395 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT); | |
396 | break; | |
397 | default: | |
398 | printf("do not support this i2c bus\n"); | |
399 | return -EINVAL; | |
400 | } | |
401 | ||
402 | return DIV_TO_RATE(GPLL_HZ, src_clk_div); | |
403 | } | |
404 | ||
7cd4ebab DW |
405 | static ulong rk3328_gmac2io_set_clk(struct rk3328_cru *cru, ulong rate) |
406 | { | |
407 | struct rk3328_grf_regs *grf; | |
408 | ulong ret; | |
409 | ||
410 | grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); | |
411 | ||
412 | /* | |
413 | * The RGMII CLK can be derived either from an external "clkin" | |
414 | * or can be generated from internally by a divider from SCLK_MAC. | |
415 | */ | |
416 | if (readl(&grf->mac_con[1]) & BIT(10) && | |
417 | readl(&grf->soc_con[4]) & BIT(14)) { | |
418 | /* An external clock will always generate the right rate... */ | |
419 | ret = rate; | |
420 | } else { | |
421 | u32 con = readl(&cru->clksel_con[27]); | |
422 | ulong pll_rate; | |
423 | u8 div; | |
424 | ||
425 | if ((con >> GMAC2IO_PLL_SEL_SHIFT) & GMAC2IO_PLL_SEL_GPLL) | |
426 | pll_rate = GPLL_HZ; | |
427 | else | |
428 | pll_rate = CPLL_HZ; | |
429 | ||
430 | div = DIV_ROUND_UP(pll_rate, rate) - 1; | |
431 | if (div <= 0x1f) | |
432 | rk_clrsetreg(&cru->clksel_con[27], GMAC2IO_CLK_DIV_MASK, | |
433 | div << GMAC2IO_CLK_DIV_SHIFT); | |
434 | else | |
435 | debug("Unsupported div for gmac:%d\n", div); | |
436 | ||
437 | return DIV_TO_RATE(pll_rate, div); | |
438 | } | |
439 | ||
440 | return ret; | |
441 | } | |
442 | ||
41793000 KY |
443 | static ulong rk3328_mmc_get_clk(struct rk3328_cru *cru, uint clk_id) |
444 | { | |
445 | u32 div, con, con_id; | |
446 | ||
447 | switch (clk_id) { | |
448 | case HCLK_SDMMC: | |
85c91cb6 | 449 | case SCLK_SDMMC: |
41793000 KY |
450 | con_id = 30; |
451 | break; | |
452 | case HCLK_EMMC: | |
85c91cb6 | 453 | case SCLK_EMMC: |
41793000 KY |
454 | con_id = 32; |
455 | break; | |
456 | default: | |
457 | return -EINVAL; | |
458 | } | |
459 | con = readl(&cru->clksel_con[con_id]); | |
460 | div = (con & CLK_EMMC_DIV_CON_MASK) >> CLK_EMMC_DIV_CON_SHIFT; | |
461 | ||
462 | if ((con & CLK_EMMC_PLL_MASK) >> CLK_EMMC_PLL_SHIFT | |
463 | == CLK_EMMC_PLL_SEL_24M) | |
3a94d75d | 464 | return DIV_TO_RATE(OSC_HZ, div) / 2; |
41793000 | 465 | else |
3a94d75d | 466 | return DIV_TO_RATE(GPLL_HZ, div) / 2; |
41793000 KY |
467 | } |
468 | ||
469 | static ulong rk3328_mmc_set_clk(struct rk3328_cru *cru, | |
470 | ulong clk_id, ulong set_rate) | |
471 | { | |
472 | int src_clk_div; | |
473 | u32 con_id; | |
474 | ||
475 | switch (clk_id) { | |
476 | case HCLK_SDMMC: | |
85c91cb6 | 477 | case SCLK_SDMMC: |
41793000 KY |
478 | con_id = 30; |
479 | break; | |
480 | case HCLK_EMMC: | |
85c91cb6 | 481 | case SCLK_EMMC: |
41793000 KY |
482 | con_id = 32; |
483 | break; | |
484 | default: | |
485 | return -EINVAL; | |
486 | } | |
487 | /* Select clk_sdmmc/emmc source from GPLL by default */ | |
3a94d75d KY |
488 | /* mmc clock defaulg div 2 internal, need provide double in cru */ |
489 | src_clk_div = DIV_ROUND_UP(GPLL_HZ / 2, set_rate); | |
41793000 KY |
490 | |
491 | if (src_clk_div > 127) { | |
492 | /* use 24MHz source for 400KHz clock */ | |
3a94d75d | 493 | src_clk_div = DIV_ROUND_UP(OSC_HZ / 2, set_rate); |
41793000 KY |
494 | rk_clrsetreg(&cru->clksel_con[con_id], |
495 | CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, | |
496 | CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT | | |
497 | (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT); | |
498 | } else { | |
499 | rk_clrsetreg(&cru->clksel_con[con_id], | |
500 | CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, | |
501 | CLK_EMMC_PLL_SEL_GPLL << CLK_EMMC_PLL_SHIFT | | |
502 | (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT); | |
503 | } | |
504 | ||
505 | return rk3328_mmc_get_clk(cru, clk_id); | |
506 | } | |
507 | ||
508 | static ulong rk3328_pwm_get_clk(struct rk3328_cru *cru) | |
509 | { | |
510 | u32 div, con; | |
511 | ||
512 | con = readl(&cru->clksel_con[24]); | |
513 | div = (con & CLK_PWM_DIV_CON_MASK) >> CLK_PWM_DIV_CON_SHIFT; | |
514 | ||
515 | return DIV_TO_RATE(GPLL_HZ, div); | |
516 | } | |
517 | ||
518 | static ulong rk3328_pwm_set_clk(struct rk3328_cru *cru, uint hz) | |
519 | { | |
520 | u32 div = GPLL_HZ / hz; | |
521 | ||
522 | rk_clrsetreg(&cru->clksel_con[24], | |
523 | CLK_PWM_PLL_SEL_MASK | CLK_PWM_DIV_CON_MASK, | |
524 | CLK_PWM_PLL_SEL_GPLL << CLK_PWM_PLL_SEL_SHIFT | | |
525 | (div - 1) << CLK_PWM_DIV_CON_SHIFT); | |
526 | ||
527 | return DIV_TO_RATE(GPLL_HZ, div); | |
528 | } | |
529 | ||
b375d841 DW |
530 | static ulong rk3328_saradc_get_clk(struct rk3328_cru *cru) |
531 | { | |
532 | u32 div, val; | |
533 | ||
534 | val = readl(&cru->clksel_con[23]); | |
535 | div = bitfield_extract(val, CLK_SARADC_DIV_CON_SHIFT, | |
536 | CLK_SARADC_DIV_CON_WIDTH); | |
537 | ||
538 | return DIV_TO_RATE(OSC_HZ, div); | |
539 | } | |
540 | ||
541 | static ulong rk3328_saradc_set_clk(struct rk3328_cru *cru, uint hz) | |
542 | { | |
543 | int src_clk_div; | |
544 | ||
545 | src_clk_div = DIV_ROUND_UP(OSC_HZ, hz) - 1; | |
546 | assert(src_clk_div < 128); | |
547 | ||
548 | rk_clrsetreg(&cru->clksel_con[23], | |
549 | CLK_SARADC_DIV_CON_MASK, | |
550 | src_clk_div << CLK_SARADC_DIV_CON_SHIFT); | |
551 | ||
552 | return rk3328_saradc_get_clk(cru); | |
553 | } | |
554 | ||
41793000 KY |
555 | static ulong rk3328_clk_get_rate(struct clk *clk) |
556 | { | |
557 | struct rk3328_clk_priv *priv = dev_get_priv(clk->dev); | |
558 | ulong rate = 0; | |
559 | ||
560 | switch (clk->id) { | |
561 | case 0 ... 29: | |
562 | return 0; | |
563 | case HCLK_SDMMC: | |
564 | case HCLK_EMMC: | |
85c91cb6 XZ |
565 | case SCLK_SDMMC: |
566 | case SCLK_EMMC: | |
41793000 KY |
567 | rate = rk3328_mmc_get_clk(priv->cru, clk->id); |
568 | break; | |
569 | case SCLK_I2C0: | |
570 | case SCLK_I2C1: | |
571 | case SCLK_I2C2: | |
572 | case SCLK_I2C3: | |
573 | rate = rk3328_i2c_get_clk(priv->cru, clk->id); | |
574 | break; | |
575 | case SCLK_PWM: | |
576 | rate = rk3328_pwm_get_clk(priv->cru); | |
577 | break; | |
b375d841 DW |
578 | case SCLK_SARADC: |
579 | rate = rk3328_saradc_get_clk(priv->cru); | |
580 | break; | |
41793000 KY |
581 | default: |
582 | return -ENOENT; | |
583 | } | |
584 | ||
585 | return rate; | |
586 | } | |
587 | ||
588 | static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate) | |
589 | { | |
590 | struct rk3328_clk_priv *priv = dev_get_priv(clk->dev); | |
591 | ulong ret = 0; | |
592 | ||
593 | switch (clk->id) { | |
594 | case 0 ... 29: | |
595 | return 0; | |
596 | case HCLK_SDMMC: | |
597 | case HCLK_EMMC: | |
85c91cb6 XZ |
598 | case SCLK_SDMMC: |
599 | case SCLK_EMMC: | |
41793000 KY |
600 | ret = rk3328_mmc_set_clk(priv->cru, clk->id, rate); |
601 | break; | |
602 | case SCLK_I2C0: | |
603 | case SCLK_I2C1: | |
604 | case SCLK_I2C2: | |
605 | case SCLK_I2C3: | |
606 | ret = rk3328_i2c_set_clk(priv->cru, clk->id, rate); | |
607 | break; | |
7cd4ebab DW |
608 | case SCLK_MAC2IO: |
609 | ret = rk3328_gmac2io_set_clk(priv->cru, rate); | |
610 | break; | |
41793000 KY |
611 | case SCLK_PWM: |
612 | ret = rk3328_pwm_set_clk(priv->cru, rate); | |
613 | break; | |
b375d841 DW |
614 | case SCLK_SARADC: |
615 | ret = rk3328_saradc_set_clk(priv->cru, rate); | |
616 | break; | |
7cd4ebab DW |
617 | case DCLK_LCDC: |
618 | case SCLK_PDM: | |
619 | case SCLK_RTC32K: | |
620 | case SCLK_UART0: | |
621 | case SCLK_UART1: | |
622 | case SCLK_UART2: | |
623 | case SCLK_SDIO: | |
624 | case SCLK_TSP: | |
625 | case SCLK_WIFI: | |
626 | case ACLK_BUS_PRE: | |
627 | case HCLK_BUS_PRE: | |
628 | case PCLK_BUS_PRE: | |
629 | case ACLK_PERI_PRE: | |
630 | case HCLK_PERI: | |
631 | case PCLK_PERI: | |
632 | case ACLK_VIO_PRE: | |
633 | case HCLK_VIO_PRE: | |
634 | case ACLK_RGA_PRE: | |
635 | case SCLK_RGA: | |
636 | case ACLK_VOP_PRE: | |
637 | case ACLK_RKVDEC_PRE: | |
638 | case ACLK_RKVENC: | |
639 | case ACLK_VPU_PRE: | |
640 | case SCLK_VDEC_CABAC: | |
641 | case SCLK_VDEC_CORE: | |
642 | case SCLK_VENC_CORE: | |
643 | case SCLK_VENC_DSP: | |
644 | case SCLK_EFUSE: | |
645 | case PCLK_DDR: | |
646 | case ACLK_GMAC: | |
647 | case PCLK_GMAC: | |
648 | case SCLK_USB3OTG_SUSPEND: | |
649 | return 0; | |
41793000 KY |
650 | default: |
651 | return -ENOENT; | |
652 | } | |
653 | ||
654 | return ret; | |
655 | } | |
656 | ||
7cd4ebab DW |
657 | static int rk3328_gmac2io_set_parent(struct clk *clk, struct clk *parent) |
658 | { | |
659 | struct rk3328_grf_regs *grf; | |
660 | const char *clock_output_name; | |
661 | int ret; | |
662 | ||
663 | grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); | |
664 | ||
665 | /* | |
666 | * If the requested parent is in the same clock-controller and the id | |
667 | * is SCLK_MAC2IO_SRC ("clk_mac2io_src"), switch to the internal clock. | |
668 | */ | |
669 | if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC2IO_SRC)) { | |
670 | debug("%s: switching RGMII to SCLK_MAC2IO_SRC\n", __func__); | |
671 | rk_clrreg(&grf->mac_con[1], BIT(10)); | |
672 | return 0; | |
673 | } | |
674 | ||
675 | /* | |
676 | * Otherwise, we need to check the clock-output-names of the | |
677 | * requested parent to see if the requested id is "gmac_clkin". | |
678 | */ | |
679 | ret = dev_read_string_index(parent->dev, "clock-output-names", | |
680 | parent->id, &clock_output_name); | |
681 | if (ret < 0) | |
682 | return -ENODATA; | |
683 | ||
684 | /* If this is "gmac_clkin", switch to the external clock input */ | |
685 | if (!strcmp(clock_output_name, "gmac_clkin")) { | |
686 | debug("%s: switching RGMII to CLKIN\n", __func__); | |
687 | rk_setreg(&grf->mac_con[1], BIT(10)); | |
688 | return 0; | |
689 | } | |
690 | ||
691 | return -EINVAL; | |
692 | } | |
693 | ||
694 | static int rk3328_gmac2io_ext_set_parent(struct clk *clk, struct clk *parent) | |
695 | { | |
696 | struct rk3328_grf_regs *grf; | |
697 | const char *clock_output_name; | |
698 | int ret; | |
699 | ||
700 | grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); | |
701 | ||
702 | /* | |
703 | * If the requested parent is in the same clock-controller and the id | |
704 | * is SCLK_MAC2IO ("clk_mac2io"), switch to the internal clock. | |
705 | */ | |
706 | if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC2IO)) { | |
707 | debug("%s: switching RGMII to SCLK_MAC2IO\n", __func__); | |
708 | rk_clrreg(&grf->soc_con[4], BIT(14)); | |
709 | return 0; | |
710 | } | |
711 | ||
712 | /* | |
713 | * Otherwise, we need to check the clock-output-names of the | |
714 | * requested parent to see if the requested id is "gmac_clkin". | |
715 | */ | |
716 | ret = dev_read_string_index(parent->dev, "clock-output-names", | |
717 | parent->id, &clock_output_name); | |
718 | if (ret < 0) | |
719 | return -ENODATA; | |
720 | ||
721 | /* If this is "gmac_clkin", switch to the external clock input */ | |
722 | if (!strcmp(clock_output_name, "gmac_clkin")) { | |
723 | debug("%s: switching RGMII to CLKIN\n", __func__); | |
724 | rk_setreg(&grf->soc_con[4], BIT(14)); | |
725 | return 0; | |
726 | } | |
727 | ||
728 | return -EINVAL; | |
729 | } | |
730 | ||
731 | static int rk3328_clk_set_parent(struct clk *clk, struct clk *parent) | |
732 | { | |
733 | switch (clk->id) { | |
734 | case SCLK_MAC2IO: | |
735 | return rk3328_gmac2io_set_parent(clk, parent); | |
736 | case SCLK_MAC2IO_EXT: | |
737 | return rk3328_gmac2io_ext_set_parent(clk, parent); | |
738 | case DCLK_LCDC: | |
739 | case SCLK_PDM: | |
740 | case SCLK_RTC32K: | |
741 | case SCLK_UART0: | |
742 | case SCLK_UART1: | |
743 | case SCLK_UART2: | |
744 | return 0; | |
745 | } | |
746 | ||
747 | debug("%s: unsupported clk %ld\n", __func__, clk->id); | |
748 | return -ENOENT; | |
749 | } | |
750 | ||
41793000 KY |
751 | static struct clk_ops rk3328_clk_ops = { |
752 | .get_rate = rk3328_clk_get_rate, | |
753 | .set_rate = rk3328_clk_set_rate, | |
7cd4ebab | 754 | .set_parent = rk3328_clk_set_parent, |
41793000 KY |
755 | }; |
756 | ||
757 | static int rk3328_clk_probe(struct udevice *dev) | |
758 | { | |
759 | struct rk3328_clk_priv *priv = dev_get_priv(dev); | |
760 | ||
761 | rkclk_init(priv->cru); | |
762 | ||
763 | return 0; | |
764 | } | |
765 | ||
766 | static int rk3328_clk_ofdata_to_platdata(struct udevice *dev) | |
767 | { | |
768 | struct rk3328_clk_priv *priv = dev_get_priv(dev); | |
769 | ||
a821c4af | 770 | priv->cru = (struct rk3328_cru *)devfdt_get_addr(dev); |
41793000 KY |
771 | |
772 | return 0; | |
773 | } | |
774 | ||
775 | static int rk3328_clk_bind(struct udevice *dev) | |
776 | { | |
777 | int ret; | |
f24e36da KY |
778 | struct udevice *sys_child; |
779 | struct sysreset_reg *priv; | |
41793000 KY |
780 | |
781 | /* The reset driver does not have a device node, so bind it here */ | |
f24e36da KY |
782 | ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset", |
783 | &sys_child); | |
784 | if (ret) { | |
785 | debug("Warning: No sysreset driver: ret=%d\n", ret); | |
786 | } else { | |
787 | priv = malloc(sizeof(struct sysreset_reg)); | |
788 | priv->glb_srst_fst_value = offsetof(struct rk3328_cru, | |
789 | glb_srst_fst_value); | |
790 | priv->glb_srst_snd_value = offsetof(struct rk3328_cru, | |
791 | glb_srst_snd_value); | |
792 | sys_child->priv = priv; | |
793 | } | |
41793000 | 794 | |
538f67c3 EZ |
795 | #if CONFIG_IS_ENABLED(CONFIG_RESET_ROCKCHIP) |
796 | ret = offsetof(struct rk3328_cru, softrst_con[0]); | |
797 | ret = rockchip_reset_bind(dev, ret, 12); | |
798 | if (ret) | |
799 | debug("Warning: software reset driver bind faile\n"); | |
800 | #endif | |
801 | ||
41793000 KY |
802 | return ret; |
803 | } | |
804 | ||
805 | static const struct udevice_id rk3328_clk_ids[] = { | |
806 | { .compatible = "rockchip,rk3328-cru" }, | |
807 | { } | |
808 | }; | |
809 | ||
810 | U_BOOT_DRIVER(rockchip_rk3328_cru) = { | |
811 | .name = "rockchip_rk3328_cru", | |
812 | .id = UCLASS_CLK, | |
813 | .of_match = rk3328_clk_ids, | |
814 | .priv_auto_alloc_size = sizeof(struct rk3328_clk_priv), | |
815 | .ofdata_to_platdata = rk3328_clk_ofdata_to_platdata, | |
816 | .ops = &rk3328_clk_ops, | |
817 | .bind = rk3328_clk_bind, | |
818 | .probe = rk3328_clk_probe, | |
819 | }; |