]>
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> | |
8 | #include <clk-uclass.h> | |
9 | #include <dm.h> | |
10 | #include <errno.h> | |
11 | #include <syscon.h> | |
12 | #include <asm/arch/clock.h> | |
13 | #include <asm/arch/cru_rk3328.h> | |
14 | #include <asm/arch/hardware.h> | |
15 | #include <asm/io.h> | |
16 | #include <dm/lists.h> | |
17 | #include <dt-bindings/clock/rk3328-cru.h> | |
18 | ||
19 | DECLARE_GLOBAL_DATA_PTR; | |
20 | ||
21 | struct pll_div { | |
22 | u32 refdiv; | |
23 | u32 fbdiv; | |
24 | u32 postdiv1; | |
25 | u32 postdiv2; | |
26 | u32 frac; | |
27 | }; | |
28 | ||
29 | #define RATE_TO_DIV(input_rate, output_rate) \ | |
30 | ((input_rate) / (output_rate) - 1); | |
31 | #define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) | |
32 | ||
33 | #define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\ | |
34 | .refdiv = _refdiv,\ | |
35 | .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\ | |
36 | .postdiv1 = _postdiv1, .postdiv2 = _postdiv2}; | |
37 | ||
38 | static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 1, 4, 1); | |
39 | static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 2, 2, 1); | |
40 | ||
41 | static const struct pll_div apll_816_cfg = PLL_DIVISORS(816 * MHz, 1, 2, 1); | |
42 | static const struct pll_div apll_600_cfg = PLL_DIVISORS(600 * MHz, 1, 3, 1); | |
43 | ||
44 | static const struct pll_div *apll_cfgs[] = { | |
45 | [APLL_816_MHZ] = &apll_816_cfg, | |
46 | [APLL_600_MHZ] = &apll_600_cfg, | |
47 | }; | |
48 | ||
49 | enum { | |
50 | /* PLL_CON0 */ | |
51 | PLL_POSTDIV1_SHIFT = 12, | |
52 | PLL_POSTDIV1_MASK = 0x7 << PLL_POSTDIV1_SHIFT, | |
53 | PLL_FBDIV_SHIFT = 0, | |
54 | PLL_FBDIV_MASK = 0xfff, | |
55 | ||
56 | /* PLL_CON1 */ | |
57 | PLL_DSMPD_SHIFT = 12, | |
58 | PLL_DSMPD_MASK = 1 << PLL_DSMPD_SHIFT, | |
59 | PLL_INTEGER_MODE = 1, | |
60 | PLL_LOCK_STATUS_SHIFT = 10, | |
61 | PLL_LOCK_STATUS_MASK = 1 << PLL_LOCK_STATUS_SHIFT, | |
62 | PLL_POSTDIV2_SHIFT = 6, | |
63 | PLL_POSTDIV2_MASK = 0x7 << PLL_POSTDIV2_SHIFT, | |
64 | PLL_REFDIV_SHIFT = 0, | |
65 | PLL_REFDIV_MASK = 0x3f, | |
66 | ||
67 | /* PLL_CON2 */ | |
68 | PLL_FRACDIV_SHIFT = 0, | |
69 | PLL_FRACDIV_MASK = 0xffffff, | |
70 | ||
71 | /* MODE_CON */ | |
72 | APLL_MODE_SHIFT = 0, | |
73 | NPLL_MODE_SHIFT = 1, | |
74 | DPLL_MODE_SHIFT = 4, | |
75 | CPLL_MODE_SHIFT = 8, | |
76 | GPLL_MODE_SHIFT = 12, | |
77 | PLL_MODE_SLOW = 0, | |
78 | PLL_MODE_NORM, | |
79 | ||
80 | /* CLKSEL_CON0 */ | |
81 | CLK_CORE_PLL_SEL_APLL = 0, | |
82 | CLK_CORE_PLL_SEL_GPLL, | |
83 | CLK_CORE_PLL_SEL_DPLL, | |
84 | CLK_CORE_PLL_SEL_NPLL, | |
85 | CLK_CORE_PLL_SEL_SHIFT = 6, | |
86 | CLK_CORE_PLL_SEL_MASK = 3 << CLK_CORE_PLL_SEL_SHIFT, | |
87 | CLK_CORE_DIV_SHIFT = 0, | |
88 | CLK_CORE_DIV_MASK = 0x1f, | |
89 | ||
90 | /* CLKSEL_CON1 */ | |
91 | ACLKM_CORE_DIV_SHIFT = 4, | |
92 | ACLKM_CORE_DIV_MASK = 0x7 << ACLKM_CORE_DIV_SHIFT, | |
93 | PCLK_DBG_DIV_SHIFT = 0, | |
94 | PCLK_DBG_DIV_MASK = 0xF << PCLK_DBG_DIV_SHIFT, | |
95 | ||
96 | /* CLKSEL_CON28 */ | |
97 | ACLK_PERIHP_PLL_SEL_CPLL = 0, | |
98 | ACLK_PERIHP_PLL_SEL_GPLL, | |
99 | ACLK_PERIHP_PLL_SEL_HDMIPHY, | |
100 | ACLK_PERIHP_PLL_SEL_SHIFT = 6, | |
101 | ACLK_PERIHP_PLL_SEL_MASK = 3 << ACLK_PERIHP_PLL_SEL_SHIFT, | |
102 | ACLK_PERIHP_DIV_CON_SHIFT = 0, | |
103 | ACLK_PERIHP_DIV_CON_MASK = 0x1f, | |
104 | ||
105 | /* CLKSEL_CON29 */ | |
106 | PCLK_PERIHP_DIV_CON_SHIFT = 4, | |
107 | PCLK_PERIHP_DIV_CON_MASK = 0x7 << PCLK_PERIHP_DIV_CON_SHIFT, | |
108 | HCLK_PERIHP_DIV_CON_SHIFT = 0, | |
109 | HCLK_PERIHP_DIV_CON_MASK = 3 << HCLK_PERIHP_DIV_CON_SHIFT, | |
110 | ||
111 | /* CLKSEL_CON22 */ | |
112 | CLK_TSADC_DIV_CON_SHIFT = 0, | |
113 | CLK_TSADC_DIV_CON_MASK = 0x3ff, | |
114 | ||
115 | /* CLKSEL_CON23 */ | |
116 | CLK_SARADC_DIV_CON_SHIFT = 0, | |
117 | CLK_SARADC_DIV_CON_MASK = 0x3ff << CLK_SARADC_DIV_CON_SHIFT, | |
118 | ||
119 | /* CLKSEL_CON24 */ | |
120 | CLK_PWM_PLL_SEL_CPLL = 0, | |
121 | CLK_PWM_PLL_SEL_GPLL, | |
122 | CLK_PWM_PLL_SEL_SHIFT = 15, | |
123 | CLK_PWM_PLL_SEL_MASK = 1 << CLK_PWM_PLL_SEL_SHIFT, | |
124 | CLK_PWM_DIV_CON_SHIFT = 8, | |
125 | CLK_PWM_DIV_CON_MASK = 0x7f << CLK_PWM_DIV_CON_SHIFT, | |
126 | ||
127 | CLK_SPI_PLL_SEL_CPLL = 0, | |
128 | CLK_SPI_PLL_SEL_GPLL, | |
129 | CLK_SPI_PLL_SEL_SHIFT = 7, | |
130 | CLK_SPI_PLL_SEL_MASK = 1 << CLK_SPI_PLL_SEL_SHIFT, | |
131 | CLK_SPI_DIV_CON_SHIFT = 0, | |
132 | CLK_SPI_DIV_CON_MASK = 0x7f << CLK_SPI_DIV_CON_SHIFT, | |
133 | ||
134 | /* CLKSEL_CON30 */ | |
135 | CLK_SDMMC_PLL_SEL_CPLL = 0, | |
136 | CLK_SDMMC_PLL_SEL_GPLL, | |
137 | CLK_SDMMC_PLL_SEL_24M, | |
138 | CLK_SDMMC_PLL_SEL_USBPHY, | |
139 | CLK_SDMMC_PLL_SHIFT = 8, | |
140 | CLK_SDMMC_PLL_MASK = 0x3 << CLK_SDMMC_PLL_SHIFT, | |
141 | CLK_SDMMC_DIV_CON_SHIFT = 0, | |
142 | CLK_SDMMC_DIV_CON_MASK = 0xff << CLK_SDMMC_DIV_CON_SHIFT, | |
143 | ||
144 | /* CLKSEL_CON32 */ | |
145 | CLK_EMMC_PLL_SEL_CPLL = 0, | |
146 | CLK_EMMC_PLL_SEL_GPLL, | |
147 | CLK_EMMC_PLL_SEL_24M, | |
148 | CLK_EMMC_PLL_SEL_USBPHY, | |
149 | CLK_EMMC_PLL_SHIFT = 8, | |
150 | CLK_EMMC_PLL_MASK = 0x3 << CLK_EMMC_PLL_SHIFT, | |
151 | CLK_EMMC_DIV_CON_SHIFT = 0, | |
152 | CLK_EMMC_DIV_CON_MASK = 0xff << CLK_EMMC_DIV_CON_SHIFT, | |
153 | ||
154 | /* CLKSEL_CON34 */ | |
155 | CLK_I2C_PLL_SEL_CPLL = 0, | |
156 | CLK_I2C_PLL_SEL_GPLL, | |
157 | CLK_I2C_DIV_CON_MASK = 0x7f, | |
158 | CLK_I2C_PLL_SEL_MASK = 1, | |
159 | CLK_I2C1_PLL_SEL_SHIFT = 15, | |
160 | CLK_I2C1_DIV_CON_SHIFT = 8, | |
161 | CLK_I2C0_PLL_SEL_SHIFT = 7, | |
162 | CLK_I2C0_DIV_CON_SHIFT = 0, | |
163 | ||
164 | /* CLKSEL_CON35 */ | |
165 | CLK_I2C3_PLL_SEL_SHIFT = 15, | |
166 | CLK_I2C3_DIV_CON_SHIFT = 8, | |
167 | CLK_I2C2_PLL_SEL_SHIFT = 7, | |
168 | CLK_I2C2_DIV_CON_SHIFT = 0, | |
169 | }; | |
170 | ||
171 | #define VCO_MAX_KHZ (3200 * (MHz / KHz)) | |
172 | #define VCO_MIN_KHZ (800 * (MHz / KHz)) | |
173 | #define OUTPUT_MAX_KHZ (3200 * (MHz / KHz)) | |
174 | #define OUTPUT_MIN_KHZ (16 * (MHz / KHz)) | |
175 | ||
176 | /* | |
177 | * the div restructions of pll in integer mode, these are defined in | |
178 | * * CRU_*PLL_CON0 or PMUCRU_*PLL_CON0 | |
179 | */ | |
180 | #define PLL_DIV_MIN 16 | |
181 | #define PLL_DIV_MAX 3200 | |
182 | ||
183 | /* | |
184 | * How to calculate the PLL(from TRM V0.3 Part 1 Page 63): | |
185 | * Formulas also embedded within the Fractional PLL Verilog model: | |
186 | * If DSMPD = 1 (DSM is disabled, "integer mode") | |
187 | * FOUTVCO = FREF / REFDIV * FBDIV | |
188 | * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2 | |
189 | * Where: | |
190 | * FOUTVCO = Fractional PLL non-divided output frequency | |
191 | * FOUTPOSTDIV = Fractional PLL divided output frequency | |
192 | * (output of second post divider) | |
193 | * FREF = Fractional PLL input reference frequency, (the OSC_HZ 24MHz input) | |
194 | * REFDIV = Fractional PLL input reference clock divider | |
195 | * FBDIV = Integer value programmed into feedback divide | |
196 | * | |
197 | */ | |
198 | static void rkclk_set_pll(struct rk3328_cru *cru, enum rk_clk_id clk_id, | |
199 | const struct pll_div *div) | |
200 | { | |
201 | u32 *pll_con; | |
202 | u32 mode_shift, mode_mask; | |
203 | ||
204 | pll_con = NULL; | |
205 | mode_shift = 0; | |
206 | switch (clk_id) { | |
207 | case CLK_ARM: | |
208 | pll_con = cru->apll_con; | |
209 | mode_shift = APLL_MODE_SHIFT; | |
210 | break; | |
211 | case CLK_DDR: | |
212 | pll_con = cru->dpll_con; | |
213 | mode_shift = DPLL_MODE_SHIFT; | |
214 | break; | |
215 | case CLK_CODEC: | |
216 | pll_con = cru->cpll_con; | |
217 | mode_shift = CPLL_MODE_SHIFT; | |
218 | break; | |
219 | case CLK_GENERAL: | |
220 | pll_con = cru->gpll_con; | |
221 | mode_shift = GPLL_MODE_SHIFT; | |
222 | break; | |
223 | case CLK_NEW: | |
224 | pll_con = cru->npll_con; | |
225 | mode_shift = NPLL_MODE_SHIFT; | |
226 | break; | |
227 | default: | |
228 | break; | |
229 | } | |
230 | mode_mask = 1 << mode_shift; | |
231 | ||
232 | /* All 8 PLLs have same VCO and output frequency range restrictions. */ | |
233 | u32 vco_khz = OSC_HZ / 1000 * div->fbdiv / div->refdiv; | |
234 | u32 output_khz = vco_khz / div->postdiv1 / div->postdiv2; | |
235 | ||
236 | debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, \ | |
237 | postdiv2=%d, vco=%u khz, output=%u khz\n", | |
238 | pll_con, div->fbdiv, div->refdiv, div->postdiv1, | |
239 | div->postdiv2, vco_khz, output_khz); | |
240 | assert(vco_khz >= VCO_MIN_KHZ && vco_khz <= VCO_MAX_KHZ && | |
241 | output_khz >= OUTPUT_MIN_KHZ && output_khz <= OUTPUT_MAX_KHZ && | |
242 | div->fbdiv >= PLL_DIV_MIN && div->fbdiv <= PLL_DIV_MAX); | |
243 | ||
244 | /* | |
245 | * When power on or changing PLL setting, | |
246 | * we must force PLL into slow mode to ensure output stable clock. | |
247 | */ | |
248 | rk_clrsetreg(&cru->mode_con, mode_mask, PLL_MODE_SLOW << mode_shift); | |
249 | ||
250 | /* use integer mode */ | |
251 | rk_clrsetreg(&pll_con[1], PLL_DSMPD_MASK, | |
252 | PLL_INTEGER_MODE << PLL_DSMPD_SHIFT); | |
253 | ||
254 | rk_clrsetreg(&pll_con[0], | |
255 | PLL_FBDIV_MASK | PLL_POSTDIV1_MASK, | |
256 | (div->fbdiv << PLL_FBDIV_SHIFT) | | |
257 | (div->postdiv1 << PLL_POSTDIV1_SHIFT)); | |
258 | rk_clrsetreg(&pll_con[1], | |
259 | PLL_POSTDIV2_MASK | PLL_REFDIV_MASK, | |
260 | (div->postdiv2 << PLL_POSTDIV2_SHIFT) | | |
261 | (div->refdiv << PLL_REFDIV_SHIFT)); | |
262 | ||
263 | /* waiting for pll lock */ | |
264 | while (!(readl(&pll_con[1]) & (1 << PLL_LOCK_STATUS_SHIFT))) | |
265 | udelay(1); | |
266 | ||
267 | /* pll enter normal mode */ | |
268 | rk_clrsetreg(&cru->mode_con, mode_mask, PLL_MODE_NORM << mode_shift); | |
269 | } | |
270 | ||
271 | static void rkclk_init(struct rk3328_cru *cru) | |
272 | { | |
273 | u32 aclk_div; | |
274 | u32 hclk_div; | |
275 | u32 pclk_div; | |
276 | ||
277 | /* configure gpll cpll */ | |
278 | rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); | |
279 | rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg); | |
280 | ||
281 | /* configure perihp aclk, hclk, pclk */ | |
282 | aclk_div = GPLL_HZ / PERIHP_ACLK_HZ - 1; | |
283 | hclk_div = PERIHP_ACLK_HZ / PERIHP_HCLK_HZ - 1; | |
284 | pclk_div = PERIHP_ACLK_HZ / PERIHP_PCLK_HZ - 1; | |
285 | ||
286 | rk_clrsetreg(&cru->clksel_con[28], | |
287 | ACLK_PERIHP_PLL_SEL_MASK | ACLK_PERIHP_DIV_CON_MASK, | |
288 | ACLK_PERIHP_PLL_SEL_GPLL << ACLK_PERIHP_PLL_SEL_SHIFT | | |
289 | aclk_div << ACLK_PERIHP_DIV_CON_SHIFT); | |
290 | rk_clrsetreg(&cru->clksel_con[29], | |
291 | PCLK_PERIHP_DIV_CON_MASK | HCLK_PERIHP_DIV_CON_MASK, | |
292 | pclk_div << PCLK_PERIHP_DIV_CON_SHIFT | | |
293 | hclk_div << HCLK_PERIHP_DIV_CON_SHIFT); | |
294 | } | |
295 | ||
296 | void rk3328_configure_cpu(struct rk3328_cru *cru, | |
297 | enum apll_frequencies apll_freq) | |
298 | { | |
299 | u32 clk_core_div; | |
300 | u32 aclkm_div; | |
301 | u32 pclk_dbg_div; | |
302 | ||
303 | rkclk_set_pll(cru, CLK_ARM, apll_cfgs[apll_freq]); | |
304 | ||
305 | clk_core_div = APLL_HZ / CLK_CORE_HZ - 1; | |
306 | aclkm_div = APLL_HZ / ACLKM_CORE_HZ / (clk_core_div + 1) - 1; | |
307 | pclk_dbg_div = APLL_HZ / PCLK_DBG_HZ / (clk_core_div + 1) - 1; | |
308 | ||
309 | rk_clrsetreg(&cru->clksel_con[0], | |
310 | CLK_CORE_PLL_SEL_MASK | CLK_CORE_DIV_MASK, | |
311 | CLK_CORE_PLL_SEL_APLL << CLK_CORE_PLL_SEL_SHIFT | | |
312 | clk_core_div << CLK_CORE_DIV_SHIFT); | |
313 | ||
314 | rk_clrsetreg(&cru->clksel_con[1], | |
315 | PCLK_DBG_DIV_MASK | ACLKM_CORE_DIV_MASK, | |
316 | pclk_dbg_div << PCLK_DBG_DIV_SHIFT | | |
317 | aclkm_div << ACLKM_CORE_DIV_SHIFT); | |
318 | } | |
319 | ||
320 | ||
321 | static ulong rk3328_i2c_get_clk(struct rk3328_cru *cru, ulong clk_id) | |
322 | { | |
323 | u32 div, con; | |
324 | ||
325 | switch (clk_id) { | |
326 | case SCLK_I2C0: | |
327 | con = readl(&cru->clksel_con[34]); | |
328 | div = con >> CLK_I2C0_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
329 | break; | |
330 | case SCLK_I2C1: | |
331 | con = readl(&cru->clksel_con[34]); | |
332 | div = con >> CLK_I2C1_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
333 | break; | |
334 | case SCLK_I2C2: | |
335 | con = readl(&cru->clksel_con[35]); | |
336 | div = con >> CLK_I2C2_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
337 | break; | |
338 | case SCLK_I2C3: | |
339 | con = readl(&cru->clksel_con[35]); | |
340 | div = con >> CLK_I2C3_DIV_CON_SHIFT & CLK_I2C_DIV_CON_MASK; | |
341 | break; | |
342 | default: | |
343 | printf("do not support this i2c bus\n"); | |
344 | return -EINVAL; | |
345 | } | |
346 | ||
347 | return DIV_TO_RATE(GPLL_HZ, div); | |
348 | } | |
349 | ||
350 | static ulong rk3328_i2c_set_clk(struct rk3328_cru *cru, ulong clk_id, uint hz) | |
351 | { | |
352 | int src_clk_div; | |
353 | ||
354 | src_clk_div = GPLL_HZ / hz; | |
355 | assert(src_clk_div - 1 < 127); | |
356 | ||
357 | switch (clk_id) { | |
358 | case SCLK_I2C0: | |
359 | rk_clrsetreg(&cru->clksel_con[34], | |
360 | CLK_I2C_DIV_CON_MASK << CLK_I2C0_DIV_CON_SHIFT | | |
361 | CLK_I2C_PLL_SEL_MASK << CLK_I2C0_PLL_SEL_SHIFT, | |
362 | (src_clk_div - 1) << CLK_I2C0_DIV_CON_SHIFT | | |
363 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C0_PLL_SEL_SHIFT); | |
364 | break; | |
365 | case SCLK_I2C1: | |
366 | rk_clrsetreg(&cru->clksel_con[34], | |
367 | CLK_I2C_DIV_CON_MASK << CLK_I2C1_DIV_CON_SHIFT | | |
368 | CLK_I2C_PLL_SEL_MASK << CLK_I2C1_PLL_SEL_SHIFT, | |
369 | (src_clk_div - 1) << CLK_I2C1_DIV_CON_SHIFT | | |
370 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C1_PLL_SEL_SHIFT); | |
371 | break; | |
372 | case SCLK_I2C2: | |
373 | rk_clrsetreg(&cru->clksel_con[35], | |
374 | CLK_I2C_DIV_CON_MASK << CLK_I2C2_DIV_CON_SHIFT | | |
375 | CLK_I2C_PLL_SEL_MASK << CLK_I2C2_PLL_SEL_SHIFT, | |
376 | (src_clk_div - 1) << CLK_I2C2_DIV_CON_SHIFT | | |
377 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C2_PLL_SEL_SHIFT); | |
378 | break; | |
379 | case SCLK_I2C3: | |
380 | rk_clrsetreg(&cru->clksel_con[35], | |
381 | CLK_I2C_DIV_CON_MASK << CLK_I2C3_DIV_CON_SHIFT | | |
382 | CLK_I2C_PLL_SEL_MASK << CLK_I2C3_PLL_SEL_SHIFT, | |
383 | (src_clk_div - 1) << CLK_I2C3_DIV_CON_SHIFT | | |
384 | CLK_I2C_PLL_SEL_GPLL << CLK_I2C3_PLL_SEL_SHIFT); | |
385 | break; | |
386 | default: | |
387 | printf("do not support this i2c bus\n"); | |
388 | return -EINVAL; | |
389 | } | |
390 | ||
391 | return DIV_TO_RATE(GPLL_HZ, src_clk_div); | |
392 | } | |
393 | ||
394 | static ulong rk3328_mmc_get_clk(struct rk3328_cru *cru, uint clk_id) | |
395 | { | |
396 | u32 div, con, con_id; | |
397 | ||
398 | switch (clk_id) { | |
399 | case HCLK_SDMMC: | |
85c91cb6 | 400 | case SCLK_SDMMC: |
41793000 KY |
401 | con_id = 30; |
402 | break; | |
403 | case HCLK_EMMC: | |
85c91cb6 | 404 | case SCLK_EMMC: |
41793000 KY |
405 | con_id = 32; |
406 | break; | |
407 | default: | |
408 | return -EINVAL; | |
409 | } | |
410 | con = readl(&cru->clksel_con[con_id]); | |
411 | div = (con & CLK_EMMC_DIV_CON_MASK) >> CLK_EMMC_DIV_CON_SHIFT; | |
412 | ||
413 | if ((con & CLK_EMMC_PLL_MASK) >> CLK_EMMC_PLL_SHIFT | |
414 | == CLK_EMMC_PLL_SEL_24M) | |
415 | return DIV_TO_RATE(OSC_HZ, div); | |
416 | else | |
417 | return DIV_TO_RATE(GPLL_HZ, div); | |
418 | } | |
419 | ||
420 | static ulong rk3328_mmc_set_clk(struct rk3328_cru *cru, | |
421 | ulong clk_id, ulong set_rate) | |
422 | { | |
423 | int src_clk_div; | |
424 | u32 con_id; | |
425 | ||
426 | switch (clk_id) { | |
427 | case HCLK_SDMMC: | |
85c91cb6 | 428 | case SCLK_SDMMC: |
41793000 KY |
429 | con_id = 30; |
430 | break; | |
431 | case HCLK_EMMC: | |
85c91cb6 | 432 | case SCLK_EMMC: |
41793000 KY |
433 | con_id = 32; |
434 | break; | |
435 | default: | |
436 | return -EINVAL; | |
437 | } | |
438 | /* Select clk_sdmmc/emmc source from GPLL by default */ | |
439 | src_clk_div = GPLL_HZ / set_rate; | |
440 | ||
441 | if (src_clk_div > 127) { | |
442 | /* use 24MHz source for 400KHz clock */ | |
443 | src_clk_div = OSC_HZ / set_rate; | |
444 | rk_clrsetreg(&cru->clksel_con[con_id], | |
445 | CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, | |
446 | CLK_EMMC_PLL_SEL_24M << CLK_EMMC_PLL_SHIFT | | |
447 | (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT); | |
448 | } else { | |
449 | rk_clrsetreg(&cru->clksel_con[con_id], | |
450 | CLK_EMMC_PLL_MASK | CLK_EMMC_DIV_CON_MASK, | |
451 | CLK_EMMC_PLL_SEL_GPLL << CLK_EMMC_PLL_SHIFT | | |
452 | (src_clk_div - 1) << CLK_EMMC_DIV_CON_SHIFT); | |
453 | } | |
454 | ||
455 | return rk3328_mmc_get_clk(cru, clk_id); | |
456 | } | |
457 | ||
458 | static ulong rk3328_pwm_get_clk(struct rk3328_cru *cru) | |
459 | { | |
460 | u32 div, con; | |
461 | ||
462 | con = readl(&cru->clksel_con[24]); | |
463 | div = (con & CLK_PWM_DIV_CON_MASK) >> CLK_PWM_DIV_CON_SHIFT; | |
464 | ||
465 | return DIV_TO_RATE(GPLL_HZ, div); | |
466 | } | |
467 | ||
468 | static ulong rk3328_pwm_set_clk(struct rk3328_cru *cru, uint hz) | |
469 | { | |
470 | u32 div = GPLL_HZ / hz; | |
471 | ||
472 | rk_clrsetreg(&cru->clksel_con[24], | |
473 | CLK_PWM_PLL_SEL_MASK | CLK_PWM_DIV_CON_MASK, | |
474 | CLK_PWM_PLL_SEL_GPLL << CLK_PWM_PLL_SEL_SHIFT | | |
475 | (div - 1) << CLK_PWM_DIV_CON_SHIFT); | |
476 | ||
477 | return DIV_TO_RATE(GPLL_HZ, div); | |
478 | } | |
479 | ||
480 | static ulong rk3328_clk_get_rate(struct clk *clk) | |
481 | { | |
482 | struct rk3328_clk_priv *priv = dev_get_priv(clk->dev); | |
483 | ulong rate = 0; | |
484 | ||
485 | switch (clk->id) { | |
486 | case 0 ... 29: | |
487 | return 0; | |
488 | case HCLK_SDMMC: | |
489 | case HCLK_EMMC: | |
85c91cb6 XZ |
490 | case SCLK_SDMMC: |
491 | case SCLK_EMMC: | |
41793000 KY |
492 | rate = rk3328_mmc_get_clk(priv->cru, clk->id); |
493 | break; | |
494 | case SCLK_I2C0: | |
495 | case SCLK_I2C1: | |
496 | case SCLK_I2C2: | |
497 | case SCLK_I2C3: | |
498 | rate = rk3328_i2c_get_clk(priv->cru, clk->id); | |
499 | break; | |
500 | case SCLK_PWM: | |
501 | rate = rk3328_pwm_get_clk(priv->cru); | |
502 | break; | |
503 | default: | |
504 | return -ENOENT; | |
505 | } | |
506 | ||
507 | return rate; | |
508 | } | |
509 | ||
510 | static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate) | |
511 | { | |
512 | struct rk3328_clk_priv *priv = dev_get_priv(clk->dev); | |
513 | ulong ret = 0; | |
514 | ||
515 | switch (clk->id) { | |
516 | case 0 ... 29: | |
517 | return 0; | |
518 | case HCLK_SDMMC: | |
519 | case HCLK_EMMC: | |
85c91cb6 XZ |
520 | case SCLK_SDMMC: |
521 | case SCLK_EMMC: | |
41793000 KY |
522 | ret = rk3328_mmc_set_clk(priv->cru, clk->id, rate); |
523 | break; | |
524 | case SCLK_I2C0: | |
525 | case SCLK_I2C1: | |
526 | case SCLK_I2C2: | |
527 | case SCLK_I2C3: | |
528 | ret = rk3328_i2c_set_clk(priv->cru, clk->id, rate); | |
529 | break; | |
530 | case SCLK_PWM: | |
531 | ret = rk3328_pwm_set_clk(priv->cru, rate); | |
532 | break; | |
533 | default: | |
534 | return -ENOENT; | |
535 | } | |
536 | ||
537 | return ret; | |
538 | } | |
539 | ||
540 | static struct clk_ops rk3328_clk_ops = { | |
541 | .get_rate = rk3328_clk_get_rate, | |
542 | .set_rate = rk3328_clk_set_rate, | |
543 | }; | |
544 | ||
545 | static int rk3328_clk_probe(struct udevice *dev) | |
546 | { | |
547 | struct rk3328_clk_priv *priv = dev_get_priv(dev); | |
548 | ||
549 | rkclk_init(priv->cru); | |
550 | ||
551 | return 0; | |
552 | } | |
553 | ||
554 | static int rk3328_clk_ofdata_to_platdata(struct udevice *dev) | |
555 | { | |
556 | struct rk3328_clk_priv *priv = dev_get_priv(dev); | |
557 | ||
a821c4af | 558 | priv->cru = (struct rk3328_cru *)devfdt_get_addr(dev); |
41793000 KY |
559 | |
560 | return 0; | |
561 | } | |
562 | ||
563 | static int rk3328_clk_bind(struct udevice *dev) | |
564 | { | |
565 | int ret; | |
566 | ||
567 | /* The reset driver does not have a device node, so bind it here */ | |
568 | ret = device_bind_driver(gd->dm_root, "rk3328_sysreset", "reset", &dev); | |
569 | if (ret) | |
570 | printf("Warning: No RK3328 reset driver: ret=%d\n", ret); | |
571 | ||
572 | return ret; | |
573 | } | |
574 | ||
575 | static const struct udevice_id rk3328_clk_ids[] = { | |
576 | { .compatible = "rockchip,rk3328-cru" }, | |
577 | { } | |
578 | }; | |
579 | ||
580 | U_BOOT_DRIVER(rockchip_rk3328_cru) = { | |
581 | .name = "rockchip_rk3328_cru", | |
582 | .id = UCLASS_CLK, | |
583 | .of_match = rk3328_clk_ids, | |
584 | .priv_auto_alloc_size = sizeof(struct rk3328_clk_priv), | |
585 | .ofdata_to_platdata = rk3328_clk_ofdata_to_platdata, | |
586 | .ops = &rk3328_clk_ops, | |
587 | .bind = rk3328_clk_bind, | |
588 | .probe = rk3328_clk_probe, | |
589 | }; |