1 1.5 skrll /* $NetBSD: imx6_ccm.c,v 1.5 2024/09/01 07:55:27 skrll Exp $ */ 2 1.1 skrll 3 1.1 skrll /* 4 1.1 skrll * Copyright (c) 2010-2012, 2014 Genetec Corporation. All rights reserved. 5 1.1 skrll * Written by Hashimoto Kenichi for Genetec Corporation. 6 1.1 skrll * 7 1.1 skrll * Redistribution and use in source and binary forms, with or without 8 1.1 skrll * modification, are permitted provided that the following conditions 9 1.1 skrll * are met: 10 1.1 skrll * 1. Redistributions of source code must retain the above copyright 11 1.1 skrll * notice, this list of conditions and the following disclaimer. 12 1.1 skrll * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 skrll * notice, this list of conditions and the following disclaimer in the 14 1.1 skrll * documentation and/or other materials provided with the distribution. 15 1.1 skrll * 16 1.1 skrll * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND 17 1.1 skrll * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 skrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 skrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION 20 1.1 skrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 skrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 skrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 skrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 skrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 skrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 skrll * POSSIBILITY OF SUCH DAMAGE. 27 1.1 skrll */ 28 1.1 skrll /* 29 1.1 skrll * Clock Controller Module (CCM) for i.MX6 30 1.1 skrll */ 31 1.1 skrll 32 1.1 skrll #include <sys/cdefs.h> 33 1.5 skrll __KERNEL_RCSID(0, "$NetBSD: imx6_ccm.c,v 1.5 2024/09/01 07:55:27 skrll Exp $"); 34 1.1 skrll 35 1.1 skrll #include "opt_imx.h" 36 1.1 skrll #include "opt_cputypes.h" 37 1.1 skrll 38 1.1 skrll #include "locators.h" 39 1.1 skrll 40 1.1 skrll #include <sys/types.h> 41 1.1 skrll #include <sys/time.h> 42 1.1 skrll #include <sys/bus.h> 43 1.1 skrll #include <sys/device.h> 44 1.1 skrll #include <sys/sysctl.h> 45 1.1 skrll #include <sys/cpufreq.h> 46 1.1 skrll #include <sys/param.h> 47 1.1 skrll 48 1.1 skrll #include <machine/cpu.h> 49 1.1 skrll 50 1.1 skrll #include <arm/nxp/imx6_ccmvar.h> 51 1.1 skrll #include <arm/nxp/imx6_ccmreg.h> 52 1.1 skrll 53 1.1 skrll #include <dev/clk/clk_backend.h> 54 1.1 skrll 55 1.4 bouyer static void imxccm_init_clocks(struct imx6ccm_softc *, 56 1.4 bouyer struct imxccm_init_parent *); 57 1.1 skrll static struct clk *imxccm_clk_get(void *, const char *); 58 1.1 skrll static void imxccm_clk_put(void *, struct clk *); 59 1.1 skrll static u_int imxccm_clk_get_rate(void *, struct clk *); 60 1.1 skrll static int imxccm_clk_set_rate(void *, struct clk *, u_int); 61 1.1 skrll static int imxccm_clk_enable(void *, struct clk *); 62 1.1 skrll static int imxccm_clk_disable(void *, struct clk *); 63 1.1 skrll static int imxccm_clk_set_parent(void *, struct clk *, struct clk *); 64 1.1 skrll static struct clk *imxccm_clk_get_parent(void *, struct clk *); 65 1.1 skrll 66 1.1 skrll static const struct clk_funcs imxccm_clk_funcs = { 67 1.1 skrll .get = imxccm_clk_get, 68 1.1 skrll .put = imxccm_clk_put, 69 1.1 skrll .get_rate = imxccm_clk_get_rate, 70 1.1 skrll .set_rate = imxccm_clk_set_rate, 71 1.1 skrll .enable = imxccm_clk_enable, 72 1.1 skrll .disable = imxccm_clk_disable, 73 1.1 skrll .set_parent = imxccm_clk_set_parent, 74 1.1 skrll .get_parent = imxccm_clk_get_parent, 75 1.1 skrll }; 76 1.1 skrll 77 1.1 skrll void 78 1.5 skrll imx6ccm_attach_common(device_t self, struct imx6_clk *imx6_clks, int size, 79 1.4 bouyer struct imxccm_init_parent *imxccm_init_parents) 80 1.1 skrll { 81 1.1 skrll struct imx6ccm_softc * const sc = device_private(self); 82 1.1 skrll 83 1.1 skrll sc->sc_dev = self; 84 1.3 bouyer sc->sc_imx6_clks = imx6_clks; 85 1.3 bouyer sc->sc_imx6_clksize = size; 86 1.1 skrll 87 1.1 skrll sc->sc_clkdom.name = device_xname(self); 88 1.1 skrll sc->sc_clkdom.funcs = &imxccm_clk_funcs; 89 1.1 skrll sc->sc_clkdom.priv = sc; 90 1.3 bouyer for (u_int n = 0; n < size; n++) { 91 1.1 skrll imx6_clks[n].base.domain = &sc->sc_clkdom; 92 1.1 skrll clk_attach(&imx6_clks[n].base); 93 1.1 skrll } 94 1.1 skrll 95 1.4 bouyer imxccm_init_clocks(sc, imxccm_init_parents); 96 1.1 skrll 97 1.3 bouyer for (int n = 0; n < size; n++) { 98 1.1 skrll struct clk *clk = &imx6_clks[n].base; 99 1.1 skrll struct clk *clk_parent = clk_get_parent(clk); 100 1.1 skrll const char *parent_str = clk_parent ? clk_parent->name : "none"; 101 1.4 bouyer 102 1.1 skrll aprint_verbose_dev(self, "%s (%s) : %u Hz\n", clk->name, 103 1.1 skrll parent_str, clk_get_rate(clk)); 104 1.1 skrll } 105 1.1 skrll } 106 1.1 skrll 107 1.1 skrll struct clk * 108 1.3 bouyer imx6_get_clock(struct imx6ccm_softc *sc, const char *name) 109 1.1 skrll { 110 1.1 skrll struct imx6_clk *iclk; 111 1.3 bouyer iclk = imx6_clk_find(sc, name); 112 1.1 skrll 113 1.1 skrll if (iclk == NULL) 114 1.1 skrll return NULL; 115 1.1 skrll 116 1.1 skrll return &iclk->base; 117 1.1 skrll } 118 1.1 skrll 119 1.3 bouyer struct imx6_clk * 120 1.3 bouyer imx6_clk_find(struct imx6ccm_softc *sc, const char *name) 121 1.1 skrll { 122 1.1 skrll if (name == NULL) 123 1.1 skrll return NULL; 124 1.1 skrll 125 1.3 bouyer for (int n = 0; n < sc->sc_imx6_clksize; n++) { 126 1.3 bouyer if (strcmp(sc->sc_imx6_clks[n].base.name, name) == 0) 127 1.3 bouyer return &sc->sc_imx6_clks[n]; 128 1.1 skrll } 129 1.1 skrll 130 1.1 skrll return NULL; 131 1.1 skrll } 132 1.1 skrll 133 1.1 skrll static void 134 1.4 bouyer imxccm_init_clocks(struct imx6ccm_softc *sc, 135 1.4 bouyer struct imxccm_init_parent *imxccm_init_parents) 136 1.1 skrll { 137 1.1 skrll struct clk *clk; 138 1.1 skrll struct clk *clk_parent; 139 1.1 skrll 140 1.4 bouyer for (u_int n = 0; imxccm_init_parents[n].clock != NULL; n++) { 141 1.1 skrll clk = clk_get(&sc->sc_clkdom, imxccm_init_parents[n].clock); 142 1.1 skrll KASSERT(clk != NULL); 143 1.1 skrll clk_parent = clk_get(&sc->sc_clkdom, imxccm_init_parents[n].parent); 144 1.1 skrll KASSERT(clk_parent != NULL); 145 1.1 skrll 146 1.1 skrll int error = clk_set_parent(clk, clk_parent); 147 1.1 skrll if (error) { 148 1.1 skrll aprint_error_dev(sc->sc_dev, 149 1.1 skrll "couldn't set '%s' parent to '%s': %d\n", 150 1.1 skrll clk->name, clk_parent->name, error); 151 1.1 skrll } 152 1.1 skrll clk_put(clk_parent); 153 1.1 skrll clk_put(clk); 154 1.1 skrll } 155 1.1 skrll } 156 1.1 skrll 157 1.1 skrll static u_int 158 1.1 skrll imxccm_clk_get_rate_pll_generic(struct imx6ccm_softc *sc, struct imx6_clk *iclk, 159 1.1 skrll const u_int rate_parent) 160 1.1 skrll { 161 1.1 skrll struct imx6_clk_pll *pll = &iclk->clk.pll; 162 1.1 skrll uint64_t freq = rate_parent; 163 1.1 skrll 164 1.1 skrll KASSERT((pll->type == IMX6_CLK_PLL_GENERIC) || 165 1.1 skrll (pll->type == IMX6_CLK_PLL_USB)); 166 1.1 skrll 167 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg); 168 1.1 skrll uint32_t div = __SHIFTOUT(v, pll->mask); 169 1.1 skrll 170 1.1 skrll return freq * ((div == 1) ? 22 : 20); 171 1.1 skrll } 172 1.1 skrll 173 1.1 skrll static u_int 174 1.1 skrll imxccm_clk_get_rate_pll_sys(struct imx6ccm_softc *sc, struct imx6_clk *iclk, 175 1.1 skrll const u_int rate_parent) 176 1.1 skrll { 177 1.1 skrll struct imx6_clk_pll *pll = &iclk->clk.pll; 178 1.1 skrll uint64_t freq = rate_parent; 179 1.1 skrll 180 1.1 skrll KASSERT(pll->type == IMX6_CLK_PLL_SYS); 181 1.1 skrll 182 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg); 183 1.1 skrll uint32_t div = __SHIFTOUT(v, pll->mask); 184 1.1 skrll 185 1.1 skrll return freq * div / 2; 186 1.1 skrll } 187 1.1 skrll 188 1.1 skrll #define PLL_AUDIO_VIDEO_NUM_OFFSET 0x10 189 1.1 skrll #define PLL_AUDIO_VIDEO_DENOM_OFFSET 0x20 190 1.1 skrll 191 1.1 skrll static u_int 192 1.1 skrll imxccm_clk_get_rate_pll_audio_video(struct imx6ccm_softc *sc, 193 1.1 skrll struct imx6_clk *iclk, const u_int rate_parent) 194 1.1 skrll { 195 1.1 skrll struct imx6_clk_pll *pll = &iclk->clk.pll; 196 1.1 skrll uint64_t freq = rate_parent; 197 1.1 skrll 198 1.1 skrll KASSERT(pll->type == IMX6_CLK_PLL_AUDIO_VIDEO); 199 1.1 skrll 200 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pll->reg); 201 1.1 skrll uint32_t div = __SHIFTOUT(v, pll->mask); 202 1.1 skrll uint32_t num = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, 203 1.1 skrll pll->reg + PLL_AUDIO_VIDEO_NUM_OFFSET); 204 1.1 skrll uint32_t denom = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, 205 1.1 skrll pll->reg + PLL_AUDIO_VIDEO_DENOM_OFFSET); 206 1.1 skrll 207 1.1 skrll uint64_t tmp = freq * num / denom; 208 1.1 skrll 209 1.1 skrll return freq * div + tmp; 210 1.1 skrll } 211 1.1 skrll 212 1.1 skrll static u_int 213 1.1 skrll imxccm_clk_get_rate_pll_enet(struct imx6ccm_softc *sc, 214 1.1 skrll struct imx6_clk *iclk, const u_int rate_parent) 215 1.1 skrll { 216 1.1 skrll struct imx6_clk_pll *pll = &iclk->clk.pll; 217 1.1 skrll 218 1.1 skrll KASSERT(pll->type == IMX6_CLK_PLL_ENET); 219 1.1 skrll 220 1.1 skrll return pll->ref; 221 1.1 skrll } 222 1.1 skrll 223 1.1 skrll static u_int 224 1.1 skrll imxccm_clk_get_rate_fixed_factor(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 225 1.1 skrll { 226 1.1 skrll struct imx6_clk_fixed_factor *fixed_factor = &iclk->clk.fixed_factor; 227 1.1 skrll struct imx6_clk *parent; 228 1.1 skrll 229 1.1 skrll KASSERT(iclk->type == IMX6_CLK_FIXED_FACTOR); 230 1.1 skrll 231 1.3 bouyer parent = imx6_clk_find(sc, iclk->parent); 232 1.1 skrll KASSERT(parent != NULL); 233 1.1 skrll 234 1.1 skrll uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base); 235 1.1 skrll 236 1.1 skrll return rate_parent * fixed_factor->mult / fixed_factor->div; 237 1.1 skrll } 238 1.1 skrll 239 1.1 skrll static u_int 240 1.1 skrll imxccm_clk_get_rate_pll(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 241 1.1 skrll { 242 1.1 skrll struct imx6_clk_pll *pll = &iclk->clk.pll; 243 1.1 skrll struct imx6_clk *parent; 244 1.1 skrll 245 1.1 skrll KASSERT(iclk->type == IMX6_CLK_PLL); 246 1.1 skrll 247 1.3 bouyer parent = imx6_clk_find(sc, iclk->parent); 248 1.1 skrll KASSERT(parent != NULL); 249 1.1 skrll 250 1.1 skrll uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base); 251 1.1 skrll 252 1.1 skrll switch(pll->type) { 253 1.1 skrll case IMX6_CLK_PLL_GENERIC: 254 1.1 skrll return imxccm_clk_get_rate_pll_generic(sc, iclk, rate_parent); 255 1.1 skrll case IMX6_CLK_PLL_SYS: 256 1.1 skrll return imxccm_clk_get_rate_pll_sys(sc, iclk, rate_parent); 257 1.1 skrll case IMX6_CLK_PLL_USB: 258 1.1 skrll return imxccm_clk_get_rate_pll_generic(sc, iclk, rate_parent); 259 1.1 skrll case IMX6_CLK_PLL_AUDIO_VIDEO: 260 1.1 skrll return imxccm_clk_get_rate_pll_audio_video(sc, iclk, rate_parent); 261 1.1 skrll case IMX6_CLK_PLL_ENET: 262 1.1 skrll return imxccm_clk_get_rate_pll_enet(sc, iclk, rate_parent); 263 1.1 skrll default: 264 1.1 skrll panic("imx6: unknown pll type %d", iclk->type); 265 1.1 skrll } 266 1.1 skrll } 267 1.1 skrll 268 1.1 skrll static u_int 269 1.1 skrll imxccm_clk_get_rate_div(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 270 1.1 skrll { 271 1.1 skrll struct imx6_clk_div *div = &iclk->clk.div; 272 1.1 skrll struct imx6_clk *parent; 273 1.1 skrll 274 1.1 skrll KASSERT(iclk->type == IMX6_CLK_DIV); 275 1.1 skrll 276 1.3 bouyer parent = imx6_clk_find(sc, iclk->parent); 277 1.1 skrll KASSERT(parent != NULL); 278 1.1 skrll 279 1.1 skrll u_int rate = imxccm_clk_get_rate(sc, &parent->base); 280 1.1 skrll 281 1.1 skrll bus_space_handle_t ioh; 282 1.1 skrll if (div->base == IMX6_CLK_REG_CCM_ANALOG) 283 1.1 skrll ioh = sc->sc_ioh_analog; 284 1.1 skrll else 285 1.1 skrll ioh = sc->sc_ioh; 286 1.1 skrll 287 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, div->reg); 288 1.1 skrll uint32_t n = __SHIFTOUT(v, div->mask); 289 1.1 skrll 290 1.1 skrll if (div->type == IMX6_CLK_DIV_TABLE) { 291 1.1 skrll KASSERT(div->tbl != NULL); 292 1.1 skrll 293 1.1 skrll for (int i = 0; div->tbl[i] != 0; i++) 294 1.1 skrll if (i == n) 295 1.1 skrll rate /= div->tbl[i]; 296 1.1 skrll } else { 297 1.1 skrll rate /= n + 1; 298 1.1 skrll } 299 1.1 skrll 300 1.1 skrll return rate; 301 1.1 skrll } 302 1.1 skrll 303 1.1 skrll static u_int 304 1.1 skrll imxccm_clk_get_rate_pfd(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 305 1.1 skrll { 306 1.1 skrll struct imx6_clk_pfd *pfd = &iclk->clk.pfd; 307 1.1 skrll struct imx6_clk *parent; 308 1.1 skrll 309 1.1 skrll KASSERT(iclk->type == IMX6_CLK_PFD); 310 1.1 skrll 311 1.3 bouyer parent = imx6_clk_find(sc, iclk->parent); 312 1.1 skrll KASSERT(parent != NULL); 313 1.1 skrll 314 1.1 skrll uint64_t rate_parent = imxccm_clk_get_rate(sc, &parent->base); 315 1.1 skrll 316 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, sc->sc_ioh_analog, pfd->reg); 317 1.1 skrll uint32_t n = __SHIFTOUT(v, __BITS(5, 0) << (pfd->index * 8)); 318 1.1 skrll 319 1.1 skrll KASSERT(n != 0); 320 1.1 skrll 321 1.1 skrll return (rate_parent * 18) / n; 322 1.1 skrll } 323 1.1 skrll 324 1.1 skrll static int 325 1.1 skrll imxccm_clk_mux_wait(struct imx6ccm_softc *sc, struct imx6_clk_mux *mux) 326 1.1 skrll { 327 1.1 skrll KASSERT(mux->busy_reg == 0); 328 1.1 skrll KASSERT(mux->busy_mask == 0); 329 1.1 skrll 330 1.1 skrll bus_space_handle_t ioh; 331 1.1 skrll if (mux->base == IMX6_CLK_REG_CCM_ANALOG) 332 1.1 skrll ioh = sc->sc_ioh_analog; 333 1.1 skrll else 334 1.1 skrll ioh = sc->sc_ioh; 335 1.1 skrll 336 1.1 skrll while (bus_space_read_4(sc->sc_iot, ioh, mux->busy_reg) & mux->busy_mask) 337 1.1 skrll delay(10); 338 1.1 skrll 339 1.1 skrll return 0; 340 1.1 skrll } 341 1.1 skrll 342 1.1 skrll static int 343 1.1 skrll imxccm_clk_set_parent_mux(struct imx6ccm_softc *sc, 344 1.1 skrll struct imx6_clk *iclk, struct clk *parent) 345 1.1 skrll { 346 1.1 skrll struct imx6_clk_mux *mux = &iclk->clk.mux; 347 1.1 skrll const char *pname = parent->name; 348 1.1 skrll u_int sel; 349 1.1 skrll 350 1.1 skrll KASSERT(iclk->type == IMX6_CLK_MUX); 351 1.1 skrll 352 1.1 skrll for (sel = 0; sel < mux->nparents; sel++) 353 1.1 skrll if (strcmp(pname, mux->parents[sel]) == 0) 354 1.1 skrll break; 355 1.1 skrll 356 1.1 skrll if (sel == mux->nparents) 357 1.1 skrll return EINVAL; 358 1.1 skrll 359 1.1 skrll bus_space_handle_t ioh; 360 1.1 skrll if (mux->base == IMX6_CLK_REG_CCM_ANALOG) 361 1.1 skrll ioh = sc->sc_ioh_analog; 362 1.1 skrll else 363 1.1 skrll ioh = sc->sc_ioh; 364 1.1 skrll 365 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, mux->reg); 366 1.1 skrll v &= ~mux->mask; 367 1.1 skrll v |= __SHIFTIN(sel, mux->mask); 368 1.1 skrll 369 1.1 skrll bus_space_write_4(sc->sc_iot, ioh, mux->reg, v); 370 1.1 skrll 371 1.1 skrll iclk->parent = pname; 372 1.1 skrll 373 1.1 skrll if (mux->type == IMX6_CLK_MUX_BUSY) 374 1.1 skrll imxccm_clk_mux_wait(sc, mux); 375 1.1 skrll 376 1.1 skrll return 0; 377 1.1 skrll } 378 1.1 skrll 379 1.1 skrll static struct imx6_clk * 380 1.1 skrll imxccm_clk_get_parent_mux(struct imx6ccm_softc *sc, struct imx6_clk *iclk) 381 1.1 skrll { 382 1.1 skrll struct imx6_clk_mux *mux = &iclk->clk.mux; 383 1.1 skrll 384 1.1 skrll KASSERT(iclk->type == IMX6_CLK_MUX); 385 1.1 skrll 386 1.1 skrll bus_space_handle_t ioh; 387 1.1 skrll if (mux->base == IMX6_CLK_REG_CCM_ANALOG) 388 1.1 skrll ioh = sc->sc_ioh_analog; 389 1.1 skrll else 390 1.1 skrll ioh = sc->sc_ioh; 391 1.1 skrll 392 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, mux->reg); 393 1.1 skrll u_int sel = __SHIFTOUT(v, mux->mask); 394 1.1 skrll 395 1.4 bouyer KASSERTMSG(sel < mux->nparents, "mux %s sel %d nparents %d", 396 1.4 bouyer iclk->base.name, sel, mux->nparents); 397 1.1 skrll 398 1.1 skrll iclk->parent = mux->parents[sel]; 399 1.1 skrll 400 1.3 bouyer return imx6_clk_find(sc, iclk->parent); 401 1.1 skrll } 402 1.1 skrll 403 1.1 skrll static int 404 1.1 skrll imxccm_clk_set_rate_pll(struct imx6ccm_softc *sc, 405 1.1 skrll struct imx6_clk *iclk, u_int rate) 406 1.1 skrll { 407 1.1 skrll /* ToDo */ 408 1.1 skrll 409 1.1 skrll return EOPNOTSUPP; 410 1.1 skrll } 411 1.1 skrll 412 1.1 skrll static int 413 1.1 skrll imxccm_clk_set_rate_div(struct imx6ccm_softc *sc, 414 1.1 skrll struct imx6_clk *iclk, u_int rate) 415 1.1 skrll { 416 1.1 skrll struct imx6_clk_div *div = &iclk->clk.div; 417 1.1 skrll struct imx6_clk *parent; 418 1.1 skrll 419 1.1 skrll KASSERT(iclk->type == IMX6_CLK_DIV); 420 1.1 skrll 421 1.3 bouyer parent = imx6_clk_find(sc, iclk->parent); 422 1.1 skrll KASSERT(parent != NULL); 423 1.1 skrll 424 1.1 skrll u_int rate_parent = imxccm_clk_get_rate(sc, &parent->base); 425 1.1 skrll u_int divider = uimax(1, rate_parent / rate); 426 1.1 skrll 427 1.1 skrll bus_space_handle_t ioh; 428 1.1 skrll if (div->base == IMX6_CLK_REG_CCM_ANALOG) 429 1.1 skrll ioh = sc->sc_ioh_analog; 430 1.1 skrll else 431 1.1 skrll ioh = sc->sc_ioh; 432 1.1 skrll 433 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, div->reg); 434 1.1 skrll v &= ~div->mask; 435 1.1 skrll if (div->type == IMX6_CLK_DIV_TABLE) { 436 1.1 skrll int n = -1; 437 1.1 skrll 438 1.1 skrll KASSERT(div->tbl != NULL); 439 1.1 skrll for (int i = 0; div->tbl[i] != 0; i++) 440 1.1 skrll if (div->tbl[i] == divider) 441 1.1 skrll n = i; 442 1.1 skrll 443 1.1 skrll if (n >= 0) 444 1.1 skrll v |= __SHIFTIN(n, div->mask); 445 1.1 skrll else 446 1.1 skrll return EINVAL; 447 1.1 skrll } else { 448 1.1 skrll v |= __SHIFTIN(divider - 1, div->mask); 449 1.1 skrll } 450 1.1 skrll bus_space_write_4(sc->sc_iot, ioh, div->reg, v); 451 1.1 skrll 452 1.1 skrll return 0; 453 1.1 skrll } 454 1.1 skrll 455 1.1 skrll /* 456 1.1 skrll * CLK Driver APIs 457 1.1 skrll */ 458 1.1 skrll static struct clk * 459 1.1 skrll imxccm_clk_get(void *priv, const char *name) 460 1.1 skrll { 461 1.1 skrll struct imx6_clk *iclk; 462 1.3 bouyer struct imx6ccm_softc *sc = priv; 463 1.1 skrll 464 1.3 bouyer iclk = imx6_clk_find(sc, name); 465 1.1 skrll if (iclk == NULL) 466 1.1 skrll return NULL; 467 1.1 skrll 468 1.1 skrll atomic_inc_uint(&iclk->refcnt); 469 1.1 skrll 470 1.1 skrll return &iclk->base; 471 1.1 skrll } 472 1.1 skrll 473 1.1 skrll static void 474 1.1 skrll imxccm_clk_put(void *priv, struct clk *clk) 475 1.1 skrll { 476 1.1 skrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 477 1.1 skrll 478 1.1 skrll KASSERT(iclk->refcnt > 0); 479 1.1 skrll 480 1.1 skrll atomic_dec_uint(&iclk->refcnt); 481 1.1 skrll } 482 1.1 skrll 483 1.1 skrll static u_int 484 1.1 skrll imxccm_clk_get_rate(void *priv, struct clk *clk) 485 1.1 skrll { 486 1.1 skrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 487 1.1 skrll struct clk *parent; 488 1.1 skrll struct imx6ccm_softc *sc = priv; 489 1.1 skrll 490 1.1 skrll switch (iclk->type) { 491 1.1 skrll case IMX6_CLK_FIXED: 492 1.1 skrll return iclk->clk.fixed.rate; 493 1.1 skrll case IMX6_CLK_FIXED_FACTOR: 494 1.1 skrll return imxccm_clk_get_rate_fixed_factor(sc, iclk); 495 1.1 skrll case IMX6_CLK_PLL: 496 1.1 skrll return imxccm_clk_get_rate_pll(sc, iclk); 497 1.1 skrll case IMX6_CLK_MUX: 498 1.1 skrll case IMX6_CLK_GATE: 499 1.1 skrll parent = imxccm_clk_get_parent(sc, clk); 500 1.1 skrll return imxccm_clk_get_rate(sc, parent); 501 1.1 skrll case IMX6_CLK_DIV: 502 1.1 skrll return imxccm_clk_get_rate_div(sc, iclk); 503 1.1 skrll case IMX6_CLK_PFD: 504 1.1 skrll return imxccm_clk_get_rate_pfd(sc, iclk); 505 1.1 skrll default: 506 1.1 skrll panic("imx6: unknown clk type %d", iclk->type); 507 1.1 skrll } 508 1.1 skrll } 509 1.1 skrll 510 1.1 skrll static int 511 1.1 skrll imxccm_clk_set_rate(void *priv, struct clk *clk, u_int rate) 512 1.1 skrll { 513 1.1 skrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 514 1.1 skrll struct imx6ccm_softc *sc = priv; 515 1.1 skrll 516 1.1 skrll switch (iclk->type) { 517 1.1 skrll case IMX6_CLK_FIXED: 518 1.1 skrll case IMX6_CLK_FIXED_FACTOR: 519 1.1 skrll return ENXIO; 520 1.1 skrll case IMX6_CLK_PLL: 521 1.1 skrll return imxccm_clk_set_rate_pll(sc, iclk, rate); 522 1.1 skrll case IMX6_CLK_MUX: 523 1.1 skrll return ENXIO; 524 1.1 skrll case IMX6_CLK_GATE: 525 1.1 skrll return ENXIO; 526 1.1 skrll case IMX6_CLK_DIV: 527 1.1 skrll return imxccm_clk_set_rate_div(sc, iclk, rate); 528 1.1 skrll case IMX6_CLK_PFD: 529 1.1 skrll return EINVAL; 530 1.1 skrll default: 531 1.1 skrll panic("imx6: unknown clk type %d", iclk->type); 532 1.1 skrll } 533 1.1 skrll } 534 1.1 skrll 535 1.1 skrll static int 536 1.1 skrll imxccm_clk_enable_pll(struct imx6ccm_softc *sc, struct imx6_clk *iclk, bool enable) 537 1.1 skrll { 538 1.1 skrll struct imx6_clk_pll *pll = &iclk->clk.pll; 539 1.1 skrll 540 1.1 skrll KASSERT(iclk->type == IMX6_CLK_PLL); 541 1.1 skrll 542 1.1 skrll /* Power up bit */ 543 1.1 skrll if (pll->type == IMX6_CLK_PLL_USB) 544 1.1 skrll enable = !enable; 545 1.1 skrll 546 1.1 skrll bus_space_handle_t ioh = sc->sc_ioh_analog; 547 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, pll->reg); 548 1.1 skrll if (__SHIFTOUT(v, pll->powerdown) != enable) 549 1.1 skrll return 0; 550 1.1 skrll if (enable) 551 1.1 skrll v &= ~pll->powerdown; 552 1.1 skrll else 553 1.1 skrll v |= pll->powerdown; 554 1.1 skrll bus_space_write_4(sc->sc_iot, ioh, pll->reg, v); 555 1.1 skrll 556 1.1 skrll /* wait look */ 557 1.1 skrll while (!(bus_space_read_4(sc->sc_iot, ioh, pll->reg) & CCM_ANALOG_PLL_LOCK)) 558 1.1 skrll delay(10); 559 1.1 skrll 560 1.1 skrll return 0; 561 1.1 skrll } 562 1.1 skrll 563 1.1 skrll static int 564 1.1 skrll imxccm_clk_enable_gate(struct imx6ccm_softc *sc, struct imx6_clk *iclk, bool enable) 565 1.1 skrll { 566 1.1 skrll struct imx6_clk_gate *gate = &iclk->clk.gate; 567 1.1 skrll 568 1.1 skrll KASSERT(iclk->type == IMX6_CLK_GATE); 569 1.1 skrll 570 1.1 skrll bus_space_handle_t ioh; 571 1.1 skrll if (gate->base == IMX6_CLK_REG_CCM_ANALOG) 572 1.1 skrll ioh = sc->sc_ioh_analog; 573 1.1 skrll else 574 1.1 skrll ioh = sc->sc_ioh; 575 1.1 skrll 576 1.1 skrll uint32_t v = bus_space_read_4(sc->sc_iot, ioh, gate->reg); 577 1.1 skrll if (enable) { 578 1.1 skrll if (gate->exclusive_mask) 579 1.1 skrll v &= ~gate->exclusive_mask; 580 1.1 skrll v |= gate->mask; 581 1.1 skrll } else { 582 1.1 skrll if (gate->exclusive_mask) 583 1.1 skrll v |= gate->exclusive_mask; 584 1.1 skrll v &= ~gate->mask; 585 1.1 skrll } 586 1.1 skrll bus_space_write_4(sc->sc_iot, ioh, gate->reg, v); 587 1.1 skrll 588 1.1 skrll return 0; 589 1.1 skrll } 590 1.1 skrll 591 1.1 skrll static int 592 1.1 skrll imxccm_clk_enable(void *priv, struct clk *clk) 593 1.1 skrll { 594 1.1 skrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 595 1.1 skrll struct imx6_clk *parent = NULL; 596 1.1 skrll struct imx6ccm_softc *sc = priv; 597 1.1 skrll 598 1.3 bouyer if ((parent = imx6_clk_find(sc, iclk->parent)) != NULL) 599 1.1 skrll imxccm_clk_enable(sc, &parent->base); 600 1.1 skrll 601 1.1 skrll switch (iclk->type) { 602 1.1 skrll case IMX6_CLK_FIXED: 603 1.1 skrll case IMX6_CLK_FIXED_FACTOR: 604 1.1 skrll return 0; /* always on */ 605 1.1 skrll case IMX6_CLK_PLL: 606 1.1 skrll return imxccm_clk_enable_pll(sc, iclk, true); 607 1.1 skrll case IMX6_CLK_MUX: 608 1.1 skrll case IMX6_CLK_DIV: 609 1.1 skrll case IMX6_CLK_PFD: 610 1.1 skrll return 0; 611 1.1 skrll case IMX6_CLK_GATE: 612 1.1 skrll return imxccm_clk_enable_gate(sc, iclk, true); 613 1.1 skrll default: 614 1.1 skrll panic("imx6: unknown clk type %d", iclk->type); 615 1.1 skrll } 616 1.1 skrll } 617 1.1 skrll 618 1.1 skrll static int 619 1.1 skrll imxccm_clk_disable(void *priv, struct clk *clk) 620 1.1 skrll { 621 1.1 skrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 622 1.1 skrll struct imx6ccm_softc *sc = priv; 623 1.1 skrll 624 1.1 skrll switch (iclk->type) { 625 1.1 skrll case IMX6_CLK_FIXED: 626 1.1 skrll case IMX6_CLK_FIXED_FACTOR: 627 1.1 skrll return EINVAL; /* always on */ 628 1.1 skrll case IMX6_CLK_PLL: 629 1.1 skrll return imxccm_clk_enable_pll(sc, iclk, false); 630 1.1 skrll case IMX6_CLK_MUX: 631 1.1 skrll case IMX6_CLK_DIV: 632 1.1 skrll case IMX6_CLK_PFD: 633 1.1 skrll return EINVAL; 634 1.1 skrll case IMX6_CLK_GATE: 635 1.1 skrll return imxccm_clk_enable_gate(sc, iclk, false); 636 1.1 skrll default: 637 1.1 skrll panic("imx6: unknown clk type %d", iclk->type); 638 1.1 skrll } 639 1.1 skrll } 640 1.1 skrll 641 1.1 skrll static int 642 1.1 skrll imxccm_clk_set_parent(void *priv, struct clk *clk, struct clk *parent) 643 1.1 skrll { 644 1.1 skrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 645 1.1 skrll struct imx6ccm_softc *sc = priv; 646 1.1 skrll 647 1.1 skrll switch (iclk->type) { 648 1.1 skrll case IMX6_CLK_FIXED: 649 1.1 skrll case IMX6_CLK_FIXED_FACTOR: 650 1.1 skrll case IMX6_CLK_PLL: 651 1.1 skrll case IMX6_CLK_GATE: 652 1.1 skrll case IMX6_CLK_DIV: 653 1.1 skrll case IMX6_CLK_PFD: 654 1.1 skrll return EINVAL; 655 1.1 skrll case IMX6_CLK_MUX: 656 1.1 skrll return imxccm_clk_set_parent_mux(sc, iclk, parent); 657 1.1 skrll default: 658 1.1 skrll panic("imx6: unknown clk type %d", iclk->type); 659 1.1 skrll } 660 1.1 skrll } 661 1.1 skrll 662 1.1 skrll static struct clk * 663 1.1 skrll imxccm_clk_get_parent(void *priv, struct clk *clk) 664 1.1 skrll { 665 1.1 skrll struct imx6_clk *iclk = (struct imx6_clk *)clk; 666 1.1 skrll struct imx6_clk *parent = NULL; 667 1.1 skrll struct imx6ccm_softc *sc = priv; 668 1.1 skrll 669 1.1 skrll switch (iclk->type) { 670 1.1 skrll case IMX6_CLK_FIXED: 671 1.1 skrll case IMX6_CLK_FIXED_FACTOR: 672 1.1 skrll case IMX6_CLK_PLL: 673 1.1 skrll case IMX6_CLK_GATE: 674 1.1 skrll case IMX6_CLK_DIV: 675 1.1 skrll case IMX6_CLK_PFD: 676 1.1 skrll if (iclk->parent != NULL) 677 1.3 bouyer parent = imx6_clk_find(sc, iclk->parent); 678 1.1 skrll break; 679 1.1 skrll case IMX6_CLK_MUX: 680 1.1 skrll parent = imxccm_clk_get_parent_mux(sc, iclk); 681 1.1 skrll break; 682 1.1 skrll default: 683 1.1 skrll panic("imx6: unknown clk type %d", iclk->type); 684 1.1 skrll } 685 1.1 skrll 686 1.1 skrll return (struct clk *)parent; 687 1.1 skrll } 688