blob: dc46de2a7cf97ff6fce8c6ab406004529978103e [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>
13#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
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
89struct pic32_clk_priv {
90 void __iomem *iobase;
91 void __iomem *syscfg_base;
92};
93
94static 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
117static 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
152static 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
167static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
168{
169 return pic32_get_pbclk(priv, PB7CLK);
170}
171
172static 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 */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100202 wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE,
203 false, CONFIG_SYS_HZ, false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530204
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 */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100228 return wait_for_bit_le32(reg, REFO_DIVSW_EN, false,
229 CONFIG_SYS_HZ, false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530230}
231
232static 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
283static 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
302static 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;
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100316 return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask,
317 true, get_tbclk(), false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530318}
319
320static 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);
Simon Glassdd79d6e2017-01-17 16:52:55 -0700335 rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530336 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
Stephen Warrena9622432016-06-17 09:44:00 -0600344static ulong pic32_get_rate(struct clk *clk)
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530345{
Stephen Warrena9622432016-06-17 09:44:00 -0600346 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530347 ulong rate;
348
Stephen Warrena9622432016-06-17 09:44:00 -0600349 switch (clk->id) {
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530350 case PB1CLK ... PB7CLK:
Stephen Warrena9622432016-06-17 09:44:00 -0600351 rate = pic32_get_pbclk(priv, clk->id);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530352 break;
353 case REF1CLK ... REF5CLK:
Stephen Warrena9622432016-06-17 09:44:00 -0600354 rate = pic32_get_refclk(priv, clk->id);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530355 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
Stephen Warrena9622432016-06-17 09:44:00 -0600370static ulong pic32_set_rate(struct clk *clk, ulong rate)
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530371{
Stephen Warrena9622432016-06-17 09:44:00 -0600372 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530373 ulong pll_hz;
374
Stephen Warrena9622432016-06-17 09:44:00 -0600375 switch (clk->id) {
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530376 case REF1CLK ... REF5CLK:
377 pll_hz = pic32_get_pll_rate(priv);
Stephen Warrena9622432016-06-17 09:44:00 -0600378 pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530379 break;
380 default:
381 break;
382 }
383
384 return rate;
385}
386
387static struct clk_ops pic32_pic32_clk_ops = {
Stephen Warrena9622432016-06-17 09:44:00 -0600388 .set_rate = pic32_set_rate,
389 .get_rate = pic32_get_rate,
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530390};
391
392static 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
Simon Glassdd79d6e2017-01-17 16:52:55 -0700398 addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
399 &size);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530400 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
415static const struct udevice_id pic32_clk_ids[] = {
416 { .compatible = "microchip,pic32mzda-clk"},
417 {}
418};
419
420U_BOOT_DRIVER(pic32_clk) = {
421 .name = "pic32_clk",
422 .id = UCLASS_CLK,
423 .of_match = pic32_clk_ids,
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530424 .ops = &pic32_pic32_clk_ops,
425 .probe = pic32_clk_probe,
426 .priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
427};