1*ad8ac1e6Sskrll /* $NetBSD: imx6_ccm.c,v 1.5 2024/09/01 07:55:27 skrll Exp $ */ 28644267aSskrll 38644267aSskrll /* 48644267aSskrll * Copyright (c) 2010-2012, 2014 Genetec Corporation. All rights reserved. 58644267aSskrll * Written by Hashimoto Kenichi for Genetec Corporation. 68644267aSskrll * 78644267aSskrll * Redistribution and use in source and binary forms, with or without 88644267aSskrll * modification, are permitted provided that the following conditions 98644267aSskrll * are met: 108644267aSskrll * 1. Redistributions of source code must retain the above copyright 118644267aSskrll * notice, this list of conditions and the following disclaimer. 128644267aSskrll * 2. Redistributions in binary form must reproduce the above copyright 138644267aSskrll * notice, this list of conditions and the following disclaimer in the 148644267aSskrll * documentation and/or other materials provided with the distribution. 158644267aSskrll * 168644267aSskrll * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND 178644267aSskrll * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 188644267aSskrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 198644267aSskrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION 208644267aSskrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 218644267aSskrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 228644267aSskrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 238644267aSskrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 248644267aSskrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 258644267aSskrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 268644267aSskrll * POSSIBILITY OF SUCH DAMAGE. 278644267aSskrll */ 288644267aSskrll /* 298644267aSskrll * Clock Controller Module (CCM) for i.MX6 308644267aSskrll */ 318644267aSskrll 328644267aSskrll #include <sys/cdefs.h> 33*ad8ac1e6Sskrll __KERNEL_RCSID(0, "$NetBSD: imx6_ccm.c,v 1.5 2024/09/01 07:55:27 skrll Exp $"); 348644267aSskrll 358644267aSskrll #include "opt_imx.h" 368644267aSskrll #include "opt_cputypes.h" 378644267aSskrll 388644267aSskrll #include "locators.h" 398644267aSskrll 408644267aSskrll #include <sys/types.h> 418644267aSskrll #include <sys/time.h> 428644267aSskrll #include <sys/bus.h> 438644267aSskrll #include <sys/device.h> 448644267aSskrll #include <sys/sysctl.h> 458644267aSskrll #include <sys/cpufreq.h> 468644267aSskrll #include <sys/param.h> 478644267aSskrll 488644267aSskrll #include <machine/cpu.h> 498644267aSskrll 508644267aSskrll #include <arm/nxp/imx6_ccmvar.h> 518644267aSskrll #include <arm/nxp/imx6_ccmreg.h> 528644267aSskrll 538644267aSskrll #include <dev/clk/clk_backend.h> 548644267aSskrll 555fd4005dSbouyer static void imxccm_init_clocks(struct imx6ccm_softc *, 565fd4005dSbouyer struct imxccm_init_parent *); 578644267aSskrll static struct clk *imxccm_clk_get(void *, const char *); 588644267aSskrll static void imxccm_clk_put(void *, struct clk *); 598644267aSskrll static u_int imxccm_clk_get_rate(void *, struct clk *); 608644267aSskrll static int imxccm_clk_set_rate(void *, struct clk *, u_int); 618644267aSskrll static int imxccm_clk_enable(void *, struct clk *); 628644267aSskrll static int imxccm_clk_disable(void *, struct clk *); 638644267aSskrll static int imxccm_clk_set_parent(void *, struct clk *, struct clk *); 648644267aSskrll static struct clk *imxccm_clk_get_parent(void *, struct clk *); 658644267aSskrll 668644267aSskrll static const struct clk_funcs imxccm_clk_funcs = { 678644267aSskrll .get = imxccm_clk_get, 688644267aSskrll .put = imxccm_clk_put, 698644267aSskrll .get_rate = imxccm_clk_get_rate, 708644267aSskrll .set_rate = imxccm_clk_set_rate, 718644267aSskrll .enable = imxccm_clk_enable, 728644267aSskrll .disable = imxccm_clk_disable, 738644267aSskrll .set_parent = imxccm_clk_set_parent, 748644267aSskrll .get_parent = imxccm_clk_get_parent, 758644267aSskrll }; 768644267aSskrll 778644267aSskrll void 785fd4005dSbouyer imx6ccm_attach_common(device_t self, struct imx6_clk *imx6_clks, int size, 795fd4005dSbouyer struct imxccm_init_parent *imxccm_init_parents) 808644267aSskrll { 818644267aSskrll struct imx6ccm_softc * const sc = device_private(self); 828644267aSskrll 838644267aSskrll sc->sc_dev = self; 843e5af17bSbouyer sc->sc_imx6_clks = imx6_clks; 853e5af17bSbouyer sc->sc_imx6_clksize = size; 868644267aSskrll 878644267aSskrll sc->sc_clkdom.name = device_xname(self); 888644267aSskrll sc->sc_clkdom.funcs = &imxccm_clk_funcs; 898644267aSskrll sc->sc_clkdom.priv = sc; 903e5af17bSbouyer for (u_int n = 0; n < size; n++) { 918644267aSskrll imx6_clks[n].base.domain = &sc->sc_clkdom; 928644267aSskrll clk_attach(&imx6_clks[n].base); 938644267aSskrll } 948644267aSskrll 955fd4005dSbouyer imxccm_init_clocks(sc, imxccm_init_parents); 968644267aSskrll 973e5af17bSbouyer for (int n = 0; n < size; n++) { 988644267aSskrll struct clk *clk = &imx6_clks[n].base; 998644267aSskrll struct clk *clk_parent = clk_get_parent(clk); 1008644267aSskrll const char *parent_str = clk_parent ? clk_parent->name : "none"; 1015fd4005dSbouyer 1028644267aSskrll aprint_verbose_dev(self, "%s (%s) : %u Hz\n", clk->name, 1038644267aSskrll parent_str, clk_get_rate(clk)); 1048644267aSskrll } 1058644267aSskrll } 1068644267aSskrll 1078644267aSskrll struct clk * 1083e5af17bSbouyer imx6_get_clock(struct imx6ccm_softc *sc, const char *name) 1098644267aSskrll { 1108644267aSskrll struct imx6_clk *iclk; 1113e5af17bSbouyer iclk = imx6_clk_find(sc, name); 1128644267aSskrll 1138644267aSskrll if (iclk == NULL) 1148644267aSskrll return NULL; 1158644267aSskrll 1168644267aSskrll return &iclk->base; 1178644267aSskrll } 1188644267aSskrll 1193e5af17bSbouyer struct imx6_clk * 1203e5af17bSbouyer imx6_clk_find(struct imx6ccm_softc *sc, const char *name) 1218644267aSskrll { 1228644267aSskrll if (name == NULL) 1238644267aSskrll return NULL; 1248644267aSskrll 1253e5af17bSbouyer for (int n = 0; n < sc->sc_imx6_clksize; n++) { 1263e5af17bSbouyer if (strcmp(sc->sc_imx6_clks[n].base.name, name) == 0) 1273e5af17bSbouyer return &sc->sc_imx6_clks[n]; 1288644267aSskrll } 1298644267aSskrll 1308644267aSskrll return NULL; 1318644267aSskrll } 1328644267aSskrll 1338644267aSskrll static void 1345fd4005dSbouyer imxccm_init_clocks(struct imx6ccm_softc *sc, 1355fd4005dSbouyer struct imxccm_init_parent *imxccm_init_parents) 1368644267aSskrll { 1378644267aSskrll struct clk *clk; 1388644267aSskrll struct clk *clk_parent; 1398644267aSskrll 1405fd4005dSbouyer for (u_int n = 0; imxccm_init_parents[n].clock != NULL; n++) { 1418644267aSskrll clk = clk_get(&sc->sc_clkdom, imxccm_init_parents[n].clock); 1428644267aSskrll KASSERT(clk != NULL); 1438644267aSskrll clk_parent = clk_get(&sc->sc_clkdom, imxccm_init_parents[n].parent); 1448644267aSskrll KASSERT(clk_parent != NULL); 1458644267aSskrll 1468644267aSskrll int error = clk_set_parent(clk, clk_parent); 1478644267aSskrll if (error) { 1488644267aSskrll aprint_error_dev(sc->sc_dev, 1498644267aSskrll "couldn't set '%s' parent to '%s': %d\n", 1508644267aSskrll clk->name, clk_parent->name, error); 1518644267aSskrll } 1528644267aSskrll clk_put(clk_parent); 1538644267aSskrll clk_put(clk); 1548644267aSskrll } 1558644267aSskrll } 1568644267aSskrll 1578644267aSskrll static u_int 1588644267aSskrll imxccm_clk_get_rate_pll_generic(struct imx6ccm_softc *sc, struct imx6_clk *iclk, 1598644267aSskrll const u_int rate_parent) 1608644267aSskrll { 1618644267aSskrll struct imx6_clk_pll *pll = &iclk->clk.pll; 1628644267aSskrll uint64_t freq = rate_parent; 1638644267aSskrll 1648644267aSskrll KASSERT((pll->type == IMX6_CLK_PLL_GENERIC) || 1658644267aSskrll (pll->type == IMX6_CLK_PLL_USB)); 1668644267aSskrll 1678644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg); 1688644267aSskrll uint32_t div = __SHIFTOUT(v, pll->mask); 1698644267aSskrll 1708644267aSskrll return freq * ((div == 1) ? 22 : 20); 1718644267aSskrll } 1728644267aSskrll 1738644267aSskrll static u_int 1748644267aSskrll imxccm_clk_get_rate_pll_sys(struct imx6ccm_softc *sc, struct imx6_clk *iclk, 1758644267aSskrll const u_int rate_parent) 1768644267aSskrll { 1778644267aSskrll struct imx6_clk_pll *pll = &iclk->clk.pll; 1788644267aSskrll uint64_t freq = rate_parent; 1798644267aSskrll 1808644267aSskrll KASSERT(pll->type == IMX6_CLK_PLL_SYS); 1818644267aSskrll 1828644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg); 1838644267aSskrll uint32_t div = __SHIFTOUT(v, pll->mask); 1848644267aSskrll 1858644267aSskrll return freq * div / 2; 1868644267aSskrll } 1878644267aSskrll 1888644267aSskrll #define PLL_AUDIO_VIDEO_NUM_OFFSET 0x10 1898644267aSskrll #define PLL_AUDIO_VIDEO_DENOM_OFFSET 0x20 1908644267aSskrll 1918644267aSskrll static u_int 1928644267aSskrll imxccm_clk_get_rate_pll_audio_video(struct imx6ccm_softc *sc, 1938644267aSskrll struct imx6_clk *iclk, const u_int rate_parent) 1948644267aSskrll { 1958644267aSskrll struct imx6_clk_pll *pll = &iclk->clk.pll; 1968644267aSskrll uint64_t freq = rate_parent; 1978644267aSskrll 1988644267aSskrll KASSERT(pll->type == IMX6_CLK_PLL_AUDIO_VIDEO); 1998644267aSskrll 2008644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg); 2018644267aSskrll uint32_t div = __SHIFTOUT(v, pll->mask); 2028644267aSskrll uint32_t num = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, 2038644267aSskrll pll->reg + PLL_AUDIO_VIDEO_NUM_OFFSET); 2048644267aSskrll uint32_t denom = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, 2058644267aSskrll pll->reg + PLL_AUDIO_VIDEO_DENOM_OFFSET); 2068644267aSskrll 2078644267aSskrll uint64_t tmp = freq * num / denom; 2088644267aSskrll 2098644267aSskrll return freq * div + tmp; 2108644267aSskrll } 2118644267aSskrll 2128644267aSskrll static u_int 2138644267aSskrll imxccm_clk_get_rate_pll_enet(struct imx6ccm_softc *sc, 2148644267aSskrll struct imx6_clk *iclk, const u_int rate_parent) 2158644267aSskrll { 2168644267aSskrll struct imx6_clk_pll *pll = &iclk->clk.pll; 2178644267aSskrll 2188644267aSskrll KASSERT(pll->type == IMX6_CLK_PLL_ENET); 2198644267aSskrll 2208644267aSskrll return pll->ref; 2218644267aSskrll } 2228644267aSskrll 2238644267aSskrll static u_int 2248644267aSskrll imxccm_clk_get_rate_fixed_factor(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 2258644267aSskrll { 2268644267aSskrll struct imx6_clk_fixed_factor *fixed_factor = &iclk->clk.fixed_factor; 2278644267aSskrll struct imx6_clk *parent; 2288644267aSskrll 2298644267aSskrll KASSERT(iclk->type == IMX6_CLK_FIXED_FACTOR); 2308644267aSskrll 2313e5af17bSbouyer parent = imx6_clk_find(sc, iclk->parent); 2328644267aSskrll KASSERT(parent != NULL); 2338644267aSskrll 2348644267aSskrll uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base); 2358644267aSskrll 2368644267aSskrll return rate_parent * fixed_factor->mult / fixed_factor->div; 2378644267aSskrll } 2388644267aSskrll 2398644267aSskrll static u_int 2408644267aSskrll imxccm_clk_get_rate_pll(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 2418644267aSskrll { 2428644267aSskrll struct imx6_clk_pll *pll = &iclk->clk.pll; 2438644267aSskrll struct imx6_clk *parent; 2448644267aSskrll 2458644267aSskrll KASSERT(iclk->type == IMX6_CLK_PLL); 2468644267aSskrll 2473e5af17bSbouyer parent = imx6_clk_find(sc, iclk->parent); 2488644267aSskrll KASSERT(parent != NULL); 2498644267aSskrll 2508644267aSskrll uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base); 2518644267aSskrll 2528644267aSskrll switch(pll->type) { 2538644267aSskrll case IMX6_CLK_PLL_GENERIC: 2548644267aSskrll return imxccm_clk_get_rate_pll_generic(sc, iclk, rate_parent); 2558644267aSskrll case IMX6_CLK_PLL_SYS: 2568644267aSskrll return imxccm_clk_get_rate_pll_sys(sc, iclk, rate_parent); 2578644267aSskrll case IMX6_CLK_PLL_USB: 2588644267aSskrll return imxccm_clk_get_rate_pll_generic(sc, iclk, rate_parent); 2598644267aSskrll case IMX6_CLK_PLL_AUDIO_VIDEO: 2608644267aSskrll return imxccm_clk_get_rate_pll_audio_video(sc, iclk, rate_parent); 2618644267aSskrll case IMX6_CLK_PLL_ENET: 2628644267aSskrll return imxccm_clk_get_rate_pll_enet(sc, iclk, rate_parent); 2638644267aSskrll default: 2648644267aSskrll panic("imx6: unknown pll type %d", iclk->type); 2658644267aSskrll } 2668644267aSskrll } 2678644267aSskrll 2688644267aSskrll static u_int 2698644267aSskrll imxccm_clk_get_rate_div(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 2708644267aSskrll { 2718644267aSskrll struct imx6_clk_div *div = &iclk->clk.div; 2728644267aSskrll struct imx6_clk *parent; 2738644267aSskrll 2748644267aSskrll KASSERT(iclk->type == IMX6_CLK_DIV); 2758644267aSskrll 2763e5af17bSbouyer parent = imx6_clk_find(sc, iclk->parent); 2778644267aSskrll KASSERT(parent != NULL); 2788644267aSskrll 2798644267aSskrll u_int rate = imxccm_clk_get_rate(sc, &parent->base); 2808644267aSskrll 2818644267aSskrll bus_space_handle_t ioh; 2828644267aSskrll if (div->base == IMX6_CLK_REG_CCM_ANALOG) 2838644267aSskrll ioh = sc->sc_ioh_analog; 2848644267aSskrll else 2858644267aSskrll ioh = sc->sc_ioh; 2868644267aSskrll 2878644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, div->reg); 2888644267aSskrll uint32_t n = __SHIFTOUT(v, div->mask); 2898644267aSskrll 2908644267aSskrll if (div->type == IMX6_CLK_DIV_TABLE) { 2918644267aSskrll KASSERT(div->tbl != NULL); 2928644267aSskrll 2938644267aSskrll for (int i = 0; div->tbl[i] != 0; i++) 2948644267aSskrll if (i == n) 2958644267aSskrll rate /= div->tbl[i]; 2968644267aSskrll } else { 2978644267aSskrll rate /= n + 1; 2988644267aSskrll } 2998644267aSskrll 3008644267aSskrll return rate; 3018644267aSskrll } 3028644267aSskrll 3038644267aSskrll static u_int 3048644267aSskrll imxccm_clk_get_rate_pfd(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 3058644267aSskrll { 3068644267aSskrll struct imx6_clk_pfd *pfd = &iclk->clk.pfd; 3078644267aSskrll struct imx6_clk *parent; 3088644267aSskrll 3098644267aSskrll KASSERT(iclk->type == IMX6_CLK_PFD); 3108644267aSskrll 3113e5af17bSbouyer parent = imx6_clk_find(sc, iclk->parent); 3128644267aSskrll KASSERT(parent != NULL); 3138644267aSskrll 3148644267aSskrll uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base); 3158644267aSskrll 3168644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pfd->reg); 3178644267aSskrll uint32_t n = __SHIFTOUT(v, __BITS(5, 0) << (pfd->index * 8)); 3188644267aSskrll 3198644267aSskrll KASSERT(n != 0); 3208644267aSskrll 3218644267aSskrll return (rate_parent * 18) / n; 3228644267aSskrll } 3238644267aSskrll 3248644267aSskrll static int 3258644267aSskrll imxccm_clk_mux_wait(struct imx6ccm_softc *sc, struct imx6_clk_mux *mux) 3268644267aSskrll { 3278644267aSskrll KASSERT(mux->busy_reg == 0); 3288644267aSskrll KASSERT(mux->busy_mask == 0); 3298644267aSskrll 3308644267aSskrll bus_space_handle_t ioh; 3318644267aSskrll if (mux->base == IMX6_CLK_REG_CCM_ANALOG) 3328644267aSskrll ioh = sc->sc_ioh_analog; 3338644267aSskrll else 3348644267aSskrll ioh = sc->sc_ioh; 3358644267aSskrll 3368644267aSskrll while (bus_space_read_4(sc->sc_iot, ioh, mux->busy_reg) & mux->busy_mask) 3378644267aSskrll delay(10); 3388644267aSskrll 3398644267aSskrll return 0; 3408644267aSskrll } 3418644267aSskrll 3428644267aSskrll static int 3438644267aSskrll imxccm_clk_set_parent_mux(struct imx6ccm_softc *sc, 3448644267aSskrll struct imx6_clk *iclk, struct clk *parent) 3458644267aSskrll { 3468644267aSskrll struct imx6_clk_mux *mux = &iclk->clk.mux; 3478644267aSskrll const char *pname = parent->name; 3488644267aSskrll u_int sel; 3498644267aSskrll 3508644267aSskrll KASSERT(iclk->type == IMX6_CLK_MUX); 3518644267aSskrll 3528644267aSskrll for (sel = 0; sel < mux->nparents; sel++) 3538644267aSskrll if (strcmp(pname, mux->parents[sel]) == 0) 3548644267aSskrll break; 3558644267aSskrll 3568644267aSskrll if (sel == mux->nparents) 3578644267aSskrll return EINVAL; 3588644267aSskrll 3598644267aSskrll bus_space_handle_t ioh; 3608644267aSskrll if (mux->base == IMX6_CLK_REG_CCM_ANALOG) 3618644267aSskrll ioh = sc->sc_ioh_analog; 3628644267aSskrll else 3638644267aSskrll ioh = sc->sc_ioh; 3648644267aSskrll 3658644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, mux->reg); 3668644267aSskrll v &= ~mux->mask; 3678644267aSskrll v |= __SHIFTIN(sel, mux->mask); 3688644267aSskrll 3698644267aSskrll bus_space_write_4(sc->sc_iot, ioh, mux->reg, v); 3708644267aSskrll 3718644267aSskrll iclk->parent = pname; 3728644267aSskrll 3738644267aSskrll if (mux->type == IMX6_CLK_MUX_BUSY) 3748644267aSskrll imxccm_clk_mux_wait(sc, mux); 3758644267aSskrll 3768644267aSskrll return 0; 3778644267aSskrll } 3788644267aSskrll 3798644267aSskrll static struct imx6_clk * 3808644267aSskrll imxccm_clk_get_parent_mux(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 3818644267aSskrll { 3828644267aSskrll struct imx6_clk_mux *mux = &iclk->clk.mux; 3838644267aSskrll 3848644267aSskrll KASSERT(iclk->type == IMX6_CLK_MUX); 3858644267aSskrll 3868644267aSskrll bus_space_handle_t ioh; 3878644267aSskrll if (mux->base == IMX6_CLK_REG_CCM_ANALOG) 3888644267aSskrll ioh = sc->sc_ioh_analog; 3898644267aSskrll else 3908644267aSskrll ioh = sc->sc_ioh; 3918644267aSskrll 3928644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, mux->reg); 3938644267aSskrll u_int sel = __SHIFTOUT(v, mux->mask); 3948644267aSskrll 3955fd4005dSbouyer KASSERTMSG(sel < mux->nparents, "mux %s sel %d nparents %d", 3965fd4005dSbouyer iclk->base.name, sel, mux->nparents); 3978644267aSskrll 3988644267aSskrll iclk->parent = mux->parents[sel]; 3998644267aSskrll 4003e5af17bSbouyer return imx6_clk_find(sc, iclk->parent); 4018644267aSskrll } 4028644267aSskrll 4038644267aSskrll static int 4048644267aSskrll imxccm_clk_set_rate_pll(struct imx6ccm_softc *sc, 4058644267aSskrll struct imx6_clk *iclk, u_int rate) 4068644267aSskrll { 4078644267aSskrll /* ToDo */ 4088644267aSskrll 4098644267aSskrll return EOPNOTSUPP; 4108644267aSskrll } 4118644267aSskrll 4128644267aSskrll static int 4138644267aSskrll imxccm_clk_set_rate_div(struct imx6ccm_softc *sc, 4148644267aSskrll struct imx6_clk *iclk, u_int rate) 4158644267aSskrll { 4168644267aSskrll struct imx6_clk_div *div = &iclk->clk.div; 4178644267aSskrll struct imx6_clk *parent; 4188644267aSskrll 4198644267aSskrll KASSERT(iclk->type == IMX6_CLK_DIV); 4208644267aSskrll 4213e5af17bSbouyer parent = imx6_clk_find(sc, iclk->parent); 4228644267aSskrll KASSERT(parent != NULL); 4238644267aSskrll 4248644267aSskrll u_int rate_parent = imxccm_clk_get_rate(sc, &parent->base); 4258644267aSskrll u_int divider = uimax(1, rate_parent / rate); 4268644267aSskrll 4278644267aSskrll bus_space_handle_t ioh; 4288644267aSskrll if (div->base == IMX6_CLK_REG_CCM_ANALOG) 4298644267aSskrll ioh = sc->sc_ioh_analog; 4308644267aSskrll else 4318644267aSskrll ioh = sc->sc_ioh; 4328644267aSskrll 4338644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, div->reg); 4348644267aSskrll v &= ~div->mask; 4358644267aSskrll if (div->type == IMX6_CLK_DIV_TABLE) { 4368644267aSskrll int n = -1; 4378644267aSskrll 4388644267aSskrll KASSERT(div->tbl != NULL); 4398644267aSskrll for (int i = 0; div->tbl[i] != 0; i++) 4408644267aSskrll if (div->tbl[i] == divider) 4418644267aSskrll n = i; 4428644267aSskrll 4438644267aSskrll if (n >= 0) 4448644267aSskrll v |= __SHIFTIN(n, div->mask); 4458644267aSskrll else 4468644267aSskrll return EINVAL; 4478644267aSskrll } else { 4488644267aSskrll v |= __SHIFTIN(divider - 1, div->mask); 4498644267aSskrll } 4508644267aSskrll bus_space_write_4(sc->sc_iot, ioh, div->reg, v); 4518644267aSskrll 4528644267aSskrll return 0; 4538644267aSskrll } 4548644267aSskrll 4558644267aSskrll /* 4568644267aSskrll * CLK Driver APIs 4578644267aSskrll */ 4588644267aSskrll static struct clk * 4598644267aSskrll imxccm_clk_get(void *priv, const char *name) 4608644267aSskrll { 4618644267aSskrll struct imx6_clk *iclk; 4623e5af17bSbouyer struct imx6ccm_softc *sc = priv; 4638644267aSskrll 4643e5af17bSbouyer iclk = imx6_clk_find(sc, name); 4658644267aSskrll if (iclk == NULL) 4668644267aSskrll return NULL; 4678644267aSskrll 4688644267aSskrll atomic_inc_uint(&iclk->refcnt); 4698644267aSskrll 4708644267aSskrll return &iclk->base; 4718644267aSskrll } 4728644267aSskrll 4738644267aSskrll static void 4748644267aSskrll imxccm_clk_put(void *priv, struct clk *clk) 4758644267aSskrll { 4768644267aSskrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 4778644267aSskrll 4788644267aSskrll KASSERT(iclk->refcnt > 0); 4798644267aSskrll 4808644267aSskrll atomic_dec_uint(&iclk->refcnt); 4818644267aSskrll } 4828644267aSskrll 4838644267aSskrll static u_int 4848644267aSskrll imxccm_clk_get_rate(void *priv, struct clk *clk) 4858644267aSskrll { 4868644267aSskrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 4878644267aSskrll struct clk *parent; 4888644267aSskrll struct imx6ccm_softc *sc = priv; 4898644267aSskrll 4908644267aSskrll switch (iclk->type) { 4918644267aSskrll case IMX6_CLK_FIXED: 4928644267aSskrll return iclk->clk.fixed.rate; 4938644267aSskrll case IMX6_CLK_FIXED_FACTOR: 4948644267aSskrll return imxccm_clk_get_rate_fixed_factor(sc, iclk); 4958644267aSskrll case IMX6_CLK_PLL: 4968644267aSskrll return imxccm_clk_get_rate_pll(sc, iclk); 4978644267aSskrll case IMX6_CLK_MUX: 4988644267aSskrll case IMX6_CLK_GATE: 4998644267aSskrll parent = imxccm_clk_get_parent(sc, clk); 5008644267aSskrll return imxccm_clk_get_rate(sc, parent); 5018644267aSskrll case IMX6_CLK_DIV: 5028644267aSskrll return imxccm_clk_get_rate_div(sc, iclk); 5038644267aSskrll case IMX6_CLK_PFD: 5048644267aSskrll return imxccm_clk_get_rate_pfd(sc, iclk); 5058644267aSskrll default: 5068644267aSskrll panic("imx6: unknown clk type %d", iclk->type); 5078644267aSskrll } 5088644267aSskrll } 5098644267aSskrll 5108644267aSskrll static int 5118644267aSskrll imxccm_clk_set_rate(void *priv, struct clk *clk, u_int rate) 5128644267aSskrll { 5138644267aSskrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 5148644267aSskrll struct imx6ccm_softc *sc = priv; 5158644267aSskrll 5168644267aSskrll switch (iclk->type) { 5178644267aSskrll case IMX6_CLK_FIXED: 5188644267aSskrll case IMX6_CLK_FIXED_FACTOR: 5198644267aSskrll return ENXIO; 5208644267aSskrll case IMX6_CLK_PLL: 5218644267aSskrll return imxccm_clk_set_rate_pll(sc, iclk, rate); 5228644267aSskrll case IMX6_CLK_MUX: 5238644267aSskrll return ENXIO; 5248644267aSskrll case IMX6_CLK_GATE: 5258644267aSskrll return ENXIO; 5268644267aSskrll case IMX6_CLK_DIV: 5278644267aSskrll return imxccm_clk_set_rate_div(sc, iclk, rate); 5288644267aSskrll case IMX6_CLK_PFD: 5298644267aSskrll return EINVAL; 5308644267aSskrll default: 5318644267aSskrll panic("imx6: unknown clk type %d", iclk->type); 5328644267aSskrll } 5338644267aSskrll } 5348644267aSskrll 5358644267aSskrll static int 5368644267aSskrll imxccm_clk_enable_pll(struct imx6ccm_softc *sc, struct imx6_clk *iclk, bool enable) 5378644267aSskrll { 5388644267aSskrll struct imx6_clk_pll *pll = &iclk->clk.pll; 5398644267aSskrll 5408644267aSskrll KASSERT(iclk->type == IMX6_CLK_PLL); 5418644267aSskrll 5428644267aSskrll /* Power up bit */ 5438644267aSskrll if (pll->type == IMX6_CLK_PLL_USB) 5448644267aSskrll enable = !enable; 5458644267aSskrll 5468644267aSskrll bus_space_handle_t ioh = sc->sc_ioh_analog; 5478644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, pll->reg); 5488644267aSskrll if (__SHIFTOUT(v, pll->powerdown) != enable) 5498644267aSskrll return 0; 5508644267aSskrll if (enable) 5518644267aSskrll v &= ~pll->powerdown; 5528644267aSskrll else 5538644267aSskrll v |= pll->powerdown; 5548644267aSskrll bus_space_write_4(sc->sc_iot, ioh, pll->reg, v); 5558644267aSskrll 5568644267aSskrll /* wait look */ 5578644267aSskrll while (!(bus_space_read_4(sc->sc_iot, ioh, pll->reg) & CCM_ANALOG_PLL_LOCK)) 5588644267aSskrll delay(10); 5598644267aSskrll 5608644267aSskrll return 0; 5618644267aSskrll } 5628644267aSskrll 5638644267aSskrll static int 5648644267aSskrll imxccm_clk_enable_gate(struct imx6ccm_softc *sc, struct imx6_clk *iclk, bool enable) 5658644267aSskrll { 5668644267aSskrll struct imx6_clk_gate *gate = &iclk->clk.gate; 5678644267aSskrll 5688644267aSskrll KASSERT(iclk->type == IMX6_CLK_GATE); 5698644267aSskrll 5708644267aSskrll bus_space_handle_t ioh; 5718644267aSskrll if (gate->base == IMX6_CLK_REG_CCM_ANALOG) 5728644267aSskrll ioh = sc->sc_ioh_analog; 5738644267aSskrll else 5748644267aSskrll ioh = sc->sc_ioh; 5758644267aSskrll 5768644267aSskrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, gate->reg); 5778644267aSskrll if (enable) { 5788644267aSskrll if (gate->exclusive_mask) 5798644267aSskrll v &= ~gate->exclusive_mask; 5808644267aSskrll v |= gate->mask; 5818644267aSskrll } else { 5828644267aSskrll if (gate->exclusive_mask) 5838644267aSskrll v |= gate->exclusive_mask; 5848644267aSskrll v &= ~gate->mask; 5858644267aSskrll } 5868644267aSskrll bus_space_write_4(sc->sc_iot, ioh, gate->reg, v); 5878644267aSskrll 5888644267aSskrll return 0; 5898644267aSskrll } 5908644267aSskrll 5918644267aSskrll static int 5928644267aSskrll imxccm_clk_enable(void *priv, struct clk *clk) 5938644267aSskrll { 5948644267aSskrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 5958644267aSskrll struct imx6_clk *parent = NULL; 5968644267aSskrll struct imx6ccm_softc *sc = priv; 5978644267aSskrll 5983e5af17bSbouyer if ((parent = imx6_clk_find(sc, iclk->parent)) != NULL) 5998644267aSskrll imxccm_clk_enable(sc, &parent->base); 6008644267aSskrll 6018644267aSskrll switch (iclk->type) { 6028644267aSskrll case IMX6_CLK_FIXED: 6038644267aSskrll case IMX6_CLK_FIXED_FACTOR: 6048644267aSskrll return 0; /* always on */ 6058644267aSskrll case IMX6_CLK_PLL: 6068644267aSskrll return imxccm_clk_enable_pll(sc, iclk, true); 6078644267aSskrll case IMX6_CLK_MUX: 6088644267aSskrll case IMX6_CLK_DIV: 6098644267aSskrll case IMX6_CLK_PFD: 6108644267aSskrll return 0; 6118644267aSskrll case IMX6_CLK_GATE: 6128644267aSskrll return imxccm_clk_enable_gate(sc, iclk, true); 6138644267aSskrll default: 6148644267aSskrll panic("imx6: unknown clk type %d", iclk->type); 6158644267aSskrll } 6168644267aSskrll } 6178644267aSskrll 6188644267aSskrll static int 6198644267aSskrll imxccm_clk_disable(void *priv, struct clk *clk) 6208644267aSskrll { 6218644267aSskrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 6228644267aSskrll struct imx6ccm_softc *sc = priv; 6238644267aSskrll 6248644267aSskrll switch (iclk->type) { 6258644267aSskrll case IMX6_CLK_FIXED: 6268644267aSskrll case IMX6_CLK_FIXED_FACTOR: 6278644267aSskrll return EINVAL; /* always on */ 6288644267aSskrll case IMX6_CLK_PLL: 6298644267aSskrll return imxccm_clk_enable_pll(sc, iclk, false); 6308644267aSskrll case IMX6_CLK_MUX: 6318644267aSskrll case IMX6_CLK_DIV: 6328644267aSskrll case IMX6_CLK_PFD: 6338644267aSskrll return EINVAL; 6348644267aSskrll case IMX6_CLK_GATE: 6358644267aSskrll return imxccm_clk_enable_gate(sc, iclk, false); 6368644267aSskrll default: 6378644267aSskrll panic("imx6: unknown clk type %d", iclk->type); 6388644267aSskrll } 6398644267aSskrll } 6408644267aSskrll 6418644267aSskrll static int 6428644267aSskrll imxccm_clk_set_parent(void *priv, struct clk *clk, struct clk *parent) 6438644267aSskrll { 6448644267aSskrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 6458644267aSskrll struct imx6ccm_softc *sc = priv; 6468644267aSskrll 6478644267aSskrll switch (iclk->type) { 6488644267aSskrll case IMX6_CLK_FIXED: 6498644267aSskrll case IMX6_CLK_FIXED_FACTOR: 6508644267aSskrll case IMX6_CLK_PLL: 6518644267aSskrll case IMX6_CLK_GATE: 6528644267aSskrll case IMX6_CLK_DIV: 6538644267aSskrll case IMX6_CLK_PFD: 6548644267aSskrll return EINVAL; 6558644267aSskrll case IMX6_CLK_MUX: 6568644267aSskrll return imxccm_clk_set_parent_mux(sc, iclk, parent); 6578644267aSskrll default: 6588644267aSskrll panic("imx6: unknown clk type %d", iclk->type); 6598644267aSskrll } 6608644267aSskrll } 6618644267aSskrll 6628644267aSskrll static struct clk * 6638644267aSskrll imxccm_clk_get_parent(void *priv, struct clk *clk) 6648644267aSskrll { 6658644267aSskrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 6668644267aSskrll struct imx6_clk *parent = NULL; 6678644267aSskrll struct imx6ccm_softc *sc = priv; 6688644267aSskrll 6698644267aSskrll switch (iclk->type) { 6708644267aSskrll case IMX6_CLK_FIXED: 6718644267aSskrll case IMX6_CLK_FIXED_FACTOR: 6728644267aSskrll case IMX6_CLK_PLL: 6738644267aSskrll case IMX6_CLK_GATE: 6748644267aSskrll case IMX6_CLK_DIV: 6758644267aSskrll case IMX6_CLK_PFD: 6768644267aSskrll if (iclk->parent != NULL) 6773e5af17bSbouyer parent = imx6_clk_find(sc, iclk->parent); 6788644267aSskrll break; 6798644267aSskrll case IMX6_CLK_MUX: 6808644267aSskrll parent = imxccm_clk_get_parent_mux(sc, iclk); 6818644267aSskrll break; 6828644267aSskrll default: 6838644267aSskrll panic("imx6: unknown clk type %d", iclk->type); 6848644267aSskrll } 6858644267aSskrll 6868644267aSskrll return (struct clk *)parent; 6878644267aSskrll } 688