xref: /openbsd-src/sys/arch/armv7/exynos/exclock.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: exclock.c,v 1.4 2016/07/26 22:10:10 patrick 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/queue.h>
21 #include <sys/malloc.h>
22 #include <sys/sysctl.h>
23 #include <sys/device.h>
24 #include <sys/evcount.h>
25 #include <sys/socket.h>
26 #include <sys/timeout.h>
27 #include <machine/intr.h>
28 #include <machine/bus.h>
29 #if NFDT > 0
30 #include <machine/fdt.h>
31 #endif
32 #include <armv7/armv7/armv7var.h>
33 
34 /* registers */
35 #define CLOCK_APLL_CON0				0x0100
36 #define CLOCK_APLL_CON1				0x0104
37 #define CLOCK_BPLL_CON0				0x0110
38 #define CLOCK_BPLL_CON1				0x0114
39 #define CLOCK_EPLL_CON0				0x0130
40 #define CLOCK_EPLL_CON1				0x0134
41 #define CLOCK_EPLL_CON2				0x0138
42 #define CLOCK_VPLL_CON0				0x0140
43 #define CLOCK_VPLL_CON1				0x0144
44 #define CLOCK_VPLL_CON2				0x0148
45 #define CLOCK_CLK_DIV_CPU0			0x0500
46 #define CLOCK_CLK_DIV_CPU1			0x0504
47 #define CLOCK_CLK_DIV_TOP0			0x0510
48 #define CLOCK_CLK_DIV_TOP1			0x0514
49 #define CLOCK_PLL_DIV2_SEL			0x0A24
50 #define CLOCK_MPLL_CON0				0x4100
51 #define CLOCK_MPLL_CON1				0x4104
52 
53 /* bits and bytes */
54 #define MPLL_FOUT_SEL_SHIFT			0x4
55 #define MPLL_FOUT_SEL_MASK			0x1
56 #define BPLL_FOUT_SEL_SHIFT			0x0
57 #define BPLL_FOUT_SEL_MASK			0x1
58 
59 #define HCLK_FREQ				24000
60 
61 #define HREAD4(sc, reg)							\
62 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
63 #define HWRITE4(sc, reg, val)						\
64 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
65 #define HSET4(sc, reg, bits)						\
66 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
67 #define HCLR4(sc, reg, bits)						\
68 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
69 
70 struct exclock_softc {
71 	struct device		sc_dev;
72 	bus_space_tag_t		sc_iot;
73 	bus_space_handle_t	sc_ioh;
74 };
75 
76 enum clocks {
77 	/* OSC */
78 	OSC,		/* 24 MHz OSC */
79 
80 	/* PLLs */
81 	APLL,		/* ARM core clock */
82 	MPLL,		/* System bus clock for memory controller */
83 	BPLL,		/* Graphic 3D processor clock and 1066 MHz clock for memory controller if necessary */
84 	CPLL,		/* Multi Format Video Hardware Codec clock */
85 	GPLL,		/* Graphic 3D processor clock or other clocks for DVFS flexibility */
86 	EPLL,		/* Audio interface clocks and clocks for other external device interfaces */
87 	VPLL,		/* dithered PLL, helps to reduce the EMI of display and camera */
88 };
89 
90 struct exclock_softc *exclock_sc;
91 
92 int exclock_match(struct device *parent, void *v, void *aux);
93 void exclock_attach(struct device *parent, struct device *self, void *args);
94 int exclock_cpuspeed(int *);
95 unsigned int exclock_decode_pll_clk(enum clocks, unsigned int, unsigned int);
96 unsigned int exclock_get_pll_clk(enum clocks);
97 unsigned int exclock_get_armclk(void);
98 unsigned int exclock_get_i2cclk(void);
99 
100 struct cfattach	exclock_ca = {
101 	sizeof (struct exclock_softc), NULL, exclock_attach
102 };
103 struct cfattach	exclock_fdt_ca = {
104 	sizeof (struct exclock_softc), exclock_match, exclock_attach
105 };
106 
107 struct cfdriver exclock_cd = {
108 	NULL, "exclock", DV_DULL
109 };
110 
111 int
112 exclock_match(struct device *parent, void *v, void *aux)
113 {
114 #if NFDT > 0
115 	struct armv7_attach_args *aa = aux;
116 
117 	if (fdt_node_compatible("samsung,exynos5250-clock", aa->aa_node))
118 		return 1;
119 #endif
120 
121 	return 0;
122 }
123 
124 void
125 exclock_attach(struct device *parent, struct device *self, void *args)
126 {
127 	struct armv7_attach_args *aa = args;
128 	struct exclock_softc *sc = (struct exclock_softc *) self;
129 	struct armv7mem mem;
130 
131 	exclock_sc = sc;
132 	sc->sc_iot = aa->aa_iot;
133 #if NFDT > 0
134 	if (aa->aa_node) {
135 		struct fdt_reg reg;
136 		if (fdt_get_reg(aa->aa_node, 0, &reg))
137 			panic("%s: could not extract memory data from FDT",
138 			    __func__);
139 		mem.addr = reg.addr;
140 		mem.size = reg.size;
141 	} else
142 #endif
143 	{
144 
145 		mem.addr = aa->aa_dev->mem[0].addr;
146 		mem.size = aa->aa_dev->mem[0].size;
147 	}
148 	if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh))
149 		panic("%s: bus_space_map failed!", __func__);
150 
151 	printf(": Exynos 5 CPU freq: %d MHz",
152 	    exclock_get_armclk() / 1000);
153 
154 	printf("\n");
155 
156 	cpu_cpuspeed = exclock_cpuspeed;
157 }
158 
159 int
160 exclock_cpuspeed(int *freq)
161 {
162 	*freq = exclock_get_armclk() / 1000;
163 	return (0);
164 }
165 
166 unsigned int
167 exclock_decode_pll_clk(enum clocks pll, unsigned int r, unsigned int k)
168 {
169 	uint32_t m, p, s = 0, mask, fout, freq;
170 	/*
171 	 * APLL_CON: MIDV [25:16]
172 	 * MPLL_CON: MIDV [25:16]
173 	 * EPLL_CON: MIDV [24:16]
174 	 * VPLL_CON: MIDV [24:16]
175 	 * BPLL_CON: MIDV [25:16]: Exynos5
176 	 */
177 
178 	switch (pll)
179 	{
180 	case APLL:
181 	case MPLL:
182 	case BPLL:
183 		mask = 0x3ff;
184 		break;
185 	default:
186 		mask = 0x1ff;
187 	}
188 
189 	m = (r >> 16) & mask;
190 
191 	/* PDIV [13:8] */
192 	p = (r >> 8) & 0x3f;
193 	/* SDIV [2:0] */
194 	s = r & 0x7;
195 
196 	freq = HCLK_FREQ;
197 
198 	if (pll == EPLL) {
199 		k = k & 0xffff;
200 		/* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
201 		fout = (m + k / 65536) * (freq / (p * (1 << s)));
202 	} else if (pll == VPLL) {
203 		k = k & 0xfff;
204 		/* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
205 		fout = (m + k / 1024) * (freq / (p * (1 << s)));
206 	} else {
207 		/* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
208 		fout = m * (freq / (p * (1 << s)));
209 	}
210 
211 	return fout;
212 }
213 
214 unsigned int
215 exclock_get_pll_clk(enum clocks pll)
216 {
217 	struct exclock_softc *sc = exclock_sc;
218 	uint32_t freq;
219 
220 	switch (pll) {
221 	case APLL:
222 		freq = exclock_decode_pll_clk(pll,
223 		    HREAD4(sc, CLOCK_APLL_CON0),
224 		    0);
225 		break;
226 	case MPLL:
227 		freq = exclock_decode_pll_clk(pll,
228 		    HREAD4(sc, CLOCK_MPLL_CON0),
229 		    0);
230 		break;
231 	case BPLL:
232 		freq = exclock_decode_pll_clk(pll,
233 		    HREAD4(sc, CLOCK_BPLL_CON0),
234 		    0);
235 		break;
236 	case EPLL:
237 		freq = exclock_decode_pll_clk(pll,
238 		    HREAD4(sc, CLOCK_EPLL_CON0),
239 		    HREAD4(sc, CLOCK_EPLL_CON1));
240 		break;
241 	case VPLL:
242 		freq = exclock_decode_pll_clk(pll,
243 		    HREAD4(sc, CLOCK_VPLL_CON0),
244 		    HREAD4(sc, CLOCK_VPLL_CON1));
245 		break;
246 	default:
247 		return 0;
248 	}
249 
250 	/*
251 	 * According to the user manual, in EVT1 MPLL and BPLL always gives
252 	 * 1.6GHz clock, so divide by 2 to get 800MHz MPLL clock.
253 	 */
254 	if (pll == MPLL || pll == BPLL) {
255 		uint32_t freq_sel;
256 		uint32_t pll_div2_sel = HREAD4(sc, CLOCK_PLL_DIV2_SEL);
257 
258 		switch (pll) {
259 		case MPLL:
260 			freq_sel = (pll_div2_sel >> MPLL_FOUT_SEL_SHIFT)
261 					& MPLL_FOUT_SEL_MASK;
262 			break;
263 		case BPLL:
264 			freq_sel = (pll_div2_sel >> BPLL_FOUT_SEL_SHIFT)
265 					& BPLL_FOUT_SEL_MASK;
266 			break;
267 		default:
268 			freq_sel = -1;
269 			break;
270 		}
271 
272 		if (freq_sel == 0)
273 			freq /= 2;
274 	}
275 
276 	return freq;
277 }
278 
279 unsigned int
280 exclock_get_armclk()
281 {
282 	struct exclock_softc *sc = exclock_sc;
283 	uint32_t div, armclk, arm_ratio, arm2_ratio;
284 
285 	div = HREAD4(sc, CLOCK_CLK_DIV_CPU0);
286 
287 	/* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */
288 	arm_ratio = (div >> 0) & 0x7;
289 	arm2_ratio = (div >> 28) & 0x7;
290 
291 	armclk = exclock_get_pll_clk(APLL) / (arm_ratio + 1);
292 	armclk /= (arm2_ratio + 1);
293 
294 	return armclk;
295 }
296 
297 unsigned int
298 exclock_get_i2cclk()
299 {
300 	struct exclock_softc *sc = exclock_sc;
301 	uint32_t aclk_66, aclk_66_pre, div, ratio;
302 
303 	div = HREAD4(sc, CLOCK_CLK_DIV_TOP1);
304 	ratio = (div >> 24) & 0x7;
305 	aclk_66_pre = exclock_get_pll_clk(MPLL) / (ratio + 1);
306 	div = HREAD4(sc, CLOCK_CLK_DIV_TOP0);
307 	ratio = (div >> 0) & 0x7;
308 	aclk_66 = aclk_66_pre / (ratio + 1);
309 
310 	return aclk_66;
311 }
312