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