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