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