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