xref: /netbsd-src/sys/arch/arm/nxp/imx6_ccm.c (revision ad8ac1e6343791071b2310e9dc92c3e0b782ba95)
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