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