blob: 177803943d9dc8537b64e7a02ef0f33386644bba [file] [log] [blame]
Purna Chandra Mandal699f9192016-01-28 15:30:11 +05301/*
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>
Stephen Warrena9622432016-06-17 09:44:00 -06009#include <clk-uclass.h>
Purna Chandra Mandal699f9192016-01-28 15:30:11 +053010#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
18DECLARE_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
87struct pic32_clk_priv {
88 void __iomem *iobase;
89 void __iomem *syscfg_base;
90};
91
92static 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
115static 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
150static 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
165static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
166{
167 return pic32_get_pbclk(priv, PB7CLK);
168}
169
170static 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 */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100200 wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE,
201 false, CONFIG_SYS_HZ, false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530202
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 */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100226 return wait_for_bit_le32(reg, REFO_DIVSW_EN, false,
227 CONFIG_SYS_HZ, false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530228}
229
230static 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
281static 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
300static 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;
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100314 return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask,
315 true, get_tbclk(), false);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530316}
317
318static 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);
Simon Glassdd79d6e2017-01-17 16:52:55 -0700333 rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530334 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
Stephen Warrena9622432016-06-17 09:44:00 -0600342static ulong pic32_get_rate(struct clk *clk)
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530343{
Stephen Warrena9622432016-06-17 09:44:00 -0600344 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530345 ulong rate;
346
Stephen Warrena9622432016-06-17 09:44:00 -0600347 switch (clk->id) {
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530348 case PB1CLK ... PB7CLK:
Stephen Warrena9622432016-06-17 09:44:00 -0600349 rate = pic32_get_pbclk(priv, clk->id);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530350 break;
351 case REF1CLK ... REF5CLK:
Stephen Warrena9622432016-06-17 09:44:00 -0600352 rate = pic32_get_refclk(priv, clk->id);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530353 break;
354 case PLLCLK:
355 rate = pic32_get_pll_rate(priv);
356 break;
357 case MPLL:
358 rate = pic32_get_mpll_rate(priv);
359 break;
360 default:
361 rate = 0;
362 break;
363 }
364
365 return rate;
366}
367
Stephen Warrena9622432016-06-17 09:44:00 -0600368static ulong pic32_set_rate(struct clk *clk, ulong rate)
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530369{
Stephen Warrena9622432016-06-17 09:44:00 -0600370 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530371 ulong pll_hz;
372
Stephen Warrena9622432016-06-17 09:44:00 -0600373 switch (clk->id) {
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530374 case REF1CLK ... REF5CLK:
375 pll_hz = pic32_get_pll_rate(priv);
Stephen Warrena9622432016-06-17 09:44:00 -0600376 pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530377 break;
378 default:
379 break;
380 }
381
382 return rate;
383}
384
385static struct clk_ops pic32_pic32_clk_ops = {
Stephen Warrena9622432016-06-17 09:44:00 -0600386 .set_rate = pic32_set_rate,
387 .get_rate = pic32_get_rate,
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530388};
389
390static int pic32_clk_probe(struct udevice *dev)
391{
392 struct pic32_clk_priv *priv = dev_get_priv(dev);
393 fdt_addr_t addr;
394 fdt_size_t size;
395
Simon Glassdd79d6e2017-01-17 16:52:55 -0700396 addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
397 &size);
Purna Chandra Mandal699f9192016-01-28 15:30:11 +0530398 if (addr == FDT_ADDR_T_NONE)
399 return -EINVAL;
400
401 priv->iobase = ioremap(addr, size);
402 if (!priv->iobase)
403 return -EINVAL;
404
405 priv->syscfg_base = pic32_get_syscfg_base();
406
407 /* initialize clocks */
408 pic32_clk_init(dev);
409
410 return 0;
411}
412
413static const struct udevice_id pic32_clk_ids[] = {
414 { .compatible = "microchip,pic32mzda-clk"},
415 {}
416};
417
418U_BOOT_DRIVER(pic32_clk) = {
419 .name = "pic32_clk",
420 .id = UCLASS_CLK,
421 .of_match = pic32_clk_ids,
422 .flags = DM_FLAG_PRE_RELOC,
423 .ops = &pic32_pic32_clk_ops,
424 .probe = pic32_clk_probe,
425 .priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
426};