blob: 8899b0c62db779947963a17d12136808490f40d9 [file] [log] [blame]
huang lin01aa7022015-11-17 14:20:16 +08001/*
2 * (C) Copyright 2015 Google, Inc
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6
7#include <common.h>
Stephen Warrena9622432016-06-17 09:44:00 -06008#include <clk-uclass.h>
huang lin01aa7022015-11-17 14:20:16 +08009#include <dm.h>
10#include <errno.h>
11#include <syscon.h>
12#include <asm/io.h>
13#include <asm/arch/clock.h>
14#include <asm/arch/cru_rk3036.h>
15#include <asm/arch/hardware.h>
huang lin01aa7022015-11-17 14:20:16 +080016#include <dm/lists.h>
Simon Glass8d32f4b2016-01-21 19:43:38 -070017#include <dt-bindings/clock/rk3036-cru.h>
Heiko Stübner1b7dcc32016-07-22 23:51:06 +020018#include <linux/log2.h>
huang lin01aa7022015-11-17 14:20:16 +080019
20DECLARE_GLOBAL_DATA_PTR;
21
huang lin01aa7022015-11-17 14:20:16 +080022struct rk3036_clk_priv {
23 struct rk3036_cru *cru;
24 ulong rate;
25};
26
27enum {
28 VCO_MAX_HZ = 2400U * 1000000,
29 VCO_MIN_HZ = 600 * 1000000,
30 OUTPUT_MAX_HZ = 2400U * 1000000,
31 OUTPUT_MIN_HZ = 24 * 1000000,
32};
33
34#define RATE_TO_DIV(input_rate, output_rate) \
35 ((input_rate) / (output_rate) - 1);
36
37#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
38
39#define PLL_DIVISORS(hz, _refdiv, _postdiv1, _postdiv2) {\
40 .refdiv = _refdiv,\
41 .fbdiv = (u32)((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ),\
42 .postdiv1 = _postdiv1, .postdiv2 = _postdiv2};\
43 _Static_assert(((u64)hz * _refdiv * _postdiv1 * _postdiv2 / OSC_HZ) *\
44 OSC_HZ / (_refdiv * _postdiv1 * _postdiv2) == hz,\
45 #hz "Hz cannot be hit with PLL "\
46 "divisors on line " __stringify(__LINE__));
47
48/* use interge mode*/
49static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 3, 1);
50static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2, 1);
51
Simon Glass410d45c2016-01-21 19:44:05 -070052void *rockchip_get_cru(void)
53{
54 struct udevice *dev;
55 fdt_addr_t addr;
56 int ret;
57
58 ret = uclass_get_device(UCLASS_CLK, 0, &dev);
59 if (ret)
60 return ERR_PTR(ret);
61
62 addr = dev_get_addr(dev);
63 if (addr == FDT_ADDR_T_NONE)
64 return ERR_PTR(-EINVAL);
65
66 return (void *)addr;
67}
68
huang lin01aa7022015-11-17 14:20:16 +080069static int rkclk_set_pll(struct rk3036_cru *cru, enum rk_clk_id clk_id,
70 const struct pll_div *div)
71{
72 int pll_id = rk_pll_id(clk_id);
73 struct rk3036_pll *pll = &cru->pll[pll_id];
74
75 /* All PLLs have same VCO and output frequency range restrictions. */
76 uint vco_hz = OSC_HZ / 1000 * div->fbdiv / div->refdiv * 1000;
77 uint output_hz = vco_hz / div->postdiv1 / div->postdiv2;
78
79 debug("PLL at %p: fbdiv=%d, refdiv=%d, postdiv1=%d, postdiv2=%d,\
80 vco=%u Hz, output=%u Hz\n",
81 pll, div->fbdiv, div->refdiv, div->postdiv1,
82 div->postdiv2, vco_hz, output_hz);
83 assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
84 output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ);
85
86 /* use interger mode */
87 rk_clrreg(&pll->con1, 1 << PLL_DSMPD_SHIFT);
88
89 rk_clrsetreg(&pll->con0,
90 PLL_POSTDIV1_MASK << PLL_POSTDIV1_SHIFT | PLL_FBDIV_MASK,
91 (div->postdiv1 << PLL_POSTDIV1_SHIFT) | div->fbdiv);
92 rk_clrsetreg(&pll->con1, PLL_POSTDIV2_MASK << PLL_POSTDIV2_SHIFT |
93 PLL_REFDIV_MASK << PLL_REFDIV_SHIFT,
94 (div->postdiv2 << PLL_POSTDIV2_SHIFT |
95 div->refdiv << PLL_REFDIV_SHIFT));
96
97 /* waiting for pll lock */
98 while (readl(&pll->con1) & (1 << PLL_LOCK_STATUS_SHIFT))
99 udelay(1);
100
101 return 0;
102}
103
104static void rkclk_init(struct rk3036_cru *cru)
105{
106 u32 aclk_div;
107 u32 hclk_div;
108 u32 pclk_div;
109
110 /* pll enter slow-mode */
111 rk_clrsetreg(&cru->cru_mode_con,
112 GPLL_MODE_MASK << GPLL_MODE_SHIFT |
113 APLL_MODE_MASK << APLL_MODE_SHIFT,
114 GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
115 APLL_MODE_SLOW << APLL_MODE_SHIFT);
116
117 /* init pll */
118 rkclk_set_pll(cru, CLK_ARM, &apll_init_cfg);
119 rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg);
120
121 /*
122 * select apll as core clock pll source and
123 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
124 * core hz : apll = 1:1
125 */
126 aclk_div = APLL_HZ / CORE_ACLK_HZ - 1;
127 assert((aclk_div + 1) * CORE_ACLK_HZ == APLL_HZ && aclk_div < 0x7);
128
129 pclk_div = APLL_HZ / CORE_PERI_HZ - 1;
130 assert((pclk_div + 1) * CORE_PERI_HZ == APLL_HZ && pclk_div < 0xf);
131
132 rk_clrsetreg(&cru->cru_clksel_con[0],
133 CORE_CLK_PLL_SEL_MASK << CORE_CLK_PLL_SEL_SHIFT |
134 CORE_DIV_CON_MASK << CORE_DIV_CON_SHIFT,
135 CORE_CLK_PLL_SEL_APLL << CORE_CLK_PLL_SEL_SHIFT |
136 0 << CORE_DIV_CON_SHIFT);
137
138 rk_clrsetreg(&cru->cru_clksel_con[1],
139 CORE_ACLK_DIV_MASK << CORE_ACLK_DIV_SHIFT |
140 CORE_PERI_DIV_MASK << CORE_PERI_DIV_SHIFT,
141 aclk_div << CORE_ACLK_DIV_SHIFT |
142 pclk_div << CORE_PERI_DIV_SHIFT);
143
144 /*
145 * select apll as cpu clock pll source and
146 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
147 */
148 aclk_div = APLL_HZ / CPU_ACLK_HZ - 1;
149 assert((aclk_div + 1) * CPU_ACLK_HZ == APLL_HZ && aclk_div < 0x1f);
150
151 pclk_div = APLL_HZ / CPU_PCLK_HZ - 1;
152 assert((pclk_div + 1) * CPU_PCLK_HZ == APLL_HZ && pclk_div < 0x7);
153
154 hclk_div = APLL_HZ / CPU_HCLK_HZ - 1;
155 assert((hclk_div + 1) * CPU_HCLK_HZ == APLL_HZ && hclk_div < 0x3);
156
157 rk_clrsetreg(&cru->cru_clksel_con[0],
158 CPU_CLK_PLL_SEL_MASK << CPU_CLK_PLL_SEL_SHIFT |
159 ACLK_CPU_DIV_MASK << ACLK_CPU_DIV_SHIFT,
160 CPU_CLK_PLL_SEL_APLL << CPU_CLK_PLL_SEL_SHIFT |
161 aclk_div << ACLK_CPU_DIV_SHIFT);
162
163 rk_clrsetreg(&cru->cru_clksel_con[1],
164 CPU_PCLK_DIV_MASK << CPU_PCLK_DIV_SHIFT |
165 CPU_HCLK_DIV_MASK << CPU_HCLK_DIV_SHIFT,
166 pclk_div << CPU_PCLK_DIV_SHIFT |
167 hclk_div << CPU_HCLK_DIV_SHIFT);
168
169 /*
170 * select gpll as peri clock pll source and
171 * set up dependent divisors for PCLK/HCLK and ACLK clocks.
172 */
173 aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
174 assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
175
Heiko Stübner1b7dcc32016-07-22 23:51:06 +0200176 hclk_div = ilog2(PERI_ACLK_HZ / PERI_HCLK_HZ);
huang lin01aa7022015-11-17 14:20:16 +0800177 assert((1 << hclk_div) * PERI_HCLK_HZ ==
178 PERI_ACLK_HZ && (pclk_div < 0x4));
179
Heiko Stübner1b7dcc32016-07-22 23:51:06 +0200180 pclk_div = ilog2(PERI_ACLK_HZ / PERI_PCLK_HZ);
huang lin01aa7022015-11-17 14:20:16 +0800181 assert((1 << pclk_div) * PERI_PCLK_HZ ==
182 PERI_ACLK_HZ && pclk_div < 0x8);
183
184 rk_clrsetreg(&cru->cru_clksel_con[10],
185 PERI_PLL_SEL_MASK << PERI_PLL_SEL_SHIFT |
186 PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT |
187 PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT |
188 PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT,
189 PERI_PLL_GPLL << PERI_PLL_SEL_SHIFT |
190 pclk_div << PERI_PCLK_DIV_SHIFT |
191 hclk_div << PERI_HCLK_DIV_SHIFT |
192 aclk_div << PERI_ACLK_DIV_SHIFT);
193
194 /* PLL enter normal-mode */
195 rk_clrsetreg(&cru->cru_mode_con,
196 GPLL_MODE_MASK << GPLL_MODE_SHIFT |
197 APLL_MODE_MASK << APLL_MODE_SHIFT,
198 GPLL_MODE_NORM << GPLL_MODE_SHIFT |
199 APLL_MODE_NORM << APLL_MODE_SHIFT);
200}
201
202/* Get pll rate by id */
203static uint32_t rkclk_pll_get_rate(struct rk3036_cru *cru,
204 enum rk_clk_id clk_id)
205{
206 uint32_t refdiv, fbdiv, postdiv1, postdiv2;
207 uint32_t con;
208 int pll_id = rk_pll_id(clk_id);
209 struct rk3036_pll *pll = &cru->pll[pll_id];
210 static u8 clk_shift[CLK_COUNT] = {
211 0xff, APLL_MODE_SHIFT, DPLL_MODE_SHIFT, 0xff,
212 GPLL_MODE_SHIFT, 0xff
213 };
214 static u8 clk_mask[CLK_COUNT] = {
215 0xff, APLL_MODE_MASK, DPLL_MODE_MASK, 0xff,
216 GPLL_MODE_MASK, 0xff
217 };
218 uint shift;
219 uint mask;
220
221 con = readl(&cru->cru_mode_con);
222 shift = clk_shift[clk_id];
223 mask = clk_mask[clk_id];
224
225 switch ((con >> shift) & mask) {
226 case GPLL_MODE_SLOW:
227 return OSC_HZ;
228 case GPLL_MODE_NORM:
229
230 /* normal mode */
231 con = readl(&pll->con0);
232 postdiv1 = (con >> PLL_POSTDIV1_SHIFT) & PLL_POSTDIV1_MASK;
233 fbdiv = (con >> PLL_FBDIV_SHIFT) & PLL_FBDIV_MASK;
234 con = readl(&pll->con1);
235 postdiv2 = (con >> PLL_POSTDIV2_SHIFT) & PLL_POSTDIV2_MASK;
236 refdiv = (con >> PLL_REFDIV_SHIFT) & PLL_REFDIV_MASK;
237 return (24 * fbdiv / (refdiv * postdiv1 * postdiv2)) * 1000000;
238 case GPLL_MODE_DEEP:
239 default:
240 return 32768;
241 }
242}
243
244static ulong rockchip_mmc_get_clk(struct rk3036_cru *cru, uint clk_general_rate,
Simon Glass8d32f4b2016-01-21 19:43:38 -0700245 int periph)
huang lin01aa7022015-11-17 14:20:16 +0800246{
247 uint src_rate;
248 uint div, mux;
249 u32 con;
250
251 switch (periph) {
Simon Glass8d32f4b2016-01-21 19:43:38 -0700252 case HCLK_EMMC:
huang lin01aa7022015-11-17 14:20:16 +0800253 con = readl(&cru->cru_clksel_con[12]);
254 mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK;
255 div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
256 break;
Simon Glass8d32f4b2016-01-21 19:43:38 -0700257 case HCLK_SDIO:
huang lin01aa7022015-11-17 14:20:16 +0800258 con = readl(&cru->cru_clksel_con[12]);
259 mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK;
260 div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK;
261 break;
262 default:
263 return -EINVAL;
264 }
265
266 src_rate = mux == EMMC_SEL_24M ? OSC_HZ : clk_general_rate;
267 return DIV_TO_RATE(src_rate, div);
268}
269
270static ulong rockchip_mmc_set_clk(struct rk3036_cru *cru, uint clk_general_rate,
Simon Glass8d32f4b2016-01-21 19:43:38 -0700271 int periph, uint freq)
huang lin01aa7022015-11-17 14:20:16 +0800272{
273 int src_clk_div;
274 int mux;
275
276 debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate);
277
278 /* mmc clock auto divide 2 in internal */
279 src_clk_div = (clk_general_rate / 2 + freq - 1) / freq;
280
281 if (src_clk_div > 0x7f) {
282 src_clk_div = (OSC_HZ / 2 + freq - 1) / freq;
283 mux = EMMC_SEL_24M;
284 } else {
285 mux = EMMC_SEL_GPLL;
286 }
287
288 switch (periph) {
Simon Glass8d32f4b2016-01-21 19:43:38 -0700289 case HCLK_EMMC:
huang lin01aa7022015-11-17 14:20:16 +0800290 rk_clrsetreg(&cru->cru_clksel_con[12],
291 EMMC_PLL_MASK << EMMC_PLL_SHIFT |
292 EMMC_DIV_MASK << EMMC_DIV_SHIFT,
293 mux << EMMC_PLL_SHIFT |
294 (src_clk_div - 1) << EMMC_DIV_SHIFT);
295 break;
Simon Glass8d32f4b2016-01-21 19:43:38 -0700296 case HCLK_SDIO:
huang lin01aa7022015-11-17 14:20:16 +0800297 rk_clrsetreg(&cru->cru_clksel_con[11],
298 MMC0_PLL_MASK << MMC0_PLL_SHIFT |
299 MMC0_DIV_MASK << MMC0_DIV_SHIFT,
300 mux << MMC0_PLL_SHIFT |
301 (src_clk_div - 1) << MMC0_DIV_SHIFT);
302 break;
303 default:
304 return -EINVAL;
305 }
306
307 return rockchip_mmc_get_clk(cru, clk_general_rate, periph);
308}
309
Stephen Warrena9622432016-06-17 09:44:00 -0600310static ulong rk3036_clk_get_rate(struct clk *clk)
huang lin01aa7022015-11-17 14:20:16 +0800311{
Stephen Warrena9622432016-06-17 09:44:00 -0600312 struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
huang lin01aa7022015-11-17 14:20:16 +0800313
Stephen Warrena9622432016-06-17 09:44:00 -0600314 switch (clk->id) {
315 case 0 ... 63:
316 return rkclk_pll_get_rate(priv->cru, clk->id);
317 default:
318 return -ENOENT;
319 }
huang lin01aa7022015-11-17 14:20:16 +0800320}
321
Stephen Warrena9622432016-06-17 09:44:00 -0600322static ulong rk3036_clk_set_rate(struct clk *clk, ulong rate)
huang lin01aa7022015-11-17 14:20:16 +0800323{
Stephen Warrena9622432016-06-17 09:44:00 -0600324 struct rk3036_clk_priv *priv = dev_get_priv(clk->dev);
325 ulong new_rate, gclk_rate;
huang lin01aa7022015-11-17 14:20:16 +0800326
Stephen Warrena9622432016-06-17 09:44:00 -0600327 gclk_rate = rkclk_pll_get_rate(priv->cru, CLK_GENERAL);
328 switch (clk->id) {
329 case 0 ... 63:
330 return 0;
Simon Glass8d32f4b2016-01-21 19:43:38 -0700331 case HCLK_EMMC:
Stephen Warrena9622432016-06-17 09:44:00 -0600332 new_rate = rockchip_mmc_set_clk(priv->cru, gclk_rate,
333 clk->id, rate);
huang lin01aa7022015-11-17 14:20:16 +0800334 break;
335 default:
336 return -ENOENT;
337 }
338
339 return new_rate;
340}
341
342static struct clk_ops rk3036_clk_ops = {
343 .get_rate = rk3036_clk_get_rate,
344 .set_rate = rk3036_clk_set_rate,
huang lin01aa7022015-11-17 14:20:16 +0800345};
346
347static int rk3036_clk_probe(struct udevice *dev)
348{
huang lin01aa7022015-11-17 14:20:16 +0800349 struct rk3036_clk_priv *priv = dev_get_priv(dev);
350
huang lin01aa7022015-11-17 14:20:16 +0800351 priv->cru = (struct rk3036_cru *)dev_get_addr(dev);
352 rkclk_init(priv->cru);
353
354 return 0;
355}
356
huang lin01aa7022015-11-17 14:20:16 +0800357static int rk3036_clk_bind(struct udevice *dev)
358{
Stephen Warrena9622432016-06-17 09:44:00 -0600359 int ret;
huang lin01aa7022015-11-17 14:20:16 +0800360
361 /* The reset driver does not have a device node, so bind it here */
Stephen Warren859f2562016-05-12 12:03:35 -0600362 ret = device_bind_driver(gd->dm_root, "rk3036_sysreset", "reset", &dev);
huang lin01aa7022015-11-17 14:20:16 +0800363 if (ret)
364 debug("Warning: No RK3036 reset driver: ret=%d\n", ret);
365
366 return 0;
367}
368
369static const struct udevice_id rk3036_clk_ids[] = {
370 { .compatible = "rockchip,rk3036-cru" },
371 { }
372};
373
374U_BOOT_DRIVER(clk_rk3036) = {
375 .name = "clk_rk3036",
376 .id = UCLASS_CLK,
377 .of_match = rk3036_clk_ids,
378 .priv_auto_alloc_size = sizeof(struct rk3036_clk_priv),
huang lin01aa7022015-11-17 14:20:16 +0800379 .ops = &rk3036_clk_ops,
380 .bind = rk3036_clk_bind,
381 .probe = rk3036_clk_probe,
382};