xref: /openbsd/sys/arch/armv7/exynos/exclock.c (revision 6f40fd34)
1 /* $OpenBSD: exclock.c,v 1.7 2017/05/21 17:49:45 kettenis 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/sysctl.h>
21 #include <sys/device.h>
22 
23 #include <machine/intr.h>
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/ofw_clock.h>
29 #include <dev/ofw/fdt.h>
30 
31 /* registers */
32 #define CLOCK_APLL_CON0				0x0100
33 #define CLOCK_APLL_CON1				0x0104
34 #define CLOCK_BPLL_CON0				0x0110
35 #define CLOCK_BPLL_CON1				0x0114
36 #define CLOCK_EPLL_CON0				0x0130
37 #define CLOCK_EPLL_CON1				0x0134
38 #define CLOCK_EPLL_CON2				0x0138
39 #define CLOCK_VPLL_CON0				0x0140
40 #define CLOCK_VPLL_CON1				0x0144
41 #define CLOCK_VPLL_CON2				0x0148
42 #define CLOCK_CLK_DIV_CPU0			0x0500
43 #define CLOCK_CLK_DIV_CPU1			0x0504
44 #define CLOCK_CLK_DIV_TOP0			0x0510
45 #define CLOCK_CLK_DIV_TOP1			0x0514
46 #define CLOCK_PLL_DIV2_SEL			0x0A24
47 #define CLOCK_MPLL_CON0				0x4100
48 #define CLOCK_MPLL_CON1				0x4104
49 
50 /* bits and bytes */
51 #define MPLL_FOUT_SEL_SHIFT			0x4
52 #define MPLL_FOUT_SEL_MASK			0x1
53 #define BPLL_FOUT_SEL_SHIFT			0x0
54 #define BPLL_FOUT_SEL_MASK			0x1
55 
56 #define HCLK_FREQ				24000
57 
58 #define HREAD4(sc, reg)							\
59 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
60 #define HWRITE4(sc, reg, val)						\
61 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
62 #define HSET4(sc, reg, bits)						\
63 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
64 #define HCLR4(sc, reg, bits)						\
65 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
66 
67 struct exclock_softc {
68 	struct device		sc_dev;
69 	bus_space_tag_t		sc_iot;
70 	bus_space_handle_t	sc_ioh;
71 
72 	struct clock_device	sc_cd;
73 };
74 
75 enum clocks {
76 	/* OSC */
77 	OSC,		/* 24 MHz OSC */
78 
79 	/* PLLs */
80 	APLL,		/* ARM core clock */
81 	MPLL,		/* System bus clock for memory controller */
82 	BPLL,		/* Graphic 3D processor clock and 1066 MHz clock for memory controller if necessary */
83 	CPLL,		/* Multi Format Video Hardware Codec clock */
84 	GPLL,		/* Graphic 3D processor clock or other clocks for DVFS flexibility */
85 	EPLL,		/* Audio interface clocks and clocks for other external device interfaces */
86 	VPLL,		/* dithered PLL, helps to reduce the EMI of display and camera */
87 };
88 
89 struct exclock_softc *exclock_sc;
90 
91 int exclock_match(struct device *, void *, void *);
92 void exclock_attach(struct device *, struct device *, void *);
93 int exclock_cpuspeed(int *);
94 unsigned int exclock_decode_pll_clk(enum clocks, unsigned int, unsigned int);
95 unsigned int exclock_get_pll_clk(enum clocks);
96 unsigned int exclock_get_armclk(void);
97 unsigned int exclock_get_i2cclk(void);
98 
99 struct cfattach	exclock_ca = {
100 	sizeof (struct exclock_softc), exclock_match, exclock_attach
101 };
102 
103 struct cfdriver exclock_cd = {
104 	NULL, "exclock", DV_DULL
105 };
106 
107 uint32_t exynos5250_get_frequency(void *, uint32_t *);
108 int	exynos5250_set_frequency(void *, uint32_t *, uint32_t);
109 void	exynos5250_enable(void *, uint32_t *, int);
110 uint32_t exynos5420_get_frequency(void *, uint32_t *);
111 int	exynos5420_set_frequency(void *, uint32_t *, uint32_t);
112 void	exynos5420_enable(void *, uint32_t *, int);
113 
114 int
115 exclock_match(struct device *parent, void *match, void *aux)
116 {
117 	struct fdt_attach_args *faa = aux;
118 
119 	return (OF_is_compatible(faa->fa_node, "samsung,exynos5250-clock") ||
120 	    OF_is_compatible(faa->fa_node, "samsung,exynos5800-clock"));
121 }
122 
123 void
124 exclock_attach(struct device *parent, struct device *self, void *aux)
125 {
126 	struct exclock_softc *sc = (struct exclock_softc *)self;
127 	struct fdt_attach_args *faa = aux;
128 
129 	sc->sc_iot = faa->fa_iot;
130 
131 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
132 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
133 		panic("%s: bus_space_map failed!", __func__);
134 
135 	exclock_sc = sc;
136 
137 	printf(": Exynos 5 CPU freq: %d MHz\n",
138 	    exclock_get_armclk() / 1000);
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 	cpu_cpuspeed = exclock_cpuspeed;
157 }
158 
159 /*
160  * Exynos 5250
161  */
162 
163 uint32_t
164 exynos5250_get_frequency(void *cookie, uint32_t *cells)
165 {
166 	uint32_t idx = cells[0];
167 
168 	printf("%s: 0x%08x\n", __func__, idx);
169 	return 0;
170 }
171 
172 int
173 exynos5250_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
174 {
175 	uint32_t idx = cells[0];
176 
177 	printf("%s: 0x%08x\n", __func__, idx);
178 	return -1;
179 }
180 
181 void
182 exynos5250_enable(void *cookie, uint32_t *cells, int on)
183 {
184 	uint32_t idx = cells[0];
185 
186 	printf("%s: 0x%08x\n", __func__, idx);
187 }
188 
189 /*
190  * Exynos 5420/5800
191  */
192 
193 /* Clocks */
194 #define EXYNOS5420_CLK_FIN_PLL		1
195 #define EXYNOS5420_CLK_FOUT_RPLL	6
196 #define EXYNOS5420_CLK_FOUT_SPLL	8
197 #define EXYNOS5420_CLK_SCLK_MMC2	134
198 #define EXYNOS5420_CLK_MMC2		353
199 #define EXYNOS5420_CLK_SCLK_SPLL	-1
200 
201 /* Registers */
202 #define EXYNOS5420_RPLL_CON0		0x10140
203 #define EXYNOS5420_RPLL_CON1		0x10144
204 #define EXYNOS5420_SPLL_CON0		0x10160
205 #define EXYNOS5420_SRC_TOP6		0x10218
206 #define EXYNOS5420_DIV_FSYS1		0x1054c
207 #define EXYNOS5420_SRC_FSYS		0x10244
208 #define EXYNOS5420_GATE_TOP_SCLK_FSYS	0x10840
209 #define EXYNOS5420_GATE_IP_FSYS		0x10944
210 
211 uint32_t
212 exynos5420_get_frequency(void *cookie, uint32_t *cells)
213 {
214 	struct exclock_softc *sc = cookie;
215 	uint32_t idx = cells[0];
216 	uint32_t reg, div, mux;
217 	uint32_t kdiv, mdiv, pdiv, sdiv;
218 	uint64_t freq;
219 
220 	switch (idx) {
221 	case EXYNOS5420_CLK_FIN_PLL:
222 		return 24000000;
223 	case EXYNOS5420_CLK_SCLK_MMC2:
224 		reg = HREAD4(sc, EXYNOS5420_DIV_FSYS1);
225 		div = ((reg >> 20) & ((1 << 10) - 1)) + 1;
226 		reg = HREAD4(sc, EXYNOS5420_SRC_FSYS);
227 		mux = ((reg >> 16) & ((1 << 3) - 1));
228 		switch (mux) {
229 		case 0:
230 			idx = EXYNOS5420_CLK_FIN_PLL;
231 			break;
232 		case 4:
233 			idx = EXYNOS5420_CLK_SCLK_SPLL;
234 			break;
235 		default:
236 			idx = 0;
237 			break;
238 		}
239 		return exynos5420_get_frequency(sc, &idx) / div;
240 	case EXYNOS5420_CLK_SCLK_SPLL:
241 		reg = HREAD4(sc, EXYNOS5420_SRC_TOP6);
242 		mux = ((reg >> 8) & ((1 << 1) - 1));
243 		switch (mux) {
244 		case 0:
245 			idx = EXYNOS5420_CLK_FIN_PLL;
246 			break;
247 		case 1:
248 			idx = EXYNOS5420_CLK_FOUT_SPLL;
249 			break;
250 		}
251 		return exynos5420_get_frequency(sc, &idx);
252 	case EXYNOS5420_CLK_FOUT_RPLL:
253 		reg = HREAD4(sc, EXYNOS5420_RPLL_CON0);
254 		mdiv = (reg >> 16) & 0x1ff;
255 		pdiv = (reg >> 8) & 0x3f;
256 		sdiv = (reg >> 0) & 0x7;
257 		reg = HREAD4(sc, EXYNOS5420_RPLL_CON1);
258 		kdiv = (reg >> 0) & 0xffff;
259 		idx = EXYNOS5420_CLK_FIN_PLL;
260 		freq = exynos5420_get_frequency(sc, &idx);
261 		freq = ((mdiv << 16) + kdiv) * freq / (pdiv * (1 << sdiv));
262 		return (freq >> 16);
263 	case EXYNOS5420_CLK_FOUT_SPLL:
264 		reg = HREAD4(sc, EXYNOS5420_SPLL_CON0);
265 		mdiv = (reg >> 16) & 0x3ff;
266 		pdiv = (reg >> 8) & 0x3f;
267 		sdiv = (reg >> 0) & 0x7;
268 		idx = EXYNOS5420_CLK_FIN_PLL;
269 		freq = exynos5420_get_frequency(sc, &idx);
270 		return mdiv * freq / (pdiv * (1 << sdiv));
271 	}
272 
273 	printf("%s: 0x%08x\n", __func__, idx);
274 	return 0;
275 }
276 
277 int
278 exynos5420_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
279 {
280 	uint32_t idx = cells[0];
281 
282 	printf("%s: 0x%08x\n", __func__, idx);
283 	return -1;
284 }
285 
286 void
287 exynos5420_enable(void *cookie, uint32_t *cells, int on)
288 {
289 	uint32_t idx = cells[0];
290 
291 	switch (idx) {
292 	case EXYNOS5420_CLK_SCLK_MMC2:	/* CLK_GATE_TOP_SCLK_FSYS */
293 	case EXYNOS5420_CLK_MMC2:	/* CLK_GATE_IP_FSYS */
294 		/* Enabled by default. */
295 		return;
296 	}
297 
298 	printf("%s: 0x%08x\n", __func__, idx);
299 }
300 
301 int
302 exclock_cpuspeed(int *freq)
303 {
304 	*freq = exclock_get_armclk() / 1000;
305 	return (0);
306 }
307 
308 unsigned int
309 exclock_decode_pll_clk(enum clocks pll, unsigned int r, unsigned int k)
310 {
311 	uint32_t m, p, s = 0, mask, fout, freq;
312 	/*
313 	 * APLL_CON: MIDV [25:16]
314 	 * MPLL_CON: MIDV [25:16]
315 	 * EPLL_CON: MIDV [24:16]
316 	 * VPLL_CON: MIDV [24:16]
317 	 * BPLL_CON: MIDV [25:16]: Exynos5
318 	 */
319 
320 	switch (pll)
321 	{
322 	case APLL:
323 	case MPLL:
324 	case BPLL:
325 		mask = 0x3ff;
326 		break;
327 	default:
328 		mask = 0x1ff;
329 	}
330 
331 	m = (r >> 16) & mask;
332 
333 	/* PDIV [13:8] */
334 	p = (r >> 8) & 0x3f;
335 	/* SDIV [2:0] */
336 	s = r & 0x7;
337 
338 	freq = HCLK_FREQ;
339 
340 	if (pll == EPLL) {
341 		k = k & 0xffff;
342 		/* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
343 		fout = (m + k / 65536) * (freq / (p * (1 << s)));
344 	} else if (pll == VPLL) {
345 		k = k & 0xfff;
346 		/* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
347 		fout = (m + k / 1024) * (freq / (p * (1 << s)));
348 	} else {
349 		/* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
350 		fout = m * (freq / (p * (1 << s)));
351 	}
352 
353 	return fout;
354 }
355 
356 unsigned int
357 exclock_get_pll_clk(enum clocks pll)
358 {
359 	struct exclock_softc *sc = exclock_sc;
360 	uint32_t freq;
361 
362 	switch (pll) {
363 	case APLL:
364 		freq = exclock_decode_pll_clk(pll,
365 		    HREAD4(sc, CLOCK_APLL_CON0),
366 		    0);
367 		break;
368 	case MPLL:
369 		freq = exclock_decode_pll_clk(pll,
370 		    HREAD4(sc, CLOCK_MPLL_CON0),
371 		    0);
372 		break;
373 	case BPLL:
374 		freq = exclock_decode_pll_clk(pll,
375 		    HREAD4(sc, CLOCK_BPLL_CON0),
376 		    0);
377 		break;
378 	case EPLL:
379 		freq = exclock_decode_pll_clk(pll,
380 		    HREAD4(sc, CLOCK_EPLL_CON0),
381 		    HREAD4(sc, CLOCK_EPLL_CON1));
382 		break;
383 	case VPLL:
384 		freq = exclock_decode_pll_clk(pll,
385 		    HREAD4(sc, CLOCK_VPLL_CON0),
386 		    HREAD4(sc, CLOCK_VPLL_CON1));
387 		break;
388 	default:
389 		return 0;
390 	}
391 
392 	/*
393 	 * According to the user manual, in EVT1 MPLL and BPLL always gives
394 	 * 1.6GHz clock, so divide by 2 to get 800MHz MPLL clock.
395 	 */
396 	if (pll == MPLL || pll == BPLL) {
397 		uint32_t freq_sel;
398 		uint32_t pll_div2_sel = HREAD4(sc, CLOCK_PLL_DIV2_SEL);
399 
400 		switch (pll) {
401 		case MPLL:
402 			freq_sel = (pll_div2_sel >> MPLL_FOUT_SEL_SHIFT)
403 					& MPLL_FOUT_SEL_MASK;
404 			break;
405 		case BPLL:
406 			freq_sel = (pll_div2_sel >> BPLL_FOUT_SEL_SHIFT)
407 					& BPLL_FOUT_SEL_MASK;
408 			break;
409 		default:
410 			freq_sel = -1;
411 			break;
412 		}
413 
414 		if (freq_sel == 0)
415 			freq /= 2;
416 	}
417 
418 	return freq;
419 }
420 
421 unsigned int
422 exclock_get_armclk(void)
423 {
424 	struct exclock_softc *sc = exclock_sc;
425 	uint32_t div, armclk, arm_ratio, arm2_ratio;
426 
427 	div = HREAD4(sc, CLOCK_CLK_DIV_CPU0);
428 
429 	/* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */
430 	arm_ratio = (div >> 0) & 0x7;
431 	arm2_ratio = (div >> 28) & 0x7;
432 
433 	armclk = exclock_get_pll_clk(APLL) / (arm_ratio + 1);
434 	armclk /= (arm2_ratio + 1);
435 
436 	return armclk;
437 }
438 
439 unsigned int
440 exclock_get_i2cclk(void)
441 {
442 	struct exclock_softc *sc = exclock_sc;
443 	uint32_t aclk_66, aclk_66_pre, div, ratio;
444 
445 	div = HREAD4(sc, CLOCK_CLK_DIV_TOP1);
446 	ratio = (div >> 24) & 0x7;
447 	aclk_66_pre = exclock_get_pll_clk(MPLL) / (ratio + 1);
448 	div = HREAD4(sc, CLOCK_CLK_DIV_TOP0);
449 	ratio = (div >> 0) & 0x7;
450 	aclk_66 = aclk_66_pre / (ratio + 1);
451 
452 	return aclk_66;
453 }
454