xref: /openbsd-src/sys/dev/fdt/mvclock.c (revision 4b70baf6e17fc8b27fc1f7fa7929335753fa94c3)
1 /*	$OpenBSD: mvclock.c,v 1.3 2019/04/30 20:00:25 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_SB_GBE1_CORE		0x7
251 #define PERIPH_SB_GBE0_CORE		0x8
252 #define PERIPH_SB_USB32_USB2_SYS	0xb
253 #define PERIPH_SB_USB32_SS_SYS		0xc
254 
255 #define PERIPH_TBG_SEL			0x0
256 #define  PERIPH_TBG_SEL_MASK			0x3
257 #define PERIPH_DIV_SEL0			0x4
258 #define PERIPH_DIV_SEL1			0x8
259 #define PERIPH_DIV_SEL2			0xc
260 #define  PERIPH_DIV_SEL_MASK			0x7
261 #define PERIPH_CLK_SEL			0x10
262 #define PERIPH_CLK_DIS			0x14
263 
264 void	 a3700_periph_enable(struct mvclock_softc *, uint32_t, int);
265 uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t);
266 uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t,
267 	   uint32_t, uint32_t);
268 
269 void
270 a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on)
271 {
272 	struct mvclock_softc *sc = cookie;
273 	uint32_t idx = cells[0];
274 
275 	switch (idx) {
276 	case PERIPH_NB_MMC:
277 		return a3700_periph_enable(sc, 2, on);
278 	default:
279 		break;
280 	}
281 
282 	printf("%s: 0x%08x\n", __func__, idx);
283 }
284 
285 uint32_t
286 a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells)
287 {
288 	struct mvclock_softc *sc = cookie;
289 	uint32_t idx = cells[0];
290 	uint32_t freq;
291 
292 	switch (idx) {
293 	case PERIPH_NB_MMC:
294 		freq = a3700_periph_tbg_get_frequency(sc, 0);
295 		freq /= a3700_periph_get_double_div(sc,
296 		    PERIPH_DIV_SEL2, 16, 13);
297 		return freq;
298 	default:
299 		break;
300 	}
301 
302 	printf("%s: 0x%08x\n", __func__, idx);
303 	return 0;
304 }
305 
306 void
307 a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on)
308 {
309 	struct mvclock_softc *sc = cookie;
310 	uint32_t idx = cells[0];
311 
312 	switch (idx) {
313 	case PERIPH_SB_GBE1_CORE:
314 		return a3700_periph_enable(sc, 4, on);
315 	case PERIPH_SB_GBE0_CORE:
316 		return a3700_periph_enable(sc, 5, on);
317 	case PERIPH_SB_USB32_USB2_SYS:
318 		return a3700_periph_enable(sc, 16, on);
319 	case PERIPH_SB_USB32_SS_SYS:
320 		return a3700_periph_enable(sc, 17, on);
321 	default:
322 		break;
323 	}
324 
325 	printf("%s: 0x%08x\n", __func__, idx);
326 }
327 
328 uint32_t
329 a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells)
330 {
331 	uint32_t idx = cells[0];
332 
333 	printf("%s: 0x%08x\n", __func__, idx);
334 	return 0;
335 }
336 
337 void
338 a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on)
339 {
340 	uint32_t reg;
341 
342 	reg = HREAD4(sc, PERIPH_CLK_DIS);
343 	reg &= ~(1 << idx);
344 	if (!on)
345 		reg |= (1 << idx);
346 	HWRITE4(sc, PERIPH_CLK_DIS, reg);
347 }
348 
349 uint32_t
350 a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx)
351 {
352 	uint32_t reg;
353 
354 	reg = HREAD4(sc, PERIPH_TBG_SEL);
355 	reg >>= idx;
356 	reg &= PERIPH_TBG_SEL_MASK;
357 
358 	return clock_get_frequency_idx(sc->sc_cd.cd_node, reg);
359 }
360 
361 uint32_t
362 a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off,
363     uint32_t idx0, uint32_t idx1)
364 {
365 	uint32_t reg = HREAD4(sc, off);
366 	return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) *
367 	    ((reg >> idx1) & PERIPH_DIV_SEL_MASK);
368 }
369 
370 /* Armada 3700 TBG block */
371 
372 #define TBG_A_P				0
373 #define TBG_B_P				1
374 #define TBG_A_S				2
375 #define TBG_B_S				3
376 
377 #define TBG_CTRL0			0x4
378 #define  TBG_A_FBDIV_SHIFT			2
379 #define  TBG_B_FBDIV_SHIFT			18
380 #define TBG_CTRL1			0x8
381 #define  TBG_A_VCODIV_SE_SHIFT			0
382 #define  TBG_B_VCODIV_SE_SHIFT			16
383 #define TBG_CTRL7			0x20
384 #define  TBG_A_REFDIV_SHIFT			0
385 #define  TBG_B_REFDIV_SHIFT			16
386 #define TBG_CTRL8			0x30
387 #define  TBG_A_VCODIV_DIFF_SHIFT		1
388 #define  TBG_B_VCODIV_DIFF_SHIFT		17
389 #define TBG_DIV_MASK			0x1ff
390 
391 uint32_t
392 a3700_tbg_get_frequency(void *cookie, uint32_t *cells)
393 {
394 	struct mvclock_softc *sc = cookie;
395 	uint32_t idx = cells[0];
396 	uint64_t mult, div, freq;
397 	uint32_t reg, vcodiv;
398 
399 	switch (idx) {
400 	case TBG_A_P:
401 		vcodiv = HREAD4(sc, TBG_CTRL8);
402 		vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT;
403 		vcodiv &= TBG_DIV_MASK;
404 		break;
405 	case TBG_B_P:
406 		vcodiv = HREAD4(sc, TBG_CTRL8);
407 		vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT;
408 		vcodiv &= TBG_DIV_MASK;
409 		break;
410 	case TBG_A_S:
411 		vcodiv = HREAD4(sc, TBG_CTRL1);
412 		vcodiv >>= TBG_A_VCODIV_SE_SHIFT;
413 		vcodiv &= TBG_DIV_MASK;
414 		break;
415 	case TBG_B_S:
416 		vcodiv = HREAD4(sc, TBG_CTRL1);
417 		vcodiv >>= TBG_B_VCODIV_SE_SHIFT;
418 		vcodiv &= TBG_DIV_MASK;
419 		break;
420 	default:
421 		printf("%s: 0x%08x\n", __func__, idx);
422 		return 0;
423 	}
424 
425 	reg = HREAD4(sc, TBG_CTRL0);
426 	if (idx == TBG_A_P || idx == TBG_A_S)
427 		reg >>= TBG_A_FBDIV_SHIFT;
428 	else
429 		reg >>= TBG_B_FBDIV_SHIFT;
430 	reg &= TBG_DIV_MASK;
431 	mult = reg << 2;
432 
433 	reg = HREAD4(sc, TBG_CTRL7);
434 	if (idx == TBG_A_P || idx == TBG_A_S)
435 		reg >>= TBG_A_REFDIV_SHIFT;
436 	else
437 		reg >>= TBG_B_REFDIV_SHIFT;
438 	reg &= TBG_DIV_MASK;
439 	div = reg;
440 
441 	if (div == 0)
442 		div = 1;
443 	div *= 1 << vcodiv;
444 
445 	freq = clock_get_frequency(sc->sc_cd.cd_node, NULL);
446 	return (freq * mult) / div;
447 }
448 
449 /* Armada 3700 XTAL block */
450 
451 #define XTAL			0xc
452 #define  XTAL_MODE			(1 << 31)
453 
454 uint32_t
455 a3700_xtal_get_frequency(void *cookie, uint32_t *cells)
456 {
457 	struct mvclock_softc *sc = cookie;
458 	struct regmap *rm;
459 
460 	rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
461 	KASSERT(rm != NULL);
462 
463 	if (regmap_read_4(rm, XTAL) & XTAL_MODE)
464 		return 40000000;
465 	else
466 		return 25000000;
467 }
468