blob: 7ddff2feaf3ec90b244db949829d7cf38fa267ad [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Purna Chandra Mandala0e79082016-01-28 15:30:11 +05302/*
3 * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
4 *
Purna Chandra Mandala0e79082016-01-28 15:30:11 +05305 */
6
7#include <common.h>
Stephen Warren135aa952016-06-17 09:44:00 -06008#include <clk-uclass.h>
Purna Chandra Mandala0e79082016-01-28 15:30:11 +05309#include <dm.h>
10#include <div64.h>
Simon Glass049f8d62019-12-28 10:44:59 -070011#include <time.h>
Purna Chandra Mandala0e79082016-01-28 15:30:11 +053012#include <wait_bit.h>
13#include <dm/lists.h>
14#include <asm/io.h>
Simon Glasseb41d8a2020-05-10 11:40:08 -060015#include <linux/bug.h>
Purna Chandra Mandala0e79082016-01-28 15:30:11 +053016#include <mach/pic32.h>
17#include <dt-bindings/clock/microchip,clock.h>
18
19DECLARE_GLOBAL_DATA_PTR;
20
21/* Primary oscillator */
22#define SYS_POSC_CLK_HZ 24000000
23
24/* FRC clk rate */
25#define SYS_FRC_CLK_HZ 8000000
26
27/* Clock Registers */
28#define OSCCON 0x0000
29#define OSCTUNE 0x0010
30#define SPLLCON 0x0020
31#define REFO1CON 0x0080
32#define REFO1TRIM 0x0090
33#define PB1DIV 0x0140
34
35/* SPLL */
36#define ICLK_MASK 0x00000080
37#define PLLIDIV_MASK 0x00000007
38#define PLLODIV_MASK 0x00000007
39#define CUROSC_MASK 0x00000007
40#define PLLMUL_MASK 0x0000007F
41#define FRCDIV_MASK 0x00000007
42
43/* PBCLK */
44#define PBDIV_MASK 0x00000007
45
46/* SYSCLK MUX */
47#define SCLK_SRC_FRC1 0
48#define SCLK_SRC_SPLL 1
49#define SCLK_SRC_POSC 2
50#define SCLK_SRC_FRC2 7
51
52/* Reference Oscillator Control Reg fields */
53#define REFO_SEL_MASK 0x0f
54#define REFO_SEL_SHIFT 0
55#define REFO_ACTIVE BIT(8)
56#define REFO_DIVSW_EN BIT(9)
57#define REFO_OE BIT(12)
58#define REFO_ON BIT(15)
59#define REFO_DIV_SHIFT 16
60#define REFO_DIV_MASK 0x7fff
61
62/* Reference Oscillator Trim Register Fields */
63#define REFO_TRIM_REG 0x10
64#define REFO_TRIM_MASK 0x1ff
65#define REFO_TRIM_SHIFT 23
66#define REFO_TRIM_MAX 511
67
68#define ROCLK_SRC_SCLK 0x0
69#define ROCLK_SRC_SPLL 0x7
70#define ROCLK_SRC_ROCLKI 0x8
71
72/* Memory PLL */
73#define MPLL_IDIV 0x3f
74#define MPLL_MULT 0xff
75#define MPLL_ODIV1 0x7
76#define MPLL_ODIV2 0x7
77#define MPLL_VREG_RDY BIT(23)
78#define MPLL_RDY BIT(31)
79#define MPLL_IDIV_SHIFT 0
80#define MPLL_MULT_SHIFT 8
81#define MPLL_ODIV1_SHIFT 24
82#define MPLL_ODIV2_SHIFT 27
83#define MPLL_IDIV_INIT 0x03
84#define MPLL_MULT_INIT 0x32
85#define MPLL_ODIV1_INIT 0x02
86#define MPLL_ODIV2_INIT 0x01
87
88struct pic32_clk_priv {
89 void __iomem *iobase;
90 void __iomem *syscfg_base;
91};
92
93static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv)
94{
95 u32 iclk, idiv, odiv, mult;
96 ulong plliclk, v;
97
98 v = readl(priv->iobase + SPLLCON);
99 iclk = (v & ICLK_MASK);
100 idiv = ((v >> 8) & PLLIDIV_MASK) + 1;
101 odiv = ((v >> 24) & PLLODIV_MASK);
102 mult = ((v >> 16) & PLLMUL_MASK) + 1;
103
104 plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ;
105
106 if (odiv < 2)
107 odiv = 2;
108 else if (odiv < 5)
109 odiv = (1 << odiv);
110 else
111 odiv = 32;
112
113 return ((plliclk / idiv) * mult) / odiv;
114}
115
116static ulong pic32_get_sysclk(struct pic32_clk_priv *priv)
117{
118 ulong v;
119 ulong hz;
120 ulong div, frcdiv;
121 ulong curr_osc;
122
123 /* get clk source */
124 v = readl(priv->iobase + OSCCON);
125 curr_osc = (v >> 12) & CUROSC_MASK;
126 switch (curr_osc) {
127 case SCLK_SRC_FRC1:
128 case SCLK_SRC_FRC2:
129 frcdiv = ((v >> 24) & FRCDIV_MASK);
130 div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
131 hz = SYS_FRC_CLK_HZ / div;
132 break;
133
134 case SCLK_SRC_SPLL:
135 hz = pic32_get_pll_rate(priv);
136 break;
137
138 case SCLK_SRC_POSC:
139 hz = SYS_POSC_CLK_HZ;
140 break;
141
142 default:
143 hz = 0;
144 printf("clk: unknown sclk_src.\n");
145 break;
146 }
147
148 return hz;
149}
150
151static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph)
152{
153 void __iomem *reg;
154 ulong div, clk_freq;
155
156 WARN_ON((periph < PB1CLK) || (periph > PB7CLK));
157
158 clk_freq = pic32_get_sysclk(priv);
159
160 reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10;
161 div = (readl(reg) & PBDIV_MASK) + 1;
162
163 return clk_freq / div;
164}
165
166static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
167{
168 return pic32_get_pbclk(priv, PB7CLK);
169}
170
171static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph,
172 int parent_rate, int rate, int parent_id)
173{
174 void __iomem *reg;
175 u32 div, trim, v;
176 u64 frac;
177
178 WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
179
180 /* calculate dividers,
181 * rate = parent_rate / [2 * (div + (trim / 512))]
182 */
183 if (parent_rate <= rate) {
184 div = 0;
185 trim = 0;
186 } else {
187 div = parent_rate / (rate << 1);
188 frac = parent_rate;
189 frac <<= 8;
190 do_div(frac, rate);
191 frac -= (u64)(div << 9);
192 trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac;
193 }
194
195 reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
196
197 /* disable clk */
198 writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
199
200 /* wait till previous src change is active */
Álvaro Fernández Rojas48263502018-01-23 17:14:55 +0100201 wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE,
202 false, CONFIG_SYS_HZ, false);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530203
204 /* parent_id */
205 v = readl(reg);
206 v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
207 v |= (parent_id << REFO_SEL_SHIFT);
208
209 /* apply rodiv */
210 v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
211 v |= (div << REFO_DIV_SHIFT);
212 writel(v, reg);
213
214 /* apply trim */
215 v = readl(reg + REFO_TRIM_REG);
216 v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT);
217 v |= (trim << REFO_TRIM_SHIFT);
218 writel(v, reg + REFO_TRIM_REG);
219
220 /* enable clk */
221 writel(REFO_ON | REFO_OE, reg + _SET_OFFSET);
222
223 /* switch divider */
224 writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
225
226 /* wait for divider switching to complete */
Álvaro Fernández Rojas48263502018-01-23 17:14:55 +0100227 return wait_for_bit_le32(reg, REFO_DIVSW_EN, false,
228 CONFIG_SYS_HZ, false);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530229}
230
231static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph)
232{
233 u32 rodiv, rotrim, rosel, v, parent_rate;
234 void __iomem *reg;
235 u64 rate64;
236
237 WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
238
239 reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
240 v = readl(reg);
241 /* get rosel */
242 rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
243 /* get div */
244 rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
245
246 /* get trim */
247 v = readl(reg + REFO_TRIM_REG);
248 rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
249
250 if (!rodiv)
251 return 0;
252
253 /* get parent rate */
254 switch (rosel) {
255 case ROCLK_SRC_SCLK:
256 parent_rate = pic32_get_cpuclk(priv);
257 break;
258 case ROCLK_SRC_SPLL:
259 parent_rate = pic32_get_pll_rate(priv);
260 break;
261 default:
262 parent_rate = 0;
263 break;
264 }
265
266 /* Calculation
267 * rate = parent_rate / [2 * (div + (trim / 512))]
268 */
269 if (rotrim) {
270 rodiv <<= 9;
271 rodiv += rotrim;
272 rate64 = parent_rate;
273 rate64 <<= 8;
274 do_div(rate64, rodiv);
275 v = (u32)rate64;
276 } else {
277 v = parent_rate / (rodiv << 1);
278 }
279 return v;
280}
281
282static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv)
283{
284 u32 v, idiv, mul;
285 u32 odiv1, odiv2;
286 u64 rate;
287
288 v = readl(priv->syscfg_base + CFGMPLL);
289 idiv = v & MPLL_IDIV;
290 mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT;
291 odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1;
292 odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2;
293
294 rate = (SYS_POSC_CLK_HZ / idiv) * mul;
295 do_div(rate, odiv1);
296 do_div(rate, odiv2);
297
298 return (ulong)rate;
299}
300
301static int pic32_mpll_init(struct pic32_clk_priv *priv)
302{
303 u32 v, mask;
304
305 /* initialize */
306 v = (MPLL_IDIV_INIT << MPLL_IDIV_SHIFT) |
307 (MPLL_MULT_INIT << MPLL_MULT_SHIFT) |
308 (MPLL_ODIV1_INIT << MPLL_ODIV1_SHIFT) |
309 (MPLL_ODIV2_INIT << MPLL_ODIV2_SHIFT);
310
311 writel(v, priv->syscfg_base + CFGMPLL);
312
313 /* Wait for ready */
314 mask = MPLL_RDY | MPLL_VREG_RDY;
Álvaro Fernández Rojas48263502018-01-23 17:14:55 +0100315 return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask,
316 true, get_tbclk(), false);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530317}
318
319static void pic32_clk_init(struct udevice *dev)
320{
321 const void *blob = gd->fdt_blob;
322 struct pic32_clk_priv *priv;
323 ulong rate, pll_hz;
324 char propname[50];
325 int i;
326
327 priv = dev_get_priv(dev);
328 pll_hz = pic32_get_pll_rate(priv);
329
330 /* Initialize REFOs as not initialized and enabled on reset. */
331 for (i = REF1CLK; i <= REF5CLK; i++) {
332 snprintf(propname, sizeof(propname),
333 "microchip,refo%d-frequency", i - REF1CLK + 1);
Simon Glasse160f7d2017-01-17 16:52:55 -0700334 rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530335 if (rate)
336 pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL);
337 }
338
339 /* Memory PLL */
340 pic32_mpll_init(priv);
341}
342
Stephen Warren135aa952016-06-17 09:44:00 -0600343static ulong pic32_get_rate(struct clk *clk)
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530344{
Stephen Warren135aa952016-06-17 09:44:00 -0600345 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530346 ulong rate;
347
Stephen Warren135aa952016-06-17 09:44:00 -0600348 switch (clk->id) {
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530349 case PB1CLK ... PB7CLK:
Stephen Warren135aa952016-06-17 09:44:00 -0600350 rate = pic32_get_pbclk(priv, clk->id);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530351 break;
352 case REF1CLK ... REF5CLK:
Stephen Warren135aa952016-06-17 09:44:00 -0600353 rate = pic32_get_refclk(priv, clk->id);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530354 break;
355 case PLLCLK:
356 rate = pic32_get_pll_rate(priv);
357 break;
358 case MPLL:
359 rate = pic32_get_mpll_rate(priv);
360 break;
361 default:
362 rate = 0;
363 break;
364 }
365
366 return rate;
367}
368
Stephen Warren135aa952016-06-17 09:44:00 -0600369static ulong pic32_set_rate(struct clk *clk, ulong rate)
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530370{
Stephen Warren135aa952016-06-17 09:44:00 -0600371 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530372 ulong pll_hz;
373
Stephen Warren135aa952016-06-17 09:44:00 -0600374 switch (clk->id) {
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530375 case REF1CLK ... REF5CLK:
376 pll_hz = pic32_get_pll_rate(priv);
Stephen Warren135aa952016-06-17 09:44:00 -0600377 pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530378 break;
379 default:
380 break;
381 }
382
383 return rate;
384}
385
386static struct clk_ops pic32_pic32_clk_ops = {
Stephen Warren135aa952016-06-17 09:44:00 -0600387 .set_rate = pic32_set_rate,
388 .get_rate = pic32_get_rate,
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530389};
390
391static int pic32_clk_probe(struct udevice *dev)
392{
393 struct pic32_clk_priv *priv = dev_get_priv(dev);
394 fdt_addr_t addr;
395 fdt_size_t size;
396
Simon Glasse160f7d2017-01-17 16:52:55 -0700397 addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
398 &size);
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530399 if (addr == FDT_ADDR_T_NONE)
400 return -EINVAL;
401
402 priv->iobase = ioremap(addr, size);
403 if (!priv->iobase)
404 return -EINVAL;
405
406 priv->syscfg_base = pic32_get_syscfg_base();
407
408 /* initialize clocks */
409 pic32_clk_init(dev);
410
411 return 0;
412}
413
414static const struct udevice_id pic32_clk_ids[] = {
415 { .compatible = "microchip,pic32mzda-clk"},
416 {}
417};
418
419U_BOOT_DRIVER(pic32_clk) = {
420 .name = "pic32_clk",
421 .id = UCLASS_CLK,
422 .of_match = pic32_clk_ids,
Purna Chandra Mandala0e79082016-01-28 15:30:11 +0530423 .ops = &pic32_pic32_clk_ops,
424 .probe = pic32_clk_probe,
425 .priv_auto_alloc_size = sizeof(struct pic32_clk_priv),
426};