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