diff options
-rw-r--r-- | sys/dev/fdt/mvclock.c | 279 |
1 files changed, 272 insertions, 7 deletions
diff --git a/sys/dev/fdt/mvclock.c b/sys/dev/fdt/mvclock.c index 0340bb6f47e..59f1f6e4925 100644 --- a/sys/dev/fdt/mvclock.c +++ b/sys/dev/fdt/mvclock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mvclock.c,v 1.2 2018/07/24 21:52:38 kettenis Exp $ */ +/* $OpenBSD: mvclock.c,v 1.3 2019/04/30 20:00:25 patrick Exp $ */ /* * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> * @@ -28,8 +28,19 @@ #include <dev/ofw/ofw_misc.h> #include <dev/ofw/fdt.h> +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + struct mvclock_softc { struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; struct clock_device sc_cd; }; @@ -49,13 +60,25 @@ uint32_t ap806_get_frequency(void *, uint32_t *); uint32_t cp110_get_frequency(void *, uint32_t *); void cp110_enable(void *, uint32_t *, int); +void a3700_periph_nb_enable(void *, uint32_t *, int); +uint32_t a3700_periph_nb_get_frequency(void *, uint32_t *); +void a3700_periph_sb_enable(void *, uint32_t *, int); +uint32_t a3700_periph_sb_get_frequency(void *, uint32_t *); +uint32_t a3700_tbg_get_frequency(void *, uint32_t *); +uint32_t a3700_xtal_get_frequency(void *, uint32_t *); + int mvclock_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; - - return (OF_is_compatible(faa->fa_node, "marvell,ap806-clock") || - OF_is_compatible(faa->fa_node, "marvell,cp110-clock")); + int node = faa->fa_node; + + return (OF_is_compatible(node, "marvell,ap806-clock") || + OF_is_compatible(node, "marvell,cp110-clock") || + OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb") || + OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb") || + OF_is_compatible(node, "marvell,armada-3700-tbg-clock") || + OF_is_compatible(node, "marvell,armada-3700-xtal-clock")); } void @@ -63,16 +86,36 @@ mvclock_attach(struct device *parent, struct device *self, void *aux) { struct mvclock_softc *sc = (struct mvclock_softc *)self; struct fdt_attach_args *faa = aux; + int node = faa->fa_node; + + if (faa->fa_nreg > 0) { + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + } printf("\n"); - sc->sc_cd.cd_node = faa->fa_node; + sc->sc_cd.cd_node = node; sc->sc_cd.cd_cookie = sc; - if (OF_is_compatible(faa->fa_node, "marvell,ap806-clock")) { + if (OF_is_compatible(node, "marvell,ap806-clock")) { sc->sc_cd.cd_get_frequency = ap806_get_frequency; - } else { + } else if (OF_is_compatible(node, "marvell,cp110-clock")) { sc->sc_cd.cd_get_frequency = cp110_get_frequency; sc->sc_cd.cd_enable = cp110_enable; + } else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-nb")) { + sc->sc_cd.cd_enable = a3700_periph_nb_enable; + sc->sc_cd.cd_get_frequency = a3700_periph_nb_get_frequency; + } else if (OF_is_compatible(node, "marvell,armada-3700-periph-clock-sb")) { + sc->sc_cd.cd_enable = a3700_periph_sb_enable; + sc->sc_cd.cd_get_frequency = a3700_periph_sb_get_frequency; + } else if (OF_is_compatible(node, "marvell,armada-3700-tbg-clock")) { + sc->sc_cd.cd_get_frequency = a3700_tbg_get_frequency; + } else if (OF_is_compatible(node, "marvell,armada-3700-xtal-clock")) { + sc->sc_cd.cd_get_frequency = a3700_xtal_get_frequency; } clock_register(&sc->sc_cd); } @@ -200,3 +243,225 @@ cp110_enable(void *cookie, uint32_t *cells, int on) printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx); } + +/* Armada 3700 Periph block */ + +#define PERIPH_NB_MMC 0x0 +#define PERIPH_SB_GBE1_CORE 0x7 +#define PERIPH_SB_GBE0_CORE 0x8 +#define PERIPH_SB_USB32_USB2_SYS 0xb +#define PERIPH_SB_USB32_SS_SYS 0xc + +#define PERIPH_TBG_SEL 0x0 +#define PERIPH_TBG_SEL_MASK 0x3 +#define PERIPH_DIV_SEL0 0x4 +#define PERIPH_DIV_SEL1 0x8 +#define PERIPH_DIV_SEL2 0xc +#define PERIPH_DIV_SEL_MASK 0x7 +#define PERIPH_CLK_SEL 0x10 +#define PERIPH_CLK_DIS 0x14 + +void a3700_periph_enable(struct mvclock_softc *, uint32_t, int); +uint32_t a3700_periph_tbg_get_frequency(struct mvclock_softc *, uint32_t); +uint32_t a3700_periph_get_double_div(struct mvclock_softc *, uint32_t, + uint32_t, uint32_t); + +void +a3700_periph_nb_enable(void *cookie, uint32_t *cells, int on) +{ + struct mvclock_softc *sc = cookie; + uint32_t idx = cells[0]; + + switch (idx) { + case PERIPH_NB_MMC: + return a3700_periph_enable(sc, 2, on); + default: + break; + } + + printf("%s: 0x%08x\n", __func__, idx); +} + +uint32_t +a3700_periph_nb_get_frequency(void *cookie, uint32_t *cells) +{ + struct mvclock_softc *sc = cookie; + uint32_t idx = cells[0]; + uint32_t freq; + + switch (idx) { + case PERIPH_NB_MMC: + freq = a3700_periph_tbg_get_frequency(sc, 0); + freq /= a3700_periph_get_double_div(sc, + PERIPH_DIV_SEL2, 16, 13); + return freq; + default: + break; + } + + printf("%s: 0x%08x\n", __func__, idx); + return 0; +} + +void +a3700_periph_sb_enable(void *cookie, uint32_t *cells, int on) +{ + struct mvclock_softc *sc = cookie; + uint32_t idx = cells[0]; + + switch (idx) { + case PERIPH_SB_GBE1_CORE: + return a3700_periph_enable(sc, 4, on); + case PERIPH_SB_GBE0_CORE: + return a3700_periph_enable(sc, 5, on); + case PERIPH_SB_USB32_USB2_SYS: + return a3700_periph_enable(sc, 16, on); + case PERIPH_SB_USB32_SS_SYS: + return a3700_periph_enable(sc, 17, on); + default: + break; + } + + printf("%s: 0x%08x\n", __func__, idx); +} + +uint32_t +a3700_periph_sb_get_frequency(void *cookie, uint32_t *cells) +{ + uint32_t idx = cells[0]; + + printf("%s: 0x%08x\n", __func__, idx); + return 0; +} + +void +a3700_periph_enable(struct mvclock_softc *sc, uint32_t idx, int on) +{ + uint32_t reg; + + reg = HREAD4(sc, PERIPH_CLK_DIS); + reg &= ~(1 << idx); + if (!on) + reg |= (1 << idx); + HWRITE4(sc, PERIPH_CLK_DIS, reg); +} + +uint32_t +a3700_periph_tbg_get_frequency(struct mvclock_softc *sc, uint32_t idx) +{ + uint32_t reg; + + reg = HREAD4(sc, PERIPH_TBG_SEL); + reg >>= idx; + reg &= PERIPH_TBG_SEL_MASK; + + return clock_get_frequency_idx(sc->sc_cd.cd_node, reg); +} + +uint32_t +a3700_periph_get_double_div(struct mvclock_softc *sc, uint32_t off, + uint32_t idx0, uint32_t idx1) +{ + uint32_t reg = HREAD4(sc, off); + return ((reg >> idx0) & PERIPH_DIV_SEL_MASK) * + ((reg >> idx1) & PERIPH_DIV_SEL_MASK); +} + +/* Armada 3700 TBG block */ + +#define TBG_A_P 0 +#define TBG_B_P 1 +#define TBG_A_S 2 +#define TBG_B_S 3 + +#define TBG_CTRL0 0x4 +#define TBG_A_FBDIV_SHIFT 2 +#define TBG_B_FBDIV_SHIFT 18 +#define TBG_CTRL1 0x8 +#define TBG_A_VCODIV_SE_SHIFT 0 +#define TBG_B_VCODIV_SE_SHIFT 16 +#define TBG_CTRL7 0x20 +#define TBG_A_REFDIV_SHIFT 0 +#define TBG_B_REFDIV_SHIFT 16 +#define TBG_CTRL8 0x30 +#define TBG_A_VCODIV_DIFF_SHIFT 1 +#define TBG_B_VCODIV_DIFF_SHIFT 17 +#define TBG_DIV_MASK 0x1ff + +uint32_t +a3700_tbg_get_frequency(void *cookie, uint32_t *cells) +{ + struct mvclock_softc *sc = cookie; + uint32_t idx = cells[0]; + uint64_t mult, div, freq; + uint32_t reg, vcodiv; + + switch (idx) { + case TBG_A_P: + vcodiv = HREAD4(sc, TBG_CTRL8); + vcodiv >>= TBG_A_VCODIV_DIFF_SHIFT; + vcodiv &= TBG_DIV_MASK; + break; + case TBG_B_P: + vcodiv = HREAD4(sc, TBG_CTRL8); + vcodiv >>= TBG_B_VCODIV_DIFF_SHIFT; + vcodiv &= TBG_DIV_MASK; + break; + case TBG_A_S: + vcodiv = HREAD4(sc, TBG_CTRL1); + vcodiv >>= TBG_A_VCODIV_SE_SHIFT; + vcodiv &= TBG_DIV_MASK; + break; + case TBG_B_S: + vcodiv = HREAD4(sc, TBG_CTRL1); + vcodiv >>= TBG_B_VCODIV_SE_SHIFT; + vcodiv &= TBG_DIV_MASK; + break; + default: + printf("%s: 0x%08x\n", __func__, idx); + return 0; + } + + reg = HREAD4(sc, TBG_CTRL0); + if (idx == TBG_A_P || idx == TBG_A_S) + reg >>= TBG_A_FBDIV_SHIFT; + else + reg >>= TBG_B_FBDIV_SHIFT; + reg &= TBG_DIV_MASK; + mult = reg << 2; + + reg = HREAD4(sc, TBG_CTRL7); + if (idx == TBG_A_P || idx == TBG_A_S) + reg >>= TBG_A_REFDIV_SHIFT; + else + reg >>= TBG_B_REFDIV_SHIFT; + reg &= TBG_DIV_MASK; + div = reg; + + if (div == 0) + div = 1; + div *= 1 << vcodiv; + + freq = clock_get_frequency(sc->sc_cd.cd_node, NULL); + return (freq * mult) / div; +} + +/* Armada 3700 XTAL block */ + +#define XTAL 0xc +#define XTAL_MODE (1 << 31) + +uint32_t +a3700_xtal_get_frequency(void *cookie, uint32_t *cells) +{ + struct mvclock_softc *sc = cookie; + struct regmap *rm; + + rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node)); + KASSERT(rm != NULL); + + if (regmap_read_4(rm, XTAL) & XTAL_MODE) + return 40000000; + else + return 25000000; +} |