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