xref: /openbsd/sys/dev/fdt/mvclock.c (revision 73471bf0)
1 /*	$OpenBSD: mvclock.c,v 1.9 2021/10/24 17:52:26 mpi 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
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 	return (OF_is_compatible(node, "marvell,ap806-clock") ||
76 	    OF_is_compatible(node, "marvell,cp110-clock") ||
77 	    OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb") ||
78 	    OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb") ||
79 	    OF_is_compatible(node, "marvell,armada-3700-tbg-clock") ||
80 	    OF_is_compatible(node, "marvell,armada-3700-xtal-clock"));
81 }
82 
83 void
84 mvclock_attach(struct device *parent, struct device *self, void *aux)
85 {
86 	struct mvclock_softc *sc = (struct mvclock_softc *)self;
87 	struct fdt_attach_args *faa = aux;
88 	int node = faa->fa_node;
89 
90 	if (faa->fa_nreg > 0) {
91 		sc->sc_iot = faa->fa_iot;
92 		if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
93 		    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
94 			printf(": can't map registers\n");
95 			return;
96 		}
97 	}
98 
99 	printf("\n");
100 
101 	sc->sc_cd.cd_node = node;
102 	sc->sc_cd.cd_cookie = sc;
103 	if (OF_is_compatible(node, "marvell,ap806-clock")) {
104 		sc->sc_cd.cd_get_frequency = ap806_get_frequency;
105 	} else if (OF_is_compatible(node, "marvell,cp110-clock")) {
106 		sc->sc_cd.cd_get_frequency = cp110_get_frequency;
107 		sc->sc_cd.cd_enable = cp110_enable;
108 	} else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb")) {
109 		sc->sc_cd.cd_enable = a3700_periph_nb_enable;
110 		sc->sc_cd.cd_get_frequency = a3700_periph_nb_get_frequency;
111 	} else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb")) {
112 		sc->sc_cd.cd_enable = a3700_periph_sb_enable;
113 		sc->sc_cd.cd_get_frequency = a3700_periph_sb_get_frequency;
114 	} else if (OF_is_compatible(node, "marvell,armada-3700-tbg-clock")) {
115 		sc->sc_cd.cd_get_frequency = a3700_tbg_get_frequency;
116 	}
117 	clock_register(&sc->sc_cd);
118 }
119 
120 /* AP806 block */
121 
122 #define AP806_CORE_FIXED	2
123 #define AP806_CORE_MSS		3
124 #define AP806_CORE_SDIO		4
125 
126 uint32_t
127 ap806_get_frequency(void *cookie, uint32_t *cells)
128 {
129 	uint32_t idx = cells[0];
130 
131 	switch (idx) {
132 	case AP806_CORE_FIXED:
133 		/* fixed PLL at 1200MHz */
134 		return 1200000000;
135 	case AP806_CORE_MSS:
136 		/* MSS clock is fixed clock divided by 6 */
137 		return 200000000;
138 	case AP806_CORE_SDIO:
139 		/* SDIO/eMMC clock is fixed clock divided by 3 */
140 		return 400000000;
141 	default:
142 		break;
143 	}
144 
145 	printf("%s: 0x%08x\n", __func__, idx);
146 	return 0;
147 }
148 
149 /* CP110 block */
150 
151 #define CP110_PM_CLOCK_GATING_CTRL	0x220
152 
153 #define CP110_CORE_APLL		0
154 #define CP110_CORE_PPV2		1
155 #define CP110_CORE_X2CORE	2
156 #define CP110_CORE_CORE		3
157 #define CP110_CORE_SDIO		5
158 
159 #define CP110_GATE_PPV2		3
160 #define CP110_GATE_SDIO		4
161 #define CP110_GATE_SLOW_IO	21
162 
163 uint32_t
164 cp110_get_frequency(void *cookie, uint32_t *cells)
165 {
166 	struct mvclock_softc *sc = cookie;
167 	uint32_t mod = cells[0];
168 	uint32_t idx = cells[1];
169 	uint32_t parent[2] = { 0, 0 };
170 
171 	/* Core clocks */
172 	if (mod == 0) {
173 		switch (idx) {
174 		case CP110_CORE_APLL:
175 			/* fixed PLL at 1GHz */
176 			return 1000000000;
177 		case CP110_CORE_PPV2:
178 			/* PPv2 clock is APLL/3 */
179 			return 333333333;
180 		case CP110_CORE_X2CORE:
181 			/* X2CORE clock is APLL/2 */
182 			return 500000000;
183 		case CP110_CORE_CORE:
184 			/* Core clock is X2CORE/2 */
185 			return 250000000;
186 		case CP110_CORE_SDIO:
187 			/* SDIO clock is APLL/2.5 */
188 			return 400000000;
189 		default:
190 			break;
191 		}
192 	}
193 
194 	/* Gatable clocks */
195 	if (mod == 1) {
196 		switch (idx) {
197 		case CP110_GATE_PPV2:
198 			parent[1] = CP110_CORE_PPV2;
199 			break;
200 		case CP110_GATE_SDIO:
201 			parent[1] = CP110_CORE_SDIO;
202 			break;
203 		case CP110_GATE_SLOW_IO:
204 			parent[1] = CP110_CORE_X2CORE;
205 			break;
206 		default:
207 			break;
208 		}
209 
210 		if (parent[1] != 0)
211 			return cp110_get_frequency(sc, parent);
212 	}
213 
214 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
215 	return 0;
216 }
217 
218 void
219 cp110_enable(void *cookie, uint32_t *cells, int on)
220 {
221 	struct mvclock_softc *sc = cookie;
222 	uint32_t mod = cells[0];
223 	uint32_t idx = cells[1];
224 
225 	/* Gatable clocks */
226 	if (mod == 1 && idx < 32) {
227 		struct regmap *rm;
228 		uint32_t reg;
229 
230 		rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
231 		if (rm == NULL) {
232 			printf("%s: can't enable clock 0x%08x 0x%08x\n",
233 			    sc->sc_dev.dv_xname, mod, idx);
234 			return;
235 		}
236 		reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL);
237 		if (on)
238 			reg |= (1U << idx);
239 		else
240 			reg &= ~(1U << idx);
241 		regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg);
242 		return;
243 	}
244 
245 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
246 }
247 
248 /* Armada 3700 Periph block */
249 
250 #define PERIPH_NB_MMC			0x0
251 #define PERIPH_NB_SQF			0x7
252 #define PERIPH_NB_I2C2			0x9
253 #define PERIPH_NB_I2C1			0xa
254 #define PERIPH_NB_CPU			0x10
255 #define PERIPH_SB_GBE1_CORE		0x7
256 #define PERIPH_SB_GBE0_CORE		0x8
257 #define PERIPH_SB_USB32_USB2_SYS	0xb
258 #define PERIPH_SB_USB32_SS_SYS		0xc
259 
260 #define PERIPH_TBG_SEL			0x0
261 #define  PERIPH_TBG_SEL_MASK			0x3
262 #define PERIPH_DIV_SEL0			0x4
263 #define PERIPH_DIV_SEL1			0x8
264 #define PERIPH_DIV_SEL2			0xc
265 #define  PERIPH_DIV_SEL_MASK			0x7
266 #define PERIPH_CLK_SEL			0x10
267 #define PERIPH_CLK_DIS			0x14
268 
269 void	 a3700_periph_enable(struct mvclock_softc *, uint32_t, int);
270 uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t);
271 uint32_t a3700_periph_get_div(struct mvclock_softc *, uint32_t, uint32_t);
272 uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t,
273 	   uint32_t, uint32_t);
274 
275 void
276 a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on)
277 {
278 	struct mvclock_softc *sc = cookie;
279 	uint32_t idx = cells[0];
280 
281 	switch (idx) {
282 	case PERIPH_NB_MMC:
283 		return a3700_periph_enable(sc, 2, on);
284 	case PERIPH_NB_SQF:
285 		return a3700_periph_enable(sc, 12, on);
286 	case PERIPH_NB_I2C2:
287 		return a3700_periph_enable(sc, 16, on);
288 	case PERIPH_NB_I2C1:
289 		return a3700_periph_enable(sc, 17, on);
290 	default:
291 		break;
292 	}
293 
294 	printf("%s: 0x%08x\n", __func__, idx);
295 }
296 
297 uint32_t
298 a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells)
299 {
300 	struct mvclock_softc *sc = cookie;
301 	uint32_t idx = cells[0];
302 	uint32_t freq;
303 
304 	switch (idx) {
305 	case PERIPH_NB_MMC:
306 		freq = a3700_periph_tbg_get_frequency(sc, 0);
307 		freq /= a3700_periph_get_double_div(sc,
308 		    PERIPH_DIV_SEL2, 16, 13);
309 		return freq;
310 	case PERIPH_NB_SQF:
311 		freq = a3700_periph_tbg_get_frequency(sc, 12);
312 		freq /= a3700_periph_get_double_div(sc,
313 		    PERIPH_DIV_SEL1, 27, 24);
314 		return freq;
315 	case PERIPH_NB_CPU:
316 		freq = a3700_periph_tbg_get_frequency(sc, 22);
317 		freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL0, 28);
318 		return freq;
319 	default:
320 		break;
321 	}
322 
323 	printf("%s: 0x%08x\n", __func__, idx);
324 	return 0;
325 }
326 
327 void
328 a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on)
329 {
330 	struct mvclock_softc *sc = cookie;
331 	uint32_t idx = cells[0];
332 
333 	switch (idx) {
334 	case PERIPH_SB_GBE1_CORE:
335 		return a3700_periph_enable(sc, 4, on);
336 	case PERIPH_SB_GBE0_CORE:
337 		return a3700_periph_enable(sc, 5, on);
338 	case PERIPH_SB_USB32_USB2_SYS:
339 		return a3700_periph_enable(sc, 16, on);
340 	case PERIPH_SB_USB32_SS_SYS:
341 		return a3700_periph_enable(sc, 17, on);
342 	default:
343 		break;
344 	}
345 
346 	printf("%s: 0x%08x\n", __func__, idx);
347 }
348 
349 uint32_t
350 a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells)
351 {
352 	uint32_t idx = cells[0];
353 
354 	printf("%s: 0x%08x\n", __func__, idx);
355 	return 0;
356 }
357 
358 void
359 a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on)
360 {
361 	uint32_t reg;
362 
363 	reg = HREAD4(sc, PERIPH_CLK_DIS);
364 	reg &= ~(1 << idx);
365 	if (!on)
366 		reg |= (1 << idx);
367 	HWRITE4(sc, PERIPH_CLK_DIS, reg);
368 }
369 
370 uint32_t
371 a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx)
372 {
373 	uint32_t reg;
374 
375 	reg = HREAD4(sc, PERIPH_TBG_SEL);
376 	reg >>= idx;
377 	reg &= PERIPH_TBG_SEL_MASK;
378 
379 	return clock_get_frequency_idx(sc->sc_cd.cd_node, reg);
380 }
381 
382 uint32_t
383 a3700_periph_get_div(struct mvclock_softc *sc, uint32_t off, uint32_t idx)
384 {
385 	uint32_t reg = HREAD4(sc, off);
386 	return ((reg >> idx) & PERIPH_DIV_SEL_MASK);
387 }
388 
389 uint32_t
390 a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off,
391     uint32_t idx0, uint32_t idx1)
392 {
393 	uint32_t reg = HREAD4(sc, off);
394 	return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) *
395 	    ((reg >> idx1) & PERIPH_DIV_SEL_MASK);
396 }
397 
398 /* Armada 3700 TBG block */
399 
400 #define TBG_A_P				0
401 #define TBG_B_P				1
402 #define TBG_A_S				2
403 #define TBG_B_S				3
404 
405 #define TBG_CTRL0			0x4
406 #define  TBG_A_FBDIV_SHIFT			2
407 #define  TBG_B_FBDIV_SHIFT			18
408 #define TBG_CTRL1			0x8
409 #define  TBG_A_VCODIV_SE_SHIFT			0
410 #define  TBG_B_VCODIV_SE_SHIFT			16
411 #define TBG_CTRL7			0x20
412 #define  TBG_A_REFDIV_SHIFT			0
413 #define  TBG_B_REFDIV_SHIFT			16
414 #define TBG_CTRL8			0x30
415 #define  TBG_A_VCODIV_DIFF_SHIFT		1
416 #define  TBG_B_VCODIV_DIFF_SHIFT		17
417 #define TBG_DIV_MASK			0x1ff
418 
419 uint32_t
420 a3700_tbg_get_frequency(void *cookie, uint32_t *cells)
421 {
422 	struct mvclock_softc *sc = cookie;
423 	uint32_t idx = cells[0];
424 	uint64_t mult, div, freq;
425 	uint32_t reg, vcodiv;
426 
427 	switch (idx) {
428 	case TBG_A_P:
429 		vcodiv = HREAD4(sc, TBG_CTRL8);
430 		vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT;
431 		vcodiv &= TBG_DIV_MASK;
432 		break;
433 	case TBG_B_P:
434 		vcodiv = HREAD4(sc, TBG_CTRL8);
435 		vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT;
436 		vcodiv &= TBG_DIV_MASK;
437 		break;
438 	case TBG_A_S:
439 		vcodiv = HREAD4(sc, TBG_CTRL1);
440 		vcodiv >>= TBG_A_VCODIV_SE_SHIFT;
441 		vcodiv &= TBG_DIV_MASK;
442 		break;
443 	case TBG_B_S:
444 		vcodiv = HREAD4(sc, TBG_CTRL1);
445 		vcodiv >>= TBG_B_VCODIV_SE_SHIFT;
446 		vcodiv &= TBG_DIV_MASK;
447 		break;
448 	default:
449 		printf("%s: 0x%08x\n", __func__, idx);
450 		return 0;
451 	}
452 
453 	reg = HREAD4(sc, TBG_CTRL0);
454 	if (idx == TBG_A_P || idx == TBG_A_S)
455 		reg >>= TBG_A_FBDIV_SHIFT;
456 	else
457 		reg >>= TBG_B_FBDIV_SHIFT;
458 	reg &= TBG_DIV_MASK;
459 	mult = reg << 2;
460 
461 	reg = HREAD4(sc, TBG_CTRL7);
462 	if (idx == TBG_A_P || idx == TBG_A_S)
463 		reg >>= TBG_A_REFDIV_SHIFT;
464 	else
465 		reg >>= TBG_B_REFDIV_SHIFT;
466 	reg &= TBG_DIV_MASK;
467 	div = reg;
468 
469 	if (div == 0)
470 		div = 1;
471 	div *= 1 << vcodiv;
472 
473 	freq = clock_get_frequency(sc->sc_cd.cd_node, NULL);
474 	return (freq * mult) / div;
475 }
476