xref: /openbmc/u-boot/drivers/clk/clk_pic32.c (revision 3c03f492)
1 /*
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>
9 #include <clk-uclass.h>
10 #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 
18 DECLARE_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 
87 struct pic32_clk_priv {
88 	void __iomem *iobase;
89 	void __iomem *syscfg_base;
90 };
91 
92 static 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 
115 static 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 
150 static 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 
165 static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv)
166 {
167 	return pic32_get_pbclk(priv, PB7CLK);
168 }
169 
170 static 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 */
200 	wait_for_bit(__func__, reg, REFO_DIVSW_EN | REFO_ACTIVE,
201 		     false, CONFIG_SYS_HZ, false);
202 
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 */
226 	return wait_for_bit(__func__, reg, REFO_DIVSW_EN, false,
227 			    CONFIG_SYS_HZ, false);
228 }
229 
230 static 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 
281 static 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 
300 static 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;
314 	return wait_for_bit(__func__, priv->syscfg_base + CFGMPLL, mask,
315 			    true, get_tbclk(), false);
316 }
317 
318 static 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);
333 		rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0);
334 		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 
342 static ulong pic32_get_rate(struct clk *clk)
343 {
344 	struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
345 	ulong rate;
346 
347 	switch (clk->id) {
348 	case PB1CLK ... PB7CLK:
349 		rate = pic32_get_pbclk(priv, clk->id);
350 		break;
351 	case REF1CLK ... REF5CLK:
352 		rate = pic32_get_refclk(priv, clk->id);
353 		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 
368 static ulong pic32_set_rate(struct clk *clk, ulong rate)
369 {
370 	struct pic32_clk_priv *priv = dev_get_priv(clk->dev);
371 	ulong pll_hz;
372 
373 	switch (clk->id) {
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);
377 		break;
378 	default:
379 		break;
380 	}
381 
382 	return rate;
383 }
384 
385 static struct clk_ops pic32_pic32_clk_ops = {
386 	.set_rate = pic32_set_rate,
387 	.get_rate = pic32_get_rate,
388 };
389 
390 static 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 
396 	addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
397 				    &size);
398 	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 
413 static const struct udevice_id pic32_clk_ids[] = {
414 	{ .compatible = "microchip,pic32mzda-clk"},
415 	{}
416 };
417 
418 U_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 };
427