xref: /openbsd/sys/dev/fdt/mvclock.c (revision dbec3d1c)
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