blob: b3ac0d5a92aad1d63f402789711890cb7231f161 [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>
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
17DECLARE_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
86struct pic32_clk_priv {
87 void __iomem *iobase;
88 void __iomem *syscfg_base;
89};
90
91static 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
114static 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
149static 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
164static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
165{
166 return pic32_get_pbclk(priv, PB7CLK);
167}
168
169static 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 */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100199 wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE,
200 false, CONFIG_SYS_HZ, false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530201
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 */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100225 return wait_for_bit_le32(reg, REFO_DIVSW_EN, false,
226 CONFIG_SYS_HZ, false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530227}
228
229static 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
280static 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
299static 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;
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100313 return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask,
314 true, get_tbclk(), false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530315}
316
317static 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);
Simon Glassdd79d6e2017-01-17 16:52:55 -0700332 rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530333 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
Stephen Warrena9622432016-06-17 09:44:00 -0600341static ulong pic32_get_rate(struct clk *clk)
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530342{
Stephen Warrena9622432016-06-17 09:44:00 -0600343 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530344 ulong rate;
345
Stephen Warrena9622432016-06-17 09:44:00 -0600346 switch (clk->id) {
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530347 case PB1CLK ... PB7CLK:
Stephen Warrena9622432016-06-17 09:44:00 -0600348 rate = pic32_get_pbclk(priv, clk->id);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530349 break;
350 case REF1CLK ... REF5CLK:
Stephen Warrena9622432016-06-17 09:44:00 -0600351 rate = pic32_get_refclk(priv, clk->id);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530352 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
Stephen Warrena9622432016-06-17 09:44:00 -0600367static ulong pic32_set_rate(struct clk *clk, ulong rate)
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530368{
Stephen Warrena9622432016-06-17 09:44:00 -0600369 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530370 ulong pll_hz;
371
Stephen Warrena9622432016-06-17 09:44:00 -0600372 switch (clk->id) {
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530373 case REF1CLK ... REF5CLK:
374 pll_hz = pic32_get_pll_rate(priv);
Stephen Warrena9622432016-06-17 09:44:00 -0600375 pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530376 break;
377 default:
378 break;
379 }
380
381 return rate;
382}
383
384static struct clk_ops pic32_pic32_clk_ops = {
Stephen Warrena9622432016-06-17 09:44:00 -0600385 .set_rate = pic32_set_rate,
386 .get_rate = pic32_get_rate,
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530387};
388
389static 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
Simon Glassdd79d6e2017-01-17 16:52:55 -0700395 addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
396 &size);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530397 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
412static const struct udevice_id pic32_clk_ids[] = {
413 { .compatible = "microchip,pic32mzda-clk"},
414 {}
415};
416
417U_BOOT_DRIVER(pic32_clk) = {
418 .name = "pic32_clk",
419 .id = UCLASS_CLK,
420 .of_match = pic32_clk_ids,
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530421 .ops = &pic32_pic32_clk_ops,
422 .probe = pic32_clk_probe,
423 .priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
424};