diff options
author | 2020-03-21 12:53:24 +0000 | |
---|---|---|
committer | 2020-03-21 12:53:24 +0000 | |
commit | 6e71e8fef0cb29787f0854f56fd785082a977c2b (patch) | |
tree | e21b90961283050767c538171cded0962fa7c5c0 /sys/dev/fdt/imxccm.c | |
parent | Instead of passing the flags as part of a struct that's supposed to be (diff) | |
download | wireguard-openbsd-6e71e8fef0cb29787f0854f56fd785082a977c2b.tar.xz wireguard-openbsd-6e71e8fef0cb29787f0854f56fd785082a977c2b.zip |
Add clock support for i.MX8MM. The CCM is based on the i.MX8MQ version,
so we can re-use the same mechanisms. It's not the same though, which
means that some code is duplicated. There's some room for refactoring.
The PLLs, on the other hand, work quite differently. This allows most
of our supported peripherals to work, including clock frequency scaling.
Feedback from and ok kettenis@
Diffstat (limited to 'sys/dev/fdt/imxccm.c')
-rw-r--r-- | sys/dev/fdt/imxccm.c | 354 |
1 files changed, 342 insertions, 12 deletions
diff --git a/sys/dev/fdt/imxccm.c b/sys/dev/fdt/imxccm.c index 26df53f7ffc..7fe69d26e82 100644 --- a/sys/dev/fdt/imxccm.c +++ b/sys/dev/fdt/imxccm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: imxccm.c,v 1.17 2020/03/20 17:32:25 patrick Exp $ */ +/* $OpenBSD: imxccm.c,v 1.18 2020/03/21 12:53:24 patrick Exp $ */ /* * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> * @@ -127,10 +127,26 @@ #define CCM_ANALOG_PLL_ENET_POWERDOWN (1 << 12) /* i.MX6 */ #define CCM_ANALOG_PLL_ENET_ENABLE_CLK_125MHZ (1 << 10) /* i.MX7 */ +/* Int PLL */ +#define CCM_14XX_IMX8M_ARM_PLL_GNRL_CTL 0x84 +#define CCM_14XX_IMX8M_ARM_PLL_DIV_CTL 0x88 +#define CCM_14XX_IMX8M_SYS_PLL1_GNRL_CTL 0x94 +#define CCM_14XX_IMX8M_SYS_PLL1_DIV_CTL 0x98 +#define CCM_INT_PLL_LOCK (1U << 31) +#define CCM_INT_PLL_LOCK_SEL (1 << 29) +#define CCM_INT_PLL_RST (1 << 9) +#define CCM_INT_PLL_BYPASS (1 << 4) +#define CCM_INT_PLL_MAIN_DIV_SHIFT 12 +#define CCM_INT_PLL_MAIN_DIV_MASK 0x3ff +#define CCM_INT_PLL_PRE_DIV_SHIFT 4 +#define CCM_INT_PLL_PRE_DIV_MASK 0x3f +#define CCM_INT_PLL_POST_DIV_SHIFT 0 +#define CCM_INT_PLL_POST_DIV_MASK 0x7 + /* Frac PLL */ #define CCM_FRAC_IMX8M_ARM_PLL0 0x28 #define CCM_FRAC_IMX8M_ARM_PLL1 0x2c -#define CCM_FRAC_PLL_LOCK (1 << 31) +#define CCM_FRAC_PLL_LOCK (1U << 31) #define CCM_FRAC_PLL_ENABLE (1 << 21) #define CCM_FRAC_PLL_POWERDOWN (1 << 19) #define CCM_FRAC_PLL_REFCLK_SEL_SHIFT 16 @@ -227,6 +243,10 @@ uint32_t imxccm_get_ahbclk(struct imxccm_softc *); uint32_t imxccm_get_ipgclk(struct imxccm_softc *); uint32_t imxccm_get_ipg_perclk(struct imxccm_softc *); uint32_t imxccm_get_uartclk(struct imxccm_softc *); +uint32_t imxccm_imx8mm_enet(struct imxccm_softc *sc, uint32_t); +uint32_t imxccm_imx8mm_i2c(struct imxccm_softc *sc, uint32_t); +uint32_t imxccm_imx8mm_uart(struct imxccm_softc *sc, uint32_t); +uint32_t imxccm_imx8mm_usdhc(struct imxccm_softc *sc, uint32_t); uint32_t imxccm_imx8mq_ecspi(struct imxccm_softc *sc, uint32_t); uint32_t imxccm_imx8mq_enet(struct imxccm_softc *sc, uint32_t); uint32_t imxccm_imx8mq_i2c(struct imxccm_softc *sc, uint32_t); @@ -249,6 +269,7 @@ imxccm_match(struct device *parent, void *match, void *aux) OF_is_compatible(faa->fa_node, "fsl,imx6sx-ccm") || OF_is_compatible(faa->fa_node, "fsl,imx6ul-ccm") || OF_is_compatible(faa->fa_node, "fsl,imx7d-ccm") || + OF_is_compatible(faa->fa_node, "fsl,imx8mm-ccm") || OF_is_compatible(faa->fa_node, "fsl,imx8mq-ccm")); } @@ -268,7 +289,18 @@ imxccm_attach(struct device *parent, struct device *self, void *aux) sc->sc_phandle = OF_getpropint(sc->sc_node, "phandle", 0); - if (OF_is_compatible(sc->sc_node, "fsl,imx8mq-ccm")) { + if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-ccm")) { + sc->sc_anatop = regmap_bycompatible("fsl,imx8mm-anatop"); + KASSERT(sc->sc_anatop != NULL); + sc->sc_gates = imx8mm_gates; + sc->sc_ngates = nitems(imx8mm_gates); + sc->sc_divs = imx8mm_divs; + sc->sc_ndivs = nitems(imx8mm_divs); + sc->sc_muxs = imx8mm_muxs; + sc->sc_nmuxs = nitems(imx8mm_muxs); + sc->sc_predivs = imx8mm_predivs; + sc->sc_npredivs = nitems(imx8mm_predivs); + } else if (OF_is_compatible(sc->sc_node, "fsl,imx8mq-ccm")) { sc->sc_anatop = regmap_bycompatible("fsl,imx8mq-anatop"); KASSERT(sc->sc_anatop != NULL); sc->sc_gates = imx8mq_gates; @@ -599,6 +631,214 @@ imxccm_imx7d_usdhc(struct imxccm_softc *sc, uint32_t idx) } uint32_t +imxccm_imx8mm_get_pll(struct imxccm_softc *sc, uint32_t idx) +{ + uint64_t main_div, pre_div, post_div, div; + uint32_t pll0, pll1; + uint64_t freq; + + switch (idx) { + case IMX8MM_ARM_PLL: + pll0 = regmap_read_4(sc->sc_anatop, + CCM_14XX_IMX8M_ARM_PLL_GNRL_CTL); + pll1 = regmap_read_4(sc->sc_anatop, + CCM_14XX_IMX8M_ARM_PLL_DIV_CTL); + div = 1; + break; + default: + printf("%s: 0x%08x\n", __func__, idx); + return 0; + } + + freq = clock_get_frequency(sc->sc_node, "osc_24m"); + if (pll0 & CCM_INT_PLL_BYPASS) + return freq; + + main_div = (pll1 >> CCM_INT_PLL_MAIN_DIV_SHIFT) & + CCM_INT_PLL_MAIN_DIV_MASK; + pre_div = (pll1 >> CCM_INT_PLL_PRE_DIV_SHIFT) & + CCM_INT_PLL_PRE_DIV_MASK; + post_div = (pll1 >> CCM_INT_PLL_POST_DIV_SHIFT) & + CCM_INT_PLL_POST_DIV_MASK; + + freq = freq * main_div; + freq = freq / (pre_div * (1 << post_div) * div); + return freq; +} + +int +imxccm_imx8mm_set_pll(struct imxccm_softc *sc, uint32_t idx, uint64_t freq) +{ + uint64_t main_div, pre_div, post_div; + uint32_t pll0, pll1, reg; + int i; + + switch (idx) { + case IMX8MM_ARM_PLL: + pre_div = 3; + switch (freq) { + case 1800000000U: + main_div = 225; + post_div = 0; + break; + case 1600000000U: + main_div = 200; + post_div = 0; + break; + case 1200000000U: + main_div = 300; + post_div = 1; + break; + case 1000000000U: + main_div = 250; + post_div = 1; + break; + case 800000000U: + main_div = 200; + post_div = 1; + break; + case 750000000U: + main_div = 250; + post_div = 2; + break; + case 700000000U: + main_div = 350; + post_div = 2; + break; + case 600000000U: + main_div = 300; + post_div = 2; + break; + default: + printf("%s: 0x%08x\n", __func__, idx); + return -1; + } + pll0 = CCM_14XX_IMX8M_ARM_PLL_GNRL_CTL; + pll1 = CCM_14XX_IMX8M_ARM_PLL_DIV_CTL; + break; + default: + printf("%s: 0x%08x\n", __func__, idx); + return -1; + } + + regmap_write_4(sc->sc_anatop, pll0, + regmap_read_4(sc->sc_anatop, pll0) | + CCM_INT_PLL_LOCK_SEL); + regmap_write_4(sc->sc_anatop, pll0, + regmap_read_4(sc->sc_anatop, pll0) & + ~CCM_INT_PLL_RST); + regmap_write_4(sc->sc_anatop, pll1, + main_div << CCM_INT_PLL_MAIN_DIV_SHIFT | + pre_div << CCM_INT_PLL_PRE_DIV_SHIFT | + post_div << CCM_INT_PLL_POST_DIV_SHIFT); + delay(3); + regmap_write_4(sc->sc_anatop, pll0, + regmap_read_4(sc->sc_anatop, pll0) | + CCM_INT_PLL_RST); + for (i = 0; i < 5000; i++) { + reg = regmap_read_4(sc->sc_anatop, pll0); + if (reg & CCM_INT_PLL_LOCK) + break; + delay(10); + } + if (i == 5000) + printf("%s: timeout\n", __func__); + regmap_write_4(sc->sc_anatop, pll0, + regmap_read_4(sc->sc_anatop, pll0) & + ~CCM_INT_PLL_BYPASS); + + return 0; +} + +uint32_t +imxccm_imx8mm_enet(struct imxccm_softc *sc, uint32_t idx) +{ + uint32_t mux; + + if (idx >= sc->sc_nmuxs || sc->sc_muxs[idx].reg == 0) + return 0; + + mux = HREAD4(sc, sc->sc_muxs[idx].reg); + mux >>= sc->sc_muxs[idx].shift; + mux &= sc->sc_muxs[idx].mask; + + switch (mux) { + case 0: + return clock_get_frequency(sc->sc_node, "osc_24m"); + case 1: + return 266 * 1000 * 1000; /* sys1_pll_266m */ + default: + printf("%s: 0x%08x 0x%08x\n", __func__, idx, mux); + return 0; + } +} + +uint32_t +imxccm_imx8mm_i2c(struct imxccm_softc *sc, uint32_t idx) +{ + uint32_t mux; + + if (idx >= sc->sc_nmuxs || sc->sc_muxs[idx].reg == 0) + return 0; + + mux = HREAD4(sc, sc->sc_muxs[idx].reg); + mux >>= sc->sc_muxs[idx].shift; + mux &= sc->sc_muxs[idx].mask; + + switch (mux) { + case 0: + return clock_get_frequency(sc->sc_node, "osc_24m"); + default: + printf("%s: 0x%08x 0x%08x\n", __func__, idx, mux); + return 0; + } +} + +uint32_t +imxccm_imx8mm_uart(struct imxccm_softc *sc, uint32_t idx) +{ + uint32_t mux; + + if (idx >= sc->sc_nmuxs || sc->sc_muxs[idx].reg == 0) + return 0; + + mux = HREAD4(sc, sc->sc_muxs[idx].reg); + mux >>= sc->sc_muxs[idx].shift; + mux &= sc->sc_muxs[idx].mask; + + switch (mux) { + case 0: + return clock_get_frequency(sc->sc_node, "osc_24m"); + default: + printf("%s: 0x%08x 0x%08x\n", __func__, idx, mux); + return 0; + } +} + +uint32_t +imxccm_imx8mm_usdhc(struct imxccm_softc *sc, uint32_t idx) +{ + uint32_t mux; + + if (idx >= sc->sc_nmuxs || sc->sc_muxs[idx].reg == 0) + return 0; + + mux = HREAD4(sc, sc->sc_muxs[idx].reg); + mux >>= sc->sc_muxs[idx].shift; + mux &= sc->sc_muxs[idx].mask; + + switch (mux) { + case 0: + return clock_get_frequency(sc->sc_node, "osc_24m"); + case 1: + return 400 * 1000 * 1000; /* sys1_pll_400m */ + default: + printf("%s: 0x%08x 0x%08x\n", __func__, idx, mux); + return 0; + } +} + +uint32_t imxccm_imx8mq_ecspi(struct imxccm_softc *sc, uint32_t idx) { uint32_t mux; @@ -1086,7 +1326,53 @@ imxccm_get_frequency(void *cookie, uint32_t *cells) return imxccm_get_frequency(sc, &parent) / (div + 1); } - if (sc->sc_gates == imx8mq_gates) { + if (sc->sc_gates == imx8mm_gates) { + switch (idx) { + case IMX8MM_CLK_ARM: + parent = IMX8MM_ARM_PLL; + return imxccm_get_frequency(sc, &parent); + case IMX8MM_ARM_PLL: + return imxccm_imx8mm_get_pll(sc, idx); + } + + /* These are composite clocks. */ + if (idx < sc->sc_ngates && sc->sc_gates[idx].reg && + idx < sc->sc_ndivs && sc->sc_divs[idx].reg && + idx < sc->sc_npredivs && sc->sc_predivs[idx].reg) { + switch (idx) { + case IMX8MM_CLK_ENET_AXI: + freq = imxccm_imx8mm_enet(sc, idx); + break; + case IMX8MM_CLK_I2C1: + case IMX8MM_CLK_I2C2: + case IMX8MM_CLK_I2C3: + case IMX8MM_CLK_I2C4: + freq = imxccm_imx8mm_i2c(sc, idx); + break; + case IMX8MM_CLK_UART1: + case IMX8MM_CLK_UART2: + case IMX8MM_CLK_UART3: + case IMX8MM_CLK_UART4: + freq = imxccm_imx8mm_uart(sc, idx); + break; + case IMX8MM_CLK_USDHC1: + case IMX8MM_CLK_USDHC2: + case IMX8MM_CLK_USDHC3: + freq = imxccm_imx8mm_usdhc(sc, idx); + break; + default: + printf("%s: 0x%08x\n", __func__, idx); + return 0; + } + + reg = HREAD4(sc, sc->sc_divs[idx].reg); + div = reg >> sc->sc_divs[idx].shift; + div = div & sc->sc_divs[idx].mask; + pre = reg >> sc->sc_predivs[idx].shift; + pre = pre & sc->sc_predivs[idx].mask; + return ((freq / (pre + 1)) / (div + 1)); + } + } else if (sc->sc_gates == imx8mq_gates) { switch (idx) { case IMX8MQ_CLK_ARM: parent = IMX8MQ_ARM_PLL; @@ -1219,7 +1505,38 @@ imxccm_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) uint32_t pcells[2]; int ret; - if (sc->sc_divs == imx8mq_divs) { + if (sc->sc_divs == imx8mm_divs) { + switch (idx) { + case IMX8MM_CLK_ARM: + parent = IMX8MM_CLK_A53_SRC; + return imxccm_set_frequency(cookie, &parent, freq); + case IMX8MM_CLK_A53_SRC: + pcells[0] = sc->sc_phandle; + pcells[1] = IMX8MM_SYS_PLL1_800M; + ret = imxccm_set_parent(cookie, &idx, pcells); + if (ret) + return ret; + ret = imxccm_imx8mm_set_pll(sc, IMX8MM_ARM_PLL, freq); + pcells[0] = sc->sc_phandle; + pcells[1] = IMX8MM_ARM_PLL_OUT; + imxccm_set_parent(cookie, &idx, pcells); + return ret; + case IMX8MM_CLK_USDHC1_ROOT: + case IMX8MM_CLK_USDHC2_ROOT: + case IMX8MM_CLK_USDHC3_ROOT: + parent = sc->sc_gates[idx].parent; + return imxccm_set_frequency(sc, &parent, freq); + case IMX8MM_CLK_USDHC1: + case IMX8MM_CLK_USDHC2: + case IMX8MM_CLK_USDHC3: + imxccm_enable(cookie, &idx, 1); + HCLR4(sc, sc->sc_divs[idx].reg, + sc->sc_divs[idx].mask << sc->sc_divs[idx].shift); + HCLR4(sc, sc->sc_predivs[idx].reg, + sc->sc_predivs[idx].mask << sc->sc_predivs[idx].shift); + return 0; + } + } else if (sc->sc_divs == imx8mq_divs) { switch (idx) { case IMX8MQ_CLK_ARM: parent = IMX8MQ_CLK_A53_SRC; @@ -1243,12 +1560,10 @@ imxccm_set_frequency(void *cookie, uint32_t *cells, uint32_t freq) return 0; case IMX8MQ_CLK_USDHC1: imxccm_enable(cookie, &idx, 1); - reg = HREAD4(sc, sc->sc_divs[idx].reg); - reg &= ~(sc->sc_divs[idx].mask << sc->sc_divs[idx].shift); - HWRITE4(sc, sc->sc_divs[idx].reg, reg); - reg = HREAD4(sc, sc->sc_predivs[idx].reg); - reg &= ~(sc->sc_predivs[idx].mask << sc->sc_predivs[idx].shift); - HWRITE4(sc, sc->sc_predivs[idx].reg, reg); + HCLR4(sc, sc->sc_divs[idx].reg, + sc->sc_divs[idx].mask << sc->sc_divs[idx].shift); + HCLR4(sc, sc->sc_predivs[idx].reg, + sc->sc_predivs[idx].mask << sc->sc_predivs[idx].shift); return 0; } } else if (sc->sc_divs == imx7d_divs) { @@ -1293,7 +1608,22 @@ imxccm_set_parent(void *cookie, uint32_t *cells, uint32_t *pcells) pidx = pcells[1]; - if (sc->sc_muxs == imx8mq_muxs) { + if (sc->sc_muxs == imx8mm_muxs) { + switch (idx) { + case IMX8MM_CLK_A53_SRC: + if (pidx != IMX8MM_ARM_PLL_OUT && + pidx != IMX8MM_SYS_PLL1_800M) + break; + mux = HREAD4(sc, sc->sc_muxs[idx].reg); + mux &= ~(sc->sc_muxs[idx].mask << sc->sc_muxs[idx].shift); + if (pidx == IMX8MM_ARM_PLL_OUT) + mux |= (0x1 << sc->sc_muxs[idx].shift); + if (pidx == IMX8MM_SYS_PLL1_800M) + mux |= (0x4 << sc->sc_muxs[idx].shift); + HWRITE4(sc, sc->sc_muxs[idx].reg, mux); + return 0; + } + } else if (sc->sc_muxs == imx8mq_muxs) { switch (idx) { case IMX8MQ_CLK_A53_SRC: if (pidx != IMX8MQ_ARM_PLL_OUT && |