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