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