1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
8 #include <clk-uclass.h>
13 #include <asm/global_data.h>
16 #include <linux/bitops.h>
17 #include <linux/bug.h>
18 #include <mach/pic32.h>
19 #include <dt-bindings/clock/microchip,clock.h>
21 DECLARE_GLOBAL_DATA_PTR;
23 /* Primary oscillator */
24 #define SYS_POSC_CLK_HZ 24000000
27 #define SYS_FRC_CLK_HZ 8000000
31 #define OSCTUNE 0x0010
32 #define SPLLCON 0x0020
33 #define REFO1CON 0x0080
34 #define REFO1TRIM 0x0090
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
46 #define PBDIV_MASK 0x00000007
49 #define SCLK_SRC_FRC1 0
50 #define SCLK_SRC_SPLL 1
51 #define SCLK_SRC_POSC 2
52 #define SCLK_SRC_FRC2 7
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
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
70 #define ROCLK_SRC_SCLK 0x0
71 #define ROCLK_SRC_SPLL 0x7
72 #define ROCLK_SRC_ROCLKI 0x8
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
90 struct pic32_clk_priv {
92 void __iomem *syscfg_base;
95 static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv)
97 u32 iclk, idiv, odiv, mult;
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;
106 plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ;
115 return ((plliclk / idiv) * mult) / odiv;
118 static ulong pic32_get_sysclk(struct pic32_clk_priv *priv)
126 v = readl(priv->iobase + OSCCON);
127 curr_osc = (v >> 12) & CUROSC_MASK;
131 frcdiv = ((v >> 24) & FRCDIV_MASK);
132 div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7));
133 hz = SYS_FRC_CLK_HZ / div;
137 hz = pic32_get_pll_rate(priv);
141 hz = SYS_POSC_CLK_HZ;
146 printf("clk: unknown sclk_src.\n");
153 static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph)
158 WARN_ON((periph < PB1CLK) || (periph > PB7CLK));
160 clk_freq = pic32_get_sysclk(priv);
162 reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10;
163 div = (readl(reg) & PBDIV_MASK) + 1;
165 return clk_freq / div;
168 static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
170 return pic32_get_pbclk(priv, PB7CLK);
173 static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph,
174 int parent_rate, int rate, int parent_id)
180 WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
182 /* calculate dividers,
183 * rate = parent_rate / [2 * (div + (trim / 512))]
185 if (parent_rate <= rate) {
189 div = parent_rate / (rate << 1);
193 frac -= (u64)(div << 9);
194 trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac;
197 reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
200 writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET);
202 /* wait till previous src change is active */
203 wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE,
204 false, CONFIG_SYS_HZ, false);
208 v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT);
209 v |= (parent_id << REFO_SEL_SHIFT);
212 v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT);
213 v |= (div << REFO_DIV_SHIFT);
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);
223 writel(REFO_ON | REFO_OE, reg + _SET_OFFSET);
226 writel(REFO_DIVSW_EN, reg + _SET_OFFSET);
228 /* wait for divider switching to complete */
229 return wait_for_bit_le32(reg, REFO_DIVSW_EN, false,
230 CONFIG_SYS_HZ, false);
233 static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph)
235 u32 rodiv, rotrim, rosel, v, parent_rate;
239 WARN_ON((periph < REF1CLK) || (periph > REF5CLK));
241 reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20;
244 rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK;
246 rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK;
249 v = readl(reg + REFO_TRIM_REG);
250 rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK;
255 /* get parent rate */
258 parent_rate = pic32_get_cpuclk(priv);
261 parent_rate = pic32_get_pll_rate(priv);
269 * rate = parent_rate / [2 * (div + (trim / 512))]
274 rate64 = parent_rate;
276 do_div(rate64, rodiv);
279 v = parent_rate / (rodiv << 1);
284 static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv)
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;
296 rate = (SYS_POSC_CLK_HZ / idiv) * mul;
303 static int pic32_mpll_init(struct pic32_clk_priv *priv)
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);
313 writel(v, priv->syscfg_base + CFGMPLL);
316 mask = MPLL_RDY | MPLL_VREG_RDY;
317 return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask,
318 true, get_tbclk(), false);
321 static void pic32_clk_init(struct udevice *dev)
323 const void *blob = gd->fdt_blob;
324 struct pic32_clk_priv *priv;
329 priv = dev_get_priv(dev);
330 pll_hz = pic32_get_pll_rate(priv);
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);
336 rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
338 pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL);
342 pic32_mpll_init(priv);
345 static ulong pic32_get_rate(struct clk *clk)
347 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
351 case PB1CLK ... PB7CLK:
352 rate = pic32_get_pbclk(priv, clk->id);
354 case REF1CLK ... REF5CLK:
355 rate = pic32_get_refclk(priv, clk->id);
358 rate = pic32_get_pll_rate(priv);
361 rate = pic32_get_mpll_rate(priv);
371 static ulong pic32_set_rate(struct clk *clk, ulong rate)
373 struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
377 case REF1CLK ... REF5CLK:
378 pll_hz = pic32_get_pll_rate(priv);
379 pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL);
388 static struct clk_ops pic32_pic32_clk_ops = {
389 .set_rate = pic32_set_rate,
390 .get_rate = pic32_get_rate,
393 static int pic32_clk_probe(struct udevice *dev)
395 struct pic32_clk_priv *priv = dev_get_priv(dev);
399 addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
401 if (addr == FDT_ADDR_T_NONE)
404 priv->iobase = ioremap(addr, size);
408 priv->syscfg_base = pic32_get_syscfg_base();
410 /* initialize clocks */
416 static const struct udevice_id pic32_clk_ids[] = {
417 { .compatible = "microchip,pic32mzda-clk"},
421 U_BOOT_DRIVER(pic32_clk) = {
424 .of_match = pic32_clk_ids,
425 .ops = &pic32_pic32_clk_ops,
426 .probe = pic32_clk_probe,
427 .priv_auto = sizeof(struct pic32_clk_priv),