xref: /openbsd/sys/dev/fdt/mvclock.c (revision dbec3d1c)
1*dbec3d1cSdlg /*	$OpenBSD: mvclock.c,v 1.13 2022/06/05 02:43:44 dlg 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 
31b1ee96c8Spatrick #define HREAD4(sc, reg)							\
32b1ee96c8Spatrick 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
33b1ee96c8Spatrick #define HWRITE4(sc, reg, val)						\
34b1ee96c8Spatrick 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
35b1ee96c8Spatrick #define HSET4(sc, reg, bits)						\
36b1ee96c8Spatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
37b1ee96c8Spatrick #define HCLR4(sc, reg, bits)						\
38b1ee96c8Spatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
39b1ee96c8Spatrick 
4033c180b2Skettenis struct mvclock_softc {
4133c180b2Skettenis 	struct device		sc_dev;
42b1ee96c8Spatrick 	bus_space_tag_t		sc_iot;
43b1ee96c8Spatrick 	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 
519fdf0c62Smpi const 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 
63b1ee96c8Spatrick void	 a3700_periph_nb_enable(void *, uint32_t *, int);
64b1ee96c8Spatrick uint32_t a3700_periph_nb_get_frequency(void *, uint32_t *);
65b1ee96c8Spatrick void	 a3700_periph_sb_enable(void *, uint32_t *, int);
66b1ee96c8Spatrick uint32_t a3700_periph_sb_get_frequency(void *, uint32_t *);
67b1ee96c8Spatrick uint32_t a3700_tbg_get_frequency(void *, uint32_t *);
68b1ee96c8Spatrick 
6933c180b2Skettenis int
mvclock_match(struct device * parent,void * match,void * aux)7033c180b2Skettenis mvclock_match(struct device *parent, void *match, void *aux)
7133c180b2Skettenis {
7233c180b2Skettenis 	struct fdt_attach_args *faa = aux;
73b1ee96c8Spatrick 	int node = faa->fa_node;
7433c180b2Skettenis 
75a4bef1b5Skettenis 	if (OF_is_compatible(node, "marvell,ap806-clock") ||
7601b7c282Spatrick 	    OF_is_compatible(node, "marvell,ap807-clock") ||
77b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,cp110-clock") ||
78b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb") ||
79b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb") ||
80b1ee96c8Spatrick 	    OF_is_compatible(node, "marvell,armada-3700-tbg-clock") ||
81a4bef1b5Skettenis 	    OF_is_compatible(node, "marvell,armada-3700-xtal-clock"))
82a4bef1b5Skettenis 		return 10;	/* Must beat syscon(4). */
83a4bef1b5Skettenis 
84a4bef1b5Skettenis 	return 0;
8533c180b2Skettenis }
8633c180b2Skettenis 
8733c180b2Skettenis void
mvclock_attach(struct device * parent,struct device * self,void * aux)8833c180b2Skettenis mvclock_attach(struct device *parent, struct device *self, void *aux)
8933c180b2Skettenis {
9033c180b2Skettenis 	struct mvclock_softc *sc = (struct mvclock_softc *)self;
9133c180b2Skettenis 	struct fdt_attach_args *faa = aux;
92b1ee96c8Spatrick 	int node = faa->fa_node;
93b1ee96c8Spatrick 
94b1ee96c8Spatrick 	if (faa->fa_nreg > 0) {
95b1ee96c8Spatrick 		sc->sc_iot = faa->fa_iot;
96b1ee96c8Spatrick 		if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
97b1ee96c8Spatrick 		    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
98b1ee96c8Spatrick 			printf(": can't map registers\n");
99b1ee96c8Spatrick 			return;
100b1ee96c8Spatrick 		}
101b1ee96c8Spatrick 	}
10233c180b2Skettenis 
10333c180b2Skettenis 	printf("\n");
10433c180b2Skettenis 
105b1ee96c8Spatrick 	sc->sc_cd.cd_node = node;
10633c180b2Skettenis 	sc->sc_cd.cd_cookie = sc;
10701b7c282Spatrick 	if (OF_is_compatible(node, "marvell,ap806-clock") ||
10801b7c282Spatrick 	    OF_is_compatible(node, "marvell,ap807-clock")) {
10933c180b2Skettenis 		sc->sc_cd.cd_get_frequency = ap806_get_frequency;
110b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,cp110-clock")) {
11133c180b2Skettenis 		sc->sc_cd.cd_get_frequency = cp110_get_frequency;
11233c180b2Skettenis 		sc->sc_cd.cd_enable = cp110_enable;
113b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb")) {
114b1ee96c8Spatrick 		sc->sc_cd.cd_enable = a3700_periph_nb_enable;
115b1ee96c8Spatrick 		sc->sc_cd.cd_get_frequency = a3700_periph_nb_get_frequency;
116b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb")) {
117b1ee96c8Spatrick 		sc->sc_cd.cd_enable = a3700_periph_sb_enable;
118b1ee96c8Spatrick 		sc->sc_cd.cd_get_frequency = a3700_periph_sb_get_frequency;
119b1ee96c8Spatrick 	} else if (OF_is_compatible(node, "marvell,armada-3700-tbg-clock")) {
120b1ee96c8Spatrick 		sc->sc_cd.cd_get_frequency = a3700_tbg_get_frequency;
12133c180b2Skettenis 	}
12233c180b2Skettenis 	clock_register(&sc->sc_cd);
12333c180b2Skettenis }
12433c180b2Skettenis 
12533c180b2Skettenis /* AP806 block */
12633c180b2Skettenis 
12733c180b2Skettenis #define AP806_CORE_FIXED	2
12833c180b2Skettenis #define AP806_CORE_MSS		3
12933c180b2Skettenis #define AP806_CORE_SDIO		4
13033c180b2Skettenis 
13133c180b2Skettenis uint32_t
ap806_get_frequency(void * cookie,uint32_t * cells)13233c180b2Skettenis ap806_get_frequency(void *cookie, uint32_t *cells)
13333c180b2Skettenis {
13433c180b2Skettenis 	uint32_t idx = cells[0];
13533c180b2Skettenis 
13633c180b2Skettenis 	switch (idx) {
13733c180b2Skettenis 	case AP806_CORE_FIXED:
13833c180b2Skettenis 		/* fixed PLL at 1200MHz */
13933c180b2Skettenis 		return 1200000000;
14033c180b2Skettenis 	case AP806_CORE_MSS:
14133c180b2Skettenis 		/* MSS clock is fixed clock divided by 6 */
14233c180b2Skettenis 		return 200000000;
14333c180b2Skettenis 	case AP806_CORE_SDIO:
14433c180b2Skettenis 		/* SDIO/eMMC clock is fixed clock divided by 3 */
14533c180b2Skettenis 		return 400000000;
14633c180b2Skettenis 	default:
14733c180b2Skettenis 		break;
14833c180b2Skettenis 	}
14933c180b2Skettenis 
15033c180b2Skettenis 	printf("%s: 0x%08x\n", __func__, idx);
15133c180b2Skettenis 	return 0;
15233c180b2Skettenis }
15333c180b2Skettenis 
15433c180b2Skettenis /* CP110 block */
15533c180b2Skettenis 
15633c180b2Skettenis #define CP110_PM_CLOCK_GATING_CTRL	0x220
15733c180b2Skettenis 
15833c180b2Skettenis #define CP110_CORE_APLL		0
15933c180b2Skettenis #define CP110_CORE_PPV2		1
160001c60baSkettenis #define CP110_CORE_X2CORE	2
16133c180b2Skettenis #define CP110_CORE_CORE		3
16233c180b2Skettenis #define CP110_CORE_SDIO		5
16333c180b2Skettenis 
1644e7ccb5fSpatrick #define CP110_GATE_PPV2		3
16533c180b2Skettenis #define CP110_GATE_SDIO		4
166001c60baSkettenis #define CP110_GATE_SLOW_IO	21
16733c180b2Skettenis 
16833c180b2Skettenis uint32_t
cp110_get_frequency(void * cookie,uint32_t * cells)16933c180b2Skettenis cp110_get_frequency(void *cookie, uint32_t *cells)
17033c180b2Skettenis {
17133c180b2Skettenis 	struct mvclock_softc *sc = cookie;
17233c180b2Skettenis 	uint32_t mod = cells[0];
17333c180b2Skettenis 	uint32_t idx = cells[1];
17433c180b2Skettenis 	uint32_t parent[2] = { 0, 0 };
17533c180b2Skettenis 
17633c180b2Skettenis 	/* Core clocks */
17733c180b2Skettenis 	if (mod == 0) {
17833c180b2Skettenis 		switch (idx) {
17933c180b2Skettenis 		case CP110_CORE_APLL:
18033c180b2Skettenis 			/* fixed PLL at 1GHz */
18133c180b2Skettenis 			return 1000000000;
18233c180b2Skettenis 		case CP110_CORE_PPV2:
18333c180b2Skettenis 			/* PPv2 clock is APLL/3 */
18433c180b2Skettenis 			return 333333333;
185001c60baSkettenis 		case CP110_CORE_X2CORE:
186001c60baSkettenis 			/* X2CORE clock is APLL/2 */
18733c180b2Skettenis 			return 500000000;
18833c180b2Skettenis 		case CP110_CORE_CORE:
189001c60baSkettenis 			/* Core clock is X2CORE/2 */
19033c180b2Skettenis 			return 250000000;
19133c180b2Skettenis 		case CP110_CORE_SDIO:
19233c180b2Skettenis 			/* SDIO clock is APLL/2.5 */
19333c180b2Skettenis 			return 400000000;
19433c180b2Skettenis 		default:
19533c180b2Skettenis 			break;
19633c180b2Skettenis 		}
19733c180b2Skettenis 	}
19833c180b2Skettenis 
1994b1a56afSjsg 	/* Gateable clocks */
20033c180b2Skettenis 	if (mod == 1) {
20133c180b2Skettenis 		switch (idx) {
2024e7ccb5fSpatrick 		case CP110_GATE_PPV2:
2034e7ccb5fSpatrick 			parent[1] = CP110_CORE_PPV2;
2044e7ccb5fSpatrick 			break;
20533c180b2Skettenis 		case CP110_GATE_SDIO:
20633c180b2Skettenis 			parent[1] = CP110_CORE_SDIO;
20733c180b2Skettenis 			break;
208001c60baSkettenis 		case CP110_GATE_SLOW_IO:
209001c60baSkettenis 			parent[1] = CP110_CORE_X2CORE;
210001c60baSkettenis 			break;
21133c180b2Skettenis 		default:
21233c180b2Skettenis 			break;
21333c180b2Skettenis 		}
21433c180b2Skettenis 
21533c180b2Skettenis 		if (parent[1] != 0)
21633c180b2Skettenis 			return cp110_get_frequency(sc, parent);
21733c180b2Skettenis 	}
21833c180b2Skettenis 
21933c180b2Skettenis 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
22033c180b2Skettenis 	return 0;
22133c180b2Skettenis }
22233c180b2Skettenis 
22333c180b2Skettenis void
cp110_enable(void * cookie,uint32_t * cells,int on)22433c180b2Skettenis cp110_enable(void *cookie, uint32_t *cells, int on)
22533c180b2Skettenis {
22633c180b2Skettenis 	struct mvclock_softc *sc = cookie;
22733c180b2Skettenis 	uint32_t mod = cells[0];
22833c180b2Skettenis 	uint32_t idx = cells[1];
22933c180b2Skettenis 
2304b1a56afSjsg 	/* Gateable clocks */
23133c180b2Skettenis 	if (mod == 1 && idx < 32) {
23233c180b2Skettenis 		struct regmap *rm;
23333c180b2Skettenis 		uint32_t reg;
23433c180b2Skettenis 
23533c180b2Skettenis 		rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
23633c180b2Skettenis 		if (rm == NULL) {
23733c180b2Skettenis 			printf("%s: can't enable clock 0x%08x 0x%08x\n",
23833c180b2Skettenis 			    sc->sc_dev.dv_xname, mod, idx);
23933c180b2Skettenis 			return;
24033c180b2Skettenis 		}
24133c180b2Skettenis 		reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL);
24233c180b2Skettenis 		if (on)
24333c180b2Skettenis 			reg |= (1U << idx);
24433c180b2Skettenis 		else
24533c180b2Skettenis 			reg &= ~(1U << idx);
24633c180b2Skettenis 		regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg);
24733c180b2Skettenis 		return;
24833c180b2Skettenis 	}
24933c180b2Skettenis 
25033c180b2Skettenis 	printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
25133c180b2Skettenis }
252b1ee96c8Spatrick 
253b1ee96c8Spatrick /* Armada 3700 Periph block */
254b1ee96c8Spatrick 
255*dbec3d1cSdlg /* North Bridge clocks */
256*dbec3d1cSdlg #define PERIPH_NB_MMC			0x00
257*dbec3d1cSdlg #define PERIPH_NB_SATA			0x01 /* SATA Host */
258*dbec3d1cSdlg #define PERIPH_NB_SEC_AT		0x02 /* Security AT */
259*dbec3d1cSdlg #define PERIPH_NB_SEC_DAP		0x03 /* Security DAP */
260*dbec3d1cSdlg #define PERIPH_NB_TSECM			0x04 /* Security Engine */
261*dbec3d1cSdlg #define PERIPH_NB_SETM_TMX		0x05 /* Serial Embedded Trace Module */
262*dbec3d1cSdlg #define PERIPH_NB_AVS			0x06 /* Adaptive Voltage Scaling */
263*dbec3d1cSdlg #define PERIPH_NB_SQF			0x07 /* SPI */
264*dbec3d1cSdlg #define PERIPH_NB_I2C2			0x09
265*dbec3d1cSdlg #define PERIPH_NB_I2C1			0x0a
266*dbec3d1cSdlg #define PERIPH_NB_DDR_PHY		0x0b
267*dbec3d1cSdlg #define PERIPH_NB_DDR_FCLK		0x0c
268*dbec3d1cSdlg #define PERIPH_NB_TRACE			0x0d
269*dbec3d1cSdlg #define PERIPH_NB_COUNTER		0x0e
270*dbec3d1cSdlg #define PERIPH_NB_EIO97			0x0f
271f21dc754Skettenis #define PERIPH_NB_CPU			0x10
272*dbec3d1cSdlg 
273*dbec3d1cSdlg /* South Bridge clocks */
274*dbec3d1cSdlg #define PERIPH_SB_GBE_50		0x00 /* 50MHz parent for gbe */
275*dbec3d1cSdlg #define PERIPH_SB_GBE_CORE		0x01 /* parent for gbe core */
276*dbec3d1cSdlg #define PERIPH_SB_GBE_125		0x02 /* 125MHz parent for gbe */
277*dbec3d1cSdlg #define PERIPH_SB_GBE1_50		0x03 /* 50MHz parent for gbe port 1 */
278*dbec3d1cSdlg #define PERIPH_SB_GBE0_50		0x04 /* 50MHz parent for gbe port 0 */
279*dbec3d1cSdlg #define PERIPH_SB_GBE1_125		0x05 /* 125MHz parent for gbe port 1 */
280*dbec3d1cSdlg #define PERIPH_SB_GBE0_125		0x06 /* 125MHz parent for gbe port 0 */
281*dbec3d1cSdlg #define PERIPH_SB_GBE1_CORE		0x07 /* gbe core port 1 */
282*dbec3d1cSdlg #define PERIPH_SB_GBE0_CORE		0x08 /* gbe core port 0 */
283*dbec3d1cSdlg #define PERIPH_SB_GBE_BM		0x09 /* gbe buffer manager */
284*dbec3d1cSdlg #define PERIPH_SB_SDIO			0x0a
285*dbec3d1cSdlg #define PERIPH_SB_USB32_USB2_SYS	0x0b /* USB 2 clock */
286*dbec3d1cSdlg #define PERIPH_SB_USB32_SS_SYS		0x0c /* USB 3 clock */
287*dbec3d1cSdlg #define PERIPH_SB_PCIE			0x0d
288b1ee96c8Spatrick 
289b1ee96c8Spatrick #define PERIPH_TBG_SEL			0x0
290b1ee96c8Spatrick #define  PERIPH_TBG_SEL_MASK			0x3
291b1ee96c8Spatrick #define PERIPH_DIV_SEL0			0x4
292b1ee96c8Spatrick #define PERIPH_DIV_SEL1			0x8
293b1ee96c8Spatrick #define PERIPH_DIV_SEL2			0xc
294b1ee96c8Spatrick #define  PERIPH_DIV_SEL_MASK			0x7
295b1ee96c8Spatrick #define PERIPH_CLK_SEL			0x10
296b1ee96c8Spatrick #define PERIPH_CLK_DIS			0x14
297b1ee96c8Spatrick 
298b1ee96c8Spatrick void	 a3700_periph_enable(struct mvclock_softc *, uint32_t, int);
299b1ee96c8Spatrick uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t);
300f21dc754Skettenis uint32_t a3700_periph_get_div(struct mvclock_softc *, uint32_t, uint32_t);
301b1ee96c8Spatrick uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t,
302b1ee96c8Spatrick 	   uint32_t, uint32_t);
303b1ee96c8Spatrick 
304b1ee96c8Spatrick void
a3700_periph_nb_enable(void * cookie,uint32_t * cells,int on)305b1ee96c8Spatrick a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on)
306b1ee96c8Spatrick {
307b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
308b1ee96c8Spatrick 	uint32_t idx = cells[0];
309b1ee96c8Spatrick 
310b1ee96c8Spatrick 	switch (idx) {
311b1ee96c8Spatrick 	case PERIPH_NB_MMC:
312b1ee96c8Spatrick 		return a3700_periph_enable(sc, 2, on);
31347b749f4Spatrick 	case PERIPH_NB_SQF:
31447b749f4Spatrick 		return a3700_periph_enable(sc, 12, on);
315dab0d128Spatrick 	case PERIPH_NB_I2C2:
316dab0d128Spatrick 		return a3700_periph_enable(sc, 16, on);
317dab0d128Spatrick 	case PERIPH_NB_I2C1:
318dab0d128Spatrick 		return a3700_periph_enable(sc, 17, on);
319b1ee96c8Spatrick 	default:
320b1ee96c8Spatrick 		break;
321b1ee96c8Spatrick 	}
322b1ee96c8Spatrick 
323b1ee96c8Spatrick 	printf("%s: 0x%08x\n", __func__, idx);
324b1ee96c8Spatrick }
325b1ee96c8Spatrick 
326b1ee96c8Spatrick uint32_t
a3700_periph_nb_get_frequency(void * cookie,uint32_t * cells)327b1ee96c8Spatrick a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells)
328b1ee96c8Spatrick {
329b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
330b1ee96c8Spatrick 	uint32_t idx = cells[0];
331b1ee96c8Spatrick 	uint32_t freq;
332b1ee96c8Spatrick 
333b1ee96c8Spatrick 	switch (idx) {
334b1ee96c8Spatrick 	case PERIPH_NB_MMC:
335b1ee96c8Spatrick 		freq = a3700_periph_tbg_get_frequency(sc, 0);
336b1ee96c8Spatrick 		freq /= a3700_periph_get_double_div(sc,
337b1ee96c8Spatrick 		    PERIPH_DIV_SEL2, 16, 13);
338b1ee96c8Spatrick 		return freq;
33947b749f4Spatrick 	case PERIPH_NB_SQF:
34047b749f4Spatrick 		freq = a3700_periph_tbg_get_frequency(sc, 12);
34147b749f4Spatrick 		freq /= a3700_periph_get_double_div(sc,
34247b749f4Spatrick 		    PERIPH_DIV_SEL1, 27, 24);
34347b749f4Spatrick 		return freq;
344f21dc754Skettenis 	case PERIPH_NB_CPU:
345f21dc754Skettenis 		freq = a3700_periph_tbg_get_frequency(sc, 22);
346f21dc754Skettenis 		freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL0, 28);
347f21dc754Skettenis 		return freq;
348b1ee96c8Spatrick 	default:
349b1ee96c8Spatrick 		break;
350b1ee96c8Spatrick 	}
351b1ee96c8Spatrick 
352b1ee96c8Spatrick 	printf("%s: 0x%08x\n", __func__, idx);
353b1ee96c8Spatrick 	return 0;
354b1ee96c8Spatrick }
355b1ee96c8Spatrick 
356b1ee96c8Spatrick void
a3700_periph_sb_enable(void * cookie,uint32_t * cells,int on)357b1ee96c8Spatrick a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on)
358b1ee96c8Spatrick {
359b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
360b1ee96c8Spatrick 	uint32_t idx = cells[0];
361b1ee96c8Spatrick 
362b1ee96c8Spatrick 	switch (idx) {
363b1ee96c8Spatrick 	case PERIPH_SB_GBE1_CORE:
364b1ee96c8Spatrick 		return a3700_periph_enable(sc, 4, on);
365b1ee96c8Spatrick 	case PERIPH_SB_GBE0_CORE:
366b1ee96c8Spatrick 		return a3700_periph_enable(sc, 5, on);
367b1ee96c8Spatrick 	case PERIPH_SB_USB32_USB2_SYS:
368b1ee96c8Spatrick 		return a3700_periph_enable(sc, 16, on);
369b1ee96c8Spatrick 	case PERIPH_SB_USB32_SS_SYS:
370b1ee96c8Spatrick 		return a3700_periph_enable(sc, 17, on);
371b1ee96c8Spatrick 	default:
372b1ee96c8Spatrick 		break;
373b1ee96c8Spatrick 	}
374b1ee96c8Spatrick 
375b1ee96c8Spatrick 	printf("%s: 0x%08x\n", __func__, idx);
376b1ee96c8Spatrick }
377b1ee96c8Spatrick 
378b1ee96c8Spatrick uint32_t
a3700_periph_sb_get_frequency(void * cookie,uint32_t * cells)379b1ee96c8Spatrick a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells)
380b1ee96c8Spatrick {
381*dbec3d1cSdlg 	struct mvclock_softc *sc = cookie;
382b1ee96c8Spatrick 	uint32_t idx = cells[0];
383*dbec3d1cSdlg 	uint32_t freq;
384*dbec3d1cSdlg 
385*dbec3d1cSdlg 	switch (idx) {
386*dbec3d1cSdlg 	case PERIPH_SB_GBE_CORE:
387*dbec3d1cSdlg 		freq = a3700_periph_tbg_get_frequency(sc, 8);
388*dbec3d1cSdlg 		freq /= a3700_periph_get_double_div(sc,
389*dbec3d1cSdlg 		    PERIPH_DIV_SEL1, 18, 21);
390*dbec3d1cSdlg 		return freq;
391*dbec3d1cSdlg 	case PERIPH_SB_GBE1_CORE:
392*dbec3d1cSdlg 		idx = PERIPH_SB_GBE_CORE;
393*dbec3d1cSdlg 		freq = a3700_periph_sb_get_frequency(sc, &idx);
394*dbec3d1cSdlg 		freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL1, 13) + 1;
395*dbec3d1cSdlg 		return freq;
396*dbec3d1cSdlg 	case PERIPH_SB_GBE0_CORE:
397*dbec3d1cSdlg 		idx = PERIPH_SB_GBE_CORE;
398*dbec3d1cSdlg 		freq = a3700_periph_sb_get_frequency(sc, &idx);
399*dbec3d1cSdlg 		freq /= a3700_periph_get_div(sc, PERIPH_DIV_SEL1, 14) + 1;
400*dbec3d1cSdlg 		return freq;
401*dbec3d1cSdlg 	default:
402*dbec3d1cSdlg 		break;
403*dbec3d1cSdlg 	}
404b1ee96c8Spatrick 
405b1ee96c8Spatrick 	printf("%s: 0x%08x\n", __func__, idx);
406b1ee96c8Spatrick 	return 0;
407b1ee96c8Spatrick }
408b1ee96c8Spatrick 
409b1ee96c8Spatrick void
a3700_periph_enable(struct mvclock_softc * sc,uint32_t idx,int on)410b1ee96c8Spatrick a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on)
411b1ee96c8Spatrick {
412b1ee96c8Spatrick 	uint32_t reg;
413b1ee96c8Spatrick 
414b1ee96c8Spatrick 	reg = HREAD4(sc, PERIPH_CLK_DIS);
415b1ee96c8Spatrick 	reg &= ~(1 << idx);
416b1ee96c8Spatrick 	if (!on)
417b1ee96c8Spatrick 		reg |= (1 << idx);
418b1ee96c8Spatrick 	HWRITE4(sc, PERIPH_CLK_DIS, reg);
419b1ee96c8Spatrick }
420b1ee96c8Spatrick 
421b1ee96c8Spatrick uint32_t
a3700_periph_tbg_get_frequency(struct mvclock_softc * sc,uint32_t idx)422b1ee96c8Spatrick a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx)
423b1ee96c8Spatrick {
424b1ee96c8Spatrick 	uint32_t reg;
425b1ee96c8Spatrick 
426b1ee96c8Spatrick 	reg = HREAD4(sc, PERIPH_TBG_SEL);
427b1ee96c8Spatrick 	reg >>= idx;
428b1ee96c8Spatrick 	reg &= PERIPH_TBG_SEL_MASK;
429b1ee96c8Spatrick 
430b1ee96c8Spatrick 	return clock_get_frequency_idx(sc->sc_cd.cd_node, reg);
431b1ee96c8Spatrick }
432b1ee96c8Spatrick 
433b1ee96c8Spatrick uint32_t
a3700_periph_get_div(struct mvclock_softc * sc,uint32_t off,uint32_t idx)434f21dc754Skettenis a3700_periph_get_div(struct mvclock_softc *sc, uint32_t off, uint32_t idx)
435f21dc754Skettenis {
436f21dc754Skettenis 	uint32_t reg = HREAD4(sc, off);
437f21dc754Skettenis 	return ((reg >> idx) & PERIPH_DIV_SEL_MASK);
438f21dc754Skettenis }
439f21dc754Skettenis 
440f21dc754Skettenis uint32_t
a3700_periph_get_double_div(struct mvclock_softc * sc,uint32_t off,uint32_t idx0,uint32_t idx1)441b1ee96c8Spatrick a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off,
442b1ee96c8Spatrick     uint32_t idx0, uint32_t idx1)
443b1ee96c8Spatrick {
444b1ee96c8Spatrick 	uint32_t reg = HREAD4(sc, off);
445b1ee96c8Spatrick 	return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) *
446b1ee96c8Spatrick 	    ((reg >> idx1) & PERIPH_DIV_SEL_MASK);
447b1ee96c8Spatrick }
448b1ee96c8Spatrick 
449b1ee96c8Spatrick /* Armada 3700 TBG block */
450b1ee96c8Spatrick 
451b1ee96c8Spatrick #define TBG_A_P				0
452b1ee96c8Spatrick #define TBG_B_P				1
453b1ee96c8Spatrick #define TBG_A_S				2
454b1ee96c8Spatrick #define TBG_B_S				3
455b1ee96c8Spatrick 
456b1ee96c8Spatrick #define TBG_CTRL0			0x4
457b1ee96c8Spatrick #define  TBG_A_FBDIV_SHIFT			2
458b1ee96c8Spatrick #define  TBG_B_FBDIV_SHIFT			18
459b1ee96c8Spatrick #define TBG_CTRL1			0x8
460b1ee96c8Spatrick #define  TBG_A_VCODIV_SE_SHIFT			0
461b1ee96c8Spatrick #define  TBG_B_VCODIV_SE_SHIFT			16
462b1ee96c8Spatrick #define TBG_CTRL7			0x20
463b1ee96c8Spatrick #define  TBG_A_REFDIV_SHIFT			0
464b1ee96c8Spatrick #define  TBG_B_REFDIV_SHIFT			16
465b1ee96c8Spatrick #define TBG_CTRL8			0x30
466b1ee96c8Spatrick #define  TBG_A_VCODIV_DIFF_SHIFT		1
467b1ee96c8Spatrick #define  TBG_B_VCODIV_DIFF_SHIFT		17
468b1ee96c8Spatrick #define TBG_DIV_MASK			0x1ff
469b1ee96c8Spatrick 
470b1ee96c8Spatrick uint32_t
a3700_tbg_get_frequency(void * cookie,uint32_t * cells)471b1ee96c8Spatrick a3700_tbg_get_frequency(void *cookie, uint32_t *cells)
472b1ee96c8Spatrick {
473b1ee96c8Spatrick 	struct mvclock_softc *sc = cookie;
474b1ee96c8Spatrick 	uint32_t idx = cells[0];
475b1ee96c8Spatrick 	uint64_t mult, div, freq;
476b1ee96c8Spatrick 	uint32_t reg, vcodiv;
477b1ee96c8Spatrick 
478b1ee96c8Spatrick 	switch (idx) {
479b1ee96c8Spatrick 	case TBG_A_P:
480b1ee96c8Spatrick 		vcodiv = HREAD4(sc, TBG_CTRL8);
481b1ee96c8Spatrick 		vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT;
482b1ee96c8Spatrick 		vcodiv &= TBG_DIV_MASK;
483b1ee96c8Spatrick 		break;
484b1ee96c8Spatrick 	case TBG_B_P:
485b1ee96c8Spatrick 		vcodiv = HREAD4(sc, TBG_CTRL8);
486b1ee96c8Spatrick 		vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT;
487b1ee96c8Spatrick 		vcodiv &= TBG_DIV_MASK;
488b1ee96c8Spatrick 		break;
489b1ee96c8Spatrick 	case TBG_A_S:
490b1ee96c8Spatrick 		vcodiv = HREAD4(sc, TBG_CTRL1);
491b1ee96c8Spatrick 		vcodiv >>= TBG_A_VCODIV_SE_SHIFT;
492b1ee96c8Spatrick 		vcodiv &= TBG_DIV_MASK;
493b1ee96c8Spatrick 		break;
494b1ee96c8Spatrick 	case TBG_B_S:
495b1ee96c8Spatrick 		vcodiv = HREAD4(sc, TBG_CTRL1);
496b1ee96c8Spatrick 		vcodiv >>= TBG_B_VCODIV_SE_SHIFT;
497b1ee96c8Spatrick 		vcodiv &= TBG_DIV_MASK;
498b1ee96c8Spatrick 		break;
499b1ee96c8Spatrick 	default:
500b1ee96c8Spatrick 		printf("%s: 0x%08x\n", __func__, idx);
501b1ee96c8Spatrick 		return 0;
502b1ee96c8Spatrick 	}
503b1ee96c8Spatrick 
504b1ee96c8Spatrick 	reg = HREAD4(sc, TBG_CTRL0);
505b1ee96c8Spatrick 	if (idx == TBG_A_P || idx == TBG_A_S)
506b1ee96c8Spatrick 		reg >>= TBG_A_FBDIV_SHIFT;
507b1ee96c8Spatrick 	else
508b1ee96c8Spatrick 		reg >>= TBG_B_FBDIV_SHIFT;
509b1ee96c8Spatrick 	reg &= TBG_DIV_MASK;
510b1ee96c8Spatrick 	mult = reg << 2;
511b1ee96c8Spatrick 
512b1ee96c8Spatrick 	reg = HREAD4(sc, TBG_CTRL7);
513b1ee96c8Spatrick 	if (idx == TBG_A_P || idx == TBG_A_S)
514b1ee96c8Spatrick 		reg >>= TBG_A_REFDIV_SHIFT;
515b1ee96c8Spatrick 	else
516b1ee96c8Spatrick 		reg >>= TBG_B_REFDIV_SHIFT;
517b1ee96c8Spatrick 	reg &= TBG_DIV_MASK;
518b1ee96c8Spatrick 	div = reg;
519b1ee96c8Spatrick 
520b1ee96c8Spatrick 	if (div == 0)
521b1ee96c8Spatrick 		div = 1;
522b1ee96c8Spatrick 	div *= 1 << vcodiv;
523b1ee96c8Spatrick 
524b1ee96c8Spatrick 	freq = clock_get_frequency(sc->sc_cd.cd_node, NULL);
525b1ee96c8Spatrick 	return (freq * mult) / div;
526b1ee96c8Spatrick }
527