1 /* $NetBSD: imx_ccm.c,v 1.1 2020/12/23 14:42:38 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: imx_ccm.c,v 1.1 2020/12/23 14:42:38 skrll Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/cpu.h> 35 #include <sys/device.h> 36 37 #include <dev/fdt/fdtvar.h> 38 39 #include <dev/clk/clk_backend.h> 40 41 #include <arm/nxp/imx_ccm.h> 42 43 static struct clk * 44 imx_ccm_clock_decode(device_t dev, int cc_phandle, const void *data, size_t len) 45 { 46 struct imx_ccm_softc * const sc = device_private(dev); 47 struct imx_ccm_clk *clk; 48 49 if (len != 4) 50 return NULL; 51 52 const u_int clock_id = be32dec(data); 53 54 for (int i = 0; i < sc->sc_nclks; i++) { 55 clk = &sc->sc_clks[i]; 56 if (clk->id == clock_id) 57 return &clk->base; 58 } 59 60 return NULL; 61 } 62 63 static const struct fdtbus_clock_controller_func imx_ccm_fdtclock_funcs = { 64 .decode = imx_ccm_clock_decode, 65 }; 66 67 static struct clk * 68 imx_ccm_clock_get(void *priv, const char *name) 69 { 70 struct imx_ccm_softc * const sc = priv; 71 struct imx_ccm_clk *clk; 72 73 clk = imx_ccm_clock_find(sc, name); 74 if (clk == NULL) 75 return NULL; 76 77 return &clk->base; 78 } 79 80 static void 81 imx_ccm_clock_put(void *priv, struct clk *clk) 82 { 83 } 84 85 static u_int 86 imx_ccm_clock_get_rate(void *priv, struct clk *clkp) 87 { 88 struct imx_ccm_softc * const sc = priv; 89 struct imx_ccm_clk *clk = (struct imx_ccm_clk *)clkp; 90 struct clk *clkp_parent; 91 92 if (clk->get_rate) 93 return clk->get_rate(sc, clk); 94 95 clkp_parent = clk_get_parent(clkp); 96 if (clkp_parent == NULL) { 97 aprint_debug("%s: no parent for %s\n", __func__, clk->base.name); 98 return 0; 99 } 100 101 return clk_get_rate(clkp_parent); 102 } 103 104 static int 105 imx_ccm_clock_set_rate(void *priv, struct clk *clkp, u_int rate) 106 { 107 struct imx_ccm_softc * const sc = priv; 108 struct imx_ccm_clk *clk = (struct imx_ccm_clk *)clkp; 109 struct clk *clkp_parent; 110 111 if (clkp->flags & CLK_SET_RATE_PARENT) { 112 clkp_parent = clk_get_parent(clkp); 113 if (clkp_parent == NULL) { 114 aprint_error("%s: no parent for %s\n", __func__, clk->base.name); 115 return ENXIO; 116 } 117 return clk_set_rate(clkp_parent, rate); 118 } 119 120 if (clk->set_rate) 121 return clk->set_rate(sc, clk, rate); 122 123 return ENXIO; 124 } 125 126 static u_int 127 imx_ccm_clock_round_rate(void *priv, struct clk *clkp, u_int rate) 128 { 129 struct imx_ccm_softc * const sc = priv; 130 struct imx_ccm_clk *clk = (struct imx_ccm_clk *)clkp; 131 struct clk *clkp_parent; 132 133 if (clkp->flags & CLK_SET_RATE_PARENT) { 134 clkp_parent = clk_get_parent(clkp); 135 if (clkp_parent == NULL) { 136 aprint_error("%s: no parent for %s\n", __func__, clk->base.name); 137 return 0; 138 } 139 return clk_round_rate(clkp_parent, rate); 140 } 141 142 if (clk->round_rate) 143 return clk->round_rate(sc, clk, rate); 144 145 return 0; 146 } 147 148 static int 149 imx_ccm_clock_enable(void *priv, struct clk *clkp) 150 { 151 struct imx_ccm_softc * const sc = priv; 152 struct imx_ccm_clk *clk = (struct imx_ccm_clk *)clkp; 153 struct clk *clkp_parent; 154 int error = 0; 155 156 clkp_parent = clk_get_parent(clkp); 157 if (clkp_parent != NULL) { 158 error = clk_enable(clkp_parent); 159 if (error != 0) 160 return error; 161 } 162 163 if (clk->enable) 164 error = clk->enable(sc, clk, 1); 165 166 return error; 167 } 168 169 static int 170 imx_ccm_clock_disable(void *priv, struct clk *clkp) 171 { 172 struct imx_ccm_softc * const sc = priv; 173 struct imx_ccm_clk *clk = (struct imx_ccm_clk *)clkp; 174 int error = EINVAL; 175 176 if (clk->enable) 177 error = clk->enable(sc, clk, 0); 178 179 return error; 180 } 181 182 static int 183 imx_ccm_clock_set_parent(void *priv, struct clk *clkp, 184 struct clk *clkp_parent) 185 { 186 struct imx_ccm_softc * const sc = priv; 187 struct imx_ccm_clk *clk = (struct imx_ccm_clk *)clkp; 188 189 if (clk->set_parent == NULL) 190 return EINVAL; 191 192 return clk->set_parent(sc, clk, clkp_parent->name); 193 } 194 195 static struct clk * 196 imx_ccm_clock_get_parent(void *priv, struct clk *clkp) 197 { 198 struct imx_ccm_softc * const sc = priv; 199 struct imx_ccm_clk *clk = (struct imx_ccm_clk *)clkp; 200 struct imx_ccm_clk *clk_parent; 201 const char *parent; 202 203 if (clk->get_parent == NULL) 204 return NULL; 205 206 parent = clk->get_parent(sc, clk); 207 if (parent == NULL) 208 return NULL; 209 210 clk_parent = imx_ccm_clock_find(sc, parent); 211 if (clk_parent != NULL) 212 return &clk_parent->base; 213 214 /* No parent in this domain, try FDT */ 215 return fdtbus_clock_byname(parent); 216 } 217 218 const struct clk_funcs imx_ccm_clock_funcs = { 219 .get = imx_ccm_clock_get, 220 .put = imx_ccm_clock_put, 221 .get_rate = imx_ccm_clock_get_rate, 222 .set_rate = imx_ccm_clock_set_rate, 223 .round_rate = imx_ccm_clock_round_rate, 224 .enable = imx_ccm_clock_enable, 225 .disable = imx_ccm_clock_disable, 226 .set_parent = imx_ccm_clock_set_parent, 227 .get_parent = imx_ccm_clock_get_parent, 228 }; 229 230 struct imx_ccm_clk * 231 imx_ccm_clock_find(struct imx_ccm_softc *sc, const char *name) 232 { 233 for (int i = 0; i < sc->sc_nclks; i++) { 234 if (sc->sc_clks[i].base.name == NULL) 235 continue; 236 if (strcmp(sc->sc_clks[i].base.name, name) == 0) 237 return &sc->sc_clks[i]; 238 } 239 240 return NULL; 241 } 242 243 int 244 imx_ccm_attach(struct imx_ccm_softc *sc) 245 { 246 bus_addr_t addr; 247 bus_size_t size; 248 int i; 249 250 if (fdtbus_get_reg(sc->sc_phandle, 0, &addr, &size) != 0) { 251 aprint_error(": couldn't get registers\n"); 252 return ENXIO; 253 } 254 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh[0]) != 0) { 255 aprint_error(": couldn't map registers\n"); 256 return ENXIO; 257 } 258 259 sc->sc_clkdom.name = device_xname(sc->sc_dev); 260 sc->sc_clkdom.funcs = &imx_ccm_clock_funcs; 261 sc->sc_clkdom.priv = sc; 262 for (i = 0; i < sc->sc_nclks; i++) { 263 sc->sc_clks[i].base.domain = &sc->sc_clkdom; 264 clk_attach(&sc->sc_clks[i].base); 265 } 266 267 fdtbus_register_clock_controller(sc->sc_dev, sc->sc_phandle, 268 &imx_ccm_fdtclock_funcs); 269 270 return 0; 271 } 272 273 void 274 imx_ccm_print(struct imx_ccm_softc *sc) 275 { 276 struct imx_ccm_clk *clk; 277 struct clk *clkp_parent; 278 const char *type; 279 int i; 280 281 for (i = 0; i < sc->sc_nclks; i++) { 282 clk = &sc->sc_clks[i]; 283 if (clk->type == IMX_CCM_UNKNOWN) 284 continue; 285 286 clkp_parent = clk_get_parent(&clk->base); 287 288 switch (clk->type) { 289 case IMX_CCM_EXTCLK: type = "extclk"; break; 290 case IMX_CCM_GATE: type = "gate"; break; 291 case IMX_CCM_COMPOSITE: type = "comp"; break; 292 case IMX_CCM_PLL: type = "pll"; break; 293 case IMX_CCM_FIXED: type = "fixed"; break; 294 case IMX_CCM_FIXED_FACTOR: type = "fixed-factor"; break; 295 case IMX_CCM_MUX: type = "mux"; break; 296 case IMX_CCM_DIV: type = "div"; break; 297 default: type = "???"; break; 298 } 299 300 aprint_debug_dev(sc->sc_dev, 301 "%3d %-14s %2s %-14s %-7s ", 302 clk->id, 303 clk->base.name, 304 clkp_parent ? "<-" : "", 305 clkp_parent ? clkp_parent->name : "", 306 type); 307 aprint_debug("%10d Hz\n", clk_get_rate(&clk->base)); 308 } 309 } 310