xref: /openbsd-src/sys/dev/fdt/mvclock.c (revision c020cf82e0cc147236f01a8dca7052034cf9d30d)
1 /*	$OpenBSD: mvclock.c,v 1.7 2020/05/22 10:06:59 patrick 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 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_SDIO		4
160 #define CP110_GATE_SLOW_IO	21
161 
162 uint32_t
163 cp110_get_frequency(void *cookie, uint32_t *cells)
164 {
165 	struct mvclock_softc *sc = cookie;
166 	uint32_t mod = cells[0];
167 	uint32_t idx = cells[1];
168 	uint32_t parent[2] = { 0, 0 };
169 
170 	/* Core clocks */
171 	if (mod == 0) {
172 		switch (idx) {
173 		case CP110_CORE_APLL:
174 			/* fixed PLL at 1GHz */
175 			return 1000000000;
176 		case CP110_CORE_PPV2:
177 			/* PPv2 clock is APLL/3 */
178 			return 333333333;
179 		case CP110_CORE_X2CORE:
180 			/* X2CORE clock is APLL/2 */
181 			return 500000000;
182 		case CP110_CORE_CORE:
183 			/* Core clock is X2CORE/2 */
184 			return 250000000;
185 		case CP110_CORE_SDIO:
186 			/* SDIO clock is APLL/2.5 */
187 			return 400000000;
188 		default:
189 			break;
190 		}
191 	}
192 
193 	/* Gatable clocks */
194 	if (mod == 1) {
195 		switch (idx) {
196 		case CP110_GATE_SDIO:
197 			parent[1] = CP110_CORE_SDIO;
198 			break;
199 		case CP110_GATE_SLOW_IO:
200 			parent[1] = CP110_CORE_X2CORE;
201 			break;
202 		default:
203 			break;
204 		}
205 
206 		if (parent[1] != 0)
207 			return cp110_get_frequency(sc, parent);
208 	}
209 
210 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
211 	return 0;
212 }
213 
214 void
215 cp110_enable(void *cookie, uint32_t *cells, int on)
216 {
217 	struct mvclock_softc *sc = cookie;
218 	uint32_t mod = cells[0];
219 	uint32_t idx = cells[1];
220 
221 	/* Gatable clocks */
222 	if (mod == 1 && idx < 32) {
223 		struct regmap *rm;
224 		uint32_t reg;
225 
226 		rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
227 		if (rm == NULL) {
228 			printf("%s: can't enable clock 0x%08x 0x%08x\n",
229 			    sc->sc_dev.dv_xname, mod, idx);
230 			return;
231 		}
232 		reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL);
233 		if (on)
234 			reg |= (1U << idx);
235 		else
236 			reg &= ~(1U << idx);
237 		regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg);
238 		return;
239 	}
240 
241 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
242 }
243 
244 /* Armada 3700 Periph block */
245 
246 #define PERIPH_NB_MMC			0x0
247 #define PERIPH_NB_SQF			0x7
248 #define PERIPH_NB_I2C2			0x9
249 #define PERIPH_NB_I2C1			0xa
250 #define PERIPH_NB_CPU			0x10
251 #define PERIPH_SB_GBE1_CORE		0x7
252 #define PERIPH_SB_GBE0_CORE		0x8
253 #define PERIPH_SB_USB32_USB2_SYS	0xb
254 #define PERIPH_SB_USB32_SS_SYS		0xc
255 
256 #define PERIPH_TBG_SEL			0x0
257 #define  PERIPH_TBG_SEL_MASK			0x3
258 #define PERIPH_DIV_SEL0			0x4
259 #define PERIPH_DIV_SEL1			0x8
260 #define PERIPH_DIV_SEL2			0xc
261 #define  PERIPH_DIV_SEL_MASK			0x7
262 #define PERIPH_CLK_SEL			0x10
263 #define PERIPH_CLK_DIS			0x14
264 
265 void	 a3700_periph_enable(struct mvclock_softc *, uint32_t, int);
266 uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t);
267 uint32_t a3700_periph_get_div(struct mvclock_softc *, uint32_t, uint32_t);
268 uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t,
269 	   uint32_t, uint32_t);
270 
271 void
272 a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on)
273 {
274 	struct mvclock_softc *sc = cookie;
275 	uint32_t idx = cells[0];
276 
277 	switch (idx) {
278 	case PERIPH_NB_MMC:
279 		return a3700_periph_enable(sc, 2, on);
280 	case PERIPH_NB_SQF:
281 		return a3700_periph_enable(sc, 12, on);
282 	case PERIPH_NB_I2C2:
283 		return a3700_periph_enable(sc, 16, on);
284 	case PERIPH_NB_I2C1:
285 		return a3700_periph_enable(sc, 17, on);
286 	default:
287 		break;
288 	}
289 
290 	printf("%s: 0x%08x\n", __func__, idx);
291 }
292 
293 uint32_t
294 a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells)
295 {
296 	struct mvclock_softc *sc = cookie;
297 	uint32_t idx = cells[0];
298 	uint32_t freq;
299 
300 	switch (idx) {
301 	case PERIPH_NB_MMC:
302 		freq = a3700_periph_tbg_get_frequency(sc, 0);
303 		freq /= a3700_periph_get_double_div(sc,
304 		    PERIPH_DIV_SEL2, 16, 13);
305 		return freq;
306 	case PERIPH_NB_SQF:
307 		freq = a3700_periph_tbg_get_frequency(sc, 12);
308 		freq /= a3700_periph_get_double_div(sc,
309 		    PERIPH_DIV_SEL1, 27, 24);
310 		return freq;
311 	case PERIPH_NB_CPU:
312 		freq = a3700_periph_tbg_get_frequency(sc, 22);
313 		freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL0, 28);
314 		return freq;
315 	default:
316 		break;
317 	}
318 
319 	printf("%s: 0x%08x\n", __func__, idx);
320 	return 0;
321 }
322 
323 void
324 a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on)
325 {
326 	struct mvclock_softc *sc = cookie;
327 	uint32_t idx = cells[0];
328 
329 	switch (idx) {
330 	case PERIPH_SB_GBE1_CORE:
331 		return a3700_periph_enable(sc, 4, on);
332 	case PERIPH_SB_GBE0_CORE:
333 		return a3700_periph_enable(sc, 5, on);
334 	case PERIPH_SB_USB32_USB2_SYS:
335 		return a3700_periph_enable(sc, 16, on);
336 	case PERIPH_SB_USB32_SS_SYS:
337 		return a3700_periph_enable(sc, 17, on);
338 	default:
339 		break;
340 	}
341 
342 	printf("%s: 0x%08x\n", __func__, idx);
343 }
344 
345 uint32_t
346 a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells)
347 {
348 	uint32_t idx = cells[0];
349 
350 	printf("%s: 0x%08x\n", __func__, idx);
351 	return 0;
352 }
353 
354 void
355 a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on)
356 {
357 	uint32_t reg;
358 
359 	reg = HREAD4(sc, PERIPH_CLK_DIS);
360 	reg &= ~(1 << idx);
361 	if (!on)
362 		reg |= (1 << idx);
363 	HWRITE4(sc, PERIPH_CLK_DIS, reg);
364 }
365 
366 uint32_t
367 a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx)
368 {
369 	uint32_t reg;
370 
371 	reg = HREAD4(sc, PERIPH_TBG_SEL);
372 	reg >>= idx;
373 	reg &= PERIPH_TBG_SEL_MASK;
374 
375 	return clock_get_frequency_idx(sc->sc_cd.cd_node, reg);
376 }
377 
378 uint32_t
379 a3700_periph_get_div(struct mvclock_softc *sc, uint32_t off, uint32_t idx)
380 {
381 	uint32_t reg = HREAD4(sc, off);
382 	return ((reg >> idx) & PERIPH_DIV_SEL_MASK);
383 }
384 
385 uint32_t
386 a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off,
387     uint32_t idx0, uint32_t idx1)
388 {
389 	uint32_t reg = HREAD4(sc, off);
390 	return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) *
391 	    ((reg >> idx1) & PERIPH_DIV_SEL_MASK);
392 }
393 
394 /* Armada 3700 TBG block */
395 
396 #define TBG_A_P				0
397 #define TBG_B_P				1
398 #define TBG_A_S				2
399 #define TBG_B_S				3
400 
401 #define TBG_CTRL0			0x4
402 #define  TBG_A_FBDIV_SHIFT			2
403 #define  TBG_B_FBDIV_SHIFT			18
404 #define TBG_CTRL1			0x8
405 #define  TBG_A_VCODIV_SE_SHIFT			0
406 #define  TBG_B_VCODIV_SE_SHIFT			16
407 #define TBG_CTRL7			0x20
408 #define  TBG_A_REFDIV_SHIFT			0
409 #define  TBG_B_REFDIV_SHIFT			16
410 #define TBG_CTRL8			0x30
411 #define  TBG_A_VCODIV_DIFF_SHIFT		1
412 #define  TBG_B_VCODIV_DIFF_SHIFT		17
413 #define TBG_DIV_MASK			0x1ff
414 
415 uint32_t
416 a3700_tbg_get_frequency(void *cookie, uint32_t *cells)
417 {
418 	struct mvclock_softc *sc = cookie;
419 	uint32_t idx = cells[0];
420 	uint64_t mult, div, freq;
421 	uint32_t reg, vcodiv;
422 
423 	switch (idx) {
424 	case TBG_A_P:
425 		vcodiv = HREAD4(sc, TBG_CTRL8);
426 		vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT;
427 		vcodiv &= TBG_DIV_MASK;
428 		break;
429 	case TBG_B_P:
430 		vcodiv = HREAD4(sc, TBG_CTRL8);
431 		vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT;
432 		vcodiv &= TBG_DIV_MASK;
433 		break;
434 	case TBG_A_S:
435 		vcodiv = HREAD4(sc, TBG_CTRL1);
436 		vcodiv >>= TBG_A_VCODIV_SE_SHIFT;
437 		vcodiv &= TBG_DIV_MASK;
438 		break;
439 	case TBG_B_S:
440 		vcodiv = HREAD4(sc, TBG_CTRL1);
441 		vcodiv >>= TBG_B_VCODIV_SE_SHIFT;
442 		vcodiv &= TBG_DIV_MASK;
443 		break;
444 	default:
445 		printf("%s: 0x%08x\n", __func__, idx);
446 		return 0;
447 	}
448 
449 	reg = HREAD4(sc, TBG_CTRL0);
450 	if (idx == TBG_A_P || idx == TBG_A_S)
451 		reg >>= TBG_A_FBDIV_SHIFT;
452 	else
453 		reg >>= TBG_B_FBDIV_SHIFT;
454 	reg &= TBG_DIV_MASK;
455 	mult = reg << 2;
456 
457 	reg = HREAD4(sc, TBG_CTRL7);
458 	if (idx == TBG_A_P || idx == TBG_A_S)
459 		reg >>= TBG_A_REFDIV_SHIFT;
460 	else
461 		reg >>= TBG_B_REFDIV_SHIFT;
462 	reg &= TBG_DIV_MASK;
463 	div = reg;
464 
465 	if (div == 0)
466 		div = 1;
467 	div *= 1 << vcodiv;
468 
469 	freq = clock_get_frequency(sc->sc_cd.cd_node, NULL);
470 	return (freq * mult) / div;
471 }
472