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