2 * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
4 * SPDX-License-Identifier: GPL-2.0+
9 #include <clk-uclass.h>
15 #include <mach/pic32.h>
16 #include <dt-bindings/clock/microchip,clock.h>
18 DECLARE_GLOBAL_DATA_PTR
;
20 /* Primary oscillator */
21 #define SYS_POSC_CLK_HZ 24000000
24 #define SYS_FRC_CLK_HZ 8000000
28 #define OSCTUNE 0x0010
29 #define SPLLCON 0x0020
30 #define REFO1CON 0x0080
31 #define REFO1TRIM 0x0090
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
43 #define PBDIV_MASK 0x00000007
46 #define SCLK_SRC_FRC1 0
47 #define SCLK_SRC_SPLL 1
48 #define SCLK_SRC_POSC 2
49 #define SCLK_SRC_FRC2 7
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
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
67 #define ROCLK_SRC_SCLK 0x0
68 #define ROCLK_SRC_SPLL 0x7
69 #define ROCLK_SRC_ROCLKI 0x8
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
87 struct pic32_clk_priv
{
89 void __iomem
*syscfg_base
;
92 static ulong
pic32_get_pll_rate(struct pic32_clk_priv
*priv
)
94 u32 iclk
, idiv
, odiv
, mult
;
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;
103 plliclk
= iclk
? SYS_FRC_CLK_HZ
: SYS_POSC_CLK_HZ
;
112 return ((plliclk
/ idiv
) * mult
) / odiv
;
115 static ulong
pic32_get_sysclk(struct pic32_clk_priv
*priv
)
123 v
= readl(priv
->iobase
+ OSCCON
);
124 curr_osc
= (v
>> 12) & CUROSC_MASK
;
128 frcdiv
= ((v
>> 24) & FRCDIV_MASK
);
129 div
= ((1 << frcdiv
) + 1) + (128 * (frcdiv
== 7));
130 hz
= SYS_FRC_CLK_HZ
/ div
;
134 hz
= pic32_get_pll_rate(priv
);
138 hz
= SYS_POSC_CLK_HZ
;
143 printf("clk: unknown sclk_src.\n");
150 static ulong
pic32_get_pbclk(struct pic32_clk_priv
*priv
, int periph
)
155 WARN_ON((periph
< PB1CLK
) || (periph
> PB7CLK
));
157 clk_freq
= pic32_get_sysclk(priv
);
159 reg
= priv
->iobase
+ PB1DIV
+ (periph
- PB1CLK
) * 0x10;
160 div
= (readl(reg
) & PBDIV_MASK
) + 1;
162 return clk_freq
/ div
;
165 static ulong
pic32_get_cpuclk(struct pic32_clk_priv
*priv
)
167 return pic32_get_pbclk(priv
, PB7CLK
);
170 static ulong
pic32_set_refclk(struct pic32_clk_priv
*priv
, int periph
,
171 int parent_rate
, int rate
, int parent_id
)
177 WARN_ON((periph
< REF1CLK
) || (periph
> REF5CLK
));
179 /* calculate dividers,
180 * rate = parent_rate / [2 * (div + (trim / 512))]
182 if (parent_rate
<= rate
) {
186 div
= parent_rate
/ (rate
<< 1);
190 frac
-= (u64
)(div
<< 9);
191 trim
= (frac
>= REFO_TRIM_MAX
) ? REFO_TRIM_MAX
: (u32
)frac
;
194 reg
= priv
->iobase
+ REFO1CON
+ (periph
- REF1CLK
) * 0x20;
197 writel(REFO_ON
| REFO_OE
, reg
+ _CLR_OFFSET
);
199 /* wait till previous src change is active */
200 wait_for_bit(__func__
, reg
, REFO_DIVSW_EN
| REFO_ACTIVE
,
201 false, CONFIG_SYS_HZ
, false);
205 v
&= ~(REFO_SEL_MASK
<< REFO_SEL_SHIFT
);
206 v
|= (parent_id
<< REFO_SEL_SHIFT
);
209 v
&= ~(REFO_DIV_MASK
<< REFO_DIV_SHIFT
);
210 v
|= (div
<< REFO_DIV_SHIFT
);
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
);
220 writel(REFO_ON
| REFO_OE
, reg
+ _SET_OFFSET
);
223 writel(REFO_DIVSW_EN
, reg
+ _SET_OFFSET
);
225 /* wait for divider switching to complete */
226 return wait_for_bit(__func__
, reg
, REFO_DIVSW_EN
, false,
227 CONFIG_SYS_HZ
, false);
230 static ulong
pic32_get_refclk(struct pic32_clk_priv
*priv
, int periph
)
232 u32 rodiv
, rotrim
, rosel
, v
, parent_rate
;
236 WARN_ON((periph
< REF1CLK
) || (periph
> REF5CLK
));
238 reg
= priv
->iobase
+ REFO1CON
+ (periph
- REF1CLK
) * 0x20;
241 rosel
= (v
>> REFO_SEL_SHIFT
) & REFO_SEL_MASK
;
243 rodiv
= (v
>> REFO_DIV_SHIFT
) & REFO_DIV_MASK
;
246 v
= readl(reg
+ REFO_TRIM_REG
);
247 rotrim
= (v
>> REFO_TRIM_SHIFT
) & REFO_TRIM_MASK
;
252 /* get parent rate */
255 parent_rate
= pic32_get_cpuclk(priv
);
258 parent_rate
= pic32_get_pll_rate(priv
);
266 * rate = parent_rate / [2 * (div + (trim / 512))]
271 rate64
= parent_rate
;
273 do_div(rate64
, rodiv
);
276 v
= parent_rate
/ (rodiv
<< 1);
281 static ulong
pic32_get_mpll_rate(struct pic32_clk_priv
*priv
)
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
;
293 rate
= (SYS_POSC_CLK_HZ
/ idiv
) * mul
;
300 static int pic32_mpll_init(struct pic32_clk_priv
*priv
)
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
);
310 writel(v
, priv
->syscfg_base
+ CFGMPLL
);
313 mask
= MPLL_RDY
| MPLL_VREG_RDY
;
314 return wait_for_bit(__func__
, priv
->syscfg_base
+ CFGMPLL
, mask
,
315 true, get_tbclk(), false);
318 static void pic32_clk_init(struct udevice
*dev
)
320 const void *blob
= gd
->fdt_blob
;
321 struct pic32_clk_priv
*priv
;
326 priv
= dev_get_priv(dev
);
327 pll_hz
= pic32_get_pll_rate(priv
);
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);
333 rate
= fdtdec_get_int(blob
, dev
->of_offset
, propname
, 0);
335 pic32_set_refclk(priv
, i
, pll_hz
, rate
, ROCLK_SRC_SPLL
);
339 pic32_mpll_init(priv
);
342 static ulong
pic32_get_rate(struct clk
*clk
)
344 struct pic32_clk_priv
*priv
= dev_get_priv(clk
->dev
);
348 case PB1CLK
... PB7CLK
:
349 rate
= pic32_get_pbclk(priv
, clk
->id
);
351 case REF1CLK
... REF5CLK
:
352 rate
= pic32_get_refclk(priv
, clk
->id
);
355 rate
= pic32_get_pll_rate(priv
);
358 rate
= pic32_get_mpll_rate(priv
);
368 static ulong
pic32_set_rate(struct clk
*clk
, ulong rate
)
370 struct pic32_clk_priv
*priv
= dev_get_priv(clk
->dev
);
374 case REF1CLK
... REF5CLK
:
375 pll_hz
= pic32_get_pll_rate(priv
);
376 pic32_set_refclk(priv
, clk
->id
, pll_hz
, rate
, ROCLK_SRC_SPLL
);
385 static struct clk_ops pic32_pic32_clk_ops
= {
386 .set_rate
= pic32_set_rate
,
387 .get_rate
= pic32_get_rate
,
390 static int pic32_clk_probe(struct udevice
*dev
)
392 struct pic32_clk_priv
*priv
= dev_get_priv(dev
);
396 addr
= fdtdec_get_addr_size(gd
->fdt_blob
, dev
->of_offset
, "reg", &size
);
397 if (addr
== FDT_ADDR_T_NONE
)
400 priv
->iobase
= ioremap(addr
, size
);
404 priv
->syscfg_base
= pic32_get_syscfg_base();
406 /* initialize clocks */
412 static const struct udevice_id pic32_clk_ids
[] = {
413 { .compatible
= "microchip,pic32mzda-clk"},
417 U_BOOT_DRIVER(pic32_clk
) = {
420 .of_match
= pic32_clk_ids
,
421 .flags
= DM_FLAG_PRE_RELOC
,
422 .ops
= &pic32_pic32_clk_ops
,
423 .probe
= pic32_clk_probe
,
424 .priv_auto_alloc_size
= sizeof(struct pic32_clk_priv
),