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