xref: /openbsd/sys/dev/fdt/mvclock.c (revision b1ee96c8)
1*b1ee96c8Spatrick /*	$OpenBSD: mvclock.c,v 1.3 2019/04/30 20:00:25 patrick Exp $	*/
233c180b2Skettenis /*
333c180b2Skettenis  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
433c180b2Skettenis  *
533c180b2Skettenis  * Permission to use, copy, modify, and distribute this software for any
633c180b2Skettenis  * purpose with or without fee is hereby granted, provided that the above
733c180b2Skettenis  * copyright notice and this permission notice appear in all copies.
833c180b2Skettenis  *
933c180b2Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1033c180b2Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1133c180b2Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1233c180b2Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1333c180b2Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1433c180b2Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1533c180b2Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1633c180b2Skettenis  */
1733c180b2Skettenis 
1833c180b2Skettenis #include <sys/param.h>
1933c180b2Skettenis #include <sys/systm.h>
2033c180b2Skettenis #include <sys/device.h>
2133c180b2Skettenis 
2233c180b2Skettenis #include <machine/intr.h>
2333c180b2Skettenis #include <machine/bus.h>
2433c180b2Skettenis #include <machine/fdt.h>
2533c180b2Skettenis 
2633c180b2Skettenis #include <dev/ofw/openfirm.h>
2733c180b2Skettenis #include <dev/ofw/ofw_clock.h>
2833c180b2Skettenis #include <dev/ofw/ofw_misc.h>
2933c180b2Skettenis #include <dev/ofw/fdt.h>
3033c180b2Skettenis 
31*b1ee96c8Spatrick #define HREAD4(sc, reg)							\
32*b1ee96c8Spatrick 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
33*b1ee96c8Spatrick #define HWRITE4(sc, reg, val)						\
34*b1ee96c8Spatrick 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
35*b1ee96c8Spatrick #define HSET4(sc, reg, bits)						\
36*b1ee96c8Spatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
37*b1ee96c8Spatrick #define HCLR4(sc, reg, bits)						\
38*b1ee96c8Spatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
39*b1ee96c8Spatrick 
4033c180b2Skettenis struct mvclock_softc {
4133c180b2Skettenis 	struct device		sc_dev;
42*b1ee96c8Spatrick 	bus_space_tag_t		sc_iot;
43*b1ee96c8Spatrick 	bus_space_handle_t	sc_ioh;
4433c180b2Skettenis 
4533c180b2Skettenis 	struct clock_device	sc_cd;
4633c180b2Skettenis };
4733c180b2Skettenis 
4833c180b2Skettenis int mvclock_match(struct device *, void *, void *);
4933c180b2Skettenis void mvclock_attach(struct device *, struct device *, void *);
5033c180b2Skettenis 
5133c180b2Skettenis struct cfattach	mvclock_ca = {
5233c180b2Skettenis 	sizeof (struct mvclock_softc), mvclock_match, mvclock_attach
5333c180b2Skettenis };
5433c180b2Skettenis 
5533c180b2Skettenis struct cfdriver mvclock_cd = {
5633c180b2Skettenis 	NULL, "mvclock", DV_DULL
5733c180b2Skettenis };
5833c180b2Skettenis 
5933c180b2Skettenis uint32_t ap806_get_frequency(void *, uint32_t *);
6033c180b2Skettenis uint32_t cp110_get_frequency(void *, uint32_t *);
6133c180b2Skettenis void	cp110_enable(void *, uint32_t *, int);
6233c180b2Skettenis 
63*b1ee96c8Spatrick void	 a3700_periph_nb_enable(void *, uint32_t *, int);
64*b1ee96c8Spatrick uint32_t a3700_periph_nb_get_frequency(void *, uint32_t *);
65*b1ee96c8Spatrick void	 a3700_periph_sb_enable(void *, uint32_t *, int);
66*b1ee96c8Spatrick uint32_t a3700_periph_sb_get_frequency(void *, uint32_t *);
67*b1ee96c8Spatrick uint32_t a3700_tbg_get_frequency(void *, uint32_t *);
68*b1ee96c8Spatrick uint32_t a3700_xtal_get_frequency(void *, uint32_t *);
69*b1ee96c8Spatrick 
7033c180b2Skettenis int
7133c180b2Skettenis mvclock_match(struct device *parent, void *match, void *aux)
7233c180b2Skettenis {
7333c180b2Skettenis 	struct fdt_attach_args *faa = aux;
74*b1ee96c8Spatrick 	int node = faa->fa_node;
7533c180b2Skettenis 
76*b1ee96c8Spatrick 	return (OF_is_compatible(node, "marvell,ap806-clock") ||
77*b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,cp110-clock") ||
78*b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb") ||
79*b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb") ||
80*b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,armada-3700-tbg-clock") ||
81*b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,armada-3700-xtal-clock"));
8233c180b2Skettenis }
8333c180b2Skettenis 
8433c180b2Skettenis void
8533c180b2Skettenis mvclock_attach(struct device *parent, struct device *self, void *aux)
8633c180b2Skettenis {
8733c180b2Skettenis 	struct mvclock_softc *sc = (struct mvclock_softc *)self;
8833c180b2Skettenis 	struct fdt_attach_args *faa = aux;
89*b1ee96c8Spatrick 	int node = faa->fa_node;
90*b1ee96c8Spatrick 
91*b1ee96c8Spatrick 	if (faa->fa_nreg > 0) {
92*b1ee96c8Spatrick 		sc->sc_iot = faa->fa_iot;
93*b1ee96c8Spatrick 		if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
94*b1ee96c8Spatrick 		    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
95*b1ee96c8Spatrick 			printf(": can't map registers\n");
96*b1ee96c8Spatrick 			return;
97*b1ee96c8Spatrick 		}
98*b1ee96c8Spatrick 	}
9933c180b2Skettenis 
10033c180b2Skettenis 	printf("\n");
10133c180b2Skettenis 
102*b1ee96c8Spatrick 	sc->sc_cd.cd_node = node;
10333c180b2Skettenis 	sc->sc_cd.cd_cookie = sc;
104*b1ee96c8Spatrick 	if (OF_is_compatible(node, "marvell,ap806-clock")) {
10533c180b2Skettenis 		sc->sc_cd.cd_get_frequency = ap806_get_frequency;
106*b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,cp110-clock")) {
10733c180b2Skettenis 		sc->sc_cd.cd_get_frequency = cp110_get_frequency;
10833c180b2Skettenis 		sc->sc_cd.cd_enable = cp110_enable;
109*b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb")) {
110*b1ee96c8Spatrick 		sc->sc_cd.cd_enable = a3700_periph_nb_enable;
111*b1ee96c8Spatrick 		sc->sc_cd.cd_get_frequency = a3700_periph_nb_get_frequency;
112*b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb")) {
113*b1ee96c8Spatrick 		sc->sc_cd.cd_enable = a3700_periph_sb_enable;
114*b1ee96c8Spatrick 		sc->sc_cd.cd_get_frequency = a3700_periph_sb_get_frequency;
115*b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,armada-3700-tbg-clock")) {
116*b1ee96c8Spatrick 		sc->sc_cd.cd_get_frequency = a3700_tbg_get_frequency;
117*b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,armada-3700-xtal-clock")) {
118*b1ee96c8Spatrick 		sc->sc_cd.cd_get_frequency = a3700_xtal_get_frequency;
11933c180b2Skettenis 	}
12033c180b2Skettenis 	clock_register(&sc->sc_cd);
12133c180b2Skettenis }
12233c180b2Skettenis 
12333c180b2Skettenis /* AP806 block */
12433c180b2Skettenis 
12533c180b2Skettenis #define AP806_CORE_FIXED	2
12633c180b2Skettenis #define AP806_CORE_MSS		3
12733c180b2Skettenis #define AP806_CORE_SDIO		4
12833c180b2Skettenis 
12933c180b2Skettenis uint32_t
13033c180b2Skettenis ap806_get_frequency(void *cookie, uint32_t *cells)
13133c180b2Skettenis {
13233c180b2Skettenis 	uint32_t idx = cells[0];
13333c180b2Skettenis 
13433c180b2Skettenis 	switch (idx) {
13533c180b2Skettenis 	case AP806_CORE_FIXED:
13633c180b2Skettenis 		/* fixed PLL at 1200MHz */
13733c180b2Skettenis 		return 1200000000;
13833c180b2Skettenis 	case AP806_CORE_MSS:
13933c180b2Skettenis 		/* MSS clock is fixed clock divided by 6 */
14033c180b2Skettenis 		return 200000000;
14133c180b2Skettenis 	case AP806_CORE_SDIO:
14233c180b2Skettenis 		/* SDIO/eMMC clock is fixed clock divided by 3 */
14333c180b2Skettenis 		return 400000000;
14433c180b2Skettenis 	default:
14533c180b2Skettenis 		break;
14633c180b2Skettenis 	}
14733c180b2Skettenis 
14833c180b2Skettenis 	printf("%s: 0x%08x\n", __func__, idx);
14933c180b2Skettenis 	return 0;
15033c180b2Skettenis }
15133c180b2Skettenis 
15233c180b2Skettenis /* CP110 block */
15333c180b2Skettenis 
15433c180b2Skettenis #define CP110_PM_CLOCK_GATING_CTRL	0x220
15533c180b2Skettenis 
15633c180b2Skettenis #define CP110_CORE_APLL		0
15733c180b2Skettenis #define CP110_CORE_PPV2		1
158001c60baSkettenis #define CP110_CORE_X2CORE	2
15933c180b2Skettenis #define CP110_CORE_CORE		3
16033c180b2Skettenis #define CP110_CORE_SDIO		5
16133c180b2Skettenis 
16233c180b2Skettenis #define CP110_GATE_SDIO		4
163001c60baSkettenis #define CP110_GATE_SLOW_IO	21
16433c180b2Skettenis 
16533c180b2Skettenis uint32_t
16633c180b2Skettenis cp110_get_frequency(void *cookie, uint32_t *cells)
16733c180b2Skettenis {
16833c180b2Skettenis 	struct mvclock_softc *sc = cookie;
16933c180b2Skettenis 	uint32_t mod = cells[0];
17033c180b2Skettenis 	uint32_t idx = cells[1];
17133c180b2Skettenis 	uint32_t parent[2] = { 0, 0 };
17233c180b2Skettenis 
17333c180b2Skettenis 	/* Core clocks */
17433c180b2Skettenis 	if (mod == 0) {
17533c180b2Skettenis 		switch (idx) {
17633c180b2Skettenis 		case CP110_CORE_APLL:
17733c180b2Skettenis 			/* fixed PLL at 1GHz */
17833c180b2Skettenis 			return 1000000000;
17933c180b2Skettenis 		case CP110_CORE_PPV2:
18033c180b2Skettenis 			/* PPv2 clock is APLL/3 */
18133c180b2Skettenis 			return 333333333;
182001c60baSkettenis 		case CP110_CORE_X2CORE:
183001c60baSkettenis 			/* X2CORE clock is APLL/2 */
18433c180b2Skettenis 			return 500000000;
18533c180b2Skettenis 		case CP110_CORE_CORE:
186001c60baSkettenis 			/* Core clock is X2CORE/2 */
18733c180b2Skettenis 			return 250000000;
18833c180b2Skettenis 		case CP110_CORE_SDIO:
18933c180b2Skettenis 			/* SDIO clock is APLL/2.5 */
19033c180b2Skettenis 			return 400000000;
19133c180b2Skettenis 		default:
19233c180b2Skettenis 			break;
19333c180b2Skettenis 		}
19433c180b2Skettenis 	}
19533c180b2Skettenis 
19633c180b2Skettenis 	/* Gatable clocks */
19733c180b2Skettenis 	if (mod == 1) {
19833c180b2Skettenis 		switch (idx) {
19933c180b2Skettenis 		case CP110_GATE_SDIO:
20033c180b2Skettenis 			parent[1] = CP110_CORE_SDIO;
20133c180b2Skettenis 			break;
202001c60baSkettenis 		case CP110_GATE_SLOW_IO:
203001c60baSkettenis 			parent[1] = CP110_CORE_X2CORE;
204001c60baSkettenis 			break;
20533c180b2Skettenis 		default:
20633c180b2Skettenis 			break;
20733c180b2Skettenis 		}
20833c180b2Skettenis 
20933c180b2Skettenis 		if (parent[1] != 0)
21033c180b2Skettenis 			return cp110_get_frequency(sc, parent);
21133c180b2Skettenis 	}
21233c180b2Skettenis 
21333c180b2Skettenis 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
21433c180b2Skettenis 	return 0;
21533c180b2Skettenis }
21633c180b2Skettenis 
21733c180b2Skettenis void
21833c180b2Skettenis cp110_enable(void *cookie, uint32_t *cells, int on)
21933c180b2Skettenis {
22033c180b2Skettenis 	struct mvclock_softc *sc = cookie;
22133c180b2Skettenis 	uint32_t mod = cells[0];
22233c180b2Skettenis 	uint32_t idx = cells[1];
22333c180b2Skettenis 
22433c180b2Skettenis 	/* Gatable clocks */
22533c180b2Skettenis 	if (mod == 1 && idx < 32) {
22633c180b2Skettenis 		struct regmap *rm;
22733c180b2Skettenis 		uint32_t reg;
22833c180b2Skettenis 
22933c180b2Skettenis 		rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
23033c180b2Skettenis 		if (rm == NULL) {
23133c180b2Skettenis 			printf("%s: can't enable clock 0x%08x 0x%08x\n",
23233c180b2Skettenis 			    sc->sc_dev.dv_xname, mod, idx);
23333c180b2Skettenis 			return;
23433c180b2Skettenis 		}
23533c180b2Skettenis 		reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL);
23633c180b2Skettenis 		if (on)
23733c180b2Skettenis 			reg |= (1U << idx);
23833c180b2Skettenis 		else
23933c180b2Skettenis 			reg &= ~(1U << idx);
24033c180b2Skettenis 		regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg);
24133c180b2Skettenis 		return;
24233c180b2Skettenis 	}
24333c180b2Skettenis 
24433c180b2Skettenis 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
24533c180b2Skettenis }
246*b1ee96c8Spatrick 
247*b1ee96c8Spatrick /* Armada 3700 Periph block */
248*b1ee96c8Spatrick 
249*b1ee96c8Spatrick #define PERIPH_NB_MMC			0x0
250*b1ee96c8Spatrick #define PERIPH_SB_GBE1_CORE		0x7
251*b1ee96c8Spatrick #define PERIPH_SB_GBE0_CORE		0x8
252*b1ee96c8Spatrick #define PERIPH_SB_USB32_USB2_SYS	0xb
253*b1ee96c8Spatrick #define PERIPH_SB_USB32_SS_SYS		0xc
254*b1ee96c8Spatrick 
255*b1ee96c8Spatrick #define PERIPH_TBG_SEL			0x0
256*b1ee96c8Spatrick #define  PERIPH_TBG_SEL_MASK			0x3
257*b1ee96c8Spatrick #define PERIPH_DIV_SEL0			0x4
258*b1ee96c8Spatrick #define PERIPH_DIV_SEL1			0x8
259*b1ee96c8Spatrick #define PERIPH_DIV_SEL2			0xc
260*b1ee96c8Spatrick #define  PERIPH_DIV_SEL_MASK			0x7
261*b1ee96c8Spatrick #define PERIPH_CLK_SEL			0x10
262*b1ee96c8Spatrick #define PERIPH_CLK_DIS			0x14
263*b1ee96c8Spatrick 
264*b1ee96c8Spatrick void	 a3700_periph_enable(struct mvclock_softc *, uint32_t, int);
265*b1ee96c8Spatrick uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t);
266*b1ee96c8Spatrick uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t,
267*b1ee96c8Spatrick 	   uint32_t, uint32_t);
268*b1ee96c8Spatrick 
269*b1ee96c8Spatrick void
270*b1ee96c8Spatrick a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on)
271*b1ee96c8Spatrick {
272*b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
273*b1ee96c8Spatrick 	uint32_t idx = cells[0];
274*b1ee96c8Spatrick 
275*b1ee96c8Spatrick 	switch (idx) {
276*b1ee96c8Spatrick 	case PERIPH_NB_MMC:
277*b1ee96c8Spatrick 		return a3700_periph_enable(sc, 2, on);
278*b1ee96c8Spatrick 	default:
279*b1ee96c8Spatrick 		break;
280*b1ee96c8Spatrick 	}
281*b1ee96c8Spatrick 
282*b1ee96c8Spatrick 	printf("%s: 0x%08x\n", __func__, idx);
283*b1ee96c8Spatrick }
284*b1ee96c8Spatrick 
285*b1ee96c8Spatrick uint32_t
286*b1ee96c8Spatrick a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells)
287*b1ee96c8Spatrick {
288*b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
289*b1ee96c8Spatrick 	uint32_t idx = cells[0];
290*b1ee96c8Spatrick 	uint32_t freq;
291*b1ee96c8Spatrick 
292*b1ee96c8Spatrick 	switch (idx) {
293*b1ee96c8Spatrick 	case PERIPH_NB_MMC:
294*b1ee96c8Spatrick 		freq = a3700_periph_tbg_get_frequency(sc, 0);
295*b1ee96c8Spatrick 		freq /= a3700_periph_get_double_div(sc,
296*b1ee96c8Spatrick 		    PERIPH_DIV_SEL2, 16, 13);
297*b1ee96c8Spatrick 		return freq;
298*b1ee96c8Spatrick 	default:
299*b1ee96c8Spatrick 		break;
300*b1ee96c8Spatrick 	}
301*b1ee96c8Spatrick 
302*b1ee96c8Spatrick 	printf("%s: 0x%08x\n", __func__, idx);
303*b1ee96c8Spatrick 	return 0;
304*b1ee96c8Spatrick }
305*b1ee96c8Spatrick 
306*b1ee96c8Spatrick void
307*b1ee96c8Spatrick a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on)
308*b1ee96c8Spatrick {
309*b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
310*b1ee96c8Spatrick 	uint32_t idx = cells[0];
311*b1ee96c8Spatrick 
312*b1ee96c8Spatrick 	switch (idx) {
313*b1ee96c8Spatrick 	case PERIPH_SB_GBE1_CORE:
314*b1ee96c8Spatrick 		return a3700_periph_enable(sc, 4, on);
315*b1ee96c8Spatrick 	case PERIPH_SB_GBE0_CORE:
316*b1ee96c8Spatrick 		return a3700_periph_enable(sc, 5, on);
317*b1ee96c8Spatrick 	case PERIPH_SB_USB32_USB2_SYS:
318*b1ee96c8Spatrick 		return a3700_periph_enable(sc, 16, on);
319*b1ee96c8Spatrick 	case PERIPH_SB_USB32_SS_SYS:
320*b1ee96c8Spatrick 		return a3700_periph_enable(sc, 17, on);
321*b1ee96c8Spatrick 	default:
322*b1ee96c8Spatrick 		break;
323*b1ee96c8Spatrick 	}
324*b1ee96c8Spatrick 
325*b1ee96c8Spatrick 	printf("%s: 0x%08x\n", __func__, idx);
326*b1ee96c8Spatrick }
327*b1ee96c8Spatrick 
328*b1ee96c8Spatrick uint32_t
329*b1ee96c8Spatrick a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells)
330*b1ee96c8Spatrick {
331*b1ee96c8Spatrick 	uint32_t idx = cells[0];
332*b1ee96c8Spatrick 
333*b1ee96c8Spatrick 	printf("%s: 0x%08x\n", __func__, idx);
334*b1ee96c8Spatrick 	return 0;
335*b1ee96c8Spatrick }
336*b1ee96c8Spatrick 
337*b1ee96c8Spatrick void
338*b1ee96c8Spatrick a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on)
339*b1ee96c8Spatrick {
340*b1ee96c8Spatrick 	uint32_t reg;
341*b1ee96c8Spatrick 
342*b1ee96c8Spatrick 	reg = HREAD4(sc, PERIPH_CLK_DIS);
343*b1ee96c8Spatrick 	reg &= ~(1 << idx);
344*b1ee96c8Spatrick 	if (!on)
345*b1ee96c8Spatrick 		reg |= (1 << idx);
346*b1ee96c8Spatrick 	HWRITE4(sc, PERIPH_CLK_DIS, reg);
347*b1ee96c8Spatrick }
348*b1ee96c8Spatrick 
349*b1ee96c8Spatrick uint32_t
350*b1ee96c8Spatrick a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx)
351*b1ee96c8Spatrick {
352*b1ee96c8Spatrick 	uint32_t reg;
353*b1ee96c8Spatrick 
354*b1ee96c8Spatrick 	reg = HREAD4(sc, PERIPH_TBG_SEL);
355*b1ee96c8Spatrick 	reg >>= idx;
356*b1ee96c8Spatrick 	reg &= PERIPH_TBG_SEL_MASK;
357*b1ee96c8Spatrick 
358*b1ee96c8Spatrick 	return clock_get_frequency_idx(sc->sc_cd.cd_node, reg);
359*b1ee96c8Spatrick }
360*b1ee96c8Spatrick 
361*b1ee96c8Spatrick uint32_t
362*b1ee96c8Spatrick a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off,
363*b1ee96c8Spatrick     uint32_t idx0, uint32_t idx1)
364*b1ee96c8Spatrick {
365*b1ee96c8Spatrick 	uint32_t reg = HREAD4(sc, off);
366*b1ee96c8Spatrick 	return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) *
367*b1ee96c8Spatrick 	    ((reg >> idx1) & PERIPH_DIV_SEL_MASK);
368*b1ee96c8Spatrick }
369*b1ee96c8Spatrick 
370*b1ee96c8Spatrick /* Armada 3700 TBG block */
371*b1ee96c8Spatrick 
372*b1ee96c8Spatrick #define TBG_A_P				0
373*b1ee96c8Spatrick #define TBG_B_P				1
374*b1ee96c8Spatrick #define TBG_A_S				2
375*b1ee96c8Spatrick #define TBG_B_S				3
376*b1ee96c8Spatrick 
377*b1ee96c8Spatrick #define TBG_CTRL0			0x4
378*b1ee96c8Spatrick #define  TBG_A_FBDIV_SHIFT			2
379*b1ee96c8Spatrick #define  TBG_B_FBDIV_SHIFT			18
380*b1ee96c8Spatrick #define TBG_CTRL1			0x8
381*b1ee96c8Spatrick #define  TBG_A_VCODIV_SE_SHIFT			0
382*b1ee96c8Spatrick #define  TBG_B_VCODIV_SE_SHIFT			16
383*b1ee96c8Spatrick #define TBG_CTRL7			0x20
384*b1ee96c8Spatrick #define  TBG_A_REFDIV_SHIFT			0
385*b1ee96c8Spatrick #define  TBG_B_REFDIV_SHIFT			16
386*b1ee96c8Spatrick #define TBG_CTRL8			0x30
387*b1ee96c8Spatrick #define  TBG_A_VCODIV_DIFF_SHIFT		1
388*b1ee96c8Spatrick #define  TBG_B_VCODIV_DIFF_SHIFT		17
389*b1ee96c8Spatrick #define TBG_DIV_MASK			0x1ff
390*b1ee96c8Spatrick 
391*b1ee96c8Spatrick uint32_t
392*b1ee96c8Spatrick a3700_tbg_get_frequency(void *cookie, uint32_t *cells)
393*b1ee96c8Spatrick {
394*b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
395*b1ee96c8Spatrick 	uint32_t idx = cells[0];
396*b1ee96c8Spatrick 	uint64_t mult, div, freq;
397*b1ee96c8Spatrick 	uint32_t reg, vcodiv;
398*b1ee96c8Spatrick 
399*b1ee96c8Spatrick 	switch (idx) {
400*b1ee96c8Spatrick 	case TBG_A_P:
401*b1ee96c8Spatrick 		vcodiv = HREAD4(sc, TBG_CTRL8);
402*b1ee96c8Spatrick 		vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT;
403*b1ee96c8Spatrick 		vcodiv &= TBG_DIV_MASK;
404*b1ee96c8Spatrick 		break;
405*b1ee96c8Spatrick 	case TBG_B_P:
406*b1ee96c8Spatrick 		vcodiv = HREAD4(sc, TBG_CTRL8);
407*b1ee96c8Spatrick 		vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT;
408*b1ee96c8Spatrick 		vcodiv &= TBG_DIV_MASK;
409*b1ee96c8Spatrick 		break;
410*b1ee96c8Spatrick 	case TBG_A_S:
411*b1ee96c8Spatrick 		vcodiv = HREAD4(sc, TBG_CTRL1);
412*b1ee96c8Spatrick 		vcodiv >>= TBG_A_VCODIV_SE_SHIFT;
413*b1ee96c8Spatrick 		vcodiv &= TBG_DIV_MASK;
414*b1ee96c8Spatrick 		break;
415*b1ee96c8Spatrick 	case TBG_B_S:
416*b1ee96c8Spatrick 		vcodiv = HREAD4(sc, TBG_CTRL1);
417*b1ee96c8Spatrick 		vcodiv >>= TBG_B_VCODIV_SE_SHIFT;
418*b1ee96c8Spatrick 		vcodiv &= TBG_DIV_MASK;
419*b1ee96c8Spatrick 		break;
420*b1ee96c8Spatrick 	default:
421*b1ee96c8Spatrick 		printf("%s: 0x%08x\n", __func__, idx);
422*b1ee96c8Spatrick 		return 0;
423*b1ee96c8Spatrick 	}
424*b1ee96c8Spatrick 
425*b1ee96c8Spatrick 	reg = HREAD4(sc, TBG_CTRL0);
426*b1ee96c8Spatrick 	if (idx == TBG_A_P || idx == TBG_A_S)
427*b1ee96c8Spatrick 		reg >>= TBG_A_FBDIV_SHIFT;
428*b1ee96c8Spatrick 	else
429*b1ee96c8Spatrick 		reg >>= TBG_B_FBDIV_SHIFT;
430*b1ee96c8Spatrick 	reg &= TBG_DIV_MASK;
431*b1ee96c8Spatrick 	mult = reg << 2;
432*b1ee96c8Spatrick 
433*b1ee96c8Spatrick 	reg = HREAD4(sc, TBG_CTRL7);
434*b1ee96c8Spatrick 	if (idx == TBG_A_P || idx == TBG_A_S)
435*b1ee96c8Spatrick 		reg >>= TBG_A_REFDIV_SHIFT;
436*b1ee96c8Spatrick 	else
437*b1ee96c8Spatrick 		reg >>= TBG_B_REFDIV_SHIFT;
438*b1ee96c8Spatrick 	reg &= TBG_DIV_MASK;
439*b1ee96c8Spatrick 	div = reg;
440*b1ee96c8Spatrick 
441*b1ee96c8Spatrick 	if (div == 0)
442*b1ee96c8Spatrick 		div = 1;
443*b1ee96c8Spatrick 	div *= 1 << vcodiv;
444*b1ee96c8Spatrick 
445*b1ee96c8Spatrick 	freq = clock_get_frequency(sc->sc_cd.cd_node, NULL);
446*b1ee96c8Spatrick 	return (freq * mult) / div;
447*b1ee96c8Spatrick }
448*b1ee96c8Spatrick 
449*b1ee96c8Spatrick /* Armada 3700 XTAL block */
450*b1ee96c8Spatrick 
451*b1ee96c8Spatrick #define XTAL			0xc
452*b1ee96c8Spatrick #define  XTAL_MODE			(1 << 31)
453*b1ee96c8Spatrick 
454*b1ee96c8Spatrick uint32_t
455*b1ee96c8Spatrick a3700_xtal_get_frequency(void *cookie, uint32_t *cells)
456*b1ee96c8Spatrick {
457*b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
458*b1ee96c8Spatrick 	struct regmap *rm;
459*b1ee96c8Spatrick 
460*b1ee96c8Spatrick 	rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
461*b1ee96c8Spatrick 	KASSERT(rm != NULL);
462*b1ee96c8Spatrick 
463*b1ee96c8Spatrick 	if (regmap_read_4(rm, XTAL) & XTAL_MODE)
464*b1ee96c8Spatrick 		return 40000000;
465*b1ee96c8Spatrick 	else
466*b1ee96c8Spatrick 		return 25000000;
467*b1ee96c8Spatrick }
468