clk: mediatek: add support to configure clock driver parent
[platform/kernel/u-boot.git] / drivers / clk / clk_pic32.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
4  *
5  */
6
7 #include <common.h>
8 #include <clk-uclass.h>
9 #include <dm.h>
10 #include <div64.h>
11 #include <time.h>
12 #include <wait_bit.h>
13 #include <asm/global_data.h>
14 #include <dm/lists.h>
15 #include <asm/io.h>
16 #include <linux/bitops.h>
17 #include <linux/bug.h>
18 #include <mach/pic32.h>
19 #include <dt-bindings/clock/microchip,clock.h>
20
21 DECLARE_GLOBAL_DATA_PTR;
22
23 /* Primary oscillator */
24 #define SYS_POSC_CLK_HZ 24000000
25
26 /* FRC clk rate */
27 #define SYS_FRC_CLK_HZ  8000000
28
29 /* Clock Registers */
30 #define OSCCON          0x0000
31 #define OSCTUNE         0x0010
32 #define SPLLCON         0x0020
33 #define REFO1CON        0x0080
34 #define REFO1TRIM       0x0090
35 #define PB1DIV          0x0140
36
37 /* SPLL */
38 #define ICLK_MASK       0x00000080
39 #define PLLIDIV_MASK    0x00000007
40 #define PLLODIV_MASK    0x00000007
41 #define CUROSC_MASK     0x00000007
42 #define PLLMUL_MASK     0x0000007F
43 #define FRCDIV_MASK     0x00000007
44
45 /* PBCLK */
46 #define PBDIV_MASK      0x00000007
47
48 /* SYSCLK MUX */
49 #define SCLK_SRC_FRC1   0
50 #define SCLK_SRC_SPLL   1
51 #define SCLK_SRC_POSC   2
52 #define SCLK_SRC_FRC2   7
53
54 /* Reference Oscillator Control Reg fields */
55 #define REFO_SEL_MASK   0x0f
56 #define REFO_SEL_SHIFT  0
57 #define REFO_ACTIVE     BIT(8)
58 #define REFO_DIVSW_EN   BIT(9)
59 #define REFO_OE         BIT(12)
60 #define REFO_ON         BIT(15)
61 #define REFO_DIV_SHIFT  16
62 #define REFO_DIV_MASK   0x7fff
63
64 /* Reference Oscillator Trim Register Fields */
65 #define REFO_TRIM_REG   0x10
66 #define REFO_TRIM_MASK  0x1ff
67 #define REFO_TRIM_SHIFT 23
68 #define REFO_TRIM_MAX   511
69
70 #define ROCLK_SRC_SCLK          0x0
71 #define ROCLK_SRC_SPLL          0x7
72 #define ROCLK_SRC_ROCLKI        0x8
73
74 /* Memory PLL */
75 #define MPLL_IDIV               0x3f
76 #define MPLL_MULT               0xff
77 #define MPLL_ODIV1              0x7
78 #define MPLL_ODIV2              0x7
79 #define MPLL_VREG_RDY           BIT(23)
80 #define MPLL_RDY                BIT(31)
81 #define MPLL_IDIV_SHIFT         0
82 #define MPLL_MULT_SHIFT         8
83 #define MPLL_ODIV1_SHIFT        24
84 #define MPLL_ODIV2_SHIFT        27
85 #define MPLL_IDIV_INIT          0x03
86 #define MPLL_MULT_INIT          0x32
87 #define MPLL_ODIV1_INIT         0x02
88 #define MPLL_ODIV2_INIT         0x01
89
90 struct pic32_clk_priv {
91         void __iomem *iobase;
92         void __iomem *syscfg_base;
93 };
94
95 static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv)
96 {
97         u32 iclk, idiv, odiv, mult;
98         ulong plliclk, v;
99
100         v = readl(priv->iobase + SPLLCON);
101         iclk = (v & ICLK_MASK);
102         idiv = ((v >> 8) & PLLIDIV_MASK) + 1;
103         odiv = ((v >> 24) & PLLODIV_MASK);
104         mult = ((v >> 16) & PLLMUL_MASK) + 1;
105
106         plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ;
107
108         if (odiv < 2)
109                 odiv = 2;
110         else if (odiv < 5)
111                 odiv = (1 << odiv);
112         else
113                 odiv = 32;
114
115         return ((plliclk / idiv) * mult) / odiv;
116 }
117
118 static ulong pic32_get_sysclk(struct pic32_clk_priv *priv)
119 {
120         ulong v;
121         ulong hz;
122         ulong div, frcdiv;
123         ulong curr_osc;
124
125         /* get clk source */
126         v = readl(priv->iobase + OSCCON);
127         curr_osc = (v >> 12) & CUROSC_MASK;
128         switch (curr_osc) {
129         case SCLK_SRC_FRC1:
130         case SCLK_SRC_FRC2:
131                 frcdiv = ((v >> 24) & FRCDIV_MASK);
132                 div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
133                 hz = SYS_FRC_CLK_HZ / div;
134                 break;
135
136         case SCLK_SRC_SPLL:
137                 hz = pic32_get_pll_rate(priv);
138                 break;
139
140         case SCLK_SRC_POSC:
141                 hz = SYS_POSC_CLK_HZ;
142                 break;
143
144         default:
145                 hz = 0;
146                 printf("clk: unknown sclk_src.\n");
147                 break;
148         }
149
150         return hz;
151 }
152
153 static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph)
154 {
155         void __iomem *reg;
156         ulong div, clk_freq;
157
158         WARN_ON((periph < PB1CLK) || (periph > PB7CLK));
159
160         clk_freq = pic32_get_sysclk(priv);
161
162         reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10;
163         div = (readl(reg) & PBDIV_MASK) + 1;
164
165         return clk_freq / div;
166 }
167
168 static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
169 {
170         return pic32_get_pbclk(priv, PB7CLK);
171 }
172
173 static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph,
174                               int parent_rate, int rate, int parent_id)
175 {
176         void __iomem *reg;
177         u32 div, trim, v;
178         u64 frac;
179
180         WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
181
182         /* calculate dividers,
183          *   rate = parent_rate / [2 * (div + (trim / 512))]
184          */
185         if (parent_rate <= rate) {
186                 div = 0;
187                 trim = 0;
188         } else {
189                 div = parent_rate / (rate << 1);
190                 frac = parent_rate;
191                 frac <<= 8;
192                 do_div(frac, rate);
193                 frac -= (u64)(div << 9);
194                 trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac;
195         }
196
197         reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
198
199         /* disable clk */
200         writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
201
202         /* wait till previous src change is active */
203         wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE,
204                           false, CONFIG_SYS_HZ, false);
205
206         /* parent_id */
207         v = readl(reg);
208         v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
209         v |= (parent_id << REFO_SEL_SHIFT);
210
211         /* apply rodiv */
212         v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
213         v |= (div << REFO_DIV_SHIFT);
214         writel(v, reg);
215
216         /* apply trim */
217         v = readl(reg + REFO_TRIM_REG);
218         v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
219         v |= (trim << REFO_TRIM_SHIFT);
220         writel(v, reg + REFO_TRIM_REG);
221
222         /* enable clk */
223         writel(REFO_ON | REFO_OE, reg + _SET_OFFSET);
224
225         /* switch divider */
226         writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
227
228         /* wait for divider switching to complete */
229         return wait_for_bit_le32(reg, REFO_DIVSW_EN, false,
230                                  CONFIG_SYS_HZ, false);
231 }
232
233 static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph)
234 {
235         u32 rodiv, rotrim, rosel, v, parent_rate;
236         void __iomem *reg;
237         u64 rate64;
238
239         WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
240
241         reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
242         v = readl(reg);
243         /* get rosel */
244         rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
245         /* get div */
246         rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
247
248         /* get trim */
249         v = readl(reg + REFO_TRIM_REG);
250         rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
251
252         if (!rodiv)
253                 return 0;
254
255         /* get parent rate */
256         switch (rosel) {
257         case ROCLK_SRC_SCLK:
258                 parent_rate = pic32_get_cpuclk(priv);
259                 break;
260         case ROCLK_SRC_SPLL:
261                 parent_rate = pic32_get_pll_rate(priv);
262                 break;
263         default:
264                 parent_rate = 0;
265                 break;
266         }
267
268         /* Calculation
269          * rate = parent_rate / [2 * (div + (trim / 512))]
270          */
271         if (rotrim) {
272                 rodiv <<= 9;
273                 rodiv += rotrim;
274                 rate64 = parent_rate;
275                 rate64 <<= 8;
276                 do_div(rate64, rodiv);
277                 v = (u32)rate64;
278         } else {
279                 v = parent_rate / (rodiv << 1);
280         }
281         return v;
282 }
283
284 static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv)
285 {
286         u32 v, idiv, mul;
287         u32 odiv1, odiv2;
288         u64 rate;
289
290         v = readl(priv->syscfg_base + CFGMPLL);
291         idiv = v & MPLL_IDIV;
292         mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT;
293         odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1;
294         odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2;
295
296         rate = (SYS_POSC_CLK_HZ / idiv) * mul;
297         do_div(rate, odiv1);
298         do_div(rate, odiv2);
299
300         return (ulong)rate;
301 }
302
303 static int pic32_mpll_init(struct pic32_clk_priv *priv)
304 {
305         u32 v, mask;
306
307         /* initialize */
308         v = (MPLL_IDIV_INIT << MPLL_IDIV_SHIFT) |
309             (MPLL_MULT_INIT << MPLL_MULT_SHIFT) |
310             (MPLL_ODIV1_INIT << MPLL_ODIV1_SHIFT) |
311             (MPLL_ODIV2_INIT << MPLL_ODIV2_SHIFT);
312
313         writel(v, priv->syscfg_base + CFGMPLL);
314
315         /* Wait for ready */
316         mask = MPLL_RDY | MPLL_VREG_RDY;
317         return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask,
318                                  true, get_tbclk(), false);
319 }
320
321 static void pic32_clk_init(struct udevice *dev)
322 {
323         const void *blob = gd->fdt_blob;
324         struct pic32_clk_priv *priv;
325         ulong rate, pll_hz;
326         char propname[50];
327         int i;
328
329         priv = dev_get_priv(dev);
330         pll_hz = pic32_get_pll_rate(priv);
331
332         /* Initialize REFOs as not initialized and enabled on reset. */
333         for (i = REF1CLK; i <= REF5CLK; i++) {
334                 snprintf(propname, sizeof(propname),
335                          "microchip,refo%d-frequency", i - REF1CLK + 1);
336                 rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
337                 if (rate)
338                         pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL);
339         }
340
341         /* Memory PLL */
342         pic32_mpll_init(priv);
343 }
344
345 static ulong pic32_get_rate(struct clk *clk)
346 {
347         struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
348         ulong rate;
349
350         switch (clk->id) {
351         case PB1CLK ... PB7CLK:
352                 rate = pic32_get_pbclk(priv, clk->id);
353                 break;
354         case REF1CLK ... REF5CLK:
355                 rate = pic32_get_refclk(priv, clk->id);
356                 break;
357         case PLLCLK:
358                 rate = pic32_get_pll_rate(priv);
359                 break;
360         case MPLL:
361                 rate = pic32_get_mpll_rate(priv);
362                 break;
363         default:
364                 rate = 0;
365                 break;
366         }
367
368         return rate;
369 }
370
371 static ulong pic32_set_rate(struct clk *clk, ulong rate)
372 {
373         struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
374         ulong pll_hz;
375
376         switch (clk->id) {
377         case REF1CLK ... REF5CLK:
378                 pll_hz = pic32_get_pll_rate(priv);
379                 pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
380                 break;
381         default:
382                 break;
383         }
384
385         return rate;
386 }
387
388 static struct clk_ops pic32_pic32_clk_ops = {
389         .set_rate = pic32_set_rate,
390         .get_rate = pic32_get_rate,
391 };
392
393 static int pic32_clk_probe(struct udevice *dev)
394 {
395         struct pic32_clk_priv *priv = dev_get_priv(dev);
396         fdt_addr_t addr;
397         fdt_size_t size;
398
399         addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
400                                     &size);
401         if (addr == FDT_ADDR_T_NONE)
402                 return -EINVAL;
403
404         priv->iobase = ioremap(addr, size);
405         if (!priv->iobase)
406                 return -EINVAL;
407
408         priv->syscfg_base = pic32_get_syscfg_base();
409
410         /* initialize clocks */
411         pic32_clk_init(dev);
412
413         return 0;
414 }
415
416 static const struct udevice_id pic32_clk_ids[] = {
417         { .compatible = "microchip,pic32mzda-clk"},
418         {}
419 };
420
421 U_BOOT_DRIVER(pic32_clk) = {
422         .name           = "pic32_clk",
423         .id             = UCLASS_CLK,
424         .of_match       = pic32_clk_ids,
425         .ops            = &pic32_pic32_clk_ops,
426         .probe          = pic32_clk_probe,
427         .priv_auto      = sizeof(struct pic32_clk_priv),
428 };