1 /* $NetBSD: ti_prcm.c,v 1.4 2020/06/03 14:56:09 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill (at) 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: ti_prcm.c,v 1.4 2020/06/03 14:56:09 jmcneill 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 #define TI_PRCM_PRIVATE 42 #include <arm/ti/ti_prcm.h> 43 44 static struct ti_prcm_softc *prcm_softc = NULL; 45 46 static struct clk * 47 ti_prcm_clock_get(void *priv, const char *name) 48 { 49 struct ti_prcm_softc * const sc = priv; 50 struct ti_prcm_clk *clk; 51 52 clk = ti_prcm_clock_find(sc, name); 53 if (clk == NULL) 54 return NULL; 55 56 return &clk->base; 57 } 58 59 static void 60 ti_prcm_clock_put(void *priv, struct clk *clk) 61 { 62 } 63 64 static u_int 65 ti_prcm_clock_get_rate(void *priv, struct clk *clkp) 66 { 67 struct ti_prcm_softc * const sc = priv; 68 struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp; 69 struct clk *clkp_parent; 70 71 if (clk->get_rate) 72 return clk->get_rate(sc, clk); 73 74 clkp_parent = clk_get_parent(clkp); 75 if (clkp_parent == NULL) { 76 aprint_error("%s: no parent for %s\n", __func__, clk->base.name); 77 return 0; 78 } 79 80 return clk_get_rate(clkp_parent); 81 } 82 83 static int 84 ti_prcm_clock_set_rate(void *priv, struct clk *clkp, u_int rate) 85 { 86 struct ti_prcm_softc * const sc = priv; 87 struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp; 88 struct clk *clkp_parent; 89 90 if (clkp->flags & CLK_SET_RATE_PARENT) { 91 clkp_parent = clk_get_parent(clkp); 92 if (clkp_parent == NULL) { 93 aprint_error("%s: no parent for %s\n", __func__, clk->base.name); 94 return ENXIO; 95 } 96 return clk_set_rate(clkp_parent, rate); 97 } 98 99 if (clk->set_rate) 100 return clk->set_rate(sc, clk, rate); 101 102 return ENXIO; 103 } 104 105 static int 106 ti_prcm_clock_enable(void *priv, struct clk *clkp) 107 { 108 struct ti_prcm_softc * const sc = priv; 109 struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp; 110 struct clk *clkp_parent; 111 int error = 0; 112 113 clkp_parent = clk_get_parent(clkp); 114 if (clkp_parent != NULL) { 115 error = clk_enable(clkp_parent); 116 if (error != 0) 117 return error; 118 } 119 120 if (clk->enable) 121 error = clk->enable(sc, clk, 1); 122 123 return error; 124 } 125 126 static int 127 ti_prcm_clock_disable(void *priv, struct clk *clkp) 128 { 129 struct ti_prcm_softc * const sc = priv; 130 struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp; 131 int error = EINVAL; 132 133 if (clk->enable) 134 error = clk->enable(sc, clk, 0); 135 136 return error; 137 } 138 139 static int 140 ti_prcm_clock_set_parent(void *priv, struct clk *clkp, 141 struct clk *clkp_parent) 142 { 143 struct ti_prcm_softc * const sc = priv; 144 struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp; 145 146 if (clk->set_parent == NULL) 147 return EINVAL; 148 149 return clk->set_parent(sc, clk, clkp_parent->name); 150 } 151 152 static struct clk * 153 ti_prcm_clock_get_parent(void *priv, struct clk *clkp) 154 { 155 struct ti_prcm_softc * const sc = priv; 156 struct ti_prcm_clk *clk = (struct ti_prcm_clk *)clkp; 157 struct ti_prcm_clk *clk_parent; 158 const char *parent; 159 160 if (clk->get_parent == NULL) 161 return NULL; 162 163 parent = clk->get_parent(sc, clk); 164 if (parent == NULL) 165 return NULL; 166 167 clk_parent = ti_prcm_clock_find(sc, parent); 168 if (clk_parent != NULL) 169 return &clk_parent->base; 170 171 /* No parent in this domain, try FDT */ 172 return fdtbus_clock_get(sc->sc_phandle, parent); 173 } 174 175 static const struct clk_funcs ti_prcm_clock_funcs = { 176 .get = ti_prcm_clock_get, 177 .put = ti_prcm_clock_put, 178 .get_rate = ti_prcm_clock_get_rate, 179 .set_rate = ti_prcm_clock_set_rate, 180 .enable = ti_prcm_clock_enable, 181 .disable = ti_prcm_clock_disable, 182 .set_parent = ti_prcm_clock_set_parent, 183 .get_parent = ti_prcm_clock_get_parent, 184 }; 185 186 struct ti_prcm_clk * 187 ti_prcm_clock_find(struct ti_prcm_softc *sc, const char *name) 188 { 189 for (int i = 0; i < sc->sc_nclks; i++) { 190 if (sc->sc_clks[i].base.name == NULL) 191 continue; 192 if (strcmp(sc->sc_clks[i].base.name, name) == 0) 193 return &sc->sc_clks[i]; 194 } 195 196 return NULL; 197 } 198 199 int 200 ti_prcm_attach(struct ti_prcm_softc *sc) 201 { 202 bus_addr_t addr; 203 bus_size_t size; 204 int i; 205 206 if (fdtbus_get_reg(sc->sc_phandle, 0, &addr, &size) != 0) { 207 aprint_error(": couldn't get registers\n"); 208 return ENXIO; 209 } 210 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 211 aprint_error(": couldn't map registers\n"); 212 return ENXIO; 213 } 214 215 sc->sc_clkdom.funcs = &ti_prcm_clock_funcs; 216 sc->sc_clkdom.priv = sc; 217 for (i = 0; i < sc->sc_nclks; i++) 218 sc->sc_clks[i].base.domain = &sc->sc_clkdom; 219 220 KASSERT(prcm_softc == NULL); 221 prcm_softc = sc; 222 223 return 0; 224 } 225 226 struct clk * 227 ti_prcm_get_hwmod(const int phandle, u_int index) 228 { 229 struct ti_prcm_clk *tc; 230 const char *hwmods, *p; 231 int len, resid, hwmod_phandle; 232 u_int n; 233 234 KASSERTMSG(prcm_softc != NULL, "prcm driver not attached"); 235 236 /* If this node does not have a ti,hwmods property, try the parent */ 237 hwmod_phandle = of_hasprop(phandle, "ti,hwmods") ? phandle : OF_parent(phandle); 238 239 /* Lookup clock by "ti,hwmods" name (old method) */ 240 hwmods = fdtbus_get_prop(hwmod_phandle, "ti,hwmods", &len); 241 if (len > 0) { 242 p = hwmods; 243 for (n = 0, resid = len; resid > 0; n++) { 244 if (n == index) { 245 tc = ti_prcm_clock_find(prcm_softc, p); 246 if (tc == NULL) { 247 aprint_error_dev(prcm_softc->sc_dev, 248 "no hwmod with name '%s'\n", p); 249 return NULL; 250 } 251 KASSERT(tc->type == TI_PRCM_HWMOD); 252 return &tc->base; 253 } 254 resid -= strlen(p); 255 p += strlen(p) + 1; 256 } 257 } 258 259 /* Find clock resource */ 260 return fdtbus_clock_get_index(hwmod_phandle, index); 261 } 262 263 int 264 ti_prcm_enable_hwmod(const int phandle, u_int index) 265 { 266 struct clk *clk; 267 268 clk = ti_prcm_get_hwmod(phandle, index); 269 if (clk == NULL) 270 return ENOENT; 271 272 return clk_enable(clk); 273 } 274