1 /* $OpenBSD: mvclock.c,v 1.13 2022/06/05 02:43:44 dlg Exp $ */
2 /*
3 * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
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/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_clock.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/fdt.h>
30
31 #define HREAD4(sc, reg) \
32 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
33 #define HWRITE4(sc, reg, val) \
34 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
35 #define HSET4(sc, reg, bits) \
36 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
37 #define HCLR4(sc, reg, bits) \
38 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
39
40 struct mvclock_softc {
41 struct device sc_dev;
42 bus_space_tag_t sc_iot;
43 bus_space_handle_t sc_ioh;
44
45 struct clock_device sc_cd;
46 };
47
48 int mvclock_match(struct device *, void *, void *);
49 void mvclock_attach(struct device *, struct device *, void *);
50
51 const struct cfattach mvclock_ca = {
52 sizeof (struct mvclock_softc), mvclock_match, mvclock_attach
53 };
54
55 struct cfdriver mvclock_cd = {
56 NULL, "mvclock", DV_DULL
57 };
58
59 uint32_t ap806_get_frequency(void *, uint32_t *);
60 uint32_t cp110_get_frequency(void *, uint32_t *);
61 void cp110_enable(void *, uint32_t *, int);
62
63 void a3700_periph_nb_enable(void *, uint32_t *, int);
64 uint32_t a3700_periph_nb_get_frequency(void *, uint32_t *);
65 void a3700_periph_sb_enable(void *, uint32_t *, int);
66 uint32_t a3700_periph_sb_get_frequency(void *, uint32_t *);
67 uint32_t a3700_tbg_get_frequency(void *, uint32_t *);
68
69 int
mvclock_match(struct device * parent,void * match,void * aux)70 mvclock_match(struct device *parent, void *match, void *aux)
71 {
72 struct fdt_attach_args *faa = aux;
73 int node = faa->fa_node;
74
75 if (OF_is_compatible(node, "marvell,ap806-clock") ||
76 OF_is_compatible(node, "marvell,ap807-clock") ||
77 OF_is_compatible(node, "marvell,cp110-clock") ||
78 OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb") ||
79 OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb") ||
80 OF_is_compatible(node, "marvell,armada-3700-tbg-clock") ||
81 OF_is_compatible(node, "marvell,armada-3700-xtal-clock"))
82 return 10; /* Must beat syscon(4). */
83
84 return 0;
85 }
86
87 void
mvclock_attach(struct device * parent,struct device * self,void * aux)88 mvclock_attach(struct device *parent, struct device *self, void *aux)
89 {
90 struct mvclock_softc *sc = (struct mvclock_softc *)self;
91 struct fdt_attach_args *faa = aux;
92 int node = faa->fa_node;
93
94 if (faa->fa_nreg > 0) {
95 sc->sc_iot = faa->fa_iot;
96 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
97 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
98 printf(": can't map registers\n");
99 return;
100 }
101 }
102
103 printf("\n");
104
105 sc->sc_cd.cd_node = node;
106 sc->sc_cd.cd_cookie = sc;
107 if (OF_is_compatible(node, "marvell,ap806-clock") ||
108 OF_is_compatible(node, "marvell,ap807-clock")) {
109 sc->sc_cd.cd_get_frequency = ap806_get_frequency;
110 } else if (OF_is_compatible(node, "marvell,cp110-clock")) {
111 sc->sc_cd.cd_get_frequency = cp110_get_frequency;
112 sc->sc_cd.cd_enable = cp110_enable;
113 } else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb")) {
114 sc->sc_cd.cd_enable = a3700_periph_nb_enable;
115 sc->sc_cd.cd_get_frequency = a3700_periph_nb_get_frequency;
116 } else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb")) {
117 sc->sc_cd.cd_enable = a3700_periph_sb_enable;
118 sc->sc_cd.cd_get_frequency = a3700_periph_sb_get_frequency;
119 } else if (OF_is_compatible(node, "marvell,armada-3700-tbg-clock")) {
120 sc->sc_cd.cd_get_frequency = a3700_tbg_get_frequency;
121 }
122 clock_register(&sc->sc_cd);
123 }
124
125 /* AP806 block */
126
127 #define AP806_CORE_FIXED 2
128 #define AP806_CORE_MSS 3
129 #define AP806_CORE_SDIO 4
130
131 uint32_t
ap806_get_frequency(void * cookie,uint32_t * cells)132 ap806_get_frequency(void *cookie, uint32_t *cells)
133 {
134 uint32_t idx = cells[0];
135
136 switch (idx) {
137 case AP806_CORE_FIXED:
138 /* fixed PLL at 1200MHz */
139 return 1200000000;
140 case AP806_CORE_MSS:
141 /* MSS clock is fixed clock divided by 6 */
142 return 200000000;
143 case AP806_CORE_SDIO:
144 /* SDIO/eMMC clock is fixed clock divided by 3 */
145 return 400000000;
146 default:
147 break;
148 }
149
150 printf("%s: 0x%08x\n", __func__, idx);
151 return 0;
152 }
153
154 /* CP110 block */
155
156 #define CP110_PM_CLOCK_GATING_CTRL 0x220
157
158 #define CP110_CORE_APLL 0
159 #define CP110_CORE_PPV2 1
160 #define CP110_CORE_X2CORE 2
161 #define CP110_CORE_CORE 3
162 #define CP110_CORE_SDIO 5
163
164 #define CP110_GATE_PPV2 3
165 #define CP110_GATE_SDIO 4
166 #define CP110_GATE_SLOW_IO 21
167
168 uint32_t
cp110_get_frequency(void * cookie,uint32_t * cells)169 cp110_get_frequency(void *cookie, uint32_t *cells)
170 {
171 struct mvclock_softc *sc = cookie;
172 uint32_t mod = cells[0];
173 uint32_t idx = cells[1];
174 uint32_t parent[2] = { 0, 0 };
175
176 /* Core clocks */
177 if (mod == 0) {
178 switch (idx) {
179 case CP110_CORE_APLL:
180 /* fixed PLL at 1GHz */
181 return 1000000000;
182 case CP110_CORE_PPV2:
183 /* PPv2 clock is APLL/3 */
184 return 333333333;
185 case CP110_CORE_X2CORE:
186 /* X2CORE clock is APLL/2 */
187 return 500000000;
188 case CP110_CORE_CORE:
189 /* Core clock is X2CORE/2 */
190 return 250000000;
191 case CP110_CORE_SDIO:
192 /* SDIO clock is APLL/2.5 */
193 return 400000000;
194 default:
195 break;
196 }
197 }
198
199 /* Gateable clocks */
200 if (mod == 1) {
201 switch (idx) {
202 case CP110_GATE_PPV2:
203 parent[1] = CP110_CORE_PPV2;
204 break;
205 case CP110_GATE_SDIO:
206 parent[1] = CP110_CORE_SDIO;
207 break;
208 case CP110_GATE_SLOW_IO:
209 parent[1] = CP110_CORE_X2CORE;
210 break;
211 default:
212 break;
213 }
214
215 if (parent[1] != 0)
216 return cp110_get_frequency(sc, parent);
217 }
218
219 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
220 return 0;
221 }
222
223 void
cp110_enable(void * cookie,uint32_t * cells,int on)224 cp110_enable(void *cookie, uint32_t *cells, int on)
225 {
226 struct mvclock_softc *sc = cookie;
227 uint32_t mod = cells[0];
228 uint32_t idx = cells[1];
229
230 /* Gateable clocks */
231 if (mod == 1 && idx < 32) {
232 struct regmap *rm;
233 uint32_t reg;
234
235 rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
236 if (rm == NULL) {
237 printf("%s: can't enable clock 0x%08x 0x%08x\n",
238 sc->sc_dev.dv_xname, mod, idx);
239 return;
240 }
241 reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL);
242 if (on)
243 reg |= (1U << idx);
244 else
245 reg &= ~(1U << idx);
246 regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg);
247 return;
248 }
249
250 printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
251 }
252
253 /* Armada 3700 Periph block */
254
255 /* North Bridge clocks */
256 #define PERIPH_NB_MMC 0x00
257 #define PERIPH_NB_SATA 0x01 /* SATA Host */
258 #define PERIPH_NB_SEC_AT 0x02 /* Security AT */
259 #define PERIPH_NB_SEC_DAP 0x03 /* Security DAP */
260 #define PERIPH_NB_TSECM 0x04 /* Security Engine */
261 #define PERIPH_NB_SETM_TMX 0x05 /* Serial Embedded Trace Module */
262 #define PERIPH_NB_AVS 0x06 /* Adaptive Voltage Scaling */
263 #define PERIPH_NB_SQF 0x07 /* SPI */
264 #define PERIPH_NB_I2C2 0x09
265 #define PERIPH_NB_I2C1 0x0a
266 #define PERIPH_NB_DDR_PHY 0x0b
267 #define PERIPH_NB_DDR_FCLK 0x0c
268 #define PERIPH_NB_TRACE 0x0d
269 #define PERIPH_NB_COUNTER 0x0e
270 #define PERIPH_NB_EIO97 0x0f
271 #define PERIPH_NB_CPU 0x10
272
273 /* South Bridge clocks */
274 #define PERIPH_SB_GBE_50 0x00 /* 50MHz parent for gbe */
275 #define PERIPH_SB_GBE_CORE 0x01 /* parent for gbe core */
276 #define PERIPH_SB_GBE_125 0x02 /* 125MHz parent for gbe */
277 #define PERIPH_SB_GBE1_50 0x03 /* 50MHz parent for gbe port 1 */
278 #define PERIPH_SB_GBE0_50 0x04 /* 50MHz parent for gbe port 0 */
279 #define PERIPH_SB_GBE1_125 0x05 /* 125MHz parent for gbe port 1 */
280 #define PERIPH_SB_GBE0_125 0x06 /* 125MHz parent for gbe port 0 */
281 #define PERIPH_SB_GBE1_CORE 0x07 /* gbe core port 1 */
282 #define PERIPH_SB_GBE0_CORE 0x08 /* gbe core port 0 */
283 #define PERIPH_SB_GBE_BM 0x09 /* gbe buffer manager */
284 #define PERIPH_SB_SDIO 0x0a
285 #define PERIPH_SB_USB32_USB2_SYS 0x0b /* USB 2 clock */
286 #define PERIPH_SB_USB32_SS_SYS 0x0c /* USB 3 clock */
287 #define PERIPH_SB_PCIE 0x0d
288
289 #define PERIPH_TBG_SEL 0x0
290 #define PERIPH_TBG_SEL_MASK 0x3
291 #define PERIPH_DIV_SEL0 0x4
292 #define PERIPH_DIV_SEL1 0x8
293 #define PERIPH_DIV_SEL2 0xc
294 #define PERIPH_DIV_SEL_MASK 0x7
295 #define PERIPH_CLK_SEL 0x10
296 #define PERIPH_CLK_DIS 0x14
297
298 void a3700_periph_enable(struct mvclock_softc *, uint32_t, int);
299 uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t);
300 uint32_t a3700_periph_get_div(struct mvclock_softc *, uint32_t, uint32_t);
301 uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t,
302 uint32_t, uint32_t);
303
304 void
a3700_periph_nb_enable(void * cookie,uint32_t * cells,int on)305 a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on)
306 {
307 struct mvclock_softc *sc = cookie;
308 uint32_t idx = cells[0];
309
310 switch (idx) {
311 case PERIPH_NB_MMC:
312 return a3700_periph_enable(sc, 2, on);
313 case PERIPH_NB_SQF:
314 return a3700_periph_enable(sc, 12, on);
315 case PERIPH_NB_I2C2:
316 return a3700_periph_enable(sc, 16, on);
317 case PERIPH_NB_I2C1:
318 return a3700_periph_enable(sc, 17, on);
319 default:
320 break;
321 }
322
323 printf("%s: 0x%08x\n", __func__, idx);
324 }
325
326 uint32_t
a3700_periph_nb_get_frequency(void * cookie,uint32_t * cells)327 a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells)
328 {
329 struct mvclock_softc *sc = cookie;
330 uint32_t idx = cells[0];
331 uint32_t freq;
332
333 switch (idx) {
334 case PERIPH_NB_MMC:
335 freq = a3700_periph_tbg_get_frequency(sc, 0);
336 freq /= a3700_periph_get_double_div(sc,
337 PERIPH_DIV_SEL2, 16, 13);
338 return freq;
339 case PERIPH_NB_SQF:
340 freq = a3700_periph_tbg_get_frequency(sc, 12);
341 freq /= a3700_periph_get_double_div(sc,
342 PERIPH_DIV_SEL1, 27, 24);
343 return freq;
344 case PERIPH_NB_CPU:
345 freq = a3700_periph_tbg_get_frequency(sc, 22);
346 freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL0, 28);
347 return freq;
348 default:
349 break;
350 }
351
352 printf("%s: 0x%08x\n", __func__, idx);
353 return 0;
354 }
355
356 void
a3700_periph_sb_enable(void * cookie,uint32_t * cells,int on)357 a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on)
358 {
359 struct mvclock_softc *sc = cookie;
360 uint32_t idx = cells[0];
361
362 switch (idx) {
363 case PERIPH_SB_GBE1_CORE:
364 return a3700_periph_enable(sc, 4, on);
365 case PERIPH_SB_GBE0_CORE:
366 return a3700_periph_enable(sc, 5, on);
367 case PERIPH_SB_USB32_USB2_SYS:
368 return a3700_periph_enable(sc, 16, on);
369 case PERIPH_SB_USB32_SS_SYS:
370 return a3700_periph_enable(sc, 17, on);
371 default:
372 break;
373 }
374
375 printf("%s: 0x%08x\n", __func__, idx);
376 }
377
378 uint32_t
a3700_periph_sb_get_frequency(void * cookie,uint32_t * cells)379 a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells)
380 {
381 struct mvclock_softc *sc = cookie;
382 uint32_t idx = cells[0];
383 uint32_t freq;
384
385 switch (idx) {
386 case PERIPH_SB_GBE_CORE:
387 freq = a3700_periph_tbg_get_frequency(sc, 8);
388 freq /= a3700_periph_get_double_div(sc,
389 PERIPH_DIV_SEL1, 18, 21);
390 return freq;
391 case PERIPH_SB_GBE1_CORE:
392 idx = PERIPH_SB_GBE_CORE;
393 freq = a3700_periph_sb_get_frequency(sc, &idx);
394 freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL1, 13) + 1;
395 return freq;
396 case PERIPH_SB_GBE0_CORE:
397 idx = PERIPH_SB_GBE_CORE;
398 freq = a3700_periph_sb_get_frequency(sc, &idx);
399 freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL1, 14) + 1;
400 return freq;
401 default:
402 break;
403 }
404
405 printf("%s: 0x%08x\n", __func__, idx);
406 return 0;
407 }
408
409 void
a3700_periph_enable(struct mvclock_softc * sc,uint32_t idx,int on)410 a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on)
411 {
412 uint32_t reg;
413
414 reg = HREAD4(sc, PERIPH_CLK_DIS);
415 reg &= ~(1 << idx);
416 if (!on)
417 reg |= (1 << idx);
418 HWRITE4(sc, PERIPH_CLK_DIS, reg);
419 }
420
421 uint32_t
a3700_periph_tbg_get_frequency(struct mvclock_softc * sc,uint32_t idx)422 a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx)
423 {
424 uint32_t reg;
425
426 reg = HREAD4(sc, PERIPH_TBG_SEL);
427 reg >>= idx;
428 reg &= PERIPH_TBG_SEL_MASK;
429
430 return clock_get_frequency_idx(sc->sc_cd.cd_node, reg);
431 }
432
433 uint32_t
a3700_periph_get_div(struct mvclock_softc * sc,uint32_t off,uint32_t idx)434 a3700_periph_get_div(struct mvclock_softc *sc, uint32_t off, uint32_t idx)
435 {
436 uint32_t reg = HREAD4(sc, off);
437 return ((reg >> idx) & PERIPH_DIV_SEL_MASK);
438 }
439
440 uint32_t
a3700_periph_get_double_div(struct mvclock_softc * sc,uint32_t off,uint32_t idx0,uint32_t idx1)441 a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off,
442 uint32_t idx0, uint32_t idx1)
443 {
444 uint32_t reg = HREAD4(sc, off);
445 return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) *
446 ((reg >> idx1) & PERIPH_DIV_SEL_MASK);
447 }
448
449 /* Armada 3700 TBG block */
450
451 #define TBG_A_P 0
452 #define TBG_B_P 1
453 #define TBG_A_S 2
454 #define TBG_B_S 3
455
456 #define TBG_CTRL0 0x4
457 #define TBG_A_FBDIV_SHIFT 2
458 #define TBG_B_FBDIV_SHIFT 18
459 #define TBG_CTRL1 0x8
460 #define TBG_A_VCODIV_SE_SHIFT 0
461 #define TBG_B_VCODIV_SE_SHIFT 16
462 #define TBG_CTRL7 0x20
463 #define TBG_A_REFDIV_SHIFT 0
464 #define TBG_B_REFDIV_SHIFT 16
465 #define TBG_CTRL8 0x30
466 #define TBG_A_VCODIV_DIFF_SHIFT 1
467 #define TBG_B_VCODIV_DIFF_SHIFT 17
468 #define TBG_DIV_MASK 0x1ff
469
470 uint32_t
a3700_tbg_get_frequency(void * cookie,uint32_t * cells)471 a3700_tbg_get_frequency(void *cookie, uint32_t *cells)
472 {
473 struct mvclock_softc *sc = cookie;
474 uint32_t idx = cells[0];
475 uint64_t mult, div, freq;
476 uint32_t reg, vcodiv;
477
478 switch (idx) {
479 case TBG_A_P:
480 vcodiv = HREAD4(sc, TBG_CTRL8);
481 vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT;
482 vcodiv &= TBG_DIV_MASK;
483 break;
484 case TBG_B_P:
485 vcodiv = HREAD4(sc, TBG_CTRL8);
486 vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT;
487 vcodiv &= TBG_DIV_MASK;
488 break;
489 case TBG_A_S:
490 vcodiv = HREAD4(sc, TBG_CTRL1);
491 vcodiv >>= TBG_A_VCODIV_SE_SHIFT;
492 vcodiv &= TBG_DIV_MASK;
493 break;
494 case TBG_B_S:
495 vcodiv = HREAD4(sc, TBG_CTRL1);
496 vcodiv >>= TBG_B_VCODIV_SE_SHIFT;
497 vcodiv &= TBG_DIV_MASK;
498 break;
499 default:
500 printf("%s: 0x%08x\n", __func__, idx);
501 return 0;
502 }
503
504 reg = HREAD4(sc, TBG_CTRL0);
505 if (idx == TBG_A_P || idx == TBG_A_S)
506 reg >>= TBG_A_FBDIV_SHIFT;
507 else
508 reg >>= TBG_B_FBDIV_SHIFT;
509 reg &= TBG_DIV_MASK;
510 mult = reg << 2;
511
512 reg = HREAD4(sc, TBG_CTRL7);
513 if (idx == TBG_A_P || idx == TBG_A_S)
514 reg >>= TBG_A_REFDIV_SHIFT;
515 else
516 reg >>= TBG_B_REFDIV_SHIFT;
517 reg &= TBG_DIV_MASK;
518 div = reg;
519
520 if (div == 0)
521 div = 1;
522 div *= 1 << vcodiv;
523
524 freq = clock_get_frequency(sc->sc_cd.cd_node, NULL);
525 return (freq * mult) / div;
526 }
527