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