xref: /openbsd/sys/arch/armv7/exynos/exclock.c (revision 9fdf0c62)
1 /* $OpenBSD: exclock.c,v 1.10 2021/10/24 17:52:27 mpi Exp $ */
2 /*
3  * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 
22 #include <machine/bus.h>
23 #include <machine/fdt.h>
24 
25 #include <dev/ofw/openfirm.h>
26 #include <dev/ofw/ofw_clock.h>
27 #include <dev/ofw/fdt.h>
28 
29 /* registers */
30 #define CLOCK_APLL_CON0				0x0100
31 #define CLOCK_APLL_CON1				0x0104
32 #define CLOCK_BPLL_CON0				0x0110
33 #define CLOCK_BPLL_CON1				0x0114
34 #define CLOCK_EPLL_CON0				0x0130
35 #define CLOCK_EPLL_CON1				0x0134
36 #define CLOCK_EPLL_CON2				0x0138
37 #define CLOCK_VPLL_CON0				0x0140
38 #define CLOCK_VPLL_CON1				0x0144
39 #define CLOCK_VPLL_CON2				0x0148
40 #define CLOCK_CLK_DIV_CPU0			0x0500
41 #define CLOCK_CLK_DIV_CPU1			0x0504
42 #define CLOCK_CLK_DIV_TOP0			0x0510
43 #define CLOCK_CLK_DIV_TOP1			0x0514
44 #define CLOCK_PLL_DIV2_SEL			0x0A24
45 #define CLOCK_MPLL_CON0				0x4100
46 #define CLOCK_MPLL_CON1				0x4104
47 
48 /* bits and bytes */
49 #define MPLL_FOUT_SEL_SHIFT			0x4
50 #define MPLL_FOUT_SEL_MASK			0x1
51 #define BPLL_FOUT_SEL_SHIFT			0x0
52 #define BPLL_FOUT_SEL_MASK			0x1
53 
54 #define HCLK_FREQ				24000000
55 
56 #define HREAD4(sc, reg)							\
57 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
58 #define HWRITE4(sc, reg, val)						\
59 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
60 #define HSET4(sc, reg, bits)						\
61 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
62 #define HCLR4(sc, reg, bits)						\
63 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
64 
65 struct exclock_softc {
66 	struct device		sc_dev;
67 	bus_space_tag_t		sc_iot;
68 	bus_space_handle_t	sc_ioh;
69 
70 	struct clock_device	sc_cd;
71 };
72 
73 enum clocks {
74 	/* OSC */
75 	OSC,		/* 24 MHz OSC */
76 
77 	/* PLLs */
78 	APLL,		/* ARM core clock */
79 	MPLL,		/* System bus clock for memory controller */
80 	BPLL,		/* Graphic 3D processor clock and 1066 MHz clock for memory controller if necessary */
81 	CPLL,		/* Multi Format Video Hardware Codec clock */
82 	GPLL,		/* Graphic 3D processor clock or other clocks for DVFS flexibility */
83 	EPLL,		/* Audio interface clocks and clocks for other external device interfaces */
84 	VPLL,		/* dithered PLL, helps to reduce the EMI of display and camera */
85 	KPLL,
86 };
87 
88 struct exclock_softc *exclock_sc;
89 
90 int	exclock_match(struct device *, void *, void *);
91 void	exclock_attach(struct device *, struct device *, void *);
92 uint32_t exclock_decode_pll_clk(enum clocks, unsigned int, unsigned int);
93 uint32_t exclock_get_pll_clk(struct exclock_softc *, enum clocks);
94 uint32_t exclock_get_armclk(struct exclock_softc *);
95 uint32_t exclock_get_kfcclk(struct exclock_softc *);
96 unsigned int exclock_get_i2cclk(void);
97 
98 const struct cfattach	exclock_ca = {
99 	sizeof (struct exclock_softc), exclock_match, exclock_attach
100 };
101 
102 struct cfdriver exclock_cd = {
103 	NULL, "exclock", DV_DULL
104 };
105 
106 uint32_t exynos5250_get_frequency(void *, uint32_t *);
107 int	exynos5250_set_frequency(void *, uint32_t *, uint32_t);
108 void	exynos5250_enable(void *, uint32_t *, int);
109 uint32_t exynos5420_get_frequency(void *, uint32_t *);
110 int	exynos5420_set_frequency(void *, uint32_t *, uint32_t);
111 void	exynos5420_enable(void *, uint32_t *, int);
112 
113 int
exclock_match(struct device * parent,void * match,void * aux)114 exclock_match(struct device *parent, void *match, void *aux)
115 {
116 	struct fdt_attach_args *faa = aux;
117 
118 	if (OF_is_compatible(faa->fa_node, "samsung,exynos5250-clock") ||
119 	    OF_is_compatible(faa->fa_node, "samsung,exynos5800-clock"))
120 		return 10;	/* Must beat syscon(4). */
121 
122 	return 0;
123 }
124 
125 void
exclock_attach(struct device * parent,struct device * self,void * aux)126 exclock_attach(struct device *parent, struct device *self, void *aux)
127 {
128 	struct exclock_softc *sc = (struct exclock_softc *)self;
129 	struct fdt_attach_args *faa = aux;
130 
131 	sc->sc_iot = faa->fa_iot;
132 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
133 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
134 		panic("%s: bus_space_map failed!", __func__);
135 
136 	exclock_sc = sc;
137 
138 	printf("\n");
139 
140 	if (OF_is_compatible(faa->fa_node, "samsung,exynos5250-clock")) {
141 		/* Exynos 5250 */
142 		sc->sc_cd.cd_enable = exynos5250_enable;
143 		sc->sc_cd.cd_get_frequency = exynos5250_get_frequency;
144 		sc->sc_cd.cd_set_frequency = exynos5250_set_frequency;
145 	} else {
146 		/* Exynos 5420/5800 */
147 		sc->sc_cd.cd_enable = exynos5420_enable;
148 		sc->sc_cd.cd_get_frequency = exynos5420_get_frequency;
149 		sc->sc_cd.cd_set_frequency = exynos5420_set_frequency;
150 	}
151 
152 	sc->sc_cd.cd_node = faa->fa_node;
153 	sc->sc_cd.cd_cookie = sc;
154 	clock_register(&sc->sc_cd);
155 }
156 
157 /*
158  * Exynos 5250
159  */
160 
161 /* Clocks */
162 #define EXYNOS5250_CLK_ARM_CLK		9
163 
164 uint32_t
exynos5250_get_frequency(void * cookie,uint32_t * cells)165 exynos5250_get_frequency(void *cookie, uint32_t *cells)
166 {
167 	struct exclock_softc *sc = cookie;
168 	uint32_t idx = cells[0];
169 
170 	switch (idx) {
171 	case EXYNOS5250_CLK_ARM_CLK:
172 		return exclock_get_armclk(sc);
173 	}
174 
175 	printf("%s: 0x%08x\n", __func__, idx);
176 	return 0;
177 }
178 
179 int
exynos5250_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)180 exynos5250_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
181 {
182 	uint32_t idx = cells[0];
183 
184 	switch (idx) {
185 	case EXYNOS5250_CLK_ARM_CLK:
186 		return -1;
187 	}
188 
189 	printf("%s: 0x%08x\n", __func__, idx);
190 	return -1;
191 }
192 
193 void
exynos5250_enable(void * cookie,uint32_t * cells,int on)194 exynos5250_enable(void *cookie, uint32_t *cells, int on)
195 {
196 	uint32_t idx = cells[0];
197 
198 	printf("%s: 0x%08x\n", __func__, idx);
199 }
200 
201 /*
202  * Exynos 5420/5800
203  */
204 
205 /* Clocks */
206 #define EXYNOS5420_CLK_FIN_PLL		1
207 #define EXYNOS5420_CLK_FOUT_RPLL	6
208 #define EXYNOS5420_CLK_FOUT_SPLL	8
209 #define EXYNOS5420_CLK_ARM_CLK		13
210 #define EXYNOS5420_CLK_KFC_CLK		14
211 #define EXYNOS5420_CLK_SCLK_MMC2	134
212 #define EXYNOS5420_CLK_MMC2		353
213 #define EXYNOS5420_CLK_USBH20		365
214 #define EXYNOS5420_CLK_USBD300		366
215 #define EXYNOS5420_CLK_USBD301		367
216 #define EXYNOS5420_CLK_SCLK_SPLL	-1
217 
218 /* Registers */
219 #define EXYNOS5420_RPLL_CON0		0x10140
220 #define EXYNOS5420_RPLL_CON1		0x10144
221 #define EXYNOS5420_SPLL_CON0		0x10160
222 #define EXYNOS5420_SRC_TOP6		0x10218
223 #define EXYNOS5420_DIV_FSYS1		0x1054c
224 #define EXYNOS5420_SRC_FSYS		0x10244
225 #define EXYNOS5420_GATE_TOP_SCLK_FSYS	0x10840
226 #define EXYNOS5420_GATE_IP_FSYS		0x10944
227 #define EXYNOS5420_KPLL_CON0		0x28100
228 #define EXYNOS5420_SRC_KFC		0x28200
229 #define EXYNOS5420_DIV_KFC0		0x28500
230 
231 uint32_t
exynos5420_get_frequency(void * cookie,uint32_t * cells)232 exynos5420_get_frequency(void *cookie, uint32_t *cells)
233 {
234 	struct exclock_softc *sc = cookie;
235 	uint32_t idx = cells[0];
236 	uint32_t reg, div, mux;
237 	uint32_t kdiv, mdiv, pdiv, sdiv;
238 	uint64_t freq;
239 
240 	switch (idx) {
241 	case EXYNOS5420_CLK_FIN_PLL:
242 		return 24000000;
243 	case EXYNOS5420_CLK_ARM_CLK:
244 		return exclock_get_armclk(sc);
245 	case EXYNOS5420_CLK_KFC_CLK:
246 		return exclock_get_kfcclk(sc);
247 	case EXYNOS5420_CLK_SCLK_MMC2:
248 		reg = HREAD4(sc, EXYNOS5420_DIV_FSYS1);
249 		div = ((reg >> 20) & ((1 << 10) - 1)) + 1;
250 		reg = HREAD4(sc, EXYNOS5420_SRC_FSYS);
251 		mux = ((reg >> 16) & ((1 << 3) - 1));
252 		switch (mux) {
253 		case 0:
254 			idx = EXYNOS5420_CLK_FIN_PLL;
255 			break;
256 		case 4:
257 			idx = EXYNOS5420_CLK_SCLK_SPLL;
258 			break;
259 		default:
260 			idx = 0;
261 			break;
262 		}
263 		return exynos5420_get_frequency(sc, &idx) / div;
264 	case EXYNOS5420_CLK_SCLK_SPLL:
265 		reg = HREAD4(sc, EXYNOS5420_SRC_TOP6);
266 		mux = ((reg >> 8) & ((1 << 1) - 1));
267 		switch (mux) {
268 		case 0:
269 			idx = EXYNOS5420_CLK_FIN_PLL;
270 			break;
271 		case 1:
272 			idx = EXYNOS5420_CLK_FOUT_SPLL;
273 			break;
274 		}
275 		return exynos5420_get_frequency(sc, &idx);
276 	case EXYNOS5420_CLK_FOUT_RPLL:
277 		reg = HREAD4(sc, EXYNOS5420_RPLL_CON0);
278 		mdiv = (reg >> 16) & 0x1ff;
279 		pdiv = (reg >> 8) & 0x3f;
280 		sdiv = (reg >> 0) & 0x7;
281 		reg = HREAD4(sc, EXYNOS5420_RPLL_CON1);
282 		kdiv = (reg >> 0) & 0xffff;
283 		idx = EXYNOS5420_CLK_FIN_PLL;
284 		freq = exynos5420_get_frequency(sc, &idx);
285 		freq = ((mdiv << 16) + kdiv) * freq / (pdiv * (1 << sdiv));
286 		return (freq >> 16);
287 	case EXYNOS5420_CLK_FOUT_SPLL:
288 		reg = HREAD4(sc, EXYNOS5420_SPLL_CON0);
289 		mdiv = (reg >> 16) & 0x3ff;
290 		pdiv = (reg >> 8) & 0x3f;
291 		sdiv = (reg >> 0) & 0x7;
292 		idx = EXYNOS5420_CLK_FIN_PLL;
293 		freq = exynos5420_get_frequency(sc, &idx);
294 		return mdiv * freq / (pdiv * (1 << sdiv));
295 	}
296 
297 	printf("%s: 0x%08x\n", __func__, idx);
298 	return 0;
299 }
300 
301 int
exynos5420_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)302 exynos5420_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
303 {
304 	uint32_t idx = cells[0];
305 
306 	switch (idx) {
307 	case EXYNOS5420_CLK_ARM_CLK:
308 	case EXYNOS5420_CLK_KFC_CLK:
309 		return -1;
310 	}
311 
312 	printf("%s: 0x%08x\n", __func__, idx);
313 	return -1;
314 }
315 
316 void
exynos5420_enable(void * cookie,uint32_t * cells,int on)317 exynos5420_enable(void *cookie, uint32_t *cells, int on)
318 {
319 	uint32_t idx = cells[0];
320 
321 	switch (idx) {
322 	case EXYNOS5420_CLK_SCLK_MMC2:	/* CLK_GATE_TOP_SCLK_FSYS */
323 	case EXYNOS5420_CLK_MMC2:	/* CLK_GATE_IP_FSYS */
324 	case EXYNOS5420_CLK_USBH20:	/* CLK_GATE_IP_FSYS */
325 	case EXYNOS5420_CLK_USBD300:	/* CLK_GATE_IP_FSYS */
326 	case EXYNOS5420_CLK_USBD301:	/* CLK_GATE_IP_FSYS */
327 		/* Enabled by default. */
328 		return;
329 	}
330 
331 	printf("%s: 0x%08x\n", __func__, idx);
332 }
333 
334 uint32_t
exclock_decode_pll_clk(enum clocks pll,unsigned int r,unsigned int k)335 exclock_decode_pll_clk(enum clocks pll, unsigned int r, unsigned int k)
336 {
337 	uint64_t freq;
338 	uint32_t m, p, s = 0, mask, fout;
339 	/*
340 	 * APLL_CON: MIDV [25:16]
341 	 * MPLL_CON: MIDV [25:16]
342 	 * EPLL_CON: MIDV [24:16]
343 	 * VPLL_CON: MIDV [24:16]
344 	 * BPLL_CON: MIDV [25:16]: Exynos5
345 	 */
346 
347 	switch (pll)
348 	{
349 	case APLL:
350 	case MPLL:
351 	case BPLL:
352 	case KPLL:
353 		mask = 0x3ff;
354 		break;
355 	default:
356 		mask = 0x1ff;
357 	}
358 
359 	m = (r >> 16) & mask;
360 
361 	/* PDIV [13:8] */
362 	p = (r >> 8) & 0x3f;
363 	/* SDIV [2:0] */
364 	s = r & 0x7;
365 
366 	freq = HCLK_FREQ;
367 
368 	if (pll == EPLL) {
369 		k = k & 0xffff;
370 		/* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
371 		fout = (m + k / 65536) * (freq / (p * (1 << s)));
372 	} else if (pll == VPLL) {
373 		k = k & 0xfff;
374 		/* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
375 		fout = (m + k / 1024) * (freq / (p * (1 << s)));
376 	} else {
377 		/* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
378 		fout = m * (freq / (p * (1 << s)));
379 	}
380 
381 	return fout;
382 }
383 
384 uint32_t
exclock_get_pll_clk(struct exclock_softc * sc,enum clocks pll)385 exclock_get_pll_clk(struct exclock_softc *sc, enum clocks pll)
386 {
387 	uint32_t freq;
388 
389 	switch (pll) {
390 	case APLL:
391 		freq = exclock_decode_pll_clk(pll,
392 		    HREAD4(sc, CLOCK_APLL_CON0), 0);
393 		break;
394 	case MPLL:
395 		freq = exclock_decode_pll_clk(pll,
396 		    HREAD4(sc, CLOCK_MPLL_CON0), 0);
397 		break;
398 	case BPLL:
399 		freq = exclock_decode_pll_clk(pll,
400 		    HREAD4(sc, CLOCK_BPLL_CON0), 0);
401 		break;
402 	case EPLL:
403 		freq = exclock_decode_pll_clk(pll,
404 		    HREAD4(sc, CLOCK_EPLL_CON0),
405 		    HREAD4(sc, CLOCK_EPLL_CON1));
406 		break;
407 	case VPLL:
408 		freq = exclock_decode_pll_clk(pll,
409 		    HREAD4(sc, CLOCK_VPLL_CON0),
410 		    HREAD4(sc, CLOCK_VPLL_CON1));
411 		break;
412 	case KPLL:
413 		freq = exclock_decode_pll_clk(pll,
414 		    HREAD4(sc, EXYNOS5420_KPLL_CON0), 0);
415 		break;
416 	default:
417 		return 0;
418 	}
419 
420 	/*
421 	 * According to the user manual, in EVT1 MPLL and BPLL always gives
422 	 * 1.6GHz clock, so divide by 2 to get 800MHz MPLL clock.
423 	 */
424 	if (pll == MPLL || pll == BPLL) {
425 		uint32_t freq_sel;
426 		uint32_t pll_div2_sel = HREAD4(sc, CLOCK_PLL_DIV2_SEL);
427 
428 		switch (pll) {
429 		case MPLL:
430 			freq_sel = (pll_div2_sel >> MPLL_FOUT_SEL_SHIFT)
431 					& MPLL_FOUT_SEL_MASK;
432 			break;
433 		case BPLL:
434 			freq_sel = (pll_div2_sel >> BPLL_FOUT_SEL_SHIFT)
435 					& BPLL_FOUT_SEL_MASK;
436 			break;
437 		default:
438 			freq_sel = -1;
439 			break;
440 		}
441 
442 		if (freq_sel == 0)
443 			freq /= 2;
444 	}
445 
446 	return freq;
447 }
448 
449 uint32_t
exclock_get_armclk(struct exclock_softc * sc)450 exclock_get_armclk(struct exclock_softc *sc)
451 {
452 	uint32_t div, armclk, arm_ratio, arm2_ratio;
453 
454 	div = HREAD4(sc, CLOCK_CLK_DIV_CPU0);
455 
456 	/* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */
457 	arm_ratio = (div >> 0) & 0x7;
458 	arm2_ratio = (div >> 28) & 0x7;
459 
460 	armclk = exclock_get_pll_clk(sc, APLL) / (arm_ratio + 1);
461 	armclk /= (arm2_ratio + 1);
462 
463 	return armclk;
464 }
465 
466 uint32_t
exclock_get_kfcclk(struct exclock_softc * sc)467 exclock_get_kfcclk(struct exclock_softc *sc)
468 {
469 	uint32_t div, kfc_ratio;
470 
471 	div = HREAD4(sc, EXYNOS5420_DIV_KFC0);
472 
473 	/* KFC_RATIO: [2:0] */
474 	kfc_ratio = (div >> 0) & 0x7;
475 
476 	return exclock_get_pll_clk(sc, KPLL) / (kfc_ratio + 1);
477 }
478 
479 unsigned int
exclock_get_i2cclk(void)480 exclock_get_i2cclk(void)
481 {
482 	struct exclock_softc *sc = exclock_sc;
483 	uint32_t aclk_66, aclk_66_pre, div, ratio;
484 
485 	div = HREAD4(sc, CLOCK_CLK_DIV_TOP1);
486 	ratio = (div >> 24) & 0x7;
487 	aclk_66_pre = exclock_get_pll_clk(sc, MPLL) / (ratio + 1);
488 	div = HREAD4(sc, CLOCK_CLK_DIV_TOP0);
489 	ratio = (div >> 0) & 0x7;
490 	aclk_66 = aclk_66_pre / (ratio + 1);
491 
492 	return aclk_66 / 1000;
493 }
494